From bd1ff3fca6da02f1eedc5531d3a19a2e01331b26 Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:45:53 +0000 Subject: [PATCH 1/4] =?UTF-8?q?feat(configurator):=20add=20Changes=20panel?= =?UTF-8?q?=20=E2=80=94=20every=20override=20grouped=20by=20consequence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New central overview (nav → Changes) that answers "what have I actually changed, and does any of it quietly break the system?". Organised by consequence rather than by panel: - Invalid — structurally unsafe, will be dropped on export - Detached — a concrete override on an output / alias / generated scale step; frozen and disconnected from what would produce it - Re-linked — re-pointed at another token instead of a fixed value - Custom — a source value you set (expected/safe) - Not in this build — override keys that aren't tokens in this framework version Each entry reuses TokenRow (role badge, inherits, used-by, Detached/Invalid warning + one-click restore) and deep-links to the owning panel. Scale-shadow callouts surface pinned ladder steps that make a source knob inert, with a "Restore generated scale" bulk action. A per-group "Reset N" and a global "Reset all" round it out; an empty state confirms you're on framework defaults. Model additions in tokenModel.ts: - summarizeChanges(): buckets overrides by consequence (unit-tested) - isStructurallySafe(): the authoritative export-safety gate; tokenState's `invalid` now uses ONLY this, so the "will be dropped on export" copy is truthful. Decoupled from the semantic CSS.supports probe, which wrongly flagged safe values (e.g. a fractional number for a token, via a z-index probe). validateTokenValue keeps the richer probe (fixed → opacity, →z-index) as an advisory signal for the value editor. Wired into SidebarNav (Changes tool with a total-overrides badge), DomainPanel routing and App labels. Tests: 257/257 pass (tokenModel now 39). check + lint clean; screenshot-verified in Chromium. --- configurator/src/App.svelte | 2 +- .../src/components/DomainPanel.svelte | 5 +- .../src/components/panels/ChangesPanel.svelte | 197 ++++++++++++++++++ .../src/components/shell/SidebarNav.svelte | 11 +- configurator/src/lib/tokenModel.ts | 111 +++++++++- configurator/tests/tokenModel.test.ts | 61 +++++- 6 files changed, 371 insertions(+), 16 deletions(-) create mode 100644 configurator/src/components/panels/ChangesPanel.svelte diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index 22d3f0316..41265ac6e 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -20,7 +20,7 @@ home: "Home", colors: "Colors", typography: "Typography", spacing: "Spacing", layout: "Layout", borders: "Borders", shadows: "Shadows", motion: "Motion", effects: "Effects", macros: "Macros", misc: "Misc", components: "Components", - themes: "Themes", wcag: "WCAG", setup: "Install", cheatsheet: "Classes", + changes: "Changes", themes: "Themes", wcag: "WCAG", setup: "Install", cheatsheet: "Classes", }; function overridesByDomain(ov: Record): Record { diff --git a/configurator/src/components/DomainPanel.svelte b/configurator/src/components/DomainPanel.svelte index a638d1f90..7368f83b4 100644 --- a/configurator/src/components/DomainPanel.svelte +++ b/configurator/src/components/DomainPanel.svelte @@ -17,6 +17,7 @@ import ThemesPanel from './panels/ThemesPanel.svelte'; import ExportPanel from './panels/ExportPanel.svelte'; import CheatsheetPanel from './panels/CheatsheetPanel.svelte'; + import ChangesPanel from './panels/ChangesPanel.svelte'; import GenericTokenPanel from './panels/GenericTokenPanel.svelte'; import AllTokensTab from './panels/AllTokensTab.svelte'; import WcagPanel from './panels/WcagPanel.svelte'; @@ -35,7 +36,7 @@ // Domains that skip the two-tab treatment - const NO_CONTROLS_TAB = new Set(["home", "themes", "wcag", "setup", "cheatsheet"]); + const NO_CONTROLS_TAB = new Set(["home", "changes", "themes", "wcag", "setup", "cheatsheet"]); let view = $state<"controls" | "tokens">("controls"); @@ -55,6 +56,8 @@ {#if NO_CONTROLS_TAB.has(domain)} {#if domain === "home"} + {:else if domain === "changes"} + {:else if domain === "themes"} {:else if domain === "wcag"} diff --git a/configurator/src/components/panels/ChangesPanel.svelte b/configurator/src/components/panels/ChangesPanel.svelte new file mode 100644 index 000000000..82691fee7 --- /dev/null +++ b/configurator/src/components/panels/ChangesPanel.svelte @@ -0,0 +1,197 @@ + + +
+ +
+
+
+

Changes

+

+ Every active override, grouped by what it does to the system. +

+
+ {#if summary.total > 0} + + {/if} +
+ + {#if summary.total > 0} + +
+ {#each GROUPS as g (g.key)} + {@const n = summary[g.key].length} + {#if n > 0} + + {n} {g.label} + + {/if} + {/each} +
+ {/if} +
+ +
+ + +
+ {#if summary.total === 0} +
+
+ +
+

No changes yet

+

+ You're on the framework defaults. Edit any panel and your overrides will collect here. +

+
+ {:else} + + {#each shadows as sh (sh.family.id)} +
+
+ +
+
+ {sh.shadowedSteps.length} {sh.family.label} step{sh.shadowedSteps.length !== 1 ? "s" : ""} pinned +
+
+ These fixed values override the generated ladder, so its source + knob{sh.overriddenSources.length ? "" : ""} can't move them. +
+ +
+
+
+ {/each} + + + {#each GROUPS as g (g.key)} + {@const entries = summary[g.key]} + {#if entries.length > 0} +
+
+ + {g.label} + {entries.length} +
+ +
+

{g.blurb}

+ +
+ {#each entries as entry (entry.name)} + {#if entry.token} + {@const dom = domainOf(entry.name)} +
+ + onSet(entry.name, v)} + onReset={() => onReset(entry.name)} + /> +
+ {:else} + +
+
+
+ {entry.name.replace("--sf-", "")} +
+
+ {entry.value} +
+
+ +
+ {/if} + {/each} +
+
+ {/if} + {/each} + {/if} +
+
diff --git a/configurator/src/components/shell/SidebarNav.svelte b/configurator/src/components/shell/SidebarNav.svelte index 576983121..1e300b5c1 100644 --- a/configurator/src/components/shell/SidebarNav.svelte +++ b/configurator/src/components/shell/SidebarNav.svelte @@ -1,7 +1,7 @@