pagescene: add applyJson + Reset-to-original menu item

Adds Editor.applyJson(jsonString) that delegates to the existing
save_manager.loadCache JSON-deserialize path and additionally clears
the undo stack and emits SceneResetEvent. Used by the new Reset flow
so the user can't "undo" a reset back into their discarded edits.

PageSceneAdapter gains an optional resetToOriginal() hook. When the
host implements it AND a sceneToken is loaded, the File menu shows a
destructive "Reset to original" item that opens a confirm modal and
then defers to the host (which knows where to find the original
snapshot — the webapp's per-token cache lands in a follow-up commit).
Tauri leaves the hook off so nothing changes there.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Tanish MankuandClaude Opus 4.7 committed 2026-05-13 15:18:01 -07:00
1 parent 14ad3d387b
commit a09f1c5cfd
3 files changed
+56

No files matched your search

@@ -209,6 +209,15 @@ export interface PageSceneAdapter {
// is always signed in. Webapp host wires this to its auth modal.
promptSignup?(reason?: string): void;
// Roll the current editor session back to the original scene the
// host loaded. Called when the user confirms the destructive Reset
// modal in the File menu. Implementations typically pull the
// original scene JSON from a host-side cache and feed it back
// through `editor.applyJson(json)`. When undefined the Reset menu
// item is hidden — Tauri leaves it off; webapp wires it to its
// per-token cache's stored `original` snapshot.
resetToOriginal?(): Promise<void> | void;
// Optional event hooks — telemetry, host-side modals, tab title sync.
onSelectionChange?(
sel: { uuid: string; assetType: string } | null,
@@ -284,6 +284,41 @@ export const ControlsTopButtons = () => {
dialogProps: saveAsCopyDialogProps,
},
]),
// Reset-to-original — destructive, only meaningful when a
// sceneToken is in play (URL-loaded scene) AND the host
// implements the reset adapter (the webapp does via its
// per-token cache; Tauri leaves it undefined and the item
// simply doesn't render).
...(sceneMeta.token && editor?.adapter.resetToOriginal
? [
{
label: "Reset to original",
className:
"text-error-400 hover:bg-error-500/15 focus:bg-error-500/15",
divider: true,
dialogProps: {
title: "Reset scene to original?",
content: (
<h4>
This will discard every change you&apos;ve made
in this session and restore the scene to the
version the author shared. This cannot be
undone.
</h4>
),
confirmButtonProps: {
label: "Reset",
variant: "destructive" as const,
onClick: async () => {
await editor.adapter.resetToOriginal?.();
},
},
closeButtonProps: { label: "Cancel" },
showClose: true,
},
},
]
: []),
]}
/>
@@ -770,6 +770,18 @@ class Editor {
this.selection.refreshOutliner();
}
// Replace the active scene with the provided serialized JSON, clear
// the undo stack, and fire SceneResetEvent. Used by the Reset-to-
// original flow so the user can't "undo" the reset back into their
// edits. Delegates to save_manager.loadCache which already owns the
// JSON-string deserialization path used on initial mount.
public async applyJson(jsonString: string) {
await this.save_manager.loadCache(jsonString);
this.history.clear();
this.bus.emit(new SceneResetEvent());
this.selection.refreshOutliner();
}
public async saveScene({
sceneTitle,
sceneToken,