diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index 661bd5c5..a5f9b769 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -151,7 +151,7 @@ // Persist the navigation prefs so a reload restores where the user was. // Restore (with validation) happens in store.svelte.js via sanitiseUiState. $effect(() => { - const snapshot = JSON.stringify({ domain: ui.domain, outputMode: ui.outputMode, uiTheme: ui.uiTheme, bundle: ui.bundle }); + const snapshot = JSON.stringify({ domain: ui.domain, outputMode: ui.outputMode, uiTheme: ui.uiTheme, bundle: ui.bundle, showTokens: ui.showTokens }); try { localStorage.setItem(UI_STORAGE_KEY, snapshot); } catch { diff --git a/configurator/src/components/Header.svelte b/configurator/src/components/Header.svelte index 2273248b..d20f6d74 100644 --- a/configurator/src/components/Header.svelte +++ b/configurator/src/components/Header.svelte @@ -53,6 +53,7 @@
+
diff --git a/configurator/src/components/editors/ColorStudio.svelte b/configurator/src/components/editors/ColorStudio.svelte index 022876da..d768f2f1 100644 --- a/configurator/src/components/editors/ColorStudio.svelte +++ b/configurator/src/components/editors/ColorStudio.svelte @@ -5,9 +5,11 @@ import BrandColorRow from '../BrandColorRow.svelte'; import ColorAssignments from '../ColorAssignments.svelte'; import FriendlyControl from '../FriendlyControl.svelte'; + import { ui } from '../../lib/store.svelte.js'; import ShadeRamp from '../ShadeRamp.svelte'; import StudioFrame from './StudioFrame.svelte'; + const showToken = $derived(ui.showTokens); const main = BRAND_COLOR_KEYS.filter((c) => c.group === 'brand'); const status = BRAND_COLOR_KEYS.filter((c) => c.group === 'status'); const tuning = [ @@ -60,7 +62,7 @@
Core brand colors
{#each main as {key,label} (key)}{/each}
Semantic colors
{#each status as {key,label} (key)}{/each}
-
Contrast & palette tuning
{#each tuning as token (token.name)}{/each}
+
Contrast & palette tuning
{#each tuning as token (token.name)}{/each}
Semantic role preview
Generated shade ramp
diff --git a/configurator/src/components/editors/StudioControls.svelte b/configurator/src/components/editors/StudioControls.svelte index d6de97bb..b318d5bb 100644 --- a/configurator/src/components/editors/StudioControls.svelte +++ b/configurator/src/components/editors/StudioControls.svelte @@ -1,9 +1,10 @@
@@ -11,7 +12,7 @@ overrides[token.name] != null).length}>
{#each group.tokens as token (token.name)} - + {/each}
diff --git a/configurator/src/lib/store.svelte.js b/configurator/src/lib/store.svelte.js index 6dfad727..ed02ee43 100644 --- a/configurator/src/lib/store.svelte.js +++ b/configurator/src/lib/store.svelte.js @@ -74,6 +74,8 @@ export const ui = $state({ uiTheme: savedUi.uiTheme ?? 'dark', /** Selected dist bundle id for the install/setup picker (see lib/bundles.js). */ bundle: savedUi.bundle ?? 'optimal', + /** Show raw --sf-* token names in friendly/studio controls. */ + showTokens: savedUi.showTokens ?? false, /** Sidebar collapse — for narrow viewports / a focus-mode. */ sidebarOpen: true, /** @@ -109,7 +111,7 @@ export const savedThemes = $state(loadSavedThemes()); * Load the persisted UI preferences (domain / output format / theme / bundle), * validated against the live taxonomy. Persisting happens in App.svelte * via an $effect over the same fields. - * @returns {{ domain?: string, outputMode?: 'layer'|'root', uiTheme?: 'light'|'dark', bundle?: string }} + * @returns {{ domain?: string, outputMode?: 'layer'|'root', uiTheme?: 'light'|'dark', bundle?: string, showTokens?: boolean }} */ function loadUiState() { if (typeof localStorage === 'undefined') return {}; diff --git a/configurator/src/lib/uiState.js b/configurator/src/lib/uiState.js index 3e459887..064d96ff 100644 --- a/configurator/src/lib/uiState.js +++ b/configurator/src/lib/uiState.js @@ -19,7 +19,7 @@ export const UI_STORAGE_KEY = 'slashed-configurator/ui/v1'; * simply ignored — the flat IA has no complexity mode. * * @param {string|null} raw JSON string from localStorage (or null) - * @returns {{ domain?: string, outputMode?: 'layer'|'root', uiTheme?: 'light'|'dark', bundle?: string }} + * @returns {{ domain?: string, outputMode?: 'layer'|'root', uiTheme?: 'light'|'dark', bundle?: string, showTokens?: boolean }} */ export function sanitiseUiState(raw) { const out = {}; @@ -49,5 +49,9 @@ export function sanitiseUiState(raw) { out.bundle = parsed.bundle; } + if (typeof parsed.showTokens === 'boolean') { + out.showTokens = parsed.showTokens; + } + return out; } diff --git a/configurator/tests-components/studios.test.js b/configurator/tests-components/studios.test.js index e5e10d49..742091d9 100644 --- a/configurator/tests-components/studios.test.js +++ b/configurator/tests-components/studios.test.js @@ -18,9 +18,12 @@ import MotionStudio from '../src/components/editors/MotionStudio.svelte'; import EffectsStudio from '../src/components/editors/EffectsStudio.svelte'; import FriendlyControl from '../src/components/FriendlyControl.svelte'; import { tokenByName } from '../src/lib/model.js'; -import { clearAll, overrides } from '../src/lib/store.svelte.js'; +import { clearAll, overrides, ui } from '../src/lib/store.svelte.js'; -beforeEach(() => clearAll()); +beforeEach(() => { + clearAll(); + ui.showTokens = false; +}); const STUDIOS = [ ['Typography Studio', TypographyStudio, ['The quick brown fox', 'Fluid scale']], @@ -46,12 +49,38 @@ describe('visual studios', () => { } test('Typography Studio switches scopes and renders active panel controls', async () => { + ui.showTokens = true; const { getByRole, getByText, getAllByText } = render(TypographyStudio); await fireEvent.click(getByRole('tab', { name: 'Headings' })); expect(getAllByText('Heading aliases, line-height, tracking and max-width constraints.').length).toBeGreaterThan(0); expect(getByText('--sf-h1-size')).toBeInTheDocument(); }); + + + test('Studio token names follow the global show/hide token setting', async () => { + const { queryByText, getByText } = render(TypographyStudio); + + expect(queryByText('--sf-font-body')).not.toBeInTheDocument(); + + ui.showTokens = true; + await tick(); + expect(getByText('--sf-font-body')).toBeInTheDocument(); + + ui.showTokens = false; + await tick(); + expect(queryByText('--sf-font-body')).not.toBeInTheDocument(); + }); + + test('FriendlyControl can still reveal raw token info locally', async () => { + const token = tokenByName.get('--sf-heading-text-wrap'); + const { getByRole, getByText, queryByText } = render(FriendlyControl, { props: { token } }); + + expect(queryByText('--sf-heading-text-wrap')).not.toBeInTheDocument(); + await fireEvent.click(getByRole('button', { name: 'Show token' })); + expect(getByText('--sf-heading-text-wrap')).toBeInTheDocument(); + }); + test('FriendlyControl renders schema select controls and writes overrides', async () => { const token = tokenByName.get('--sf-heading-text-wrap'); const { getByLabelText } = render(FriendlyControl, { props: { token, showToken: true } });