22// SPDX-License-Identifier: Apache-2.0
33
44import { BlockModel } from "@/app/block/block-model" ;
5+ import { Modal } from "@/app/modals/modal" ;
56import { cn , fireAndForget } from "@/util/util" ;
7+ import { useAtomValue } from "jotai" ;
68import { memo , useEffect , useRef , useState } from "react" ;
79import { WaveUIMessagePart } from "./aitypes" ;
810import { WaveAIModel } from "./waveai-model" ;
@@ -127,6 +129,54 @@ const AIToolUseBatch = memo(({ parts, isStreaming }: AIToolUseBatchProps) => {
127129
128130AIToolUseBatch . displayName = "AIToolUseBatch" ;
129131
132+ interface RestoreBackupModalProps {
133+ part : WaveUIMessagePart & { type : "data-tooluse" } ;
134+ }
135+
136+ const RestoreBackupModal = memo ( ( { part } : RestoreBackupModalProps ) => {
137+ const model = WaveAIModel . getInstance ( ) ;
138+ const toolData = part . data ;
139+
140+ const formatTimestamp = ( ts : number ) => {
141+ if ( ! ts ) return "" ;
142+ const date = new Date ( ts ) ;
143+ return date . toLocaleString ( ) ;
144+ } ;
145+
146+ const handleConfirm = ( ) => {
147+ model . restoreBackup ( toolData . toolcallid , toolData . inputfilename ) ;
148+ } ;
149+
150+ const handleCancel = ( ) => {
151+ model . closeRestoreBackupModal ( ) ;
152+ } ;
153+
154+ return (
155+ < Modal
156+ className = "restore-backup-modal pb-5 pr-5"
157+ onClose = { handleCancel }
158+ onCancel = { handleCancel }
159+ onOk = { handleConfirm }
160+ okLabel = "Confirm Restore"
161+ cancelLabel = "Cancel"
162+ >
163+ < div className = "flex flex-col gap-4 pt-4 pb-4 max-w-xl" >
164+ < div className = "font-semibold text-lg" > Restore File Backup</ div >
165+ < div className = "text-sm text-gray-300 leading-relaxed" >
166+ This will restore < span className = "font-mono text-white break-all" > { toolData . inputfilename } </ span > { " " }
167+ to its state before this edit was made
168+ { toolData . runts && < span > ({ formatTimestamp ( toolData . runts ) } )</ span > } .
169+ </ div >
170+ < div className = "text-sm text-gray-300 leading-relaxed" >
171+ Any changes made by this edit and subsequent edits will be lost.
172+ </ div >
173+ </ div >
174+ </ Modal >
175+ ) ;
176+ } ) ;
177+
178+ RestoreBackupModal . displayName = "RestoreBackupModal" ;
179+
130180interface AIToolUseProps {
131181 part : WaveUIMessagePart & { type : "data-tooluse" } ;
132182 isStreaming : boolean ;
@@ -135,6 +185,9 @@ interface AIToolUseProps {
135185const AIToolUse = memo ( ( { part, isStreaming } : AIToolUseProps ) => {
136186 const toolData = part . data ;
137187 const [ userApprovalOverride , setUserApprovalOverride ] = useState < string | null > ( null ) ;
188+ const model = WaveAIModel . getInstance ( ) ;
189+ const restoreModalToolCallId = useAtomValue ( model . restoreBackupModalToolCallId ) ;
190+ const showRestoreModal = restoreModalToolCallId === toolData . toolcallid ;
138191 const highlightTimeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
139192 const highlightedBlockIdRef = useRef < string | null > ( null ) ;
140193
@@ -224,6 +277,16 @@ const AIToolUse = memo(({ part, isStreaming }: AIToolUseProps) => {
224277 < span className = "font-bold" > { statusIcon } </ span >
225278 < div className = "font-semibold" > { toolData . toolname } </ div >
226279 < div className = "flex-1" />
280+ { isFileWriteTool && toolData . inputfilename && toolData . writebackupfilename && (
281+ < button
282+ onClick = { ( ) => model . openRestoreBackupModal ( toolData . toolcallid ) }
283+ className = "flex-shrink-0 px-1.5 py-0.5 border border-gray-600 hover:border-gray-500 hover:bg-gray-700 rounded cursor-pointer transition-colors flex items-center gap-1 text-gray-400"
284+ title = "Restore backup file"
285+ >
286+ < span className = "text-xs" > Restore Backup</ span >
287+ < i className = "fa fa-clock-rotate-left text-xs" > </ i >
288+ </ button >
289+ ) }
227290 { isFileWriteTool && toolData . inputfilename && (
228291 < button
229292 onClick = { handleOpenDiff }
@@ -244,6 +307,7 @@ const AIToolUse = memo(({ part, isStreaming }: AIToolUseProps) => {
244307 < AIToolApprovalButtons count = { 1 } onApprove = { handleApprove } onDeny = { handleDeny } />
245308 </ div >
246309 ) }
310+ { showRestoreModal && < RestoreBackupModal part = { part } /> }
247311 </ div >
248312 ) ;
249313} ) ;
0 commit comments