diff --git a/frontend/libs/components/pagescene/src/lib/adapter.ts b/frontend/libs/components/pagescene/src/lib/adapter.ts index 3168d3826a..f97ca94b8b 100644 --- a/frontend/libs/components/pagescene/src/lib/adapter.ts +++ b/frontend/libs/components/pagescene/src/lib/adapter.ts @@ -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; + // Optional event hooks — telemetry, host-side modals, tab title sync. onSelectionChange?( sel: { uuid: string; assetType: string } | null, diff --git a/frontend/libs/components/pagescene/src/lib/comps/ControlsTopButtons/ControlsTopButtons.tsx b/frontend/libs/components/pagescene/src/lib/comps/ControlsTopButtons/ControlsTopButtons.tsx index 99b25d2d3d..66943ff227 100644 --- a/frontend/libs/components/pagescene/src/lib/comps/ControlsTopButtons/ControlsTopButtons.tsx +++ b/frontend/libs/components/pagescene/src/lib/comps/ControlsTopButtons/ControlsTopButtons.tsx @@ -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: ( +

+ This will discard every change you've made + in this session and restore the scene to the + version the author shared. This cannot be + undone. +

+ ), + confirmButtonProps: { + label: "Reset", + variant: "destructive" as const, + onClick: async () => { + await editor.adapter.resetToOriginal?.(); + }, + }, + closeButtonProps: { label: "Cancel" }, + showClose: true, + }, + }, + ] + : []), ]} /> diff --git a/frontend/libs/components/pagescene/src/lib/engine/editor.ts b/frontend/libs/components/pagescene/src/lib/engine/editor.ts index 6b63f90b63..fc23807566 100644 --- a/frontend/libs/components/pagescene/src/lib/engine/editor.ts +++ b/frontend/libs/components/pagescene/src/lib/engine/editor.ts @@ -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,