From bf6c62a0d956143cf79f48cb5e2837b00b782e1e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 07:52:19 +0000 Subject: [PATCH 1/3] feat(configurator): add reset-all confirmation dialog; move save button left - Reset All (trash icon) now shows a confirmation dialog before clearing overrides, reducing accidental data loss - Save button moved to the left of undo/redo icons for quicker access --- .../src/components/shell/StudioHeader.svelte | 99 +++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/configurator/src/components/shell/StudioHeader.svelte b/configurator/src/components/shell/StudioHeader.svelte index 66eb22d7..aa63cb08 100644 --- a/configurator/src/components/shell/StudioHeader.svelte +++ b/configurator/src/components/shell/StudioHeader.svelte @@ -18,6 +18,7 @@ } = $props(); let shareFeedback = $state(false); + let showResetConfirm = $state(false); async function handleShare() { try { @@ -28,6 +29,19 @@ // ignore } } + + function handleResetAllClick() { + showResetConfirm = true; + } + + function confirmReset() { + showResetConfirm = false; + onResetAll(); + } + + function cancelReset() { + showResetConfirm = false; + }
@@ -60,6 +74,31 @@
+ + - -
- -
+ +{#if showResetConfirm} + +{/if} From e1b95c103f9eb771fe409510126b5cfd3dc88ea3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 07:56:48 +0000 Subject: [PATCH 2/3] perf: replace JSON.stringify comparisons with shallow equality for slider responsiveness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JSON.stringify(overrides) was called on every input change — twice in setOverrides and once per tick via the hasPendingChanges derived — serialising potentially hundreds of token values at 60fps. Replaced with a cheap shallow key-value comparison. --- configurator/src/App.svelte | 19 +++++++++++++------ .../src/components/shell/StudioHeader.svelte | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index 5cd8b467..49aca477 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -48,10 +48,17 @@ let canUndo = $derived(past.length > 0); let canRedo = $derived(future.length > 0); + // Shallow equality for flat string records — much cheaper than JSON.stringify on every tick. + function shallowEq(a: Record, b: Record): boolean { + const ak = Object.keys(a); + if (ak.length !== Object.keys(b).length) return false; + return ak.every((k) => a[k] === b[k]); + } + // Save state — hasPendingChanges is derived so undo/redo update it automatically. - let lastSavedSnapshot = $state(JSON.stringify(overrides)); + let lastSavedOverrides = $state>({ ...overrides }); let saveState = $state<'idle' | 'saving' | 'saved'>('idle'); - let hasPendingChanges = $derived(JSON.stringify(overrides) !== lastSavedSnapshot); + let hasPendingChanges = $derived(!shallowEq(overrides, lastSavedOverrides)); let saveStateTimer: ReturnType | null = null; // Live CSS preview on every change — actual persistence only on explicit save. @@ -60,7 +67,7 @@ function setOverrides(updater: ((prev: Record) => Record) | Record) { const prev = overrides; const next = typeof updater === "function" ? updater(prev) : updater; - if (JSON.stringify(prev) !== JSON.stringify(next)) { + if (!shallowEq(prev, next)) { past = [...past.slice(-49), prev]; future = []; if (saveState === 'saved') saveState = 'idle'; @@ -70,13 +77,13 @@ async function handleSave() { if (!hasPendingChanges || saveState === 'saving') return; - const snapshot = JSON.stringify(overrides); + const snapshot = { ...overrides }; saveState = 'saving'; try { await saveOverrides(overrides); // Only mark clean if overrides haven't changed since save started. - if (JSON.stringify(overrides) === snapshot) { - lastSavedSnapshot = snapshot; + if (shallowEq(overrides, snapshot)) { + lastSavedOverrides = snapshot; saveState = 'saved'; if (saveStateTimer) clearTimeout(saveStateTimer); saveStateTimer = setTimeout(() => { diff --git a/configurator/src/components/shell/StudioHeader.svelte b/configurator/src/components/shell/StudioHeader.svelte index aa63cb08..a1068e08 100644 --- a/configurator/src/components/shell/StudioHeader.svelte +++ b/configurator/src/components/shell/StudioHeader.svelte @@ -79,7 +79,7 @@ disabled={!hasPendingChanges || saveState === 'saving'} title={hasPendingChanges ? "Save changes (Ctrl+S)" : "No unsaved changes"} class={[ - "flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[11px] font-bold transition-all cursor-pointer", + "flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[11px] font-bold transition-colors cursor-pointer", saveState === 'saved' ? "bg-emerald-900/40 border border-emerald-500/30 text-emerald-300" : hasPendingChanges From 6f72c950d6f241620ef120377ce948fa1d43d7c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 29 Jun 2026 08:04:28 +0000 Subject: [PATCH 3/3] fix: add focus management to reset confirmation dialog in StudioHeader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auto-focus Cancel button on open, trap Tab between Cancel/Reset-all, Escape dismisses, backdrop click dismisses — consistent with CommandPalette --- .../src/components/shell/StudioHeader.svelte | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/configurator/src/components/shell/StudioHeader.svelte b/configurator/src/components/shell/StudioHeader.svelte index a1068e08..3004fb3c 100644 --- a/configurator/src/components/shell/StudioHeader.svelte +++ b/configurator/src/components/shell/StudioHeader.svelte @@ -1,4 +1,5 @@