Skip to content

Commit 478c8c4

Browse files
committed
working on file revert modal...
1 parent 02de9a1 commit 478c8c4

5 files changed

Lines changed: 105 additions & 11 deletions

File tree

frontend/app/aipanel/aimessage.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ import { WaveUIMessage, WaveUIMessagePart } from "./aitypes";
1111
import { WaveAIModel } from "./waveai-model";
1212

1313
const AIThinking = memo(
14-
({ message = "AI is thinking...", reasoningText }: { message?: string; reasoningText?: string }) => {
14+
({
15+
message = "AI is thinking...",
16+
reasoningText,
17+
isWaitingApproval = false,
18+
}: {
19+
message?: string;
20+
reasoningText?: string;
21+
isWaitingApproval?: boolean;
22+
}) => {
1523
const scrollRef = useRef<HTMLDivElement>(null);
1624

1725
useEffect(() => {
@@ -30,17 +38,21 @@ const AIThinking = memo(
3038
return (
3139
<div className="flex flex-col gap-1">
3240
<div className="flex items-center gap-2">
33-
<div className="animate-pulse flex items-center">
34-
<i className="fa fa-circle text-[10px]"></i>
35-
<i className="fa fa-circle text-[10px] mx-1"></i>
36-
<i className="fa fa-circle text-[10px]"></i>
37-
</div>
41+
{isWaitingApproval ? (
42+
<i className="fa fa-clock text-base text-yellow-500"></i>
43+
) : (
44+
<div className="animate-pulse flex items-center">
45+
<i className="fa fa-circle text-[10px]"></i>
46+
<i className="fa fa-circle text-[10px] mx-1"></i>
47+
<i className="fa fa-circle text-[10px]"></i>
48+
</div>
49+
)}
3850
{message && <span className="text-sm text-gray-400">{message}</span>}
3951
</div>
4052
{displayText && (
4153
<div
4254
ref={scrollRef}
43-
className="text-sm text-gray-500 overflow-y-auto max-h-[2lh] max-w-[600px] pl-9"
55+
className="text-sm text-gray-500 overflow-y-auto max-h-[3lh] max-w-[600px] pl-9"
4456
>
4557
{displayText}
4658
</div>
@@ -172,7 +184,7 @@ const getThinkingMessage = (
172184
parts: WaveUIMessagePart[],
173185
isStreaming: boolean,
174186
role: string
175-
): { message: string; reasoningText?: string } | null => {
187+
): { message: string; reasoningText?: string; isWaitingApproval?: boolean } | null => {
176188
if (!isStreaming || role !== "assistant") {
177189
return null;
178190
}
@@ -182,7 +194,7 @@ const getThinkingMessage = (
182194
);
183195

184196
if (hasPendingApprovals) {
185-
return { message: "Waiting for Tool Approvals..." };
197+
return { message: "Waiting for Tool Approvals...", isWaitingApproval: true };
186198
}
187199

188200
const lastPart = parts[parts.length - 1];
@@ -234,7 +246,11 @@ export const AIMessage = memo(({ message, isStreaming }: AIMessageProps) => {
234246
)}
235247
{thinkingData != null && (
236248
<div className="mt-2">
237-
<AIThinking message={thinkingData.message} reasoningText={thinkingData.reasoningText} />
249+
<AIThinking
250+
message={thinkingData.message}
251+
reasoningText={thinkingData.reasoningText}
252+
isWaitingApproval={thinkingData.isWaitingApproval}
253+
/>
238254
</div>
239255
)}
240256
</>

frontend/app/aipanel/aitooluse.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { BlockModel } from "@/app/block/block-model";
5+
import { Modal } from "@/app/modals/modal";
56
import { cn, fireAndForget } from "@/util/util";
7+
import { useAtomValue } from "jotai";
68
import { memo, useEffect, useRef, useState } from "react";
79
import { WaveUIMessagePart } from "./aitypes";
810
import { WaveAIModel } from "./waveai-model";
@@ -127,6 +129,54 @@ const AIToolUseBatch = memo(({ parts, isStreaming }: AIToolUseBatchProps) => {
127129

128130
AIToolUseBatch.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+
130180
interface AIToolUseProps {
131181
part: WaveUIMessagePart & { type: "data-tooluse" };
132182
isStreaming: boolean;
@@ -135,6 +185,9 @@ interface AIToolUseProps {
135185
const 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
});

frontend/app/aipanel/aitypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type WaveUIDataTypes = {
2121
errormessage?: string;
2222
approval?: "needs-approval" | "user-approved" | "user-denied" | "auto-approved" | "timeout";
2323
blockid?: string;
24+
writebackupfilename?: string;
2425
inputfilename?: string;
2526
};
2627
};

frontend/app/aipanel/waveai-model.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export class WaveAIModel {
5656
isChatEmpty: boolean = true;
5757
isWaveAIFocusedAtom!: jotai.Atom<boolean>;
5858
panelVisibleAtom!: jotai.Atom<boolean>;
59+
restoreBackupModalToolCallId: jotai.PrimitiveAtom<string | null> = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
5960

6061
private constructor(orefContext: ORef, inBuilder: boolean) {
6162
this.orefContext = orefContext;
@@ -452,4 +453,17 @@ export class WaveAIModel {
452453
};
453454
await createBlock(blockDef, false, true);
454455
}
456+
457+
openRestoreBackupModal(toolcallid: string) {
458+
globalStore.set(this.restoreBackupModalToolCallId, toolcallid);
459+
}
460+
461+
closeRestoreBackupModal() {
462+
globalStore.set(this.restoreBackupModalToolCallId, null);
463+
}
464+
465+
async restoreBackup(toolcallid: string, filename: string) {
466+
console.log("Restore backup called for:", { toolcallid, filename });
467+
this.closeRestoreBackupModal();
468+
}
455469
}

frontend/layout/lib/layoutModel.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,6 @@ export class LayoutModel {
748748
// Process ephemeral node, if present.
749749
const ephemeralNode = this.getter(this.ephemeralNode);
750750
if (ephemeralNode) {
751-
console.log("updateTree ephemeralNode", ephemeralNode);
752751
this.updateEphemeralNodeProps(
753752
ephemeralNode,
754753
newAdditionalProps,

0 commit comments

Comments
 (0)