@adea-ai/ui 0.128.2 → 0.128.3

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.
Files changed (132) hide show
  1. package/dist/components/theme/appearance-panel.js +1 -1
  2. package/dist/components/theme/index.js +1 -1
  3. package/dist/components/theme/lazy.d.ts +15 -0
  4. package/dist/components/theme/lazy.d.ts.map +1 -0
  5. package/dist/components/theme/lazy.js +9 -0
  6. package/dist/components/theme/theme-catalogue-source.d.ts +15 -0
  7. package/dist/components/theme/theme-catalogue-source.d.ts.map +1 -0
  8. package/dist/components/theme/theme-catalogue-source.js +51 -0
  9. package/dist/components/theme/theme-catalogue-source.js.map +1 -0
  10. package/dist/components/theme/theme-document-core.d.ts +123 -0
  11. package/dist/components/theme/theme-document-core.d.ts.map +1 -0
  12. package/dist/components/theme/{theme-document.js → theme-document-core.js} +12 -12
  13. package/dist/components/theme/theme-document-core.js.map +1 -0
  14. package/dist/components/theme/theme-document.d.ts +8 -107
  15. package/dist/components/theme/theme-document.d.ts.map +1 -1
  16. package/dist/components/theme/theme-persistence.d.ts +1 -1
  17. package/dist/components/theme/theme-persistence.d.ts.map +1 -1
  18. package/dist/components/theme/theme-persistence.js +9 -6
  19. package/dist/components/theme/theme-persistence.js.map +1 -1
  20. package/dist/components/theme/theme-provider-core.d.ts +19 -0
  21. package/dist/components/theme/theme-provider-core.d.ts.map +1 -0
  22. package/dist/components/theme/theme-provider-core.js +28 -0
  23. package/dist/components/theme/theme-provider-core.js.map +1 -0
  24. package/dist/components/theme/theme-provider-lazy.d.ts +28 -0
  25. package/dist/components/theme/theme-provider-lazy.d.ts.map +1 -0
  26. package/dist/components/theme/theme-provider-lazy.js +36 -0
  27. package/dist/components/theme/theme-provider-lazy.js.map +1 -0
  28. package/dist/components/theme/theme-provider-value.d.ts +175 -0
  29. package/dist/components/theme/theme-provider-value.d.ts.map +1 -0
  30. package/dist/components/theme/theme-provider-value.js +200 -0
  31. package/dist/components/theme/theme-provider-value.js.map +1 -0
  32. package/dist/components/theme/theme-provider.d.ts +11 -136
  33. package/dist/components/theme/theme-provider.d.ts.map +1 -1
  34. package/dist/components/theme/theme-provider.js +27 -149
  35. package/dist/components/theme/theme-provider.js.map +1 -1
  36. package/dist/components/theme/theme-script.js +4 -3
  37. package/dist/components/theme/theme-script.js.map +1 -1
  38. package/dist/components/theme/theme-state-core.d.ts +248 -0
  39. package/dist/components/theme/theme-state-core.d.ts.map +1 -0
  40. package/dist/components/theme/{theme-state.js → theme-state-core.js} +8 -7
  41. package/dist/components/theme/theme-state-core.js.map +1 -0
  42. package/dist/components/theme/theme-state.d.ts +8 -214
  43. package/dist/components/theme/theme-state.d.ts.map +1 -1
  44. package/dist/components/theme/theme-toggle.js.map +1 -1
  45. package/dist/index.js +7 -4
  46. package/dist/lib/accent-presets.d.ts +26 -0
  47. package/dist/lib/accent-presets.d.ts.map +1 -0
  48. package/dist/lib/accent-presets.js +11 -0
  49. package/dist/lib/accent-presets.js.map +1 -0
  50. package/dist/lib/theme-catalogue.d.ts +3 -0
  51. package/dist/lib/theme-catalogue.d.ts.map +1 -0
  52. package/dist/lib/theme-catalogue.js +22 -0
  53. package/dist/lib/theme-catalogue.js.map +1 -0
  54. package/dist/lib/theme-css.d.ts +43 -0
  55. package/dist/lib/theme-css.d.ts.map +1 -0
  56. package/dist/lib/theme-css.js +167 -0
  57. package/dist/lib/theme-css.js.map +1 -0
  58. package/dist/lib/theme-defaults.d.ts +46 -0
  59. package/dist/lib/theme-defaults.d.ts.map +1 -0
  60. package/dist/lib/theme-defaults.generated.d.ts +13 -0
  61. package/dist/lib/theme-defaults.generated.d.ts.map +1 -0
  62. package/dist/lib/theme-defaults.generated.js +318 -0
  63. package/dist/lib/theme-defaults.generated.js.map +1 -0
  64. package/dist/lib/theme-defaults.js +47 -0
  65. package/dist/lib/theme-defaults.js.map +1 -0
  66. package/dist/lib/theme-engine-light.d.ts +3 -0
  67. package/dist/lib/theme-engine-light.d.ts.map +1 -0
  68. package/dist/lib/theme-engine-light.js +75 -0
  69. package/dist/lib/theme-engine-light.js.map +1 -0
  70. package/dist/lib/theme-engine.d.ts +69 -0
  71. package/dist/lib/theme-engine.d.ts.map +1 -0
  72. package/dist/lib/theme-engine.js +33 -0
  73. package/dist/lib/theme-engine.js.map +1 -0
  74. package/dist/lib/themes.d.ts +14 -48
  75. package/dist/lib/themes.d.ts.map +1 -1
  76. package/dist/lib/themes.js +21 -163
  77. package/dist/lib/themes.js.map +1 -1
  78. package/dist/lib/tokens.d.ts +1 -25
  79. package/dist/lib/tokens.d.ts.map +1 -1
  80. package/dist/lib/tokens.js +1 -24
  81. package/dist/lib/tokens.js.map +1 -1
  82. package/dist/r/lib.json +35 -0
  83. package/dist/r/registry.json +70 -0
  84. package/dist/r/src/components/theme/lazy.ts +35 -0
  85. package/dist/r/src/components/theme/theme-catalogue-source.ts +59 -0
  86. package/dist/r/src/components/theme/theme-document-core.ts +262 -0
  87. package/dist/r/src/components/theme/theme-document.ts +23 -240
  88. package/dist/r/src/components/theme/theme-persistence.ts +14 -6
  89. package/dist/r/src/components/theme/theme-provider-core.tsx +40 -0
  90. package/dist/r/src/components/theme/theme-provider-lazy.tsx +34 -0
  91. package/dist/r/src/components/theme/theme-provider-value.ts +445 -0
  92. package/dist/r/src/components/theme/theme-provider.tsx +23 -346
  93. package/dist/r/src/components/theme/theme-script.ts +3 -3
  94. package/dist/r/src/components/theme/theme-state-core.ts +364 -0
  95. package/dist/r/src/components/theme/theme-state.ts +10 -329
  96. package/dist/r/src/components/theme/theme-toggle.tsx +1 -1
  97. package/dist/r/src/lib/accent-presets.ts +43 -0
  98. package/dist/r/src/lib/theme-catalogue.ts +26 -0
  99. package/dist/r/src/lib/theme-css.ts +201 -0
  100. package/dist/r/src/lib/theme-defaults.generated.ts +319 -0
  101. package/dist/r/src/lib/theme-defaults.ts +79 -0
  102. package/dist/r/src/lib/theme-engine-light.ts +117 -0
  103. package/dist/r/src/lib/theme-engine.ts +111 -0
  104. package/dist/r/src/lib/themes.ts +27 -191
  105. package/dist/r/src/lib/tokens.ts +1 -35
  106. package/dist/r/theming.json +35 -0
  107. package/package.json +10 -2
  108. package/registry.json +70 -0
  109. package/src/components/theme/lazy.ts +35 -0
  110. package/src/components/theme/theme-catalogue-source.ts +59 -0
  111. package/src/components/theme/theme-document-core.ts +262 -0
  112. package/src/components/theme/theme-document.ts +23 -240
  113. package/src/components/theme/theme-persistence.ts +14 -6
  114. package/src/components/theme/theme-provider-core.tsx +40 -0
  115. package/src/components/theme/theme-provider-lazy.tsx +34 -0
  116. package/src/components/theme/theme-provider-value.ts +445 -0
  117. package/src/components/theme/theme-provider.tsx +23 -346
  118. package/src/components/theme/theme-script.ts +3 -3
  119. package/src/components/theme/theme-state-core.ts +364 -0
  120. package/src/components/theme/theme-state.ts +10 -329
  121. package/src/components/theme/theme-toggle.tsx +1 -1
  122. package/src/lib/accent-presets.ts +43 -0
  123. package/src/lib/theme-catalogue.ts +26 -0
  124. package/src/lib/theme-css.ts +201 -0
  125. package/src/lib/theme-defaults.generated.ts +319 -0
  126. package/src/lib/theme-defaults.ts +79 -0
  127. package/src/lib/theme-engine-light.ts +117 -0
  128. package/src/lib/theme-engine.ts +111 -0
  129. package/src/lib/themes.ts +27 -191
  130. package/src/lib/tokens.ts +1 -35
  131. package/dist/components/theme/theme-document.js.map +0 -1
  132. package/dist/components/theme/theme-state.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { cn } from "../../lib/utils.js";
2
2
  import { Heading } from "../ui/typography/typography.js";
3
3
  import { RadioGroup as RadioGroup$1, RadioGroupItem } from "../ui/radio-group/radio-group.js";
4
- import { accentPresets } from "../../lib/tokens.js";
4
+ import { accentPresets } from "../../lib/accent-presets.js";
5
5
  import { themesForAppearance } from "../../lib/themes.js";
6
6
  import { DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS } from "../../lib/appearance-font-settings.js";
7
7
  import { AccentSwatchGroups } from "../composites/appearance-editor/accent-swatch-groups.js";
@@ -1,6 +1,6 @@
1
1
  import { ThemePicker, ThemePreview, ThemeSwatch } from "./theme-picker.js";
2
2
  import { useOptionalTheme, useTheme } from "./theme-context.js";
3
- import { defaultThemeSelection, resolvedDensity } from "./theme-state.js";
3
+ import { defaultThemeSelection, resolvedDensity } from "./theme-state-core.js";
4
4
  import { createVersionedPersistence, localStoragePersistence } from "./theme-persistence.js";
5
5
  import { inlineScriptLiteral, themeScript } from "./theme-script.js";
6
6
  import { ThemeProvider } from "./theme-provider.js";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The theme provider with the catalogue out of the start-up path.
3
+ *
4
+ * Everything here is free of the 34-theme catalogue: `import` this entry instead of
5
+ * `@adea-ai/ui/components/theme` in a host that paints its theme from a stylesheet.
6
+ * The catalogue loads, in a chunk of its own, the first time a stored or previewed
7
+ * theme needs it. See {@link ThemeProvider} for the rules and `docs/consumption.md`
8
+ * for the setup.
9
+ */
10
+ export { loadThemeCatalogue } from './theme-catalogue-source';
11
+ export { ThemeProvider } from './theme-provider-lazy';
12
+ export { createVersionedPersistence, defaultThemeSelection, inlineScriptLiteral, localStoragePersistence, resolvedDensity, themeScript, useOptionalTheme, useTheme, type EditorRolesMode, type ThemeCatalogueSource, type ThemeContextValue, type ThemeDensity, type ThemePersistence, type ThemeProviderProps, type ThemeScriptOptions, type ThemeSelection, type ThemeStorageSchema, type ThemeSurface, type VersionedCodec, type VersionedPersistenceOptions, } from './theme-provider-core';
13
+ export { ThemeToggle, type ThemeToggleProps } from './theme-toggle';
14
+ export { ThemeModeToggle, type ThemeModeToggleProps } from './theme-mode-toggle';
15
+ //# sourceMappingURL=lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../../src/components/theme/lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,2BAA2B,GACjC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { useOptionalTheme, useTheme } from "./theme-context.js";
2
+ import { defaultThemeSelection, resolvedDensity } from "./theme-state-core.js";
3
+ import { createVersionedPersistence, localStoragePersistence } from "./theme-persistence.js";
4
+ import { inlineScriptLiteral, themeScript } from "./theme-script.js";
5
+ import { ThemeToggle } from "./theme-toggle.js";
6
+ import { ThemeModeToggle } from "./theme-mode-toggle.js";
7
+ import { loadThemeCatalogue } from "./theme-catalogue-source.js";
8
+ import { ThemeProvider } from "./theme-provider-lazy.js";
9
+ export { ThemeModeToggle, ThemeProvider, ThemeToggle, createVersionedPersistence, defaultThemeSelection, inlineScriptLiteral, loadThemeCatalogue, localStoragePersistence, resolvedDensity, themeScript, useOptionalTheme, useTheme };
@@ -0,0 +1,15 @@
1
+ import type { ThemeEngine } from '#lib/theme-engine';
2
+ import type { ThemeCatalogueSource } from './theme-provider-value';
3
+ /**
4
+ * Load the whole theme catalogue (about 129 KB raw, 34 KB gzipped) and resolve to the
5
+ * engine backed by it. Idempotent: every call shares one import, and a failed import is
6
+ * forgotten so the next call retries.
7
+ *
8
+ * The lazy provider calls this itself the moment a selection needs a theme beyond the
9
+ * default pair. Call it earlier — when a settings page mounts, or on hover of its
10
+ * entry — to have the picker ready by the time it opens.
11
+ */
12
+ export declare function loadThemeCatalogue(): Promise<ThemeEngine>;
13
+ /** A catalogue source for one provider: the shipped defaults until the load resolves. */
14
+ export declare function createLazyCatalogue(): ThemeCatalogueSource;
15
+ //# sourceMappingURL=theme-catalogue-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-catalogue-source.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-catalogue-source.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAUlE;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,WAAW,CAAC,CAazD;AAED,yFAAyF;AACzF,wBAAgB,mBAAmB,IAAI,oBAAoB,CAoB1D"}
@@ -0,0 +1,51 @@
1
+ import { lightThemeEngine } from "../../lib/theme-engine-light.js";
2
+ import { createSignal } from "solid-js";
3
+ //#region src/components/theme/theme-catalogue-source.ts
4
+ /**
5
+ * The full catalogue engine once something has asked for it, shared by every lazy
6
+ * provider and every caller of {@link loadThemeCatalogue} in the page. A provider
7
+ * created after the load starts with it already in hand.
8
+ */
9
+ var loadedEngine;
10
+ var loading;
11
+ /**
12
+ * Load the whole theme catalogue (about 129 KB raw, 34 KB gzipped) and resolve to the
13
+ * engine backed by it. Idempotent: every call shares one import, and a failed import is
14
+ * forgotten so the next call retries.
15
+ *
16
+ * The lazy provider calls this itself the moment a selection needs a theme beyond the
17
+ * default pair. Call it earlier — when a settings page mounts, or on hover of its
18
+ * entry — to have the picker ready by the time it opens.
19
+ */
20
+ function loadThemeCatalogue() {
21
+ if (loadedEngine) return Promise.resolve(loadedEngine);
22
+ loading ??= import("../../lib/theme-catalogue.js").then((module) => {
23
+ loadedEngine = module.catalogueThemeEngine;
24
+ return loadedEngine;
25
+ }, (error) => {
26
+ loading = void 0;
27
+ throw error;
28
+ });
29
+ return loading;
30
+ }
31
+ /** A catalogue source for one provider: the shipped defaults until the load resolves. */
32
+ function createLazyCatalogue() {
33
+ const [full, setFull] = createSignal(loadedEngine);
34
+ const [failed, setFailed] = createSignal(false);
35
+ const load = () => loadThemeCatalogue().then((loaded) => {
36
+ setFailed(false);
37
+ setFull(() => loaded);
38
+ }, () => {
39
+ setFailed(true);
40
+ });
41
+ return {
42
+ engine: () => full() ?? lightThemeEngine,
43
+ loaded: () => full() !== void 0,
44
+ failed,
45
+ load
46
+ };
47
+ }
48
+ //#endregion
49
+ export { createLazyCatalogue, loadThemeCatalogue };
50
+
51
+ //# sourceMappingURL=theme-catalogue-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-catalogue-source.js","names":[],"sources":["../../../src/components/theme/theme-catalogue-source.ts"],"sourcesContent":["import { createSignal } from 'solid-js'\nimport type { ThemeEngine } from '#lib/theme-engine'\nimport { lightThemeEngine } from '#lib/theme-engine-light'\nimport type { ThemeCatalogueSource } from './theme-provider-value'\n\n/**\n * The full catalogue engine once something has asked for it, shared by every lazy\n * provider and every caller of {@link loadThemeCatalogue} in the page. A provider\n * created after the load starts with it already in hand.\n */\nlet loadedEngine: ThemeEngine | undefined\nlet loading: Promise<ThemeEngine> | undefined\n\n/**\n * Load the whole theme catalogue (about 129 KB raw, 34 KB gzipped) and resolve to the\n * engine backed by it. Idempotent: every call shares one import, and a failed import is\n * forgotten so the next call retries.\n *\n * The lazy provider calls this itself the moment a selection needs a theme beyond the\n * default pair. Call it earlier — when a settings page mounts, or on hover of its\n * entry — to have the picker ready by the time it opens.\n */\nexport function loadThemeCatalogue(): Promise<ThemeEngine> {\n if (loadedEngine) return Promise.resolve(loadedEngine)\n loading ??= import('#lib/theme-catalogue').then(\n (module) => {\n loadedEngine = module.catalogueThemeEngine\n return loadedEngine\n },\n (error: unknown) => {\n loading = undefined\n throw error\n }\n )\n return loading\n}\n\n/** A catalogue source for one provider: the shipped defaults until the load resolves. */\nexport function createLazyCatalogue(): ThemeCatalogueSource {\n const [full, setFull] = createSignal<ThemeEngine | undefined>(loadedEngine)\n const [failed, setFailed] = createSignal(false)\n const load = () =>\n loadThemeCatalogue().then(\n (loaded) => {\n setFailed(false)\n setFull(() => loaded)\n },\n () => {\n // The theme the stylesheet painted stays; the next request retries.\n setFailed(true)\n }\n )\n return {\n engine: () => full() ?? lightThemeEngine,\n loaded: () => full() !== undefined,\n failed,\n load,\n }\n}\n"],"mappings":";;;;;;;;AAUA,IAAI;AACJ,IAAI;;;;;;;;;;AAWJ,SAAgB,qBAA2C;CACzD,IAAI,cAAc,OAAO,QAAQ,QAAQ,YAAY;CACrD,YAAY,OAAO,+BAAuB,CAAC,MACxC,WAAW;EACV,eAAe,OAAO;EACtB,OAAO;CACT,IACC,UAAmB;EAClB,UAAU,KAAA;EACV,MAAM;CACR,CACF;CACA,OAAO;AACT;;AAGA,SAAgB,sBAA4C;CAC1D,MAAM,CAAC,MAAM,WAAW,aAAsC,YAAY;CAC1E,MAAM,CAAC,QAAQ,aAAa,aAAa,KAAK;CAC9C,MAAM,aACJ,mBAAmB,CAAC,CAAC,MAClB,WAAW;EACV,UAAU,KAAK;EACf,cAAc,MAAM;CACtB,SACM;EAEJ,UAAU,IAAI;CAChB,CACF;CACF,OAAO;EACL,cAAc,KAAK,KAAK;EACxB,cAAc,KAAK,MAAM,KAAA;EACzB;EACA;CACF;AACF"}
@@ -0,0 +1,123 @@
1
+ import type { ThemeEngine } from '#lib/theme-engine';
2
+ import type { EditorRolesMode, ThemeAppearance, ThemeVariant } from '#lib/themes';
3
+ import { type SurfaceState } from '#lib/theme-surface';
4
+ import { type ThemeSelection } from './theme-state-core';
5
+ /**
6
+ * The document half of the theme provider: the single writer of `<html>`.
7
+ *
8
+ * Written against the few members of an element it uses, so the rules can be tested
9
+ * on a stand-in and the provider's effect stays a one-line call.
10
+ *
11
+ * This is the engine-parameterised half: every question the catalogue answers goes
12
+ * through the {@link ThemeEngine} it is given, so it imports no catalogue code or data.
13
+ * `theme-document.ts` binds the full catalogue and keeps the signatures the eager
14
+ * provider and the tests have always used.
15
+ */
16
+ export type ThemeRoot = Pick<HTMLElement, 'classList' | 'dataset' | 'style' | 'removeAttribute' | 'setAttribute'>;
17
+ export type ThemeDocumentState = {
18
+ /** What the document shows: the committed selection, or a preview laid over it. */
19
+ selection: ThemeSelection;
20
+ appearance: ThemeAppearance;
21
+ variant: ThemeVariant;
22
+ /**
23
+ * The variant whose terminal palette is pinned over the interface variant's.
24
+ * Absent, the terminal follows the interface theme.
25
+ */
26
+ terminalVariant?: ThemeVariant;
27
+ /**
28
+ * The resolved surface axis. Absent, the axis is off: no attribute and no
29
+ * `--surface-alpha` is written, and any this writer wrote before is removed.
30
+ */
31
+ surface?: SurfaceState;
32
+ };
33
+ export type ThemeDocumentOptions = {
34
+ /**
35
+ * Rewrites the custom properties just before they are written: the variant's
36
+ * roles with the accent laid over them. It sees every property of the pass and
37
+ * must return the full map; a name it drops is not written.
38
+ */
39
+ transformTokens?: (tokens: Record<string, string>) => Record<string, string>;
40
+ /**
41
+ * The contrast an accent is held to against the variant's background. Absent, a
42
+ * preset is painted verbatim (the catalogue measured it for its own themes) and
43
+ * only a custom `#rrggbb` is held to the default floor of 3:1.
44
+ */
45
+ accentContrastFloor?: number;
46
+ /**
47
+ * Where a catalogue variant's palette comes from. `inline` (the default) writes
48
+ * every role onto the root. `stylesheet` writes none for a catalogue variant,
49
+ * because the host loads a stylesheet that carries a `[data-theme]` block for it;
50
+ * a variant the catalogue does not hold (a host's imported theme) is still written
51
+ * inline, since no stylesheet can know it. The accent, the terminal pin, the
52
+ * surface alpha and the fonts are written the same in both.
53
+ *
54
+ * Switching from an imported variant back to a catalogue one removes what the
55
+ * imported one wrote, so nothing inline outlives it to beat the stylesheet.
56
+ */
57
+ palette?: 'inline' | 'stylesheet';
58
+ /**
59
+ * The `--editor-*` projection. `syntax` (the default) is the catalogue's quiet
60
+ * syntax roles, which is what `theme-variants.css` carries; `floor` holds every
61
+ * editor role to 4.5:1 on the canvas, as hex.
62
+ *
63
+ * With `palette: 'stylesheet'`, `floor` is the one palette write a catalogue
64
+ * variant still gets: the stylesheet is generated for the default projection, so
65
+ * the sixteen `--editor-*` properties are written inline over it. The rest of the
66
+ * palette stays in the stylesheet.
67
+ */
68
+ editorRoles?: EditorRolesMode;
69
+ };
70
+ /**
71
+ * The custom properties a state paints: the variant's roles, with the accent
72
+ * selection laid over them, the pinned terminal palette over that, the surface
73
+ * alpha, then `transformTokens`.
74
+ *
75
+ * The accent is applied *here* rather than left to the `[data-accent]` blocks in
76
+ * `theme.css`, because the properties are written inline and an inline declaration
77
+ * outranks every stylesheet selector however specific it is. The blocks still exist
78
+ * and are still correct — they are what styles the window before this runs — but
79
+ * they cannot win from here, so leaving the accent to them meant the attribute was
80
+ * set, the rules were present, and nothing changed on screen.
81
+ */
82
+ export declare function themeDocumentTokensWith(engine: ThemeEngine, state: ThemeDocumentState, options?: ThemeDocumentOptions): {
83
+ tokens: Record<string, string>;
84
+ accented: boolean;
85
+ };
86
+ /**
87
+ * Write a state onto a root and report which custom properties it owns.
88
+ *
89
+ * Pass the previous call's return as `previous` and any palette property that pass
90
+ * wrote and this one does not is removed. For the catalogue the two sets are the
91
+ * same, so nothing is ever removed; it matters for a host variant that carries
92
+ * `tokens` the catalogue does not, which would otherwise stay inline, beating the
93
+ * stylesheet, after the user switched away from it.
94
+ */
95
+ export declare function applyThemeToRootWith(engine: ThemeEngine, root: ThemeRoot, state: ThemeDocumentState, options?: ThemeDocumentOptions, previous?: ReadonlySet<string>): ReadonlySet<string>;
96
+ /** What {@link applyWithoutTransitions} needs of a document. */
97
+ export type TransitionDocument = {
98
+ head: {
99
+ appendChild(node: unknown): unknown;
100
+ };
101
+ createElement(tag: 'style'): {
102
+ textContent: string | null;
103
+ offsetHeight?: number;
104
+ remove(): void;
105
+ };
106
+ };
107
+ /**
108
+ * Run `apply` with every CSS transition switched off for the frame that carries
109
+ * it, so a theme switch cannot animate page-wide.
110
+ *
111
+ * Without this, flipping the custom properties on `<html>` starts a transition on
112
+ * every element that declares one, and each fades from the old palette to the new
113
+ * over its own duration: a theme change that looks like a rendering fault. The
114
+ * rule is removed two frames later, after the browser has committed the new styles
115
+ * with transitions off, and `restore` runs it early (the provider calls it when it
116
+ * is disposed mid-switch).
117
+ *
118
+ * `schedule` is the frame source; it defaults to `requestAnimationFrame` and falls
119
+ * back to running at once where there is none, which leaves transitions on rather
120
+ * than off.
121
+ */
122
+ export declare function applyWithoutTransitions(doc: TransitionDocument, apply: () => void, schedule?: (callback: () => void) => void): () => void;
123
+ //# sourceMappingURL=theme-document-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-document-core.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-document-core.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEjF,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEzE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GAAG,IAAI,CAC1B,WAAW,EACX,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,iBAAiB,GAAG,cAAc,CACvE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,mFAAmF;IACnF,SAAS,EAAE,cAAc,CAAA;IACzB,UAAU,EAAE,eAAe,CAAA;IAC3B,OAAO,EAAE,YAAY,CAAA;IACrB;;;OAGG;IACH,eAAe,CAAC,EAAE,YAAY,CAAA;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5E;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAA;IACjC;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,eAAe,CAAA;CAC9B,CAAA;AAoBD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,oBAAyB,GACjC;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAsBvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,oBAAyB,EAClC,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC7B,WAAW,CAAC,MAAM,CAAC,CAiDrB;AAED,gEAAgE;AAChE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE;QAAE,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAA;IAC7C,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,MAAM,IAAI,IAAI,CAAA;KACf,CAAA;CACF,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,IAAI,EACjB,QAAQ,GAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAsB,GACzD,MAAM,IAAI,CAqBZ"}
@@ -1,17 +1,17 @@
1
1
  import { isCustomAccent } from "../../lib/accent-roles.js";
2
- import { accentVariables, isCatalogueVariant, terminalCssVariables, themeCssVariables } from "../../lib/themes.js";
2
+ import { terminalCssVariables } from "../../lib/theme-css.js";
3
3
  import { applyAppearanceFontSettings, normalizeAppearanceEditorFontSettings } from "../../lib/appearance-font-settings.js";
4
4
  import { surfaceAlpha } from "../../lib/theme-surface.js";
5
- import { resolvedDensity } from "./theme-state.js";
6
- //#region src/components/theme/theme-document.ts
5
+ import { resolvedDensity } from "./theme-state-core.js";
6
+ //#region src/components/theme/theme-document-core.ts
7
7
  /**
8
8
  * What a catalogue variant still writes inline when its palette is the stylesheet's:
9
9
  * nothing, unless the editor projection is not the one the stylesheet was generated
10
10
  * for.
11
11
  */
12
- function stylesheetOverrides(variant, editorRoles) {
12
+ function stylesheetOverrides(engine, variant, editorRoles) {
13
13
  if (editorRoles !== "floor") return {};
14
- return Object.fromEntries(Object.entries(themeCssVariables(variant, { editor: "floor" })).filter(([name]) => name.startsWith("--editor-")));
14
+ return Object.fromEntries(Object.entries(engine.themeCssVariables(variant, { editor: "floor" })).filter(([name]) => name.startsWith("--editor-")));
15
15
  }
16
16
  /**
17
17
  * The custom properties a state paints: the variant's roles, with the accent
@@ -25,9 +25,9 @@ function stylesheetOverrides(variant, editorRoles) {
25
25
  * they cannot win from here, so leaving the accent to them meant the attribute was
26
26
  * set, the rules were present, and nothing changed on screen.
27
27
  */
28
- function themeDocumentTokens(state, options = {}) {
29
- const properties = options.palette === "stylesheet" && isCatalogueVariant(state.variant) ? stylesheetOverrides(state.variant, options.editorRoles) : { ...themeCssVariables(state.variant, { editor: options.editorRoles }) };
30
- const accent = accentVariables(state.selection.accent, state.appearance, state.selection, {
28
+ function themeDocumentTokensWith(engine, state, options = {}) {
29
+ const properties = options.palette === "stylesheet" && engine.isCatalogueVariant(state.variant) ? stylesheetOverrides(engine, state.variant, options.editorRoles) : { ...engine.themeCssVariables(state.variant, { editor: options.editorRoles }) };
30
+ const accent = engine.accentVariables(state.selection.accent, state.appearance, state.selection, {
31
31
  background: state.variant.colors.background,
32
32
  floor: options.accentContrastFloor,
33
33
  variantId: state.variant.id
@@ -49,8 +49,8 @@ function themeDocumentTokens(state, options = {}) {
49
49
  * `tokens` the catalogue does not, which would otherwise stay inline, beating the
50
50
  * stylesheet, after the user switched away from it.
51
51
  */
52
- function applyThemeToRoot(root, state, options = {}, previous) {
53
- const { tokens, accented } = themeDocumentTokens(state, options);
52
+ function applyThemeToRootWith(engine, root, state, options = {}, previous) {
53
+ const { tokens, accented } = themeDocumentTokensWith(engine, state, options);
54
54
  for (const [name, value] of Object.entries(tokens)) root.style.setProperty(name, value);
55
55
  if (previous) {
56
56
  for (const name of previous) if (!(name in tokens)) root.style.removeProperty(name);
@@ -113,6 +113,6 @@ function defaultSchedule(callback) {
113
113
  else callback();
114
114
  }
115
115
  //#endregion
116
- export { applyThemeToRoot, applyWithoutTransitions, themeDocumentTokens };
116
+ export { applyThemeToRootWith, applyWithoutTransitions, themeDocumentTokensWith };
117
117
 
118
- //# sourceMappingURL=theme-document.js.map
118
+ //# sourceMappingURL=theme-document-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-document-core.js","names":[],"sources":["../../../src/components/theme/theme-document-core.ts"],"sourcesContent":["import {\n applyAppearanceFontSettings,\n normalizeAppearanceEditorFontSettings,\n} from '#lib/appearance-font-settings'\nimport { terminalCssVariables } from '#lib/theme-css'\nimport type { ThemeEngine } from '#lib/theme-engine'\nimport type { EditorRolesMode, ThemeAppearance, ThemeVariant } from '#lib/themes'\nimport { isCustomAccent } from '#lib/accent-roles'\nimport { surfaceAlpha, type SurfaceState } from '#lib/theme-surface'\nimport { resolvedDensity, type ThemeSelection } from './theme-state-core'\n\n/**\n * The document half of the theme provider: the single writer of `<html>`.\n *\n * Written against the few members of an element it uses, so the rules can be tested\n * on a stand-in and the provider's effect stays a one-line call.\n *\n * This is the engine-parameterised half: every question the catalogue answers goes\n * through the {@link ThemeEngine} it is given, so it imports no catalogue code or data.\n * `theme-document.ts` binds the full catalogue and keeps the signatures the eager\n * provider and the tests have always used.\n */\nexport type ThemeRoot = Pick<\n HTMLElement,\n 'classList' | 'dataset' | 'style' | 'removeAttribute' | 'setAttribute'\n>\n\nexport type ThemeDocumentState = {\n /** What the document shows: the committed selection, or a preview laid over it. */\n selection: ThemeSelection\n appearance: ThemeAppearance\n variant: ThemeVariant\n /**\n * The variant whose terminal palette is pinned over the interface variant's.\n * Absent, the terminal follows the interface theme.\n */\n terminalVariant?: ThemeVariant\n /**\n * The resolved surface axis. Absent, the axis is off: no attribute and no\n * `--surface-alpha` is written, and any this writer wrote before is removed.\n */\n surface?: SurfaceState\n}\n\nexport type ThemeDocumentOptions = {\n /**\n * Rewrites the custom properties just before they are written: the variant's\n * roles with the accent laid over them. It sees every property of the pass and\n * must return the full map; a name it drops is not written.\n */\n transformTokens?: (tokens: Record<string, string>) => Record<string, string>\n /**\n * The contrast an accent is held to against the variant's background. Absent, a\n * preset is painted verbatim (the catalogue measured it for its own themes) and\n * only a custom `#rrggbb` is held to the default floor of 3:1.\n */\n accentContrastFloor?: number\n /**\n * Where a catalogue variant's palette comes from. `inline` (the default) writes\n * every role onto the root. `stylesheet` writes none for a catalogue variant,\n * because the host loads a stylesheet that carries a `[data-theme]` block for it;\n * a variant the catalogue does not hold (a host's imported theme) is still written\n * inline, since no stylesheet can know it. The accent, the terminal pin, the\n * surface alpha and the fonts are written the same in both.\n *\n * Switching from an imported variant back to a catalogue one removes what the\n * imported one wrote, so nothing inline outlives it to beat the stylesheet.\n */\n palette?: 'inline' | 'stylesheet'\n /**\n * The `--editor-*` projection. `syntax` (the default) is the catalogue's quiet\n * syntax roles, which is what `theme-variants.css` carries; `floor` holds every\n * editor role to 4.5:1 on the canvas, as hex.\n *\n * With `palette: 'stylesheet'`, `floor` is the one palette write a catalogue\n * variant still gets: the stylesheet is generated for the default projection, so\n * the sixteen `--editor-*` properties are written inline over it. The rest of the\n * palette stays in the stylesheet.\n */\n editorRoles?: EditorRolesMode\n}\n\n/**\n * What a catalogue variant still writes inline when its palette is the stylesheet's:\n * nothing, unless the editor projection is not the one the stylesheet was generated\n * for.\n */\nfunction stylesheetOverrides(\n engine: ThemeEngine,\n variant: ThemeVariant,\n editorRoles: EditorRolesMode | undefined\n): Record<string, string> {\n if (editorRoles !== 'floor') return {}\n return Object.fromEntries(\n Object.entries(engine.themeCssVariables(variant, { editor: 'floor' })).filter(([name]) =>\n name.startsWith('--editor-')\n )\n )\n}\n\n/**\n * The custom properties a state paints: the variant's roles, with the accent\n * selection laid over them, the pinned terminal palette over that, the surface\n * alpha, then `transformTokens`.\n *\n * The accent is applied *here* rather than left to the `[data-accent]` blocks in\n * `theme.css`, because the properties are written inline and an inline declaration\n * outranks every stylesheet selector however specific it is. The blocks still exist\n * and are still correct — they are what styles the window before this runs — but\n * they cannot win from here, so leaving the accent to them meant the attribute was\n * set, the rules were present, and nothing changed on screen.\n */\nexport function themeDocumentTokensWith(\n engine: ThemeEngine,\n state: ThemeDocumentState,\n options: ThemeDocumentOptions = {}\n): { tokens: Record<string, string>; accented: boolean } {\n const fromStylesheet =\n options.palette === 'stylesheet' && engine.isCatalogueVariant(state.variant)\n const properties = fromStylesheet\n ? stylesheetOverrides(engine, state.variant, options.editorRoles)\n : { ...engine.themeCssVariables(state.variant, { editor: options.editorRoles }) }\n const accent = engine.accentVariables(state.selection.accent, state.appearance, state.selection, {\n background: state.variant.colors.background,\n floor: options.accentContrastFloor,\n variantId: state.variant.id,\n })\n if (accent) Object.assign(properties, accent)\n // Written after the variant and the accent, so it wins over the interface\n // variant's own terminal ramp (and over the stylesheet's, which it outranks as an\n // inline declaration). Dropping the pin needs no removal of its own: the ramp is\n // then rewritten by the variant, or swept back to the stylesheet.\n if (state.terminalVariant) Object.assign(properties, terminalCssVariables(state.terminalVariant))\n if (state.surface) properties['--surface-alpha'] = surfaceAlpha[state.surface.surface]\n return {\n tokens: options.transformTokens ? options.transformTokens(properties) : properties,\n accented: accent !== undefined,\n }\n}\n\n/**\n * Write a state onto a root and report which custom properties it owns.\n *\n * Pass the previous call's return as `previous` and any palette property that pass\n * wrote and this one does not is removed. For the catalogue the two sets are the\n * same, so nothing is ever removed; it matters for a host variant that carries\n * `tokens` the catalogue does not, which would otherwise stay inline, beating the\n * stylesheet, after the user switched away from it.\n */\nexport function applyThemeToRootWith(\n engine: ThemeEngine,\n root: ThemeRoot,\n state: ThemeDocumentState,\n options: ThemeDocumentOptions = {},\n previous?: ReadonlySet<string>\n): ReadonlySet<string> {\n const { tokens, accented } = themeDocumentTokensWith(engine, state, options)\n\n for (const [name, value] of Object.entries(tokens)) {\n root.style.setProperty(name, value)\n }\n if (previous) {\n for (const name of previous) {\n if (!(name in tokens)) root.style.removeProperty(name)\n }\n }\n\n root.classList.toggle('dark', state.appearance === 'dark')\n root.dataset['theme'] = state.variant.id\n root.dataset['appearance'] = state.appearance\n root.style.colorScheme = state.appearance\n\n // A custom colour has no stylesheet block to select, and its value is the\n // attribute's worst possible content, so it is reported as `custom`.\n if (!accented) root.removeAttribute('data-accent')\n else\n root.dataset['accent'] = isCustomAccent(state.selection.accent)\n ? 'custom'\n : state.selection.accent\n\n // The surface axis is opt-in, so its absence removes rather than defaults: a host\n // that never enables it keeps an `<html>` with neither attribute, and one that\n // turns it off leaves nothing behind. (`--surface-alpha` is swept with the tokens.)\n if (state.surface) {\n root.dataset['surface'] = state.surface.surface\n root.dataset['reduceTransparency'] = state.surface.reduceTransparencyActive ? 'true' : 'false'\n } else {\n root.removeAttribute('data-surface')\n root.removeAttribute('data-reduce-transparency')\n }\n\n applyAppearanceFontSettings(\n root,\n normalizeAppearanceEditorFontSettings(state.selection.fonts, state.selection.font).settings\n )\n\n // Density is a pure token shift, so unlike the colour axes there is nothing to\n // compute: the attribute selects a block in `theme.css`. It is applied here for\n // the same reason as the others — this is the one place that knows what the\n // current selection is, and it is the only writer of these attributes.\n if (resolvedDensity(state.selection) === 'comfortable') root.removeAttribute('data-density')\n else root.dataset['density'] = resolvedDensity(state.selection)\n\n return new Set(Object.keys(tokens))\n}\n\n/** What {@link applyWithoutTransitions} needs of a document. */\nexport type TransitionDocument = {\n head: { appendChild(node: unknown): unknown }\n createElement(tag: 'style'): {\n textContent: string | null\n offsetHeight?: number\n remove(): void\n }\n}\n\n/**\n * Run `apply` with every CSS transition switched off for the frame that carries\n * it, so a theme switch cannot animate page-wide.\n *\n * Without this, flipping the custom properties on `<html>` starts a transition on\n * every element that declares one, and each fades from the old palette to the new\n * over its own duration: a theme change that looks like a rendering fault. The\n * rule is removed two frames later, after the browser has committed the new styles\n * with transitions off, and `restore` runs it early (the provider calls it when it\n * is disposed mid-switch).\n *\n * `schedule` is the frame source; it defaults to `requestAnimationFrame` and falls\n * back to running at once where there is none, which leaves transitions on rather\n * than off.\n */\nexport function applyWithoutTransitions(\n doc: TransitionDocument,\n apply: () => void,\n schedule: (callback: () => void) => void = defaultSchedule\n): () => void {\n const style = doc.createElement('style')\n style.textContent = '*{transition:none !important}'\n doc.head.appendChild(style)\n // Reading a layout property forces the browser to commit the rule before the\n // properties change; without it the two can be batched into one style recalc.\n void style.offsetHeight\n let removed = false\n const restore = () => {\n if (removed) return\n removed = true\n style.remove()\n }\n try {\n apply()\n } catch (error) {\n restore()\n throw error\n }\n schedule(() => schedule(restore))\n return restore\n}\n\nfunction defaultSchedule(callback: () => void): void {\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(callback)\n else callback()\n}\n"],"mappings":";;;;;;;;;;;AAuFA,SAAS,oBACP,QACA,SACA,aACwB;CACxB,IAAI,gBAAgB,SAAS,OAAO,CAAC;CACrC,OAAO,OAAO,YACZ,OAAO,QAAQ,OAAO,kBAAkB,SAAS,EAAE,QAAQ,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAC9E,KAAK,WAAW,WAAW,CAC7B,CACF;AACF;;;;;;;;;;;;;AAcA,SAAgB,wBACd,QACA,OACA,UAAgC,CAAC,GACsB;CAGvD,MAAM,aADJ,QAAQ,YAAY,gBAAgB,OAAO,mBAAmB,MAAM,OAAO,IAEzE,oBAAoB,QAAQ,MAAM,SAAS,QAAQ,WAAW,IAC9D,EAAE,GAAG,OAAO,kBAAkB,MAAM,SAAS,EAAE,QAAQ,QAAQ,YAAY,CAAC,EAAE;CAClF,MAAM,SAAS,OAAO,gBAAgB,MAAM,UAAU,QAAQ,MAAM,YAAY,MAAM,WAAW;EAC/F,YAAY,MAAM,QAAQ,OAAO;EACjC,OAAO,QAAQ;EACf,WAAW,MAAM,QAAQ;CAC3B,CAAC;CACD,IAAI,QAAQ,OAAO,OAAO,YAAY,MAAM;CAK5C,IAAI,MAAM,iBAAiB,OAAO,OAAO,YAAY,qBAAqB,MAAM,eAAe,CAAC;CAChG,IAAI,MAAM,SAAS,WAAW,qBAAqB,aAAa,MAAM,QAAQ;CAC9E,OAAO;EACL,QAAQ,QAAQ,kBAAkB,QAAQ,gBAAgB,UAAU,IAAI;EACxE,UAAU,WAAW,KAAA;CACvB;AACF;;;;;;;;;;AAWA,SAAgB,qBACd,QACA,MACA,OACA,UAAgC,CAAC,GACjC,UACqB;CACrB,MAAM,EAAE,QAAQ,aAAa,wBAAwB,QAAQ,OAAO,OAAO;CAE3E,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAC/C,KAAK,MAAM,YAAY,MAAM,KAAK;CAEpC,IAAI,UACG;OAAA,MAAM,QAAQ,UACjB,IAAI,EAAE,QAAQ,SAAS,KAAK,MAAM,eAAe,IAAI;CAAA;CAIzD,KAAK,UAAU,OAAO,QAAQ,MAAM,eAAe,MAAM;CACzD,KAAK,QAAQ,WAAW,MAAM,QAAQ;CACtC,KAAK,QAAQ,gBAAgB,MAAM;CACnC,KAAK,MAAM,cAAc,MAAM;CAI/B,IAAI,CAAC,UAAU,KAAK,gBAAgB,aAAa;MAE/C,KAAK,QAAQ,YAAY,eAAe,MAAM,UAAU,MAAM,IAC1D,WACA,MAAM,UAAU;CAKtB,IAAI,MAAM,SAAS;EACjB,KAAK,QAAQ,aAAa,MAAM,QAAQ;EACxC,KAAK,QAAQ,wBAAwB,MAAM,QAAQ,2BAA2B,SAAS;CACzF,OAAO;EACL,KAAK,gBAAgB,cAAc;EACnC,KAAK,gBAAgB,0BAA0B;CACjD;CAEA,4BACE,MACA,sCAAsC,MAAM,UAAU,OAAO,MAAM,UAAU,IAAI,CAAC,CAAC,QACrF;CAMA,IAAI,gBAAgB,MAAM,SAAS,MAAM,eAAe,KAAK,gBAAgB,cAAc;MACtF,KAAK,QAAQ,aAAa,gBAAgB,MAAM,SAAS;CAE9D,OAAO,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AACpC;;;;;;;;;;;;;;;;AA2BA,SAAgB,wBACd,KACA,OACA,WAA2C,iBAC/B;CACZ,MAAM,QAAQ,IAAI,cAAc,OAAO;CACvC,MAAM,cAAc;CACpB,IAAI,KAAK,YAAY,KAAK;CAG1B,MAAW;CACX,IAAI,UAAU;CACd,MAAM,gBAAgB;EACpB,IAAI,SAAS;EACb,UAAU;EACV,MAAM,OAAO;CACf;CACA,IAAI;EACF,MAAM;CACR,SAAS,OAAO;EACd,QAAQ;EACR,MAAM;CACR;CACA,eAAe,SAAS,OAAO,CAAC;CAChC,OAAO;AACT;AAEA,SAAS,gBAAgB,UAA4B;CACnD,IAAI,OAAO,0BAA0B,YAAY,sBAAsB,QAAQ;MAC1E,SAAS;AAChB"}
@@ -1,117 +1,18 @@
1
- import { type EditorRolesMode, type ThemeAppearance, type ThemeVariant } from '#lib/themes';
2
- import { type SurfaceState } from '#lib/theme-surface';
3
- import { type ThemeSelection } from './theme-state';
1
+ import { type ThemeDocumentOptions, type ThemeDocumentState, type ThemeRoot } from './theme-document-core';
2
+ export { applyWithoutTransitions, type ThemeDocumentOptions, type ThemeDocumentState, type ThemeRoot, type TransitionDocument, } from './theme-document-core';
4
3
  /**
5
- * The document half of the theme provider: the single writer of `<html>`.
6
- *
7
- * Written against the few members of an element it uses, so the rules can be tested
8
- * on a stand-in and the provider's effect stays a one-line call.
9
- */
10
- export type ThemeRoot = Pick<HTMLElement, 'classList' | 'dataset' | 'style' | 'removeAttribute' | 'setAttribute'>;
11
- export type ThemeDocumentState = {
12
- /** What the document shows: the committed selection, or a preview laid over it. */
13
- selection: ThemeSelection;
14
- appearance: ThemeAppearance;
15
- variant: ThemeVariant;
16
- /**
17
- * The variant whose terminal palette is pinned over the interface variant's.
18
- * Absent, the terminal follows the interface theme.
19
- */
20
- terminalVariant?: ThemeVariant;
21
- /**
22
- * The resolved surface axis. Absent, the axis is off: no attribute and no
23
- * `--surface-alpha` is written, and any this writer wrote before is removed.
24
- */
25
- surface?: SurfaceState;
26
- };
27
- export type ThemeDocumentOptions = {
28
- /**
29
- * Rewrites the custom properties just before they are written: the variant's
30
- * roles with the accent laid over them. It sees every property of the pass and
31
- * must return the full map; a name it drops is not written.
32
- */
33
- transformTokens?: (tokens: Record<string, string>) => Record<string, string>;
34
- /**
35
- * The contrast an accent is held to against the variant's background. Absent, a
36
- * preset is painted verbatim (the catalogue measured it for its own themes) and
37
- * only a custom `#rrggbb` is held to the default floor of 3:1.
38
- */
39
- accentContrastFloor?: number;
40
- /**
41
- * Where a catalogue variant's palette comes from. `inline` (the default) writes
42
- * every role onto the root. `stylesheet` writes none for a catalogue variant,
43
- * because the host loads a stylesheet that carries a `[data-theme]` block for it;
44
- * a variant the catalogue does not hold (a host's imported theme) is still written
45
- * inline, since no stylesheet can know it. The accent, the terminal pin, the
46
- * surface alpha and the fonts are written the same in both.
47
- *
48
- * Switching from an imported variant back to a catalogue one removes what the
49
- * imported one wrote, so nothing inline outlives it to beat the stylesheet.
50
- */
51
- palette?: 'inline' | 'stylesheet';
52
- /**
53
- * The `--editor-*` projection. `syntax` (the default) is the catalogue's quiet
54
- * syntax roles, which is what `theme-variants.css` carries; `floor` holds every
55
- * editor role to 4.5:1 on the canvas, as hex.
56
- *
57
- * With `palette: 'stylesheet'`, `floor` is the one palette write a catalogue
58
- * variant still gets: the stylesheet is generated for the default projection, so
59
- * the sixteen `--editor-*` properties are written inline over it. The rest of the
60
- * palette stays in the stylesheet.
61
- */
62
- editorRoles?: EditorRolesMode;
63
- };
64
- /**
65
- * The custom properties a state paints: the variant's roles, with the accent
66
- * selection laid over them, the pinned terminal palette over that, the surface
67
- * alpha, then `transformTokens`.
68
- *
69
- * The accent is applied *here* rather than left to the `[data-accent]` blocks in
70
- * `theme.css`, because the properties are written inline and an inline declaration
71
- * outranks every stylesheet selector however specific it is. The blocks still exist
72
- * and are still correct — they are what styles the window before this runs — but
73
- * they cannot win from here, so leaving the accent to them meant the attribute was
74
- * set, the rules were present, and nothing changed on screen.
4
+ * The custom properties a state paints, resolved against the whole catalogue: the
5
+ * variant's roles, with the accent selection laid over them, the pinned terminal
6
+ * palette over that, the surface alpha, then `transformTokens`. See
7
+ * {@link themeDocumentTokensWith} for the rules.
75
8
  */
76
9
  export declare function themeDocumentTokens(state: ThemeDocumentState, options?: ThemeDocumentOptions): {
77
10
  tokens: Record<string, string>;
78
11
  accented: boolean;
79
12
  };
80
13
  /**
81
- * Write a state onto a root and report which custom properties it owns.
82
- *
83
- * Pass the previous call's return as `previous` and any palette property that pass
84
- * wrote and this one does not is removed. For the catalogue the two sets are the
85
- * same, so nothing is ever removed; it matters for a host variant that carries
86
- * `tokens` the catalogue does not, which would otherwise stay inline, beating the
87
- * stylesheet, after the user switched away from it.
14
+ * Write a state onto a root and report which custom properties it owns, resolved
15
+ * against the whole catalogue. See {@link applyThemeToRootWith} for the rules.
88
16
  */
89
17
  export declare function applyThemeToRoot(root: ThemeRoot, state: ThemeDocumentState, options?: ThemeDocumentOptions, previous?: ReadonlySet<string>): ReadonlySet<string>;
90
- /** What {@link applyWithoutTransitions} needs of a document. */
91
- export type TransitionDocument = {
92
- head: {
93
- appendChild(node: unknown): unknown;
94
- };
95
- createElement(tag: 'style'): {
96
- textContent: string | null;
97
- offsetHeight?: number;
98
- remove(): void;
99
- };
100
- };
101
- /**
102
- * Run `apply` with every CSS transition switched off for the frame that carries
103
- * it, so a theme switch cannot animate page-wide.
104
- *
105
- * Without this, flipping the custom properties on `<html>` starts a transition on
106
- * every element that declares one, and each fades from the old palette to the new
107
- * over its own duration: a theme change that looks like a rendering fault. The
108
- * rule is removed two frames later, after the browser has committed the new styles
109
- * with transitions off, and `restore` runs it early (the provider calls it when it
110
- * is disposed mid-switch).
111
- *
112
- * `schedule` is the frame source; it defaults to `requestAnimationFrame` and falls
113
- * back to running at once where there is none, which leaves transitions on rather
114
- * than off.
115
- */
116
- export declare function applyWithoutTransitions(doc: TransitionDocument, apply: () => void, schedule?: (callback: () => void) => void): () => void;
117
18
  //# sourceMappingURL=theme-document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme-document.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-document.ts"],"names":[],"mappings":"AAIA,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAEpE;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,IAAI,CAC1B,WAAW,EACX,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,iBAAiB,GAAG,cAAc,CACvE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,mFAAmF;IACnF,SAAS,EAAE,cAAc,CAAA;IACzB,UAAU,EAAE,eAAe,CAAA;IAC3B,OAAO,EAAE,YAAY,CAAA;IACrB;;;OAGG;IACH,eAAe,CAAC,EAAE,YAAY,CAAA;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5E;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAA;IACjC;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,eAAe,CAAA;CAC9B,CAAA;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,oBAAyB,GACjC;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAqBvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,oBAAyB,EAClC,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC7B,WAAW,CAAC,MAAM,CAAC,CAiDrB;AAED,gEAAgE;AAChE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE;QAAE,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAA;IAC7C,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,MAAM,IAAI,IAAI,CAAA;KACf,CAAA;CACF,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,MAAM,IAAI,EACjB,QAAQ,GAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAsB,GACzD,MAAM,IAAI,CAqBZ"}
1
+ {"version":3,"file":"theme-document.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-document.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,uBAAuB,EACvB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAA;AAE9B;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,oBAAyB,GACjC;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAEvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,oBAAyB,EAClC,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC7B,WAAW,CAAC,MAAM,CAAC,CAErB"}
@@ -1,5 +1,5 @@
1
1
  import { type PreferenceStorage } from '#lib/preference-store';
2
- import { type ThemeSelection } from './theme-state';
2
+ import { type ThemeSelection } from './theme-state-core';
3
3
  /**
4
4
  * Where a selection is kept between sessions.
5
5
  *
@@ -1 +1 @@
1
- {"version":3,"file":"theme-persistence.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-persistence.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAE3F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAA;IAC3C,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;CAClC,CAAA;AAyED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,gBAAgB,CAatF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAMhG;AAED,+FAA+F;AAC/F,wBAAgB,cAAc,CAAC,WAAW,EAAE,gBAAgB,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAOxF;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,eAAe,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACvC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,GAAG,IAAI;IAChC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IAC1E;;;OAGG;IACH,UAAU,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAA;IAC/D;;;OAGG;IACH,QAAQ,CAAC,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAA;IAChD;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,GAAG,CAAA;CAChF,CAAA;AAED,0EAA0E;AAC1E,MAAM,MAAM,2BAA2B,GAAG;IACxC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAA;IAC7C,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAC5C,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,EAC1B,OAAO,GAAE,2BAAgC,GACxC,gBAAgB,CAqDlB"}
1
+ {"version":3,"file":"theme-persistence.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-persistence.ts"],"names":[],"mappings":"AAUA,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEhG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAA;IAC3C,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;CAClC,CAAA;AA4ED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,gBAAgB,CAatF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAMhG;AAED,+FAA+F;AAC/F,wBAAgB,cAAc,CAAC,WAAW,EAAE,gBAAgB,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAOxF;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,eAAe,CAAC,EAAE,YAAY,GAAG,MAAM,CAAA;IACvC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,GAAG,IAAI;IAChC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IAC1E;;;OAGG;IACH,UAAU,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAA;IAC/D;;;OAGG;IACH,QAAQ,CAAC,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAA;IAChD;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,GAAG,CAAA;CAChF,CAAA;AAED,0EAA0E;AAC1E,MAAM,MAAM,2BAA2B,GAAG;IACxC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAA;IAC7C,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAC5C,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,EAC1B,OAAO,GAAE,2BAAgC,GACxC,gBAAgB,CAqDlB"}
@@ -1,10 +1,10 @@
1
- import { accentPresets } from "../../lib/tokens.js";
1
+ import { accentPresets } from "../../lib/accent-presets.js";
2
2
  import { normalizeCustomAccent } from "../../lib/accent-roles.js";
3
3
  import { isThemeAccentId } from "../../lib/theme-accents.js";
4
- import { defaultDarkThemeId, defaultLightThemeId, themeById } from "../../lib/themes.js";
4
+ import { catalogueThemeAppearance, defaultDarkThemeId, defaultLightThemeId } from "../../lib/theme-defaults.js";
5
5
  import { normalizeAppearanceEditorFontSettings } from "../../lib/appearance-font-settings.js";
6
6
  import { normalizeSurface } from "../../lib/theme-surface.js";
7
- import { defaultThemeSelection, resolvedDensity } from "./theme-state.js";
7
+ import { defaultThemeSelection, resolvedDensity } from "./theme-state-core.js";
8
8
  import { defaultPreferenceStorage, readLegacyValue, readVersionedRecord, recoveryKeyFor, writeVersionedRecord } from "../../lib/preference-store.js";
9
9
  //#region src/components/theme/theme-persistence.ts
10
10
  /**
@@ -19,7 +19,10 @@ import { defaultPreferenceStorage, readLegacyValue, readVersionedRecord, recover
19
19
  function preferenceStore() {
20
20
  return typeof localStorage === "undefined" ? void 0 : localStorage;
21
21
  }
22
- /** The stored preference, validated against the catalogue. */
22
+ /**
23
+ * The stored preference, validated against the catalogue's ids (`theme-defaults`), so
24
+ * reading it never loads the catalogue.
25
+ */
23
26
  function readSelection(storageKey) {
24
27
  try {
25
28
  const raw = preferenceStore()?.getItem(storageKey);
@@ -28,8 +31,8 @@ function readSelection(storageKey) {
28
31
  const fonts = normalizeAppearanceEditorFontSettings(parsed.fonts, parsed.font).settings;
29
32
  return {
30
33
  appearance: parsed.appearance === "light" || parsed.appearance === "dark" || parsed.appearance === "system" ? parsed.appearance : defaultThemeSelection.appearance,
31
- lightThemeId: themeById(parsed.lightThemeId ?? "")?.appearance === "light" ? parsed.lightThemeId : defaultLightThemeId,
32
- darkThemeId: themeById(parsed.darkThemeId ?? "")?.appearance === "dark" ? parsed.darkThemeId : defaultDarkThemeId,
34
+ lightThemeId: catalogueThemeAppearance(parsed.lightThemeId ?? "") === "light" ? parsed.lightThemeId : defaultLightThemeId,
35
+ darkThemeId: catalogueThemeAppearance(parsed.darkThemeId ?? "") === "dark" ? parsed.darkThemeId : defaultDarkThemeId,
33
36
  accent: typeof parsed.accent === "string" && (accentPresets.some((preset) => preset.id === parsed.accent) || isThemeAccentId(parsed.accent)) ? parsed.accent : normalizeCustomAccent(parsed.accent) ?? defaultThemeSelection.accent,
34
37
  font: fonts.ui.family,
35
38
  fonts,