@adea-ai/ui 0.125.0 → 0.128.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/NOTICE +40 -0
- package/dist/components/theme/index.d.ts +1 -1
- package/dist/components/theme/index.d.ts.map +1 -1
- package/dist/components/theme/index.js +4 -3
- package/dist/components/theme/theme-document.d.ts +37 -2
- package/dist/components/theme/theme-document.d.ts.map +1 -1
- package/dist/components/theme/theme-document.js +23 -3
- package/dist/components/theme/theme-document.js.map +1 -1
- package/dist/components/theme/theme-persistence.d.ts +87 -0
- package/dist/components/theme/theme-persistence.d.ts.map +1 -1
- package/dist/components/theme/theme-persistence.js +63 -2
- package/dist/components/theme/theme-persistence.js.map +1 -1
- package/dist/components/theme/theme-provider.d.ts +55 -41
- package/dist/components/theme/theme-provider.d.ts.map +1 -1
- package/dist/components/theme/theme-provider.js +59 -52
- package/dist/components/theme/theme-provider.js.map +1 -1
- package/dist/components/theme/theme-script.d.ts +100 -0
- package/dist/components/theme/theme-script.d.ts.map +1 -0
- package/dist/components/theme/theme-script.js +57 -0
- package/dist/components/theme/theme-script.js.map +1 -0
- package/dist/components/theme/theme-state.d.ts +58 -0
- package/dist/components/theme/theme-state.d.ts.map +1 -1
- package/dist/components/theme/theme-state.js +22 -1
- package/dist/components/theme/theme-state.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/lib/font-zoom.d.ts +25 -0
- package/dist/lib/font-zoom.d.ts.map +1 -0
- package/dist/lib/font-zoom.js +45 -0
- package/dist/lib/font-zoom.js.map +1 -0
- package/dist/lib/preference-store.d.ts +126 -0
- package/dist/lib/preference-store.d.ts.map +1 -0
- package/dist/lib/preference-store.js +126 -0
- package/dist/lib/preference-store.js.map +1 -0
- package/dist/lib/theme-surface.d.ts +71 -0
- package/dist/lib/theme-surface.d.ts.map +1 -0
- package/dist/lib/theme-surface.js +62 -0
- package/dist/lib/theme-surface.js.map +1 -0
- package/dist/lib/themes.d.ts +57 -1
- package/dist/lib/themes.d.ts.map +1 -1
- package/dist/lib/themes.js +81 -4
- package/dist/lib/themes.js.map +1 -1
- package/dist/lib/tokens.d.ts.map +1 -1
- package/dist/lib/tokens.js +15 -0
- package/dist/lib/tokens.js.map +1 -1
- package/dist/r/lib.json +15 -0
- package/dist/r/registry.json +26 -1
- package/dist/r/src/components/theme/index.ts +7 -0
- package/dist/r/src/components/theme/theme-document.ts +77 -2
- package/dist/r/src/components/theme/theme-persistence.ts +167 -0
- package/dist/r/src/components/theme/theme-provider.tsx +132 -50
- package/dist/r/src/components/theme/theme-script.ts +201 -0
- package/dist/r/src/components/theme/theme-state.ts +72 -0
- package/dist/r/src/lib/font-zoom.ts +74 -0
- package/dist/r/src/lib/preference-store.ts +216 -0
- package/dist/r/src/lib/theme-surface.ts +110 -0
- package/dist/r/src/lib/themes.ts +115 -2
- package/dist/r/src/lib/tokens.ts +17 -0
- package/dist/r/src/styles/theme-variants.css +3010 -0
- package/dist/r/src/styles/theme.css +38 -0
- package/dist/r/theme.json +6 -1
- package/dist/r/theming.json +5 -0
- package/package.json +2 -1
- package/registry.json +26 -1
- package/src/components/theme/index.ts +7 -0
- package/src/components/theme/theme-document.ts +77 -2
- package/src/components/theme/theme-persistence.ts +167 -0
- package/src/components/theme/theme-provider.tsx +132 -50
- package/src/components/theme/theme-script.ts +201 -0
- package/src/components/theme/theme-state.ts +72 -0
- package/src/index.ts +29 -0
- package/src/lib/font-zoom.ts +74 -0
- package/src/lib/preference-store.ts +216 -0
- package/src/lib/theme-surface.ts +110 -0
- package/src/lib/themes.ts +115 -2
- package/src/lib/tokens.ts +17 -0
- package/src/styles/theme-variants.css +3010 -0
- package/src/styles/theme.css +38 -0
package/dist/lib/tokens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.js","names":[],"sources":["../../src/lib/tokens.ts"],"sourcesContent":["/**\n * The design system's token manifest.\n *\n * `theme.css` is the source of truth for token *values*. This file is the\n * source of truth for token *names and meaning* — what each one is for, and\n * which ones a theme may vary.\n *\n * It exists because a manifest that is only documentation rots. Two things read\n * it instead:\n *\n * - the token galleries in Storybook, so the review surface shows every token\n * rather than the ones an author remembered;\n * - `tests/tokens.test.ts`, which parses `theme.css` and fails when a token\n * is declared in one place and missing from the other.\n *\n * That pairing is what makes \"every token is documented and every documented\n * token exists\" a property the build enforces rather than a promise.\n */\n\nimport { ACCENTS } from '@adea-ai/themes'\nexport { fontOptions } from './font-catalog'\nexport type { FontOption } from './font-catalog'\n\nexport type TokenKind =\n | 'color'\n | 'dimension'\n | 'font-family'\n | 'number'\n | 'duration'\n | 'easing'\n | 'shadow'\n | 'text'\n\nexport type TokenDefinition = {\n /** The custom property name, without the leading `--`. */\n name: string\n kind: TokenKind\n /** One line: what this token is for. Shown in the gallery. */\n description: string\n /** True when the light and dark themes give it different values. */\n themed?: boolean\n}\n\n/**\n * Colour roles, in the order they appear in a page: surfaces first, then text,\n * then the lines between them, then the semantic fills. The order is the\n * documentation — reading the gallery top to bottom walks the surface ladder.\n */\nexport const colorTokens: TokenDefinition[] = [\n // Surfaces\n {\n name: 'background',\n kind: 'color',\n themed: true,\n description: 'The canvas every panel sits on.',\n },\n {\n name: 'foreground',\n kind: 'color',\n themed: true,\n description: 'Default body text on the canvas.',\n },\n {\n name: 'card',\n kind: 'color',\n themed: true,\n description: 'A panel or card raised one rung above the canvas.',\n },\n { name: 'card-foreground', kind: 'color', themed: true, description: 'Text on `card`.' },\n {\n name: 'popover',\n kind: 'color',\n themed: true,\n description: 'A floating surface: dialog, menu, popover, tooltip-free.',\n },\n { name: 'popover-foreground', kind: 'color', themed: true, description: 'Text on `popover`.' },\n {\n name: 'surface',\n kind: 'color',\n themed: true,\n description: 'Alias of `background`, for code that means \"a surface\" not \"the page\".',\n },\n {\n name: 'surface-sunken',\n kind: 'color',\n themed: true,\n description: 'A well: code blocks, inset lists, empty states.',\n },\n {\n name: 'surface-raised',\n kind: 'color',\n themed: true,\n description: 'Alias of `card`, for panels that sit on the canvas.',\n },\n {\n name: 'surface-overlay',\n kind: 'color',\n themed: true,\n description: 'Alias of `popover`, for floating layers.',\n },\n {\n name: 'surface-hover',\n kind: 'color',\n themed: true,\n description: 'The hover fill shared by rows, menu items and ghost controls.',\n },\n {\n name: 'surface-active',\n kind: 'color',\n themed: true,\n description: 'The pressed/selected fill, one rung past hover.',\n },\n {\n name: 'chrome',\n kind: 'color',\n themed: true,\n description: 'Window chrome: the top bar and its frosted fill.',\n },\n {\n name: 'chrome-alpha',\n kind: 'number',\n themed: true,\n description: 'Opacity of the chrome fill behind a blur.',\n },\n\n // The brand action\n {\n name: 'primary',\n kind: 'color',\n themed: true,\n description: 'The Adea emerald. The one primary action in a view.',\n },\n {\n name: 'primary-foreground',\n kind: 'color',\n themed: true,\n description: 'Label on `primary`. Black in dark, white in light — the accent flips polarity.',\n },\n {\n name: 'primary-hover',\n kind: 'color',\n themed: true,\n description: 'Hover rung for a primary action.',\n },\n {\n name: 'primary-subtle',\n kind: 'color',\n themed: true,\n description: 'Tinted primary fill: selected rows, focus rings, subtle buttons.',\n },\n\n {\n name: 'secondary',\n kind: 'color',\n themed: true,\n description: 'A quieter filled surface for a secondary action.',\n },\n {\n name: 'secondary-foreground',\n kind: 'color',\n themed: true,\n description: 'Label on `secondary`.',\n },\n {\n name: 'muted',\n kind: 'color',\n themed: true,\n description: 'A neutral fill: skeletons, inert chips, table footers.',\n },\n {\n name: 'muted-foreground',\n kind: 'color',\n themed: true,\n description: 'De-emphasised text. Measured to clear 4.5:1 on both `background` and `card`.',\n },\n {\n name: 'accent',\n kind: 'color',\n themed: true,\n description: \"shadcn's hover-surface accent. Not the brand colour — that is `primary`.\",\n },\n { name: 'accent-foreground', kind: 'color', themed: true, description: 'Text on `accent`.' },\n\n // Status\n {\n name: 'destructive',\n kind: 'color',\n themed: true,\n description: 'The canonical error hue for semantic status indicators and tinted notices.',\n },\n {\n name: 'destructive-foreground',\n kind: 'color',\n themed: true,\n description:\n 'Text on the canonical error hue; solid actions use `destructive-action-foreground`.',\n },\n {\n name: 'destructive-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque destructive tint against the canvas; subtle labels use foreground.',\n },\n {\n name: 'destructive-action',\n kind: 'color',\n themed: true,\n description: 'Contrast-safe solid fill for destructive actions, derived from the error role.',\n },\n {\n name: 'destructive-action-foreground',\n kind: 'color',\n themed: true,\n description: 'Text on the destructive action fill and its 90% hover composition.',\n },\n { name: 'success', kind: 'color', themed: true, description: 'A completed or healthy state.' },\n { name: 'success-foreground', kind: 'color', themed: true, description: 'Label on `success`.' },\n {\n name: 'success-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque success tint against the canvas; subtle labels use foreground.',\n },\n {\n name: 'warning',\n kind: 'color',\n themed: true,\n description: 'Something needs attention but is not broken.',\n },\n { name: 'warning-foreground', kind: 'color', themed: true, description: 'Label on `warning`.' },\n {\n name: 'warning-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque warning tint against the canvas; subtle labels use foreground.',\n },\n { name: 'info', kind: 'color', themed: true, description: 'Neutral information.' },\n { name: 'info-foreground', kind: 'color', themed: true, description: 'Label on `info`.' },\n {\n name: 'info-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque info tint against the canvas; subtle labels use foreground.',\n },\n\n // Lines\n {\n name: 'border',\n kind: 'color',\n themed: true,\n description: 'Every hairline: panel edges, dividers, card outlines.',\n },\n {\n name: 'input',\n kind: 'color',\n themed: true,\n description:\n 'The edge of an editable control. A rung stronger than `border` so a field is findable.',\n },\n {\n name: 'ring',\n kind: 'color',\n themed: true,\n description: 'The focus ring. Always paired with `primary-subtle` as its glow.',\n },\n\n // Theme-invariant glass\n {\n name: 'scrim',\n kind: 'color',\n description: 'Dark glass for overlays and on-screen controls. Same in both themes.',\n },\n { name: 'scrim-foreground', kind: 'color', description: 'Text drawn on `scrim`.' },\n { name: 'scrim-edge', kind: 'color', description: 'The hairline drawn on `scrim`.' },\n\n // Diff and code\n { name: 'diff-add', kind: 'color', themed: true, description: 'Background of an added line.' },\n {\n name: 'diff-add-foreground',\n kind: 'color',\n themed: true,\n description: 'Text of an added line.',\n },\n {\n name: 'diff-delete',\n kind: 'color',\n themed: true,\n description: 'Background of a removed line.',\n },\n {\n name: 'diff-delete-foreground',\n kind: 'color',\n themed: true,\n description: 'Text of a removed line.',\n },\n { name: 'diff-hunk', kind: 'color', themed: true, description: 'Background of a hunk header.' },\n {\n name: 'diff-hunk-foreground',\n kind: 'color',\n themed: true,\n description: 'Text of a hunk header.',\n },\n {\n name: 'diff-meta-foreground',\n kind: 'color',\n themed: true,\n description: 'File headers and line numbers in a diff.',\n },\n\n // Charts\n {\n name: 'chart-1',\n kind: 'color',\n themed: true,\n description: 'Categorical series 1. The set is closed at six.',\n },\n { name: 'chart-2', kind: 'color', themed: true, description: 'Categorical series 2.' },\n { name: 'chart-3', kind: 'color', themed: true, description: 'Categorical series 3.' },\n { name: 'chart-4', kind: 'color', themed: true, description: 'Categorical series 4.' },\n { name: 'chart-5', kind: 'color', themed: true, description: 'Categorical series 5.' },\n { name: 'chart-6', kind: 'color', themed: true, description: 'Categorical series 6.' },\n\n // Sidebar (the shadcn contract, kept whole)\n {\n name: 'sidebar',\n kind: 'color',\n themed: true,\n description: 'The rail and sidebar surface, tinted against the canvas.',\n },\n {\n name: 'sidebar-foreground',\n kind: 'color',\n themed: true,\n description: 'Default text in the rail and sidebar.',\n },\n {\n name: 'sidebar-primary',\n kind: 'color',\n themed: true,\n description: 'The active destination in the rail.',\n },\n {\n name: 'sidebar-primary-foreground',\n kind: 'color',\n themed: true,\n description: 'Text on the active rail destination.',\n },\n {\n name: 'sidebar-accent',\n kind: 'color',\n themed: true,\n description: 'Hover and selected fill for rail and sidebar rows.',\n },\n {\n name: 'sidebar-accent-foreground',\n kind: 'color',\n themed: true,\n description: 'Text on `sidebar-accent`.',\n },\n {\n name: 'sidebar-border',\n kind: 'color',\n themed: true,\n description: 'The rule between the rail and the canvas.',\n },\n { name: 'sidebar-ring', kind: 'color', themed: true, description: 'Focus ring inside the rail.' },\n {\n name: 'sidebar-muted-foreground',\n kind: 'color',\n themed: true,\n description: 'Rail labels that are not the current destination.',\n },\n]\n\nexport const radiusTokens: TokenDefinition[] = [\n {\n name: 'radius-lg',\n kind: 'dimension',\n description:\n '10px — cards and panels. Also the knob: move this one value to re-round the whole system.',\n },\n { name: 'radius-sm', kind: 'dimension', description: '6px — keyboard keys, menu items.' },\n {\n name: 'radius-md',\n kind: 'dimension',\n description: '8px — every control: buttons, inputs, selects; and badges.',\n },\n { name: 'radius-xl', kind: 'dimension', description: '14px — dialogs, menus, sheets.' },\n { name: 'radius-2xl', kind: 'dimension', description: '18px — full-window surfaces and media.' },\n]\n\nexport const typographyTokens: TokenDefinition[] = [\n {\n name: 'font-ui',\n kind: 'font-family',\n description: 'The selected family for interface controls, labels and headings.',\n },\n {\n name: 'font-content',\n kind: 'font-family',\n description: 'The selected family for prose, transcripts and long-form reading.',\n },\n {\n name: 'font-code',\n kind: 'font-family',\n description: 'The selected family for code, terminals, keyboard keys, ids and paths.',\n },\n {\n name: 'font-sans',\n kind: 'font-family',\n description: 'Compatibility alias for the selected UI family.',\n },\n {\n name: 'font-mono',\n kind: 'font-family',\n description: 'Compatibility alias for the selected code family.',\n },\n {\n name: 'font-family-space-grotesk',\n kind: 'font-family',\n description: 'The self-hosted Space Grotesk family.',\n },\n {\n name: 'font-family-jetbrains-mono',\n kind: 'font-family',\n description: 'The monospace default. Code, terminals, diffs.',\n },\n {\n name: 'font-family-geist',\n kind: 'font-family',\n description: 'An alternative interface face. cortana already ships it.',\n },\n {\n name: 'font-family-geist-mono',\n kind: 'font-family',\n description: 'The monospace counterpart to Geist.',\n },\n {\n name: 'font-family-system',\n kind: 'font-family',\n description: \"The platform's own interface face. First-class, not a fallback.\",\n },\n {\n name: 'font-family-system-mono',\n kind: 'font-family',\n description: \"The platform's own monospace face.\",\n },\n {\n name: 'font-ui-size',\n kind: 'dimension',\n description: 'Runtime-projected interface size in pixels; scales the shared UI text ladder.',\n },\n {\n name: 'font-content-size',\n kind: 'dimension',\n description: 'Runtime-projected prose and transcript size in pixels.',\n },\n {\n name: 'font-code-size',\n kind: 'dimension',\n description: 'Runtime-projected code, terminal and keyboard-key size in pixels.',\n },\n {\n name: 'font-ui-scale',\n kind: 'number',\n description: 'The UI size ratio applied to the interface text ladder.',\n },\n {\n name: 'font-content-scale',\n kind: 'number',\n description: 'The content size ratio applied to rem-relative prose text.',\n },\n {\n name: 'font-code-scale',\n kind: 'number',\n description: 'The code size ratio applied to rem-relative code text.',\n },\n {\n name: 'ui-tracking',\n kind: 'text',\n description:\n 'Letter spacing for measured chrome. `normal` on a proportional face, tightened when the UI font is monospace — which is roughly 20% wider at the same size.',\n },\n {\n name: 'ui-word-spacing',\n kind: 'text',\n description:\n \"Word spacing for measured chrome. Mono's space is a full advance-width cell, so a two-word label reads as two floating words without this.\",\n },\n {\n name: 'text-2xs',\n kind: 'text',\n description: 'The smallest UI-scaled label: status bar and compact metadata.',\n },\n { name: 'text-xs', kind: 'text', description: 'UI-scaled metadata, badges and secondary rows.' },\n {\n name: 'text-sm',\n kind: 'text',\n description: 'The UI-scaled interface default for labels and controls.',\n },\n { name: 'text-base', kind: 'text', description: '16px — card titles and dialog titles.' },\n { name: 'text-lg', kind: 'text', description: '18px — a section heading inside a page.' },\n { name: 'text-xl', kind: 'text', description: '20px — a page title.' },\n { name: 'text-2xl', kind: 'text', description: '24px — a display figure.' },\n {\n name: 'text-3xl',\n kind: 'text',\n description: 'The largest UI-scaled rung. Reserved for an empty-state headline.',\n },\n {\n name: 'text-content',\n kind: 'text',\n description: 'The selected content size with the prose line-height.',\n },\n {\n name: 'text-code',\n kind: 'text',\n description: 'The selected code size with the code line-height.',\n },\n]\n\n/** Height tokens also size both axes of icon controls through Tailwind's --size\n * namespace. Padding has its own --spacing mapping. Nominal pixel descriptions\n * use a 16px rem; rendered dimensions follow the consumer's root font size. */\nexport const densityTokens: TokenDefinition[] = [\n { name: 'control-height-2xs', kind: 'dimension', description: '20px — an inline chip control.' },\n { name: 'control-height-xs', kind: 'dimension', description: '24px — a dense table row action.' },\n { name: 'control-height-sm', kind: 'dimension', description: '28px — the toolbar default.' },\n {\n name: 'control-height-md',\n kind: 'dimension',\n description: '32px — the form default. Button and Input are this size unless told otherwise.',\n },\n { name: 'control-height-lg', kind: 'dimension', description: '36px — a primary form action.' },\n {\n name: 'control-height-xl',\n kind: 'dimension',\n description: '40px — a hero control in an empty state.',\n },\n {\n name: 'control-height-2xl',\n kind: 'dimension',\n description: '48px — a stable touch target, unchanged by compact density.',\n },\n {\n name: 'control-padding-2xs',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 20px control.',\n },\n {\n name: 'control-padding-xs',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 24px control.',\n },\n {\n name: 'control-padding-sm',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 28px control.',\n },\n {\n name: 'control-padding-md',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 32px control.',\n },\n {\n name: 'control-padding-lg',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 36px control.',\n },\n {\n name: 'control-padding-xl',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 40px control.',\n },\n {\n name: 'control-padding-2xl',\n kind: 'dimension',\n description: 'Inline padding that pairs with the stable 48px touch target.',\n },\n { name: 'row-height-sm', kind: 'dimension', description: '28px — a dense list row.' },\n { name: 'row-height-md', kind: 'dimension', description: '32px — the default list row.' },\n { name: 'row-height-lg', kind: 'dimension', description: '36px — a two-line list row.' },\n { name: 'rail-width', kind: 'dimension', description: '56px — the icon-only rail.' },\n { name: 'rail-width-expanded', kind: 'dimension', description: '236px — the labelled rail.' },\n {\n name: 'rail-item-height',\n kind: 'dimension',\n description: '36px — a rail or sidebar destination.',\n },\n {\n name: 'sidebar-width',\n kind: 'dimension',\n description: '256px — the secondary navigation column.',\n },\n {\n name: 'sidebar-width-compact',\n kind: 'dimension',\n description: '48px — the collapsed secondary column.',\n },\n {\n name: 'panel-width',\n kind: 'dimension',\n description: '390px — a side pane beside the work, and the same pane as a sheet.',\n },\n { name: 'topbar-height', kind: 'dimension', description: '48px — the window title row.' },\n { name: 'statusbar-height', kind: 'dimension', description: '28px — the bottom readout strip.' },\n {\n name: 'tooltip-max-width',\n kind: 'dimension',\n description:\n 'The lesser of the 16rem readable measure and one viewport minus a gutter — a tip wraps before it can push its document wider than itself.',\n },\n]\n\nexport const elevationTokens: TokenDefinition[] = [\n {\n name: 'shadow-2xs',\n kind: 'shadow',\n themed: true,\n description: 'Barely there: a chip resting on a card.',\n },\n {\n name: 'shadow-xs',\n kind: 'shadow',\n themed: true,\n description: 'A card or panel on the canvas.',\n },\n {\n name: 'shadow-sm',\n kind: 'shadow',\n themed: true,\n description: 'A control that lifts: a pressed toggle, a thumb.',\n },\n { name: 'shadow-md', kind: 'shadow', themed: true, description: 'A menu or popover.' },\n { name: 'shadow-lg', kind: 'shadow', themed: true, description: 'A dialog or sheet.' },\n {\n name: 'shadow-xl',\n kind: 'shadow',\n themed: true,\n description: 'The command palette, above everything.',\n },\n]\n\nexport const motionTokens: TokenDefinition[] = [\n {\n name: 'duration-fast',\n kind: 'duration',\n description: '120ms — a state change the user is already watching.',\n },\n { name: 'duration-normal', kind: 'duration', description: '200ms — an entrance.' },\n { name: 'duration-slow', kind: 'duration', description: '320ms — a layout settle.' },\n {\n name: 'ease-out',\n kind: 'easing',\n description: 'A fast start and a long settle. Reads as decisive rather than floaty.',\n },\n {\n name: 'ease-in-out',\n kind: 'easing',\n description: 'For a loop that has no start or end, such as a shimmer.',\n },\n]\n\nexport const zIndexTokens: TokenDefinition[] = [\n { name: 'z-base', kind: 'number', description: 'In flow, at the document order.' },\n {\n name: 'z-docked',\n kind: 'number',\n description: 'A sticky table header standing above its own scroll body.',\n },\n { name: 'z-sticky', kind: 'number', description: 'The top bar and any other pinned chrome.' },\n { name: 'z-drawer', kind: 'number', description: 'A drawer, below a modal dialog.' },\n { name: 'z-dialog', kind: 'number', description: 'A modal dialog and its scrim.' },\n {\n name: 'z-menu',\n kind: 'number',\n description: 'Menus, popovers and comboboxes. Above dialogs, because a dialog can contain one.',\n },\n {\n name: 'z-tooltip',\n kind: 'number',\n description: 'Tooltips. Above menus, because a menu item can carry one.',\n },\n {\n name: 'z-toast',\n kind: 'number',\n description: 'Notifications. Above everything, so they are never hidden by a dialog.',\n },\n]\n\n/**\n * The accent axis.\n *\n * adea's accent presets, as a selection rather than a constant. `theme` is the\n * default and means \"the variant's own primary\" — monochrome in adea's palette —\n * and each named preset overrides the interactive roles through the\n * `[data-accent]` blocks in `theme.css`.\n *\n * These are the same six presets `accentPresets` exports in adea's own\n * `appearance.ts`, and the values match. A consumer's appearance picker and the\n * workshop's theme toolbar read this one list, so the two cannot disagree about\n * what a preset is called or what colour it is.\n */\nexport type AccentPreset = {\n /** The `data-accent` value. `theme` is the variant's own primary. */\n id: string\n label: string\n /** What the preset is for, in the gallery and in a picker. */\n description: string\n /** The accent as it appears in the light theme. Absent for `theme`. */\n light?: string\n /** The accent as it appears in the dark theme. Absent for `theme`. */\n dark?: string\n}\n\nexport const accentPresets: readonly AccentPreset[] = Object.freeze([\n {\n id: 'theme',\n label: 'Theme',\n description:\n \"The variant's own primary. Neutral in adea's palette, so the interface stays monochrome.\",\n },\n ...ACCENTS,\n])\n\n/** Every token, in gallery order. */\nexport const designTokens = {\n color: colorTokens,\n radius: radiusTokens,\n typography: typographyTokens,\n density: densityTokens,\n elevation: elevationTokens,\n motion: motionTokens,\n zIndex: zIndexTokens,\n} as const\n\n/** Flat list, for completeness checks and for search. */\nexport const allTokens: TokenDefinition[] = Object.values(designTokens).flat()\n\n/**\n * Aliases that exist only to make a call site read better, and which therefore\n * have no gallery row of their own. Kept explicit so the completeness check\n * below can tell \"deliberately absent\" from \"forgotten\".\n */\nexport const undocumentedTokenAliases: string[] = ['radius']\n"],"mappings":"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,mBAAsC;CACjD;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aACE;CACJ;CACA;EACE,MAAM;EACN,MAAM;EACN,aACE;CACJ;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EAAE,MAAM;EAAW,MAAM;EAAQ,aAAa;CAAiD;CAC/F;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EAAE,MAAM;EAAa,MAAM;EAAQ,aAAa;CAAwC;CACxF;EAAE,MAAM;EAAW,MAAM;EAAQ,aAAa;CAA0C;CACxF;EAAE,MAAM;EAAW,MAAM;EAAQ,aAAa;CAAuB;CACrE;EAAE,MAAM;EAAY,MAAM;EAAQ,aAAa;CAA2B;CAC1E;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;AACF;;;;AAKA,IAAa,gBA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"}
|
|
1
|
+
{"version":3,"file":"tokens.js","names":[],"sources":["../../src/lib/tokens.ts"],"sourcesContent":["/**\n * The design system's token manifest.\n *\n * `theme.css` is the source of truth for token *values*. This file is the\n * source of truth for token *names and meaning* — what each one is for, and\n * which ones a theme may vary.\n *\n * It exists because a manifest that is only documentation rots. Two things read\n * it instead:\n *\n * - the token galleries in Storybook, so the review surface shows every token\n * rather than the ones an author remembered;\n * - `tests/tokens.test.ts`, which parses `theme.css` and fails when a token\n * is declared in one place and missing from the other.\n *\n * That pairing is what makes \"every token is documented and every documented\n * token exists\" a property the build enforces rather than a promise.\n */\n\nimport { ACCENTS } from '@adea-ai/themes'\nexport { fontOptions } from './font-catalog'\nexport type { FontOption } from './font-catalog'\n\nexport type TokenKind =\n | 'color'\n | 'dimension'\n | 'font-family'\n | 'number'\n | 'duration'\n | 'easing'\n | 'shadow'\n | 'text'\n\nexport type TokenDefinition = {\n /** The custom property name, without the leading `--`. */\n name: string\n kind: TokenKind\n /** One line: what this token is for. Shown in the gallery. */\n description: string\n /** True when the light and dark themes give it different values. */\n themed?: boolean\n}\n\n/**\n * Colour roles, in the order they appear in a page: surfaces first, then text,\n * then the lines between them, then the semantic fills. The order is the\n * documentation — reading the gallery top to bottom walks the surface ladder.\n */\nexport const colorTokens: TokenDefinition[] = [\n // Surfaces\n {\n name: 'background',\n kind: 'color',\n themed: true,\n description: 'The canvas every panel sits on.',\n },\n {\n name: 'foreground',\n kind: 'color',\n themed: true,\n description: 'Default body text on the canvas.',\n },\n {\n name: 'card',\n kind: 'color',\n themed: true,\n description: 'A panel or card raised one rung above the canvas.',\n },\n { name: 'card-foreground', kind: 'color', themed: true, description: 'Text on `card`.' },\n {\n name: 'popover',\n kind: 'color',\n themed: true,\n description: 'A floating surface: dialog, menu, popover, tooltip-free.',\n },\n { name: 'popover-foreground', kind: 'color', themed: true, description: 'Text on `popover`.' },\n {\n name: 'surface',\n kind: 'color',\n themed: true,\n description: 'Alias of `background`, for code that means \"a surface\" not \"the page\".',\n },\n {\n name: 'surface-sunken',\n kind: 'color',\n themed: true,\n description: 'A well: code blocks, inset lists, empty states.',\n },\n {\n name: 'surface-raised',\n kind: 'color',\n themed: true,\n description: 'Alias of `card`, for panels that sit on the canvas.',\n },\n {\n name: 'surface-overlay',\n kind: 'color',\n themed: true,\n description: 'Alias of `popover`, for floating layers.',\n },\n {\n name: 'surface-hover',\n kind: 'color',\n themed: true,\n description: 'The hover fill shared by rows, menu items and ghost controls.',\n },\n {\n name: 'surface-active',\n kind: 'color',\n themed: true,\n description: 'The pressed/selected fill, one rung past hover.',\n },\n {\n name: 'chrome',\n kind: 'color',\n themed: true,\n description: 'Window chrome: the top bar and its frosted fill.',\n },\n {\n name: 'chrome-alpha',\n kind: 'number',\n themed: true,\n description: 'Opacity of the chrome fill behind a blur.',\n },\n {\n name: 'surface-alpha',\n kind: 'number',\n description:\n \"The opacity a frosted dialog's fill keeps, written by the provider's surface axis. 1 is opaque and never matches the frost rule.\",\n },\n {\n name: 'surface-frost',\n kind: 'number',\n description: 'The fill opacity `--surface-alpha` takes for the frosted surface: 0.92.',\n },\n {\n name: 'surface-translucent',\n kind: 'number',\n description:\n 'The fill opacity `--surface-alpha` takes for the translucent surface: 0.8. A host without native translucency paints frosted instead.',\n },\n\n // The brand action\n {\n name: 'primary',\n kind: 'color',\n themed: true,\n description: 'The Adea emerald. The one primary action in a view.',\n },\n {\n name: 'primary-foreground',\n kind: 'color',\n themed: true,\n description: 'Label on `primary`. Black in dark, white in light — the accent flips polarity.',\n },\n {\n name: 'primary-hover',\n kind: 'color',\n themed: true,\n description: 'Hover rung for a primary action.',\n },\n {\n name: 'primary-subtle',\n kind: 'color',\n themed: true,\n description: 'Tinted primary fill: selected rows, focus rings, subtle buttons.',\n },\n\n {\n name: 'secondary',\n kind: 'color',\n themed: true,\n description: 'A quieter filled surface for a secondary action.',\n },\n {\n name: 'secondary-foreground',\n kind: 'color',\n themed: true,\n description: 'Label on `secondary`.',\n },\n {\n name: 'muted',\n kind: 'color',\n themed: true,\n description: 'A neutral fill: skeletons, inert chips, table footers.',\n },\n {\n name: 'muted-foreground',\n kind: 'color',\n themed: true,\n description: 'De-emphasised text. Measured to clear 4.5:1 on both `background` and `card`.',\n },\n {\n name: 'accent',\n kind: 'color',\n themed: true,\n description: \"shadcn's hover-surface accent. Not the brand colour — that is `primary`.\",\n },\n { name: 'accent-foreground', kind: 'color', themed: true, description: 'Text on `accent`.' },\n\n // Status\n {\n name: 'destructive',\n kind: 'color',\n themed: true,\n description: 'The canonical error hue for semantic status indicators and tinted notices.',\n },\n {\n name: 'destructive-foreground',\n kind: 'color',\n themed: true,\n description:\n 'Text on the canonical error hue; solid actions use `destructive-action-foreground`.',\n },\n {\n name: 'destructive-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque destructive tint against the canvas; subtle labels use foreground.',\n },\n {\n name: 'destructive-action',\n kind: 'color',\n themed: true,\n description: 'Contrast-safe solid fill for destructive actions, derived from the error role.',\n },\n {\n name: 'destructive-action-foreground',\n kind: 'color',\n themed: true,\n description: 'Text on the destructive action fill and its 90% hover composition.',\n },\n { name: 'success', kind: 'color', themed: true, description: 'A completed or healthy state.' },\n { name: 'success-foreground', kind: 'color', themed: true, description: 'Label on `success`.' },\n {\n name: 'success-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque success tint against the canvas; subtle labels use foreground.',\n },\n {\n name: 'warning',\n kind: 'color',\n themed: true,\n description: 'Something needs attention but is not broken.',\n },\n { name: 'warning-foreground', kind: 'color', themed: true, description: 'Label on `warning`.' },\n {\n name: 'warning-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque warning tint against the canvas; subtle labels use foreground.',\n },\n { name: 'info', kind: 'color', themed: true, description: 'Neutral information.' },\n { name: 'info-foreground', kind: 'color', themed: true, description: 'Label on `info`.' },\n {\n name: 'info-subtle',\n kind: 'color',\n themed: true,\n description: 'Opaque info tint against the canvas; subtle labels use foreground.',\n },\n\n // Lines\n {\n name: 'border',\n kind: 'color',\n themed: true,\n description: 'Every hairline: panel edges, dividers, card outlines.',\n },\n {\n name: 'input',\n kind: 'color',\n themed: true,\n description:\n 'The edge of an editable control. A rung stronger than `border` so a field is findable.',\n },\n {\n name: 'ring',\n kind: 'color',\n themed: true,\n description: 'The focus ring. Always paired with `primary-subtle` as its glow.',\n },\n\n // Theme-invariant glass\n {\n name: 'scrim',\n kind: 'color',\n description: 'Dark glass for overlays and on-screen controls. Same in both themes.',\n },\n { name: 'scrim-foreground', kind: 'color', description: 'Text drawn on `scrim`.' },\n { name: 'scrim-edge', kind: 'color', description: 'The hairline drawn on `scrim`.' },\n\n // Diff and code\n { name: 'diff-add', kind: 'color', themed: true, description: 'Background of an added line.' },\n {\n name: 'diff-add-foreground',\n kind: 'color',\n themed: true,\n description: 'Text of an added line.',\n },\n {\n name: 'diff-delete',\n kind: 'color',\n themed: true,\n description: 'Background of a removed line.',\n },\n {\n name: 'diff-delete-foreground',\n kind: 'color',\n themed: true,\n description: 'Text of a removed line.',\n },\n { name: 'diff-hunk', kind: 'color', themed: true, description: 'Background of a hunk header.' },\n {\n name: 'diff-hunk-foreground',\n kind: 'color',\n themed: true,\n description: 'Text of a hunk header.',\n },\n {\n name: 'diff-meta-foreground',\n kind: 'color',\n themed: true,\n description: 'File headers and line numbers in a diff.',\n },\n\n // Charts\n {\n name: 'chart-1',\n kind: 'color',\n themed: true,\n description: 'Categorical series 1. The set is closed at six.',\n },\n { name: 'chart-2', kind: 'color', themed: true, description: 'Categorical series 2.' },\n { name: 'chart-3', kind: 'color', themed: true, description: 'Categorical series 3.' },\n { name: 'chart-4', kind: 'color', themed: true, description: 'Categorical series 4.' },\n { name: 'chart-5', kind: 'color', themed: true, description: 'Categorical series 5.' },\n { name: 'chart-6', kind: 'color', themed: true, description: 'Categorical series 6.' },\n\n // Sidebar (the shadcn contract, kept whole)\n {\n name: 'sidebar',\n kind: 'color',\n themed: true,\n description: 'The rail and sidebar surface, tinted against the canvas.',\n },\n {\n name: 'sidebar-foreground',\n kind: 'color',\n themed: true,\n description: 'Default text in the rail and sidebar.',\n },\n {\n name: 'sidebar-primary',\n kind: 'color',\n themed: true,\n description: 'The active destination in the rail.',\n },\n {\n name: 'sidebar-primary-foreground',\n kind: 'color',\n themed: true,\n description: 'Text on the active rail destination.',\n },\n {\n name: 'sidebar-accent',\n kind: 'color',\n themed: true,\n description: 'Hover and selected fill for rail and sidebar rows.',\n },\n {\n name: 'sidebar-accent-foreground',\n kind: 'color',\n themed: true,\n description: 'Text on `sidebar-accent`.',\n },\n {\n name: 'sidebar-border',\n kind: 'color',\n themed: true,\n description: 'The rule between the rail and the canvas.',\n },\n { name: 'sidebar-ring', kind: 'color', themed: true, description: 'Focus ring inside the rail.' },\n {\n name: 'sidebar-muted-foreground',\n kind: 'color',\n themed: true,\n description: 'Rail labels that are not the current destination.',\n },\n]\n\nexport const radiusTokens: TokenDefinition[] = [\n {\n name: 'radius-lg',\n kind: 'dimension',\n description:\n '10px — cards and panels. Also the knob: move this one value to re-round the whole system.',\n },\n { name: 'radius-sm', kind: 'dimension', description: '6px — keyboard keys, menu items.' },\n {\n name: 'radius-md',\n kind: 'dimension',\n description: '8px — every control: buttons, inputs, selects; and badges.',\n },\n { name: 'radius-xl', kind: 'dimension', description: '14px — dialogs, menus, sheets.' },\n { name: 'radius-2xl', kind: 'dimension', description: '18px — full-window surfaces and media.' },\n]\n\nexport const typographyTokens: TokenDefinition[] = [\n {\n name: 'font-ui',\n kind: 'font-family',\n description: 'The selected family for interface controls, labels and headings.',\n },\n {\n name: 'font-content',\n kind: 'font-family',\n description: 'The selected family for prose, transcripts and long-form reading.',\n },\n {\n name: 'font-code',\n kind: 'font-family',\n description: 'The selected family for code, terminals, keyboard keys, ids and paths.',\n },\n {\n name: 'font-sans',\n kind: 'font-family',\n description: 'Compatibility alias for the selected UI family.',\n },\n {\n name: 'font-mono',\n kind: 'font-family',\n description: 'Compatibility alias for the selected code family.',\n },\n {\n name: 'font-family-space-grotesk',\n kind: 'font-family',\n description: 'The self-hosted Space Grotesk family.',\n },\n {\n name: 'font-family-jetbrains-mono',\n kind: 'font-family',\n description: 'The monospace default. Code, terminals, diffs.',\n },\n {\n name: 'font-family-geist',\n kind: 'font-family',\n description: 'An alternative interface face. cortana already ships it.',\n },\n {\n name: 'font-family-geist-mono',\n kind: 'font-family',\n description: 'The monospace counterpart to Geist.',\n },\n {\n name: 'font-family-system',\n kind: 'font-family',\n description: \"The platform's own interface face. First-class, not a fallback.\",\n },\n {\n name: 'font-family-system-mono',\n kind: 'font-family',\n description: \"The platform's own monospace face.\",\n },\n {\n name: 'font-ui-size',\n kind: 'dimension',\n description: 'Runtime-projected interface size in pixels; scales the shared UI text ladder.',\n },\n {\n name: 'font-content-size',\n kind: 'dimension',\n description: 'Runtime-projected prose and transcript size in pixels.',\n },\n {\n name: 'font-code-size',\n kind: 'dimension',\n description: 'Runtime-projected code, terminal and keyboard-key size in pixels.',\n },\n {\n name: 'font-ui-scale',\n kind: 'number',\n description: 'The UI size ratio applied to the interface text ladder.',\n },\n {\n name: 'font-content-scale',\n kind: 'number',\n description: 'The content size ratio applied to rem-relative prose text.',\n },\n {\n name: 'font-code-scale',\n kind: 'number',\n description: 'The code size ratio applied to rem-relative code text.',\n },\n {\n name: 'ui-tracking',\n kind: 'text',\n description:\n 'Letter spacing for measured chrome. `normal` on a proportional face, tightened when the UI font is monospace — which is roughly 20% wider at the same size.',\n },\n {\n name: 'ui-word-spacing',\n kind: 'text',\n description:\n \"Word spacing for measured chrome. Mono's space is a full advance-width cell, so a two-word label reads as two floating words without this.\",\n },\n {\n name: 'text-2xs',\n kind: 'text',\n description: 'The smallest UI-scaled label: status bar and compact metadata.',\n },\n { name: 'text-xs', kind: 'text', description: 'UI-scaled metadata, badges and secondary rows.' },\n {\n name: 'text-sm',\n kind: 'text',\n description: 'The UI-scaled interface default for labels and controls.',\n },\n { name: 'text-base', kind: 'text', description: '16px — card titles and dialog titles.' },\n { name: 'text-lg', kind: 'text', description: '18px — a section heading inside a page.' },\n { name: 'text-xl', kind: 'text', description: '20px — a page title.' },\n { name: 'text-2xl', kind: 'text', description: '24px — a display figure.' },\n {\n name: 'text-3xl',\n kind: 'text',\n description: 'The largest UI-scaled rung. Reserved for an empty-state headline.',\n },\n {\n name: 'text-content',\n kind: 'text',\n description: 'The selected content size with the prose line-height.',\n },\n {\n name: 'text-code',\n kind: 'text',\n description: 'The selected code size with the code line-height.',\n },\n]\n\n/** Height tokens also size both axes of icon controls through Tailwind's --size\n * namespace. Padding has its own --spacing mapping. Nominal pixel descriptions\n * use a 16px rem; rendered dimensions follow the consumer's root font size. */\nexport const densityTokens: TokenDefinition[] = [\n { name: 'control-height-2xs', kind: 'dimension', description: '20px — an inline chip control.' },\n { name: 'control-height-xs', kind: 'dimension', description: '24px — a dense table row action.' },\n { name: 'control-height-sm', kind: 'dimension', description: '28px — the toolbar default.' },\n {\n name: 'control-height-md',\n kind: 'dimension',\n description: '32px — the form default. Button and Input are this size unless told otherwise.',\n },\n { name: 'control-height-lg', kind: 'dimension', description: '36px — a primary form action.' },\n {\n name: 'control-height-xl',\n kind: 'dimension',\n description: '40px — a hero control in an empty state.',\n },\n {\n name: 'control-height-2xl',\n kind: 'dimension',\n description: '48px — a stable touch target, unchanged by compact density.',\n },\n {\n name: 'control-padding-2xs',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 20px control.',\n },\n {\n name: 'control-padding-xs',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 24px control.',\n },\n {\n name: 'control-padding-sm',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 28px control.',\n },\n {\n name: 'control-padding-md',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 32px control.',\n },\n {\n name: 'control-padding-lg',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 36px control.',\n },\n {\n name: 'control-padding-xl',\n kind: 'dimension',\n description: 'Inline padding that pairs with the 40px control.',\n },\n {\n name: 'control-padding-2xl',\n kind: 'dimension',\n description: 'Inline padding that pairs with the stable 48px touch target.',\n },\n { name: 'row-height-sm', kind: 'dimension', description: '28px — a dense list row.' },\n { name: 'row-height-md', kind: 'dimension', description: '32px — the default list row.' },\n { name: 'row-height-lg', kind: 'dimension', description: '36px — a two-line list row.' },\n { name: 'rail-width', kind: 'dimension', description: '56px — the icon-only rail.' },\n { name: 'rail-width-expanded', kind: 'dimension', description: '236px — the labelled rail.' },\n {\n name: 'rail-item-height',\n kind: 'dimension',\n description: '36px — a rail or sidebar destination.',\n },\n {\n name: 'sidebar-width',\n kind: 'dimension',\n description: '256px — the secondary navigation column.',\n },\n {\n name: 'sidebar-width-compact',\n kind: 'dimension',\n description: '48px — the collapsed secondary column.',\n },\n {\n name: 'panel-width',\n kind: 'dimension',\n description: '390px — a side pane beside the work, and the same pane as a sheet.',\n },\n { name: 'topbar-height', kind: 'dimension', description: '48px — the window title row.' },\n { name: 'statusbar-height', kind: 'dimension', description: '28px — the bottom readout strip.' },\n {\n name: 'tooltip-max-width',\n kind: 'dimension',\n description:\n 'The lesser of the 16rem readable measure and one viewport minus a gutter — a tip wraps before it can push its document wider than itself.',\n },\n]\n\nexport const elevationTokens: TokenDefinition[] = [\n {\n name: 'shadow-2xs',\n kind: 'shadow',\n themed: true,\n description: 'Barely there: a chip resting on a card.',\n },\n {\n name: 'shadow-xs',\n kind: 'shadow',\n themed: true,\n description: 'A card or panel on the canvas.',\n },\n {\n name: 'shadow-sm',\n kind: 'shadow',\n themed: true,\n description: 'A control that lifts: a pressed toggle, a thumb.',\n },\n { name: 'shadow-md', kind: 'shadow', themed: true, description: 'A menu or popover.' },\n { name: 'shadow-lg', kind: 'shadow', themed: true, description: 'A dialog or sheet.' },\n {\n name: 'shadow-xl',\n kind: 'shadow',\n themed: true,\n description: 'The command palette, above everything.',\n },\n]\n\nexport const motionTokens: TokenDefinition[] = [\n {\n name: 'duration-fast',\n kind: 'duration',\n description: '120ms — a state change the user is already watching.',\n },\n { name: 'duration-normal', kind: 'duration', description: '200ms — an entrance.' },\n { name: 'duration-slow', kind: 'duration', description: '320ms — a layout settle.' },\n {\n name: 'ease-out',\n kind: 'easing',\n description: 'A fast start and a long settle. Reads as decisive rather than floaty.',\n },\n {\n name: 'ease-in-out',\n kind: 'easing',\n description: 'For a loop that has no start or end, such as a shimmer.',\n },\n]\n\nexport const zIndexTokens: TokenDefinition[] = [\n { name: 'z-base', kind: 'number', description: 'In flow, at the document order.' },\n {\n name: 'z-docked',\n kind: 'number',\n description: 'A sticky table header standing above its own scroll body.',\n },\n { name: 'z-sticky', kind: 'number', description: 'The top bar and any other pinned chrome.' },\n { name: 'z-drawer', kind: 'number', description: 'A drawer, below a modal dialog.' },\n { name: 'z-dialog', kind: 'number', description: 'A modal dialog and its scrim.' },\n {\n name: 'z-menu',\n kind: 'number',\n description: 'Menus, popovers and comboboxes. Above dialogs, because a dialog can contain one.',\n },\n {\n name: 'z-tooltip',\n kind: 'number',\n description: 'Tooltips. Above menus, because a menu item can carry one.',\n },\n {\n name: 'z-toast',\n kind: 'number',\n description: 'Notifications. Above everything, so they are never hidden by a dialog.',\n },\n]\n\n/**\n * The accent axis.\n *\n * adea's accent presets, as a selection rather than a constant. `theme` is the\n * default and means \"the variant's own primary\" — monochrome in adea's palette —\n * and each named preset overrides the interactive roles through the\n * `[data-accent]` blocks in `theme.css`.\n *\n * These are the same six presets `accentPresets` exports in adea's own\n * `appearance.ts`, and the values match. A consumer's appearance picker and the\n * workshop's theme toolbar read this one list, so the two cannot disagree about\n * what a preset is called or what colour it is.\n */\nexport type AccentPreset = {\n /** The `data-accent` value. `theme` is the variant's own primary. */\n id: string\n label: string\n /** What the preset is for, in the gallery and in a picker. */\n description: string\n /** The accent as it appears in the light theme. Absent for `theme`. */\n light?: string\n /** The accent as it appears in the dark theme. Absent for `theme`. */\n dark?: string\n}\n\nexport const accentPresets: readonly AccentPreset[] = Object.freeze([\n {\n id: 'theme',\n label: 'Theme',\n description:\n \"The variant's own primary. Neutral in adea's palette, so the interface stays monochrome.\",\n },\n ...ACCENTS,\n])\n\n/** Every token, in gallery order. */\nexport const designTokens = {\n color: colorTokens,\n radius: radiusTokens,\n typography: typographyTokens,\n density: densityTokens,\n elevation: elevationTokens,\n motion: motionTokens,\n zIndex: zIndexTokens,\n} as const\n\n/** Flat list, for completeness checks and for search. */\nexport const allTokens: TokenDefinition[] = Object.values(designTokens).flat()\n\n/**\n * Aliases that exist only to make a call site read better, and which therefore\n * have no gallery row of their own. Kept explicit so the completeness check\n * below can tell \"deliberately absent\" from \"forgotten\".\n */\nexport const undocumentedTokenAliases: string[] = ['radius']\n"],"mappings":"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N,MAAM;EACN,aAAa;CACf;CACA;EAAE,MAAM;EAAa,MAAM;EAAa,aAAa;CAAiC;CACtF;EAAE,MAAM;EAAc,MAAM;EAAa,aAAa;CAAyC;AACjG;AAEA,IAAa,mBAAsC;CACjD;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EACE,MAAM;EACN,MAAM;EACN,aACE;CACJ;CACA;EACE,MAAM;EACN,MAAM;EACN,aACE;CACJ;CACA;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EAAE,MAAM;EAAW,MAAM;EAAQ,aAAa;CAAiD;CAC/F;EACE,MAAM;EACN,MAAM;EACN,aAAa;CACf;CACA;EAAE,MAAM;EAAa,MAAM;EAAQ,aAAa;CAAwC;CACxF;EAAE,MAAM;EAAW,MAAM;EAAQ,aAAa;CAA0C;CACxF;EAAE,MAAM;EAAW,MAAM;EAAQ,aAAa;CAAuB;CACrE;EAAE,MAAM;EAAY,MAAM;EAAQ,aAAa;CAA2B;CAC1E;EACE,MAAM;EACN,M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"}
|
package/dist/r/lib.json
CHANGED
|
@@ -40,6 +40,11 @@
|
|
|
40
40
|
"type": "registry:lib",
|
|
41
41
|
"target": "lib/font-catalog.ts"
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
"path": "src/lib/font-zoom.ts",
|
|
45
|
+
"type": "registry:lib",
|
|
46
|
+
"target": "lib/font-zoom.ts"
|
|
47
|
+
},
|
|
43
48
|
{
|
|
44
49
|
"path": "src/lib/form-field.ts",
|
|
45
50
|
"type": "registry:lib",
|
|
@@ -55,6 +60,11 @@
|
|
|
55
60
|
"type": "registry:lib",
|
|
56
61
|
"target": "lib/overlay.ts"
|
|
57
62
|
},
|
|
63
|
+
{
|
|
64
|
+
"path": "src/lib/preference-store.ts",
|
|
65
|
+
"type": "registry:lib",
|
|
66
|
+
"target": "lib/preference-store.ts"
|
|
67
|
+
},
|
|
58
68
|
{
|
|
59
69
|
"path": "src/lib/safe-script.ts",
|
|
60
70
|
"type": "registry:lib",
|
|
@@ -65,6 +75,11 @@
|
|
|
65
75
|
"type": "registry:lib",
|
|
66
76
|
"target": "lib/theme-accents.ts"
|
|
67
77
|
},
|
|
78
|
+
{
|
|
79
|
+
"path": "src/lib/theme-surface.ts",
|
|
80
|
+
"type": "registry:lib",
|
|
81
|
+
"target": "lib/theme-surface.ts"
|
|
82
|
+
},
|
|
68
83
|
{
|
|
69
84
|
"path": "src/lib/themes.ts",
|
|
70
85
|
"type": "registry:lib",
|
package/dist/r/registry.json
CHANGED
|
@@ -2557,6 +2557,11 @@
|
|
|
2557
2557
|
"type": "registry:ui",
|
|
2558
2558
|
"target": "components/theming/theme-provider.tsx"
|
|
2559
2559
|
},
|
|
2560
|
+
{
|
|
2561
|
+
"path": "src/components/theme/theme-script.ts",
|
|
2562
|
+
"type": "registry:ui",
|
|
2563
|
+
"target": "components/theming/theme-script.ts"
|
|
2564
|
+
},
|
|
2560
2565
|
{
|
|
2561
2566
|
"path": "src/components/theme/theme-state.ts",
|
|
2562
2567
|
"type": "registry:ui",
|
|
@@ -2595,7 +2600,7 @@
|
|
|
2595
2600
|
"name": "theme",
|
|
2596
2601
|
"type": "registry:style",
|
|
2597
2602
|
"title": "Adea theme",
|
|
2598
|
-
"description": "The design tokens: the semantic palette in OKLCH, the type and control scales, shells geometry, shadows, motion and the z-index stack.",
|
|
2603
|
+
"description": "The design tokens: the semantic palette in OKLCH, the type and control scales, shells geometry, shadows, motion and the z-index stack, plus the pre-paint stylesheet that carries every catalogue theme.",
|
|
2599
2604
|
"dependencies": [
|
|
2600
2605
|
"@fontsource-variable/geist@^5.3.0",
|
|
2601
2606
|
"@fontsource-variable/geist-mono@^5.3.0",
|
|
@@ -2615,6 +2620,11 @@
|
|
|
2615
2620
|
"type": "registry:style",
|
|
2616
2621
|
"target": "styles/theme.css"
|
|
2617
2622
|
},
|
|
2623
|
+
{
|
|
2624
|
+
"path": "src/styles/theme-variants.css",
|
|
2625
|
+
"type": "registry:style",
|
|
2626
|
+
"target": "styles/theme-variants.css"
|
|
2627
|
+
},
|
|
2618
2628
|
{
|
|
2619
2629
|
"path": "src/styles/base.css",
|
|
2620
2630
|
"type": "registry:style",
|
|
@@ -2674,6 +2684,11 @@
|
|
|
2674
2684
|
"type": "registry:lib",
|
|
2675
2685
|
"target": "lib/font-catalog.ts"
|
|
2676
2686
|
},
|
|
2687
|
+
{
|
|
2688
|
+
"path": "src/lib/font-zoom.ts",
|
|
2689
|
+
"type": "registry:lib",
|
|
2690
|
+
"target": "lib/font-zoom.ts"
|
|
2691
|
+
},
|
|
2677
2692
|
{
|
|
2678
2693
|
"path": "src/lib/form-field.ts",
|
|
2679
2694
|
"type": "registry:lib",
|
|
@@ -2689,6 +2704,11 @@
|
|
|
2689
2704
|
"type": "registry:lib",
|
|
2690
2705
|
"target": "lib/overlay.ts"
|
|
2691
2706
|
},
|
|
2707
|
+
{
|
|
2708
|
+
"path": "src/lib/preference-store.ts",
|
|
2709
|
+
"type": "registry:lib",
|
|
2710
|
+
"target": "lib/preference-store.ts"
|
|
2711
|
+
},
|
|
2692
2712
|
{
|
|
2693
2713
|
"path": "src/lib/safe-script.ts",
|
|
2694
2714
|
"type": "registry:lib",
|
|
@@ -2699,6 +2719,11 @@
|
|
|
2699
2719
|
"type": "registry:lib",
|
|
2700
2720
|
"target": "lib/theme-accents.ts"
|
|
2701
2721
|
},
|
|
2722
|
+
{
|
|
2723
|
+
"path": "src/lib/theme-surface.ts",
|
|
2724
|
+
"type": "registry:lib",
|
|
2725
|
+
"target": "lib/theme-surface.ts"
|
|
2726
|
+
},
|
|
2702
2727
|
{
|
|
2703
2728
|
"path": "src/lib/themes.ts",
|
|
2704
2729
|
"type": "registry:lib",
|
|
@@ -9,6 +9,7 @@ export {
|
|
|
9
9
|
} from './theme-picker'
|
|
10
10
|
export {
|
|
11
11
|
ThemeProvider,
|
|
12
|
+
createVersionedPersistence,
|
|
12
13
|
defaultThemeSelection,
|
|
13
14
|
inlineScriptLiteral,
|
|
14
15
|
localStoragePersistence,
|
|
@@ -16,11 +17,17 @@ export {
|
|
|
16
17
|
themeScript,
|
|
17
18
|
useOptionalTheme,
|
|
18
19
|
useTheme,
|
|
20
|
+
type EditorRolesMode,
|
|
19
21
|
type ThemeContextValue,
|
|
20
22
|
type ThemeDensity,
|
|
21
23
|
type ThemePersistence,
|
|
22
24
|
type ThemeProviderProps,
|
|
25
|
+
type ThemeScriptOptions,
|
|
23
26
|
type ThemeSelection,
|
|
27
|
+
type ThemeStorageSchema,
|
|
28
|
+
type ThemeSurface,
|
|
29
|
+
type VersionedCodec,
|
|
30
|
+
type VersionedPersistenceOptions,
|
|
24
31
|
} from './theme-provider'
|
|
25
32
|
export { ThemeToggle, type ThemeToggleProps } from './theme-toggle'
|
|
26
33
|
export { ThemeModeToggle, type ThemeModeToggleProps } from './theme-mode-toggle'
|
|
@@ -4,11 +4,15 @@ import {
|
|
|
4
4
|
} from '../../lib/appearance-font-settings'
|
|
5
5
|
import {
|
|
6
6
|
accentVariables,
|
|
7
|
+
isCatalogueVariant,
|
|
8
|
+
terminalCssVariables,
|
|
7
9
|
themeCssVariables,
|
|
10
|
+
type EditorRolesMode,
|
|
8
11
|
type ThemeAppearance,
|
|
9
12
|
type ThemeVariant,
|
|
10
13
|
} from '../../lib/themes'
|
|
11
14
|
import { isCustomAccent } from '../../lib/accent-roles'
|
|
15
|
+
import { surfaceAlpha, type SurfaceState } from '../../lib/theme-surface'
|
|
12
16
|
import { resolvedDensity, type ThemeSelection } from './theme-state'
|
|
13
17
|
|
|
14
18
|
/**
|
|
@@ -27,6 +31,16 @@ export type ThemeDocumentState = {
|
|
|
27
31
|
selection: ThemeSelection
|
|
28
32
|
appearance: ThemeAppearance
|
|
29
33
|
variant: ThemeVariant
|
|
34
|
+
/**
|
|
35
|
+
* The variant whose terminal palette is pinned over the interface variant's.
|
|
36
|
+
* Absent, the terminal follows the interface theme.
|
|
37
|
+
*/
|
|
38
|
+
terminalVariant?: ThemeVariant
|
|
39
|
+
/**
|
|
40
|
+
* The resolved surface axis. Absent, the axis is off: no attribute and no
|
|
41
|
+
* `--surface-alpha` is written, and any this writer wrote before is removed.
|
|
42
|
+
*/
|
|
43
|
+
surface?: SurfaceState
|
|
30
44
|
}
|
|
31
45
|
|
|
32
46
|
export type ThemeDocumentOptions = {
|
|
@@ -42,11 +56,52 @@ export type ThemeDocumentOptions = {
|
|
|
42
56
|
* only a custom `#rrggbb` is held to the default floor of 3:1.
|
|
43
57
|
*/
|
|
44
58
|
accentContrastFloor?: number
|
|
59
|
+
/**
|
|
60
|
+
* Where a catalogue variant's palette comes from. `inline` (the default) writes
|
|
61
|
+
* every role onto the root. `stylesheet` writes none for a catalogue variant,
|
|
62
|
+
* because the host loads a stylesheet that carries a `[data-theme]` block for it;
|
|
63
|
+
* a variant the catalogue does not hold (a host's imported theme) is still written
|
|
64
|
+
* inline, since no stylesheet can know it. The accent, the terminal pin, the
|
|
65
|
+
* surface alpha and the fonts are written the same in both.
|
|
66
|
+
*
|
|
67
|
+
* Switching from an imported variant back to a catalogue one removes what the
|
|
68
|
+
* imported one wrote, so nothing inline outlives it to beat the stylesheet.
|
|
69
|
+
*/
|
|
70
|
+
palette?: 'inline' | 'stylesheet'
|
|
71
|
+
/**
|
|
72
|
+
* The `--editor-*` projection. `syntax` (the default) is the catalogue's quiet
|
|
73
|
+
* syntax roles, which is what `theme-variants.css` carries; `floor` holds every
|
|
74
|
+
* editor role to 4.5:1 on the canvas, as hex.
|
|
75
|
+
*
|
|
76
|
+
* With `palette: 'stylesheet'`, `floor` is the one palette write a catalogue
|
|
77
|
+
* variant still gets: the stylesheet is generated for the default projection, so
|
|
78
|
+
* the sixteen `--editor-*` properties are written inline over it. The rest of the
|
|
79
|
+
* palette stays in the stylesheet.
|
|
80
|
+
*/
|
|
81
|
+
editorRoles?: EditorRolesMode
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* What a catalogue variant still writes inline when its palette is the stylesheet's:
|
|
86
|
+
* nothing, unless the editor projection is not the one the stylesheet was generated
|
|
87
|
+
* for.
|
|
88
|
+
*/
|
|
89
|
+
function stylesheetOverrides(
|
|
90
|
+
variant: ThemeVariant,
|
|
91
|
+
editorRoles: EditorRolesMode | undefined
|
|
92
|
+
): Record<string, string> {
|
|
93
|
+
if (editorRoles !== 'floor') return {}
|
|
94
|
+
return Object.fromEntries(
|
|
95
|
+
Object.entries(themeCssVariables(variant, { editor: 'floor' })).filter(([name]) =>
|
|
96
|
+
name.startsWith('--editor-')
|
|
97
|
+
)
|
|
98
|
+
)
|
|
45
99
|
}
|
|
46
100
|
|
|
47
101
|
/**
|
|
48
102
|
* The custom properties a state paints: the variant's roles, with the accent
|
|
49
|
-
* selection laid over them,
|
|
103
|
+
* selection laid over them, the pinned terminal palette over that, the surface
|
|
104
|
+
* alpha, then `transformTokens`.
|
|
50
105
|
*
|
|
51
106
|
* The accent is applied *here* rather than left to the `[data-accent]` blocks in
|
|
52
107
|
* `theme.css`, because the properties are written inline and an inline declaration
|
|
@@ -59,13 +114,22 @@ export function themeDocumentTokens(
|
|
|
59
114
|
state: ThemeDocumentState,
|
|
60
115
|
options: ThemeDocumentOptions = {}
|
|
61
116
|
): { tokens: Record<string, string>; accented: boolean } {
|
|
62
|
-
const
|
|
117
|
+
const fromStylesheet = options.palette === 'stylesheet' && isCatalogueVariant(state.variant)
|
|
118
|
+
const properties = fromStylesheet
|
|
119
|
+
? stylesheetOverrides(state.variant, options.editorRoles)
|
|
120
|
+
: { ...themeCssVariables(state.variant, { editor: options.editorRoles }) }
|
|
63
121
|
const accent = accentVariables(state.selection.accent, state.appearance, state.selection, {
|
|
64
122
|
background: state.variant.colors.background,
|
|
65
123
|
floor: options.accentContrastFloor,
|
|
66
124
|
variantId: state.variant.id,
|
|
67
125
|
})
|
|
68
126
|
if (accent) Object.assign(properties, accent)
|
|
127
|
+
// Written after the variant and the accent, so it wins over the interface
|
|
128
|
+
// variant's own terminal ramp (and over the stylesheet's, which it outranks as an
|
|
129
|
+
// inline declaration). Dropping the pin needs no removal of its own: the ramp is
|
|
130
|
+
// then rewritten by the variant, or swept back to the stylesheet.
|
|
131
|
+
if (state.terminalVariant) Object.assign(properties, terminalCssVariables(state.terminalVariant))
|
|
132
|
+
if (state.surface) properties['--surface-alpha'] = surfaceAlpha[state.surface.surface]
|
|
69
133
|
return {
|
|
70
134
|
tokens: options.transformTokens ? options.transformTokens(properties) : properties,
|
|
71
135
|
accented: accent !== undefined,
|
|
@@ -111,6 +175,17 @@ export function applyThemeToRoot(
|
|
|
111
175
|
? 'custom'
|
|
112
176
|
: state.selection.accent
|
|
113
177
|
|
|
178
|
+
// The surface axis is opt-in, so its absence removes rather than defaults: a host
|
|
179
|
+
// that never enables it keeps an `<html>` with neither attribute, and one that
|
|
180
|
+
// turns it off leaves nothing behind. (`--surface-alpha` is swept with the tokens.)
|
|
181
|
+
if (state.surface) {
|
|
182
|
+
root.dataset['surface'] = state.surface.surface
|
|
183
|
+
root.dataset['reduceTransparency'] = state.surface.reduceTransparencyActive ? 'true' : 'false'
|
|
184
|
+
} else {
|
|
185
|
+
root.removeAttribute('data-surface')
|
|
186
|
+
root.removeAttribute('data-reduce-transparency')
|
|
187
|
+
}
|
|
188
|
+
|
|
114
189
|
applyAppearanceFontSettings(
|
|
115
190
|
root,
|
|
116
191
|
normalizeAppearanceEditorFontSettings(state.selection.fonts, state.selection.font).settings
|
|
@@ -2,6 +2,15 @@ import { normalizeCustomAccent } from '../../lib/accent-roles'
|
|
|
2
2
|
import { accentPresets } from '../../lib/tokens'
|
|
3
3
|
import { normalizeAppearanceEditorFontSettings } from '../../lib/appearance-font-settings'
|
|
4
4
|
import { defaultDarkThemeId, defaultLightThemeId, isThemeAccentId, themeById } from '../../lib/themes'
|
|
5
|
+
import { normalizeSurface } from '../../lib/theme-surface'
|
|
6
|
+
import {
|
|
7
|
+
defaultPreferenceStorage,
|
|
8
|
+
readLegacyValue,
|
|
9
|
+
readVersionedRecord,
|
|
10
|
+
recoveryKeyFor,
|
|
11
|
+
writeVersionedRecord,
|
|
12
|
+
type PreferenceStorage,
|
|
13
|
+
} from '../../lib/preference-store'
|
|
5
14
|
import { defaultThemeSelection, resolvedDensity, type ThemeSelection } from './theme-state'
|
|
6
15
|
|
|
7
16
|
/**
|
|
@@ -83,6 +92,17 @@ function readSelection(storageKey: string): ThemeSelection {
|
|
|
83
92
|
// document effect removes the `data-density` that `themeScript` set for the first paint, and
|
|
84
93
|
// the next `setSelection` rewrites the store without it.
|
|
85
94
|
density: resolvedDensity(parsed as ThemeSelection),
|
|
95
|
+
// The newer axes are read back only when the store holds a usable value and are
|
|
96
|
+
// otherwise left `undefined`, never defaulted: `JSON.stringify` drops them, so a
|
|
97
|
+
// consumer that never sets one writes the bytes it always did. An id is kept as
|
|
98
|
+
// stored, known or not, for the same reason a theme id the registry lacks is.
|
|
99
|
+
terminalThemeId:
|
|
100
|
+
typeof parsed.terminalThemeId === 'string' && parsed.terminalThemeId.trim() !== ''
|
|
101
|
+
? parsed.terminalThemeId
|
|
102
|
+
: undefined,
|
|
103
|
+
surface: normalizeSurface(parsed.surface),
|
|
104
|
+
reduceTransparency:
|
|
105
|
+
typeof parsed.reduceTransparency === 'boolean' ? parsed.reduceTransparency : undefined,
|
|
86
106
|
}
|
|
87
107
|
} catch {
|
|
88
108
|
return defaultThemeSelection
|
|
@@ -141,3 +161,150 @@ export function writePersisted(persistence: ThemePersistence, next: ThemeSelecti
|
|
|
141
161
|
// session and does not survive the reload.
|
|
142
162
|
}
|
|
143
163
|
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Where a host's versioned preference document lives. One schema describes the
|
|
167
|
+
* document to both halves of the system: {@link createVersionedPersistence} reads and
|
|
168
|
+
* writes it, and `themeScript(options)` reads it before first paint, so the two
|
|
169
|
+
* cannot disagree about a key or a version.
|
|
170
|
+
*/
|
|
171
|
+
export type ThemeStorageSchema = {
|
|
172
|
+
/** The storage key of the document. */
|
|
173
|
+
key: string
|
|
174
|
+
/**
|
|
175
|
+
* The only top-level `version` read. Absent, the document is unversioned. A record
|
|
176
|
+
* of any other version is quarantined (see `recoveryKey`), not interpreted.
|
|
177
|
+
*/
|
|
178
|
+
version?: number
|
|
179
|
+
/**
|
|
180
|
+
* The document field that holds the appearance preference (`system`, `light`,
|
|
181
|
+
* `dark`). Read by `themeScript`; the persistence leaves field names to the codec.
|
|
182
|
+
* Default `appearance`.
|
|
183
|
+
*/
|
|
184
|
+
appearanceField?: 'appearance' | 'mode'
|
|
185
|
+
/**
|
|
186
|
+
* An older single-value key (`light` | `dark` | `system`) read only while `key` is
|
|
187
|
+
* absent, and never deleted. The persistence hands it to the codec's `fromLegacy`.
|
|
188
|
+
*/
|
|
189
|
+
legacyAppearanceKey?: string
|
|
190
|
+
/**
|
|
191
|
+
* The stored library of imported themes, for `themeScript` only: an imported
|
|
192
|
+
* theme's id and verbatim tokens, so the first paint can use the right variant. The
|
|
193
|
+
* persistence neither reads nor writes it.
|
|
194
|
+
*/
|
|
195
|
+
libraryKey?: string
|
|
196
|
+
/** Where an unreadable document is kept. Default `${key}.recovery`. */
|
|
197
|
+
recoveryKey?: string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* How a host's document maps to and from the shared {@link ThemeSelection}.
|
|
202
|
+
*
|
|
203
|
+
* The codec is the product schema: it decides which fields a document has, how an
|
|
204
|
+
* unknown value is corrected, and what a fresh install looks like. The engine decides
|
|
205
|
+
* everything about *storage*: what is quarantined, when the legacy key is read, and
|
|
206
|
+
* that nothing is written until `setSelection` asks.
|
|
207
|
+
*/
|
|
208
|
+
export type VersionedCodec<Doc> = {
|
|
209
|
+
/**
|
|
210
|
+
* A readable record (the right JSON shape and version) as a selection patch. Fields
|
|
211
|
+
* it leaves `undefined` fall back to the provider's defaults. It may keep an id the
|
|
212
|
+
* catalogue lacks; the provider resolves it, it does not rewrite it.
|
|
213
|
+
*/
|
|
214
|
+
decode(record: Readonly<Record<string, unknown>>): Partial<ThemeSelection>
|
|
215
|
+
/**
|
|
216
|
+
* The legacy key's value (never `null`) as a selection patch, or `undefined` for a
|
|
217
|
+
* value it does not recognise. Called only when the main key is absent.
|
|
218
|
+
*/
|
|
219
|
+
fromLegacy?(value: string): Partial<ThemeSelection> | undefined
|
|
220
|
+
/**
|
|
221
|
+
* What a store with nothing readable answers: a fresh install, an unreadable
|
|
222
|
+
* document, a blocked store. Absent, the provider's own defaults apply.
|
|
223
|
+
*/
|
|
224
|
+
defaults?(): Partial<ThemeSelection> | undefined
|
|
225
|
+
/**
|
|
226
|
+
* The document to store for a selection. `previous` is the record last read or
|
|
227
|
+
* written, so a codec can carry a field the selection does not model.
|
|
228
|
+
*/
|
|
229
|
+
encode(next: ThemeSelection, previous?: Readonly<Record<string, unknown>>): Doc
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Seams for the storage and the clock; both default to the real ones. */
|
|
233
|
+
export type VersionedPersistenceOptions = {
|
|
234
|
+
/** The store, or nothing. Called per access; default is `localStorage` (blocked-safe). */
|
|
235
|
+
storage?: () => PreferenceStorage | undefined
|
|
236
|
+
/** The clock for the recovery envelope's `capturedAt`. */
|
|
237
|
+
now?: () => Date
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A {@link ThemePersistence} over a versioned document and a host codec.
|
|
242
|
+
*
|
|
243
|
+
* `read` follows the engine's rules (`lib/preference-store.ts`): the main key first;
|
|
244
|
+
* an unreadable or unsupported document is quarantined byte for byte into the
|
|
245
|
+
* recovery key and the codec's `defaults` answer; the legacy key only when the main
|
|
246
|
+
* key is absent. It never writes the main key, so the first write is a user's
|
|
247
|
+
* decision. `write` stores `codec.encode(next, previous)` as JSON and swallows a
|
|
248
|
+
* failure.
|
|
249
|
+
*
|
|
250
|
+
* Neither method throws. The stored bytes are exactly what the codec encodes, so a
|
|
251
|
+
* host keeps its keys, field names and bytes by keeping its codec.
|
|
252
|
+
*/
|
|
253
|
+
export function createVersionedPersistence<Doc>(
|
|
254
|
+
schema: ThemeStorageSchema,
|
|
255
|
+
codec: VersionedCodec<Doc>,
|
|
256
|
+
options: VersionedPersistenceOptions = {}
|
|
257
|
+
): ThemePersistence {
|
|
258
|
+
const storageOf = options.storage ?? defaultPreferenceStorage
|
|
259
|
+
const recordSchema = {
|
|
260
|
+
key: schema.key,
|
|
261
|
+
version: schema.version,
|
|
262
|
+
recoveryKey: recoveryKeyFor(schema),
|
|
263
|
+
}
|
|
264
|
+
let previous: Readonly<Record<string, unknown>> | undefined
|
|
265
|
+
|
|
266
|
+
const fallback = () => {
|
|
267
|
+
try {
|
|
268
|
+
return codec.defaults?.()
|
|
269
|
+
} catch {
|
|
270
|
+
return undefined
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
read() {
|
|
276
|
+
const storage = storageOf()
|
|
277
|
+
if (!storage) return fallback()
|
|
278
|
+
try {
|
|
279
|
+
const found = readVersionedRecord(storage, recordSchema, options.now)
|
|
280
|
+
if (found.status === 'record') {
|
|
281
|
+
previous = found.record
|
|
282
|
+
return codec.decode(found.record)
|
|
283
|
+
}
|
|
284
|
+
if (found.status === 'absent' && schema.legacyAppearanceKey && codec.fromLegacy) {
|
|
285
|
+
const legacy = readLegacyValue(storage, schema.legacyAppearanceKey)
|
|
286
|
+
const migrated = legacy === null ? undefined : codec.fromLegacy(legacy)
|
|
287
|
+
if (migrated) return migrated
|
|
288
|
+
}
|
|
289
|
+
} catch {
|
|
290
|
+
// A codec that throws on a record it cannot read is an unreadable store.
|
|
291
|
+
}
|
|
292
|
+
return fallback()
|
|
293
|
+
},
|
|
294
|
+
write(next) {
|
|
295
|
+
const storage = storageOf()
|
|
296
|
+
if (!storage) return
|
|
297
|
+
try {
|
|
298
|
+
const document = codec.encode(next, previous)
|
|
299
|
+
if (writeVersionedRecord(storage, schema.key, document)) {
|
|
300
|
+
previous =
|
|
301
|
+
typeof document === 'object' && document !== null
|
|
302
|
+
? (document as Readonly<Record<string, unknown>>)
|
|
303
|
+
: undefined
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
// An encoder that throws loses the preference, not the switch.
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
}
|