Skip to content

Commit d67ed7c

Browse files
committed
fix: warn before discarding unsaved changes in waveconfig view
Cherry-pick from wavetermdev/waveterm#3099. Adds confirmation dialog when navigating away from unsaved edits in the config editor — both file sidebar and JSON/Visual tab switching.
1 parent 8530294 commit d67ed7c

2 files changed

Lines changed: 23 additions & 1 deletion

File tree

frontend/app/view/waveconfig/waveconfig-model.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,21 @@ export class WaveConfigViewModel implements ViewModel {
274274
return globalStore.get(this.hasEditedAtom);
275275
}
276276

277+
confirmDiscardChanges(): boolean {
278+
if (!this.hasChanges()) {
279+
return true;
280+
}
281+
return window.confirm("You have unsaved changes. Discard and continue?");
282+
}
283+
284+
discardChanges() {
285+
const originalContent = globalStore.get(this.originalContentAtom);
286+
globalStore.set(this.fileContentAtom, originalContent);
287+
globalStore.set(this.hasEditedAtom, false);
288+
globalStore.set(this.validationErrorAtom, null);
289+
globalStore.set(this.errorMessageAtom, null);
290+
}
291+
277292
markAsEdited() {
278293
globalStore.set(this.hasEditedAtom, true);
279294
}

frontend/app/view/waveconfig/waveconfig.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const ConfigSidebar = memo(({ model }: ConfigSidebarProps) => {
2626
const configErrorFiles = useAtomValue(model.configErrorFilesAtom);
2727

2828
const handleFileSelect = (file: ConfigFile) => {
29+
if (selectedFile?.path === file.path) return;
30+
if (!model.confirmDiscardChanges()) return;
2931
model.loadFile(file);
3032
setIsMenuOpen(false);
3133
};
@@ -228,7 +230,11 @@ const WaveConfigView = memo(({ blockId, model }: ViewComponentProps<WaveConfigVi
228230
{selectedFile.visualComponent && selectedFile.hasJsonView && (
229231
<div className="flex gap-0 border-b border-border">
230232
<button
231-
onClick={() => setActiveTab("visual")}
233+
onClick={() => {
234+
if (!model.confirmDiscardChanges()) return;
235+
model.discardChanges();
236+
setActiveTab("visual");
237+
}}
232238
className={cn(
233239
"px-4 pt-1 pb-1.5 cursor-pointer transition-colors text-secondary",
234240
activeTab === "visual"
@@ -238,6 +244,7 @@ const WaveConfigView = memo(({ blockId, model }: ViewComponentProps<WaveConfigVi
238244
>
239245
Visual
240246
</button>
247+
{/* No guard needed: visual tab saves changes immediately via RPC */}
241248
<button
242249
onClick={() => setActiveTab("json")}
243250
className={cn(

0 commit comments

Comments
 (0)