@adea-ai/ui 0.128.1 → 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 (141) 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/accent-roles.js +94 -9
  51. package/dist/lib/accent-roles.js.map +1 -1
  52. package/dist/lib/font-zoom.js +4 -2
  53. package/dist/lib/font-zoom.js.map +1 -1
  54. package/dist/lib/overlay.js +3 -1
  55. package/dist/lib/overlay.js.map +1 -1
  56. package/dist/lib/theme-catalogue.d.ts +3 -0
  57. package/dist/lib/theme-catalogue.d.ts.map +1 -0
  58. package/dist/lib/theme-catalogue.js +22 -0
  59. package/dist/lib/theme-catalogue.js.map +1 -0
  60. package/dist/lib/theme-css.d.ts +43 -0
  61. package/dist/lib/theme-css.d.ts.map +1 -0
  62. package/dist/lib/theme-css.js +167 -0
  63. package/dist/lib/theme-css.js.map +1 -0
  64. package/dist/lib/theme-defaults.d.ts +46 -0
  65. package/dist/lib/theme-defaults.d.ts.map +1 -0
  66. package/dist/lib/theme-defaults.generated.d.ts +13 -0
  67. package/dist/lib/theme-defaults.generated.d.ts.map +1 -0
  68. package/dist/lib/theme-defaults.generated.js +318 -0
  69. package/dist/lib/theme-defaults.generated.js.map +1 -0
  70. package/dist/lib/theme-defaults.js +47 -0
  71. package/dist/lib/theme-defaults.js.map +1 -0
  72. package/dist/lib/theme-engine-light.d.ts +3 -0
  73. package/dist/lib/theme-engine-light.d.ts.map +1 -0
  74. package/dist/lib/theme-engine-light.js +75 -0
  75. package/dist/lib/theme-engine-light.js.map +1 -0
  76. package/dist/lib/theme-engine.d.ts +69 -0
  77. package/dist/lib/theme-engine.d.ts.map +1 -0
  78. package/dist/lib/theme-engine.js +33 -0
  79. package/dist/lib/theme-engine.js.map +1 -0
  80. package/dist/lib/themes.d.ts +14 -48
  81. package/dist/lib/themes.d.ts.map +1 -1
  82. package/dist/lib/themes.js +24 -164
  83. package/dist/lib/themes.js.map +1 -1
  84. package/dist/lib/tokens.d.ts +1 -25
  85. package/dist/lib/tokens.d.ts.map +1 -1
  86. package/dist/lib/tokens.js +1 -24
  87. package/dist/lib/tokens.js.map +1 -1
  88. package/dist/lib/tooltip-focus-gate.js +7 -1
  89. package/dist/lib/tooltip-focus-gate.js.map +1 -1
  90. package/dist/lib/variants.js +2 -2
  91. package/dist/r/lib.json +35 -0
  92. package/dist/r/registry.json +70 -0
  93. package/dist/r/src/components/theme/lazy.ts +35 -0
  94. package/dist/r/src/components/theme/theme-catalogue-source.ts +59 -0
  95. package/dist/r/src/components/theme/theme-document-core.ts +262 -0
  96. package/dist/r/src/components/theme/theme-document.ts +23 -240
  97. package/dist/r/src/components/theme/theme-persistence.ts +14 -6
  98. package/dist/r/src/components/theme/theme-provider-core.tsx +40 -0
  99. package/dist/r/src/components/theme/theme-provider-lazy.tsx +34 -0
  100. package/dist/r/src/components/theme/theme-provider-value.ts +445 -0
  101. package/dist/r/src/components/theme/theme-provider.tsx +23 -346
  102. package/dist/r/src/components/theme/theme-script.ts +3 -3
  103. package/dist/r/src/components/theme/theme-state-core.ts +364 -0
  104. package/dist/r/src/components/theme/theme-state.ts +10 -329
  105. package/dist/r/src/components/theme/theme-toggle.tsx +1 -1
  106. package/dist/r/src/lib/accent-presets.ts +43 -0
  107. package/dist/r/src/lib/theme-catalogue.ts +26 -0
  108. package/dist/r/src/lib/theme-css.ts +201 -0
  109. package/dist/r/src/lib/theme-defaults.generated.ts +319 -0
  110. package/dist/r/src/lib/theme-defaults.ts +79 -0
  111. package/dist/r/src/lib/theme-engine-light.ts +117 -0
  112. package/dist/r/src/lib/theme-engine.ts +111 -0
  113. package/dist/r/src/lib/themes.ts +27 -191
  114. package/dist/r/src/lib/tokens.ts +1 -35
  115. package/dist/r/theming.json +35 -0
  116. package/package.json +12 -3
  117. package/registry.json +70 -0
  118. package/src/components/theme/lazy.ts +35 -0
  119. package/src/components/theme/theme-catalogue-source.ts +59 -0
  120. package/src/components/theme/theme-document-core.ts +262 -0
  121. package/src/components/theme/theme-document.ts +23 -240
  122. package/src/components/theme/theme-persistence.ts +14 -6
  123. package/src/components/theme/theme-provider-core.tsx +40 -0
  124. package/src/components/theme/theme-provider-lazy.tsx +34 -0
  125. package/src/components/theme/theme-provider-value.ts +445 -0
  126. package/src/components/theme/theme-provider.tsx +23 -346
  127. package/src/components/theme/theme-script.ts +3 -3
  128. package/src/components/theme/theme-state-core.ts +364 -0
  129. package/src/components/theme/theme-state.ts +10 -329
  130. package/src/components/theme/theme-toggle.tsx +1 -1
  131. package/src/lib/accent-presets.ts +43 -0
  132. package/src/lib/theme-catalogue.ts +26 -0
  133. package/src/lib/theme-css.ts +201 -0
  134. package/src/lib/theme-defaults.generated.ts +319 -0
  135. package/src/lib/theme-defaults.ts +79 -0
  136. package/src/lib/theme-engine-light.ts +117 -0
  137. package/src/lib/theme-engine.ts +111 -0
  138. package/src/lib/themes.ts +27 -191
  139. package/src/lib/tokens.ts +1 -35
  140. package/dist/components/theme/theme-document.js.map +0 -1
  141. package/dist/components/theme/theme-state.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider-value.js","names":[],"sources":["../../../src/components/theme/theme-provider-value.ts"],"sourcesContent":["import type { JSX } from 'solid-js'\nimport { createEffect, createMemo, createSignal, onCleanup, splitProps, untrack } from 'solid-js'\nimport { fontZoomShortcut, type FontZoomIntent } from '#lib/font-zoom'\nimport { REDUCED_TRANSPARENCY_QUERY, resolveSurfaceState } from '#lib/theme-surface'\nimport { CatalogueRequired, needsCatalogue, type ThemeEngine } from '#lib/theme-engine'\nimport type { EditorRolesMode, ThemeAppearance, ThemeVariant } from '#lib/themes'\nimport { applyThemeToRootWith, applyWithoutTransitions } from './theme-document-core'\nimport {\n localStoragePersistence,\n readPersisted,\n writePersisted,\n type ThemePersistence,\n} from './theme-persistence'\nimport {\n accentRolesForStateWith,\n initialThemeSelection,\n mergeRegistry,\n mergeThemeSelection,\n resolveTerminalVariant,\n resolveThemeState,\n zoomedFontSettings,\n type ThemeContextValue,\n type ThemeSelection,\n} from './theme-state-core'\n\n/**\n * ThemeProvider.\n *\n * Owns the appearance preference and applies it to the document. One provider near\n * the root; everything below reads it.\n *\n * ## What a preference is\n *\n * Four independent choices (`ThemeSelection`), and keeping them independent is the\n * point: appearance, a theme per appearance, an accent and fonts. They compose: a\n * user who wants Nord with a violet accent and the system font should not need\n * someone to author that combination.\n *\n * ## Where the rules live\n *\n * This component is wiring. What a selection merges to and resolves to is\n * `theme-state.ts`; what lands on `<html>` is `theme-document.ts`; where the\n * preference is kept is `theme-persistence.ts`. Each is plain code a test can call,\n * which is the reason none of it is inlined here.\n *\n * ## Why the variants are applied as inline properties\n *\n * The catalogue is data, and a theme is applied by writing its roles onto\n * `<html>` as custom properties. That is what makes an imported palette work\n * without a hand-written `[data-theme]` block per theme, and it is why a settings\n * UI can show a preview by reading the same object rather than by loading a\n * stylesheet. `theme.css` keeps the default theme's values so the first paint is\n * correct before any script runs.\n */\nexport type ThemeProviderProps = {\n children?: JSX.Element\n /** Where the preference is persisted. Distinct per application. */\n storageKey?: string\n /** Start from this instead of the stored preference. */\n initial?: Partial<ThemeSelection>\n /**\n * The selection, owned by the caller.\n *\n * For a consumer that keeps the state itself — a settings store, or a workshop\n * whose selection lives in Storybook globals — rather than handing it to this\n * provider's signal. Each change lands here and the document follows; nothing is\n * written to the store, because the caller is the one who persists. A picker\n * below the provider still works, and its change is the caller's to read back.\n *\n * Absent, the provider is the whole owner: it reads `storageKey` on mount,\n * seeds from `initial`, and persists every `setSelection`.\n */\n selection?: Partial<ThemeSelection>\n /**\n * Where the preference is kept, in place of `localStorage` under `storageKey`.\n *\n * Read once when the provider is created, so changing the prop later does not\n * move the store. `storageKey` is ignored when this is given.\n */\n persistence?: ThemePersistence\n /**\n * Variants the host adds to the built-in catalogue — themes a user imported.\n *\n * A function is read inside the provider's reactive scope, so one that reads a\n * signal keeps the picker and the document current when the set changes. A\n * stored id that names no variant, built in or added, paints the default of its\n * appearance and stays in the selection untouched.\n */\n themes?: readonly ThemeVariant[] | (() => readonly ThemeVariant[])\n /**\n * Rewrites the custom properties of each pass before they are written to\n * `<html>`: the variant's roles with the accent over them. `tokensAsHex` from\n * `lib/themes` is the one the system ships.\n */\n transformTokens?: (tokens: Record<string, string>) => Record<string, string>\n /**\n * Switch CSS transitions off for the frame that carries a change, so a theme\n * switch cannot animate the whole page. Off by default: it adds a `<style>`\n * element to `<head>` for two frames on every change.\n */\n disableTransitionOnChange?: boolean\n /**\n * The contrast ratio an accent is held to against the variant's background,\n * repaired to the nearest passing colour when it falls short. Absent, presets\n * are painted verbatim — the catalogue measured them for its own themes — and\n * only a custom `#rrggbb` is held to 3:1. A host that adds themes the catalogue\n * never measured passes 3 to hold the presets to it as well; for the catalogue's\n * own themes the floor changes no preset.\n */\n accentContrastFloor?: number\n /**\n * Turns the surface axis on: `selection.surface` and `selection.reduceTransparency`\n * are resolved against the operating system's Reduce Transparency setting and the\n * host's capability, and `data-surface`, `data-reduce-transparency` and\n * `--surface-alpha` are written to `<html>` (the frost rule in `theme.css` reads\n * them). Off by default: a provider that never opts in writes none of them, adds no\n * media-query listener, and stores nothing new.\n *\n * An object also states the host's capability. `nativeTranslucency` is whether the\n * window can render the OS's own translucency; without it a `translucent`\n * preference paints the tokenized `frosted` surface instead of pretending to\n * vibrancy. A function is read inside the provider's reactive scope, for a\n * capability that is learned after mount.\n *\n * A selection that already carries a `surface` turns the axis on for itself, so a\n * host that stores the field gets the same document without the prop.\n */\n surface?: boolean | { nativeTranslucency?: boolean | (() => boolean) }\n /**\n * Where a catalogue variant's palette comes from. `inline` (the default) writes\n * every role onto `<html>`. `stylesheet` leaves a catalogue variant to the host's\n * `[data-theme]` stylesheet and writes inline only what no stylesheet can know: a\n * host's own `themes`, the accent, the pinned terminal palette, the surface alpha\n * and the fonts. Switching from a host variant back to a catalogue one removes the\n * properties the host variant wrote.\n */\n palette?: 'inline' | 'stylesheet'\n /**\n * The projection behind the `--editor-*` ramp. `syntax` (the default) is the\n * catalogue's quiet syntax roles, a comment among them deliberately below\n * body-text contrast. `floor` holds all sixteen roles to 4.5:1 on the canvas, as\n * hex, for a host that renders code as content (a diff, a file view) rather than\n * as decoration.\n *\n * Applies to catalogue variants; a host's own `themes` keep their `editor` and\n * their `tokens`. With `palette=\"stylesheet\"` it is the one palette property a\n * catalogue variant still writes inline, because `theme-variants.css` is generated\n * for `syntax`. Absent, nothing changes.\n */\n editorRoles?: EditorRolesMode\n /**\n * Bind the platform zoom chords (Cmd or Ctrl with `=`, `+`, `-`, `0`) to the three\n * text sizes instead of the browser's page zoom: stepping them together is zoom\n * inside the persisted type system. Off by default: it adds a capturing `keydown`\n * listener to `window`, and an event already handled elsewhere is left alone.\n * `zoomFonts` on the context does the same from a menu or a host's own binding.\n */\n fontZoom?: boolean\n}\n\n/**\n * Where the provider's catalogue questions are answered, and whether the whole\n * catalogue is available yet.\n *\n * The eager provider's source is the full catalogue and is always loaded. The lazy\n * provider's source starts on the default pair that ships with it and swaps to the\n * full catalogue when {@link ThemeCatalogueSource.load} resolves. Both read the same\n * way in the provider: `engine()` is reactive, so everything derived from it follows\n * the swap.\n */\nexport type ThemeCatalogueSource = {\n /** The engine answering catalogue questions now. Changes once, when loading completes. */\n engine: () => ThemeEngine\n /** True when `engine()` is the full catalogue. */\n loaded: () => boolean\n /** True when loading failed; the provider then paints what it can without the rest. */\n failed: () => boolean\n /** Start loading the full catalogue (idempotent). Resolves when `loaded()` is true. */\n load: () => Promise<void>\n}\n\n/** The operating system's Reduce Transparency query, where there is a window to ask. */\nfunction reducedTransparencyMedia(): MediaQueryList | undefined {\n return typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia(REDUCED_TRANSPARENCY_QUERY)\n : undefined\n}\n\n/**\n * Everything the provider is, as a value: the context object, with its effects and\n * listeners created in the caller's reactive scope.\n *\n * Parameterised by where its catalogue comes from: `ThemeProvider`\n * (`theme-provider.tsx`) and the lazy `ThemeProvider` (`theme-provider-lazy.tsx`) wrap\n * this with a source. It imports no catalogue code or data, which is what lets the lazy\n * one keep them out of a consumer's start-up chunk. It is not JSX so that the unit lane,\n * which has no Solid compiler, can drive it directly.\n */\nexport function createThemeContextValue(\n props: ThemeProviderProps & { catalogue: ThemeCatalogueSource }\n): ThemeContextValue {\n const [local] = splitProps(props, [\n 'catalogue',\n 'storageKey',\n 'initial',\n 'selection',\n 'persistence',\n 'themes',\n 'transformTokens',\n 'disableTransitionOnChange',\n 'accentContrastFloor',\n 'surface',\n 'palette',\n 'editorRoles',\n 'fontZoom',\n ])\n const storageKey = () => local.storageKey ?? 'adea-appearance'\n const persistence = local.persistence ?? localStoragePersistence(storageKey)\n\n // Read before the first resolution and before the store is, so a persistence whose\n // answer depends on the extras (an id it only accepts if the theme exists) sees\n // them.\n const extraThemes = createMemo(() =>\n typeof local.themes === 'function' ? local.themes() : (local.themes ?? [])\n )\n const source = () => local.catalogue\n const engine = () => source().engine()\n const registry = createMemo(() => mergeRegistry(engine().builtinThemes, extraThemes()))\n\n const [selection, setSelectionState] = createSignal<ThemeSelection>(\n initialThemeSelection(readPersisted(persistence), local.initial)\n )\n\n // The controlled half of the contract. A patch from the owner replaces the\n // matching axes; the stored preference is untouched, since the owner is the one\n // that persists. Reading `local.selection` — a prop getter, not a destructured\n // value — is what keeps this subscribed for the provider's lifetime.\n createEffect(() => {\n const incoming = local.selection\n if (incoming) setSelectionState((current) => mergeThemeSelection(current, incoming))\n })\n\n const [systemAppearance, setSystemAppearance] = createSignal<ThemeAppearance>('light')\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const query = window.matchMedia('(prefers-color-scheme: dark)')\n setSystemAppearance(query.matches ? 'dark' : 'light')\n const onChange = (event: MediaQueryListEvent) =>\n setSystemAppearance(event.matches ? 'dark' : 'light')\n query.addEventListener('change', onChange)\n onCleanup(() => query.removeEventListener('change', onChange))\n }\n\n // What the stored preference resolves to. `selection()`, `variant()` and\n // `resolvedAppearance()` answer from this and never from a preview, so a picker\n // that previews a draft still reads back what is committed.\n const committed = createMemo(() => resolveThemeState(selection(), systemAppearance(), registry()))\n const resolvedAppearance = createMemo<ThemeAppearance>(() => committed().appearance)\n const variant = createMemo<ThemeVariant>(() => committed().variant)\n\n const [draft, setDraft] = createSignal<Partial<ThemeSelection> | undefined>()\n const shownSelection = createMemo(() => {\n const preview = draft()\n return preview ? mergeThemeSelection(selection(), preview) : selection()\n })\n const shown = createMemo(() =>\n draft() ? resolveThemeState(shownSelection(), systemAppearance(), registry()) : committed()\n )\n\n // The surface axis. Off, none of this reads the operating system: the media query\n // is subscribed only while the axis is on, so a provider that never opts in adds no\n // listener and no repaint.\n const surfaceOn = (candidate: ThemeSelection) =>\n local.surface === true || typeof local.surface === 'object' || candidate.surface !== undefined\n const nativeTranslucency = () => {\n const capability = typeof local.surface === 'object' ? local.surface.nativeTranslucency : false\n return (typeof capability === 'function' ? capability() : capability) === true\n }\n const [osReducedTransparency, setOsReducedTransparency] = createSignal(\n surfaceOn(untrack(shownSelection)) ? (reducedTransparencyMedia()?.matches ?? false) : false\n )\n const axisOn = createMemo(() => surfaceOn(shownSelection()))\n createEffect(() => {\n const query = axisOn() ? reducedTransparencyMedia() : undefined\n setOsReducedTransparency(query?.matches ?? false)\n if (!query) return\n const onChange = (event: MediaQueryListEvent) => setOsReducedTransparency(event.matches)\n query.addEventListener('change', onChange)\n onCleanup(() => query.removeEventListener('change', onChange))\n })\n const surfaceStateOf = (candidate: ThemeSelection) =>\n resolveSurfaceState(candidate.surface, {\n osReducedTransparency: osReducedTransparency(),\n userReducedTransparency: candidate.reduceTransparency === true,\n nativeTranslucency: nativeTranslucency(),\n })\n const committedSurface = createMemo(() =>\n surfaceOn(selection()) ? surfaceStateOf(selection()) : undefined\n )\n\n // The catalogue questions the selection asks. A selection that names a catalogue theme\n // beyond the shipped default pair cannot be answered, painted or previewed until the\n // whole catalogue has loaded; asking for it is the only side effect.\n const waitingFor = (candidate: ThemeSelection, appearance: ThemeAppearance) =>\n !source().loaded() && !source().failed() && needsCatalogue(candidate, appearance)\n createEffect(() => {\n if (\n needsCatalogue(selection(), committed().appearance) ||\n needsCatalogue(shownSelection(), shown().appearance)\n )\n void source().load()\n })\n\n let written: ReadonlySet<string> | undefined\n let restoreTransitions: (() => void) | undefined\n createEffect(() => {\n if (typeof document === 'undefined') return\n const current = engine()\n const display = shown()\n // Until the catalogue arrives the document keeps what is already on it — the\n // stylesheet's theme, set before first paint by `themeScript` — instead of\n // flashing the default for a theme that is only not loaded yet.\n if (waitingFor(shownSelection(), display.appearance)) return\n const state = {\n selection: shownSelection(),\n appearance: display.appearance,\n variant: display.variant,\n terminalVariant: resolveTerminalVariant(shownSelection().terminalThemeId, registry()),\n surface: axisOn() ? surfaceStateOf(shownSelection()) : undefined,\n }\n const options = {\n transformTokens: local.transformTokens,\n accentContrastFloor: local.accentContrastFloor,\n palette: local.palette,\n editorRoles: local.editorRoles,\n }\n const apply = () => {\n try {\n written = applyThemeToRootWith(current, document.documentElement, state, options, written)\n } catch (error) {\n // The catalogue failed to load and the selection needs it: leave the document\n // as it is rather than half-painting it.\n if (!(error instanceof CatalogueRequired)) throw error\n }\n }\n if (local.disableTransitionOnChange) {\n restoreTransitions?.()\n restoreTransitions = applyWithoutTransitions(document, apply)\n } else {\n apply()\n }\n })\n onCleanup(() => restoreTransitions?.())\n\n const themeAccents = createMemo(() => {\n try {\n return engine().themeAccentsFor(selection().lightThemeId, selection().darkThemeId)\n } catch (error) {\n if (!(error instanceof CatalogueRequired)) throw error\n return []\n }\n })\n\n const zoomFonts = (intent: FontZoomIntent) =>\n value.setSelection({ fonts: zoomedFontSettings(selection(), intent) })\n\n // The platform zoom chords drive the three text sizes, captured like the other\n // global chords; an event something else already handled never double-steps.\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented) return\n const intent = fontZoomShortcut(event)\n if (!intent) return\n event.preventDefault()\n zoomFonts(intent)\n }\n createEffect(() => {\n if (!local.fontZoom || typeof window === 'undefined') return\n window.addEventListener('keydown', onKeyDown, { capture: true })\n onCleanup(() => window.removeEventListener('keydown', onKeyDown, { capture: true }))\n })\n\n const value: ThemeContextValue = {\n selection,\n resolvedAppearance,\n variant,\n get themes() {\n return engine().builtinThemes\n },\n themeAccents,\n allThemes: registry,\n preview(next) {\n setDraft(next ? { ...next } : undefined)\n },\n previewing: () => draft() !== undefined,\n accentRolesFor(accent, variantId) {\n const current = engine()\n const state = {\n selection: selection(),\n variant: variant(),\n registry: registry(),\n accentContrastFloor: local.accentContrastFloor,\n }\n try {\n return accentRolesForStateWith(current, accent, variantId, state)\n } catch (error) {\n if (!(error instanceof CatalogueRequired)) throw error\n // Needs the whole catalogue to measure the tint exactly. Ask for it, and answer\n // meanwhile without the theme's record: the same four colours, the appearance's\n // own tint strength. Reading `engine()` above re-runs a reactive caller on arrival.\n void source().load()\n try {\n return accentRolesForStateWith(\n {\n accentRolesForSelection: (id, appearance, pair, options) =>\n current.accentRolesForSelection(id, appearance, pair, {\n ...options,\n variantId: undefined,\n }),\n },\n accent,\n variantId,\n state\n )\n } catch (inner) {\n if (!(inner instanceof CatalogueRequired)) throw inner\n return undefined\n }\n }\n },\n catalogueReady: () => source().loaded(),\n loadCatalogue: () => source().load(),\n effectiveSurface: () => committedSurface()?.surface ?? 'opaque',\n reduceTransparencyActive: () => committedSurface()?.reduceTransparencyActive ?? false,\n zoomFonts,\n setSelection(patch) {\n const next = mergeThemeSelection(selection(), patch)\n setSelectionState(next)\n // The committed selection is now what should be showing.\n setDraft(undefined)\n writePersisted(persistence, next)\n },\n }\n\n return value\n}\n"],"mappings":"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"}
@@ -1,148 +1,23 @@
1
- import type { JSX } from 'solid-js';
2
- import { type EditorRolesMode, type ThemeVariant } from '#lib/themes';
3
- import { type ThemePersistence } from './theme-persistence';
4
- import { type ThemeSelection } from './theme-state';
1
+ import { type ThemeProviderProps } from './theme-provider-core';
5
2
  export { useOptionalTheme, useTheme } from './theme-context';
6
3
  export { createVersionedPersistence, localStoragePersistence, type ThemePersistence, type ThemeStorageSchema, type VersionedCodec, type VersionedPersistenceOptions, } from './theme-persistence';
7
4
  export type { ThemeSurface } from '#lib/theme-surface';
8
5
  export type { EditorRolesMode } from '#lib/themes';
6
+ export type { ThemeProviderProps } from './theme-provider-core';
9
7
  export { inlineScriptLiteral, themeScript, type ThemeScriptOptions } from './theme-script';
10
- export { defaultThemeSelection, resolvedDensity, type ThemeContextValue, type ThemeDensity, type ThemeSelection, } from './theme-state';
8
+ export { defaultThemeSelection, resolvedDensity, type ThemeContextValue, type ThemeDensity, type ThemeSelection, } from './theme-state-core';
11
9
  /**
12
10
  * ThemeProvider.
13
11
  *
14
12
  * Owns the appearance preference and applies it to the document. One provider near
15
- * the root; everything below reads it.
13
+ * the root; everything below reads it. See {@link ThemeProviderProps} for the
14
+ * options, and `ThemeProviderCore` for how a selection is resolved and applied.
16
15
  *
17
- * ## What a preference is
18
- *
19
- * Four independent choices (`ThemeSelection`), and keeping them independent is the
20
- * point: appearance, a theme per appearance, an accent and fonts. They compose: a
21
- * user who wants Nord with a violet accent and the system font should not need
22
- * someone to author that combination.
23
- *
24
- * ## Where the rules live
25
- *
26
- * This component is wiring. What a selection merges to and resolves to is
27
- * `theme-state.ts`; what lands on `<html>` is `theme-document.ts`; where the
28
- * preference is kept is `theme-persistence.ts`. Each is plain code a test can call,
29
- * which is the reason none of it is inlined here.
30
- *
31
- * ## Why the variants are applied as inline properties
32
- *
33
- * The catalogue is data, and a theme is applied by writing its roles onto
34
- * `<html>` as custom properties. That is what makes an imported palette work
35
- * without a hand-written `[data-theme]` block per theme, and it is why a settings
36
- * UI can show a preview by reading the same object rather than by loading a
37
- * stylesheet. `theme.css` keeps the default theme's values so the first paint is
38
- * correct before any script runs.
16
+ * This provider imports the whole theme catalogue (about 129 KB raw, 34 KB gzipped)
17
+ * so that `useTheme().themes` is a complete array on the first render. A host that
18
+ * paints its theme from a stylesheet (`palette="stylesheet"`), keeps the default
19
+ * pair, and wants the catalogue out of its start-up chunk imports the lazy provider
20
+ * from `@adea-ai/ui/components/theme/lazy` instead; the props are the same.
39
21
  */
40
- export type ThemeProviderProps = {
41
- children?: JSX.Element;
42
- /** Where the preference is persisted. Distinct per application. */
43
- storageKey?: string;
44
- /** Start from this instead of the stored preference. */
45
- initial?: Partial<ThemeSelection>;
46
- /**
47
- * The selection, owned by the caller.
48
- *
49
- * For a consumer that keeps the state itself — a settings store, or a workshop
50
- * whose selection lives in Storybook globals — rather than handing it to this
51
- * provider's signal. Each change lands here and the document follows; nothing is
52
- * written to the store, because the caller is the one who persists. A picker
53
- * below the provider still works, and its change is the caller's to read back.
54
- *
55
- * Absent, the provider is the whole owner: it reads `storageKey` on mount,
56
- * seeds from `initial`, and persists every `setSelection`.
57
- */
58
- selection?: Partial<ThemeSelection>;
59
- /**
60
- * Where the preference is kept, in place of `localStorage` under `storageKey`.
61
- *
62
- * Read once when the provider is created, so changing the prop later does not
63
- * move the store. `storageKey` is ignored when this is given.
64
- */
65
- persistence?: ThemePersistence;
66
- /**
67
- * Variants the host adds to the built-in catalogue — themes a user imported.
68
- *
69
- * A function is read inside the provider's reactive scope, so one that reads a
70
- * signal keeps the picker and the document current when the set changes. A
71
- * stored id that names no variant, built in or added, paints the default of its
72
- * appearance and stays in the selection untouched.
73
- */
74
- themes?: readonly ThemeVariant[] | (() => readonly ThemeVariant[]);
75
- /**
76
- * Rewrites the custom properties of each pass before they are written to
77
- * `<html>`: the variant's roles with the accent over them. `tokensAsHex` from
78
- * `lib/themes` is the one the system ships.
79
- */
80
- transformTokens?: (tokens: Record<string, string>) => Record<string, string>;
81
- /**
82
- * Switch CSS transitions off for the frame that carries a change, so a theme
83
- * switch cannot animate the whole page. Off by default: it adds a `<style>`
84
- * element to `<head>` for two frames on every change.
85
- */
86
- disableTransitionOnChange?: boolean;
87
- /**
88
- * The contrast ratio an accent is held to against the variant's background,
89
- * repaired to the nearest passing colour when it falls short. Absent, presets
90
- * are painted verbatim — the catalogue measured them for its own themes — and
91
- * only a custom `#rrggbb` is held to 3:1. A host that adds themes the catalogue
92
- * never measured passes 3 to hold the presets to it as well; for the catalogue's
93
- * own themes the floor changes no preset.
94
- */
95
- accentContrastFloor?: number;
96
- /**
97
- * Turns the surface axis on: `selection.surface` and `selection.reduceTransparency`
98
- * are resolved against the operating system's Reduce Transparency setting and the
99
- * host's capability, and `data-surface`, `data-reduce-transparency` and
100
- * `--surface-alpha` are written to `<html>` (the frost rule in `theme.css` reads
101
- * them). Off by default: a provider that never opts in writes none of them, adds no
102
- * media-query listener, and stores nothing new.
103
- *
104
- * An object also states the host's capability. `nativeTranslucency` is whether the
105
- * window can render the OS's own translucency; without it a `translucent`
106
- * preference paints the tokenized `frosted` surface instead of pretending to
107
- * vibrancy. A function is read inside the provider's reactive scope, for a
108
- * capability that is learned after mount.
109
- *
110
- * A selection that already carries a `surface` turns the axis on for itself, so a
111
- * host that stores the field gets the same document without the prop.
112
- */
113
- surface?: boolean | {
114
- nativeTranslucency?: boolean | (() => boolean);
115
- };
116
- /**
117
- * Where a catalogue variant's palette comes from. `inline` (the default) writes
118
- * every role onto `<html>`. `stylesheet` leaves a catalogue variant to the host's
119
- * `[data-theme]` stylesheet and writes inline only what no stylesheet can know: a
120
- * host's own `themes`, the accent, the pinned terminal palette, the surface alpha
121
- * and the fonts. Switching from a host variant back to a catalogue one removes the
122
- * properties the host variant wrote.
123
- */
124
- palette?: 'inline' | 'stylesheet';
125
- /**
126
- * The projection behind the `--editor-*` ramp. `syntax` (the default) is the
127
- * catalogue's quiet syntax roles, a comment among them deliberately below
128
- * body-text contrast. `floor` holds all sixteen roles to 4.5:1 on the canvas, as
129
- * hex, for a host that renders code as content (a diff, a file view) rather than
130
- * as decoration.
131
- *
132
- * Applies to catalogue variants; a host's own `themes` keep their `editor` and
133
- * their `tokens`. With `palette="stylesheet"` it is the one palette property a
134
- * catalogue variant still writes inline, because `theme-variants.css` is generated
135
- * for `syntax`. Absent, nothing changes.
136
- */
137
- editorRoles?: EditorRolesMode;
138
- /**
139
- * Bind the platform zoom chords (Cmd or Ctrl with `=`, `+`, `-`, `0`) to the three
140
- * text sizes instead of the browser's page zoom: stepping them together is zoom
141
- * inside the persisted type system. Off by default: it adds a capturing `keydown`
142
- * listener to `window`, and an event already handled elsewhere is left alone.
143
- * `zoomFonts` on the context does the same from a menu or a host's own binding.
144
- */
145
- fontZoom?: boolean;
146
- };
147
- export declare function ThemeProvider(props: ThemeProviderProps): JSX.Element;
22
+ export declare function ThemeProvider(props: ThemeProviderProps): import("solid-js").JSX.Element;
148
23
  //# sourceMappingURL=theme-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInC,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AAGpB,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EASL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,2BAA2B,GACjC,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAC1F,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAA;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IACjC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IACnC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAA;IAC9B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG,CAAC,MAAM,SAAS,YAAY,EAAE,CAAC,CAAA;IAClE;;;;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,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG;QAAE,kBAAkB,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAA;KAAE,CAAA;IACtE;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAA;IACjC;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AASD,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,eAqLtD"}
1
+ {"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,2BAA2B,GACjC,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAC1F,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAA;AAU3B;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,kCAEtD"}
@@ -1,155 +1,33 @@
1
- import { builtinThemes, themeAccentsFor } from "../../lib/themes.js";
2
- import { fontZoomShortcut } from "../../lib/font-zoom.js";
3
- import { REDUCED_TRANSPARENCY_QUERY, resolveSurfaceState } from "../../lib/theme-surface.js";
4
- import { themeContext, useOptionalTheme, useTheme } from "./theme-context.js";
5
- import { accentRolesForState, defaultThemeSelection, initialThemeSelection, mergeRegistry, mergeThemeSelection, resolveTerminalVariant, resolveThemeState, resolvedDensity, zoomedFontSettings } from "./theme-state.js";
6
- import { applyThemeToRoot, applyWithoutTransitions } from "./theme-document.js";
7
- import { createVersionedPersistence, localStoragePersistence, readPersisted, writePersisted } from "./theme-persistence.js";
1
+ import { catalogueThemeEngine } from "../../lib/theme-catalogue.js";
2
+ import { useOptionalTheme, useTheme } from "./theme-context.js";
3
+ import { defaultThemeSelection, resolvedDensity } from "./theme-state-core.js";
4
+ import { createVersionedPersistence, localStoragePersistence } from "./theme-persistence.js";
8
5
  import { inlineScriptLiteral, themeScript } from "./theme-script.js";
9
- import { createComponent } from "solid-js/web";
10
- import { createEffect, createMemo, createSignal, onCleanup, splitProps, untrack } from "solid-js";
6
+ import { ThemeProviderCore } from "./theme-provider-core.js";
7
+ import { createComponent, mergeProps } from "solid-js/web";
11
8
  //#region src/components/theme/theme-provider.tsx
12
- /** The operating system's Reduce Transparency query, where there is a window to ask. */
13
- function reducedTransparencyMedia() {
14
- return typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(REDUCED_TRANSPARENCY_QUERY) : void 0;
15
- }
9
+ /** The whole catalogue, already imported: nothing to load, nothing to wait for. */
10
+ var eagerCatalogue = {
11
+ engine: () => catalogueThemeEngine,
12
+ loaded: () => true,
13
+ failed: () => false,
14
+ load: () => Promise.resolve()
15
+ };
16
+ /**
17
+ * ThemeProvider.
18
+ *
19
+ * Owns the appearance preference and applies it to the document. One provider near
20
+ * the root; everything below reads it. See {@link ThemeProviderProps} for the
21
+ * options, and `ThemeProviderCore` for how a selection is resolved and applied.
22
+ *
23
+ * This provider imports the whole theme catalogue (about 129 KB raw, 34 KB gzipped)
24
+ * so that `useTheme().themes` is a complete array on the first render. A host that
25
+ * paints its theme from a stylesheet (`palette="stylesheet"`), keeps the default
26
+ * pair, and wants the catalogue out of its start-up chunk imports the lazy provider
27
+ * from `@adea-ai/ui/components/theme/lazy` instead; the props are the same.
28
+ */
16
29
  function ThemeProvider(props) {
17
- const [local] = splitProps(props, [
18
- "children",
19
- "storageKey",
20
- "initial",
21
- "selection",
22
- "persistence",
23
- "themes",
24
- "transformTokens",
25
- "disableTransitionOnChange",
26
- "accentContrastFloor",
27
- "surface",
28
- "palette",
29
- "editorRoles",
30
- "fontZoom"
31
- ]);
32
- const storageKey = () => local.storageKey ?? "adea-appearance";
33
- const persistence = local.persistence ?? localStoragePersistence(storageKey);
34
- const extraThemes = createMemo(() => typeof local.themes === "function" ? local.themes() : local.themes ?? []);
35
- const registry = createMemo(() => mergeRegistry(builtinThemes, extraThemes()));
36
- const [selection, setSelectionState] = createSignal(initialThemeSelection(readPersisted(persistence), local.initial));
37
- createEffect(() => {
38
- const incoming = local.selection;
39
- if (incoming) setSelectionState((current) => mergeThemeSelection(current, incoming));
40
- });
41
- const [systemAppearance, setSystemAppearance] = createSignal("light");
42
- if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
43
- const query = window.matchMedia("(prefers-color-scheme: dark)");
44
- setSystemAppearance(query.matches ? "dark" : "light");
45
- const onChange = (event) => setSystemAppearance(event.matches ? "dark" : "light");
46
- query.addEventListener("change", onChange);
47
- onCleanup(() => query.removeEventListener("change", onChange));
48
- }
49
- const committed = createMemo(() => resolveThemeState(selection(), systemAppearance(), registry()));
50
- const resolvedAppearance = createMemo(() => committed().appearance);
51
- const variant = createMemo(() => committed().variant);
52
- const [draft, setDraft] = createSignal();
53
- const shownSelection = createMemo(() => {
54
- const preview = draft();
55
- return preview ? mergeThemeSelection(selection(), preview) : selection();
56
- });
57
- const shown = createMemo(() => draft() ? resolveThemeState(shownSelection(), systemAppearance(), registry()) : committed());
58
- const surfaceOn = (candidate) => local.surface === true || typeof local.surface === "object" || candidate.surface !== void 0;
59
- const nativeTranslucency = () => {
60
- const capability = typeof local.surface === "object" ? local.surface.nativeTranslucency : false;
61
- return (typeof capability === "function" ? capability() : capability) === true;
62
- };
63
- const [osReducedTransparency, setOsReducedTransparency] = createSignal(surfaceOn(untrack(shownSelection)) ? reducedTransparencyMedia()?.matches ?? false : false);
64
- const axisOn = createMemo(() => surfaceOn(shownSelection()));
65
- createEffect(() => {
66
- const query = axisOn() ? reducedTransparencyMedia() : void 0;
67
- setOsReducedTransparency(query?.matches ?? false);
68
- if (!query) return;
69
- const onChange = (event) => setOsReducedTransparency(event.matches);
70
- query.addEventListener("change", onChange);
71
- onCleanup(() => query.removeEventListener("change", onChange));
72
- });
73
- const surfaceStateOf = (candidate) => resolveSurfaceState(candidate.surface, {
74
- osReducedTransparency: osReducedTransparency(),
75
- userReducedTransparency: candidate.reduceTransparency === true,
76
- nativeTranslucency: nativeTranslucency()
77
- });
78
- const committedSurface = createMemo(() => surfaceOn(selection()) ? surfaceStateOf(selection()) : void 0);
79
- let written;
80
- let restoreTransitions;
81
- createEffect(() => {
82
- if (typeof document === "undefined") return;
83
- const state = {
84
- selection: shownSelection(),
85
- appearance: shown().appearance,
86
- variant: shown().variant,
87
- terminalVariant: resolveTerminalVariant(shownSelection().terminalThemeId, registry()),
88
- surface: axisOn() ? surfaceStateOf(shownSelection()) : void 0
89
- };
90
- const options = {
91
- transformTokens: local.transformTokens,
92
- accentContrastFloor: local.accentContrastFloor,
93
- palette: local.palette,
94
- editorRoles: local.editorRoles
95
- };
96
- const apply = () => {
97
- written = applyThemeToRoot(document.documentElement, state, options, written);
98
- };
99
- if (local.disableTransitionOnChange) {
100
- restoreTransitions?.();
101
- restoreTransitions = applyWithoutTransitions(document, apply);
102
- } else apply();
103
- });
104
- onCleanup(() => restoreTransitions?.());
105
- const themeAccents = createMemo(() => themeAccentsFor(selection().lightThemeId, selection().darkThemeId));
106
- const zoomFonts = (intent) => value.setSelection({ fonts: zoomedFontSettings(selection(), intent) });
107
- const onKeyDown = (event) => {
108
- if (event.defaultPrevented) return;
109
- const intent = fontZoomShortcut(event);
110
- if (!intent) return;
111
- event.preventDefault();
112
- zoomFonts(intent);
113
- };
114
- createEffect(() => {
115
- if (!local.fontZoom || typeof window === "undefined") return;
116
- window.addEventListener("keydown", onKeyDown, { capture: true });
117
- onCleanup(() => window.removeEventListener("keydown", onKeyDown, { capture: true }));
118
- });
119
- const value = {
120
- selection,
121
- resolvedAppearance,
122
- variant,
123
- themes: builtinThemes,
124
- themeAccents,
125
- allThemes: registry,
126
- preview(next) {
127
- setDraft(next ? { ...next } : void 0);
128
- },
129
- previewing: () => draft() !== void 0,
130
- accentRolesFor: (accent, variantId) => accentRolesForState(accent, variantId, {
131
- selection: selection(),
132
- variant: variant(),
133
- registry: registry(),
134
- accentContrastFloor: local.accentContrastFloor
135
- }),
136
- effectiveSurface: () => committedSurface()?.surface ?? "opaque",
137
- reduceTransparencyActive: () => committedSurface()?.reduceTransparencyActive ?? false,
138
- zoomFonts,
139
- setSelection(patch) {
140
- const next = mergeThemeSelection(selection(), patch);
141
- setSelectionState(next);
142
- setDraft(void 0);
143
- writePersisted(persistence, next);
144
- }
145
- };
146
- const Context = themeContext();
147
- return createComponent(Context.Provider, {
148
- value,
149
- get children() {
150
- return local.children;
151
- }
152
- });
30
+ return createComponent(ThemeProviderCore, mergeProps(props, { catalogue: eagerCatalogue }));
153
31
  }
154
32
  //#endregion
155
33
  export { ThemeProvider, createVersionedPersistence, defaultThemeSelection, inlineScriptLiteral, localStoragePersistence, resolvedDensity, themeScript, useOptionalTheme, useTheme };
@@ -1 +1 @@
1
- {"version":3,"file":"theme-provider.js","names":["JSX","createEffect","createMemo","createSignal","onCleanup","splitProps","untrack","fontZoomShortcut","FontZoomIntent","REDUCED_TRANSPARENCY_QUERY","resolveSurfaceState","builtinThemes","themeAccentsFor","EditorRolesMode","ThemeAppearance","ThemeVariant","themeContext","applyThemeToRoot","applyWithoutTransitions","localStoragePersistence","readPersisted","writePersisted","ThemePersistence","accentRolesForState","initialThemeSelection","mergeRegistry","mergeThemeSelection","resolveTerminalVariant","resolveThemeState","zoomedFontSettings","ThemeContextValue","ThemeSelection","useOptionalTheme","useTheme","createVersionedPersistence","ThemeStorageSchema","VersionedCodec","VersionedPersistenceOptions","ThemeSurface","inlineScriptLiteral","themeScript","ThemeScriptOptions","defaultThemeSelection","resolvedDensity","ThemeDensity","ThemeProviderProps","children","Element","storageKey","initial","Partial","selection","persistence","themes","transformTokens","tokens","Record","disableTransitionOnChange","accentContrastFloor","surface","nativeTranslucency","palette","editorRoles","fontZoom","reducedTransparencyMedia","MediaQueryList","window","matchMedia","undefined","ThemeProvider","props","local","extraThemes","registry","setSelectionState","incoming","current","systemAppearance","setSystemAppearance","query","matches","onChange","event","MediaQueryListEvent","addEventListener","removeEventListener","committed","resolvedAppearance","appearance","variant","draft","setDraft","shownSelection","preview","shown","surfaceOn","candidate","capability","osReducedTransparency","setOsReducedTransparency","axisOn","surfaceStateOf","userReducedTransparency","reduceTransparency","committedSurface","written","ReadonlySet","restoreTransitions","document","state","terminalVariant","terminalThemeId","options","apply","documentElement","themeAccents","lightThemeId","darkThemeId","zoomFonts","intent","value","setSelection","fonts","onKeyDown","KeyboardEvent","defaultPrevented","preventDefault","capture","allThemes","next","previewing","accentRolesFor","accent","variantId","effectiveSurface","reduceTransparencyActive","patch","Context","_$createComponent","Provider","_$createComponent"],"sources":["../../../src/components/theme/theme-provider.tsx"],"sourcesContent":["import type { JSX } from 'solid-js'\nimport { createEffect, createMemo, createSignal, onCleanup, splitProps, untrack } from 'solid-js'\nimport { fontZoomShortcut, type FontZoomIntent } from '#lib/font-zoom'\nimport { REDUCED_TRANSPARENCY_QUERY, resolveSurfaceState } from '#lib/theme-surface'\nimport {\n builtinThemes,\n themeAccentsFor,\n type EditorRolesMode,\n type ThemeAppearance,\n type ThemeVariant,\n} from '#lib/themes'\nimport { themeContext } from './theme-context'\nimport { applyThemeToRoot, applyWithoutTransitions } from './theme-document'\nimport {\n localStoragePersistence,\n readPersisted,\n writePersisted,\n type ThemePersistence,\n} from './theme-persistence'\nimport {\n accentRolesForState,\n initialThemeSelection,\n mergeRegistry,\n mergeThemeSelection,\n resolveTerminalVariant,\n resolveThemeState,\n zoomedFontSettings,\n type ThemeContextValue,\n type ThemeSelection,\n} from './theme-state'\n\nexport { useOptionalTheme, useTheme } from './theme-context'\nexport {\n createVersionedPersistence,\n localStoragePersistence,\n type ThemePersistence,\n type ThemeStorageSchema,\n type VersionedCodec,\n type VersionedPersistenceOptions,\n} from './theme-persistence'\nexport type { ThemeSurface } from '#lib/theme-surface'\nexport type { EditorRolesMode } from '#lib/themes'\nexport { inlineScriptLiteral, themeScript, type ThemeScriptOptions } from './theme-script'\nexport {\n defaultThemeSelection,\n resolvedDensity,\n type ThemeContextValue,\n type ThemeDensity,\n type ThemeSelection,\n} from './theme-state'\n\n/**\n * ThemeProvider.\n *\n * Owns the appearance preference and applies it to the document. One provider near\n * the root; everything below reads it.\n *\n * ## What a preference is\n *\n * Four independent choices (`ThemeSelection`), and keeping them independent is the\n * point: appearance, a theme per appearance, an accent and fonts. They compose: a\n * user who wants Nord with a violet accent and the system font should not need\n * someone to author that combination.\n *\n * ## Where the rules live\n *\n * This component is wiring. What a selection merges to and resolves to is\n * `theme-state.ts`; what lands on `<html>` is `theme-document.ts`; where the\n * preference is kept is `theme-persistence.ts`. Each is plain code a test can call,\n * which is the reason none of it is inlined here.\n *\n * ## Why the variants are applied as inline properties\n *\n * The catalogue is data, and a theme is applied by writing its roles onto\n * `<html>` as custom properties. That is what makes an imported palette work\n * without a hand-written `[data-theme]` block per theme, and it is why a settings\n * UI can show a preview by reading the same object rather than by loading a\n * stylesheet. `theme.css` keeps the default theme's values so the first paint is\n * correct before any script runs.\n */\nexport type ThemeProviderProps = {\n children?: JSX.Element\n /** Where the preference is persisted. Distinct per application. */\n storageKey?: string\n /** Start from this instead of the stored preference. */\n initial?: Partial<ThemeSelection>\n /**\n * The selection, owned by the caller.\n *\n * For a consumer that keeps the state itself — a settings store, or a workshop\n * whose selection lives in Storybook globals — rather than handing it to this\n * provider's signal. Each change lands here and the document follows; nothing is\n * written to the store, because the caller is the one who persists. A picker\n * below the provider still works, and its change is the caller's to read back.\n *\n * Absent, the provider is the whole owner: it reads `storageKey` on mount,\n * seeds from `initial`, and persists every `setSelection`.\n */\n selection?: Partial<ThemeSelection>\n /**\n * Where the preference is kept, in place of `localStorage` under `storageKey`.\n *\n * Read once when the provider is created, so changing the prop later does not\n * move the store. `storageKey` is ignored when this is given.\n */\n persistence?: ThemePersistence\n /**\n * Variants the host adds to the built-in catalogue — themes a user imported.\n *\n * A function is read inside the provider's reactive scope, so one that reads a\n * signal keeps the picker and the document current when the set changes. A\n * stored id that names no variant, built in or added, paints the default of its\n * appearance and stays in the selection untouched.\n */\n themes?: readonly ThemeVariant[] | (() => readonly ThemeVariant[])\n /**\n * Rewrites the custom properties of each pass before they are written to\n * `<html>`: the variant's roles with the accent over them. `tokensAsHex` from\n * `lib/themes` is the one the system ships.\n */\n transformTokens?: (tokens: Record<string, string>) => Record<string, string>\n /**\n * Switch CSS transitions off for the frame that carries a change, so a theme\n * switch cannot animate the whole page. Off by default: it adds a `<style>`\n * element to `<head>` for two frames on every change.\n */\n disableTransitionOnChange?: boolean\n /**\n * The contrast ratio an accent is held to against the variant's background,\n * repaired to the nearest passing colour when it falls short. Absent, presets\n * are painted verbatim — the catalogue measured them for its own themes — and\n * only a custom `#rrggbb` is held to 3:1. A host that adds themes the catalogue\n * never measured passes 3 to hold the presets to it as well; for the catalogue's\n * own themes the floor changes no preset.\n */\n accentContrastFloor?: number\n /**\n * Turns the surface axis on: `selection.surface` and `selection.reduceTransparency`\n * are resolved against the operating system's Reduce Transparency setting and the\n * host's capability, and `data-surface`, `data-reduce-transparency` and\n * `--surface-alpha` are written to `<html>` (the frost rule in `theme.css` reads\n * them). Off by default: a provider that never opts in writes none of them, adds no\n * media-query listener, and stores nothing new.\n *\n * An object also states the host's capability. `nativeTranslucency` is whether the\n * window can render the OS's own translucency; without it a `translucent`\n * preference paints the tokenized `frosted` surface instead of pretending to\n * vibrancy. A function is read inside the provider's reactive scope, for a\n * capability that is learned after mount.\n *\n * A selection that already carries a `surface` turns the axis on for itself, so a\n * host that stores the field gets the same document without the prop.\n */\n surface?: boolean | { nativeTranslucency?: boolean | (() => boolean) }\n /**\n * Where a catalogue variant's palette comes from. `inline` (the default) writes\n * every role onto `<html>`. `stylesheet` leaves a catalogue variant to the host's\n * `[data-theme]` stylesheet and writes inline only what no stylesheet can know: a\n * host's own `themes`, the accent, the pinned terminal palette, the surface alpha\n * and the fonts. Switching from a host variant back to a catalogue one removes the\n * properties the host variant wrote.\n */\n palette?: 'inline' | 'stylesheet'\n /**\n * The projection behind the `--editor-*` ramp. `syntax` (the default) is the\n * catalogue's quiet syntax roles, a comment among them deliberately below\n * body-text contrast. `floor` holds all sixteen roles to 4.5:1 on the canvas, as\n * hex, for a host that renders code as content (a diff, a file view) rather than\n * as decoration.\n *\n * Applies to catalogue variants; a host's own `themes` keep their `editor` and\n * their `tokens`. With `palette=\"stylesheet\"` it is the one palette property a\n * catalogue variant still writes inline, because `theme-variants.css` is generated\n * for `syntax`. Absent, nothing changes.\n */\n editorRoles?: EditorRolesMode\n /**\n * Bind the platform zoom chords (Cmd or Ctrl with `=`, `+`, `-`, `0`) to the three\n * text sizes instead of the browser's page zoom: stepping them together is zoom\n * inside the persisted type system. Off by default: it adds a capturing `keydown`\n * listener to `window`, and an event already handled elsewhere is left alone.\n * `zoomFonts` on the context does the same from a menu or a host's own binding.\n */\n fontZoom?: boolean\n}\n\n/** The operating system's Reduce Transparency query, where there is a window to ask. */\nfunction reducedTransparencyMedia(): MediaQueryList | undefined {\n return typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia(REDUCED_TRANSPARENCY_QUERY)\n : undefined\n}\n\nexport function ThemeProvider(props: ThemeProviderProps) {\n const [local] = splitProps(props, [\n 'children',\n 'storageKey',\n 'initial',\n 'selection',\n 'persistence',\n 'themes',\n 'transformTokens',\n 'disableTransitionOnChange',\n 'accentContrastFloor',\n 'surface',\n 'palette',\n 'editorRoles',\n 'fontZoom',\n ])\n const storageKey = () => local.storageKey ?? 'adea-appearance'\n const persistence = local.persistence ?? localStoragePersistence(storageKey)\n\n // Read before the first resolution and before the store is, so a persistence whose\n // answer depends on the extras (an id it only accepts if the theme exists) sees\n // them.\n const extraThemes = createMemo(() =>\n typeof local.themes === 'function' ? local.themes() : (local.themes ?? [])\n )\n const registry = createMemo(() => mergeRegistry(builtinThemes, extraThemes()))\n\n const [selection, setSelectionState] = createSignal<ThemeSelection>(\n initialThemeSelection(readPersisted(persistence), local.initial)\n )\n\n // The controlled half of the contract. A patch from the owner replaces the\n // matching axes; the stored preference is untouched, since the owner is the one\n // that persists. Reading `local.selection` — a prop getter, not a destructured\n // value — is what keeps this subscribed for the provider's lifetime.\n createEffect(() => {\n const incoming = local.selection\n if (incoming) setSelectionState((current) => mergeThemeSelection(current, incoming))\n })\n\n const [systemAppearance, setSystemAppearance] = createSignal<ThemeAppearance>('light')\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const query = window.matchMedia('(prefers-color-scheme: dark)')\n setSystemAppearance(query.matches ? 'dark' : 'light')\n const onChange = (event: MediaQueryListEvent) =>\n setSystemAppearance(event.matches ? 'dark' : 'light')\n query.addEventListener('change', onChange)\n onCleanup(() => query.removeEventListener('change', onChange))\n }\n\n // What the stored preference resolves to. `selection()`, `variant()` and\n // `resolvedAppearance()` answer from this and never from a preview, so a picker\n // that previews a draft still reads back what is committed.\n const committed = createMemo(() => resolveThemeState(selection(), systemAppearance(), registry()))\n const resolvedAppearance = createMemo<ThemeAppearance>(() => committed().appearance)\n const variant = createMemo<ThemeVariant>(() => committed().variant)\n\n const [draft, setDraft] = createSignal<Partial<ThemeSelection> | undefined>()\n const shownSelection = createMemo(() => {\n const preview = draft()\n return preview ? mergeThemeSelection(selection(), preview) : selection()\n })\n const shown = createMemo(() =>\n draft() ? resolveThemeState(shownSelection(), systemAppearance(), registry()) : committed()\n )\n\n // The surface axis. Off, none of this reads the operating system: the media query\n // is subscribed only while the axis is on, so a provider that never opts in adds no\n // listener and no repaint.\n const surfaceOn = (candidate: ThemeSelection) =>\n local.surface === true || typeof local.surface === 'object' || candidate.surface !== undefined\n const nativeTranslucency = () => {\n const capability = typeof local.surface === 'object' ? local.surface.nativeTranslucency : false\n return (typeof capability === 'function' ? capability() : capability) === true\n }\n const [osReducedTransparency, setOsReducedTransparency] = createSignal(\n surfaceOn(untrack(shownSelection)) ? (reducedTransparencyMedia()?.matches ?? false) : false\n )\n const axisOn = createMemo(() => surfaceOn(shownSelection()))\n createEffect(() => {\n const query = axisOn() ? reducedTransparencyMedia() : undefined\n setOsReducedTransparency(query?.matches ?? false)\n if (!query) return\n const onChange = (event: MediaQueryListEvent) => setOsReducedTransparency(event.matches)\n query.addEventListener('change', onChange)\n onCleanup(() => query.removeEventListener('change', onChange))\n })\n const surfaceStateOf = (candidate: ThemeSelection) =>\n resolveSurfaceState(candidate.surface, {\n osReducedTransparency: osReducedTransparency(),\n userReducedTransparency: candidate.reduceTransparency === true,\n nativeTranslucency: nativeTranslucency(),\n })\n const committedSurface = createMemo(() =>\n surfaceOn(selection()) ? surfaceStateOf(selection()) : undefined\n )\n\n let written: ReadonlySet<string> | undefined\n let restoreTransitions: (() => void) | undefined\n createEffect(() => {\n if (typeof document === 'undefined') return\n const state = {\n selection: shownSelection(),\n appearance: shown().appearance,\n variant: shown().variant,\n terminalVariant: resolveTerminalVariant(shownSelection().terminalThemeId, registry()),\n surface: axisOn() ? surfaceStateOf(shownSelection()) : undefined,\n }\n const options = {\n transformTokens: local.transformTokens,\n accentContrastFloor: local.accentContrastFloor,\n palette: local.palette,\n editorRoles: local.editorRoles,\n }\n const apply = () => {\n written = applyThemeToRoot(document.documentElement, state, options, written)\n }\n if (local.disableTransitionOnChange) {\n restoreTransitions?.()\n restoreTransitions = applyWithoutTransitions(document, apply)\n } else {\n apply()\n }\n })\n onCleanup(() => restoreTransitions?.())\n\n const themeAccents = createMemo(() =>\n themeAccentsFor(selection().lightThemeId, selection().darkThemeId)\n )\n\n const zoomFonts = (intent: FontZoomIntent) =>\n value.setSelection({ fonts: zoomedFontSettings(selection(), intent) })\n\n // The platform zoom chords drive the three text sizes, captured like the other\n // global chords; an event something else already handled never double-steps.\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented) return\n const intent = fontZoomShortcut(event)\n if (!intent) return\n event.preventDefault()\n zoomFonts(intent)\n }\n createEffect(() => {\n if (!local.fontZoom || typeof window === 'undefined') return\n window.addEventListener('keydown', onKeyDown, { capture: true })\n onCleanup(() => window.removeEventListener('keydown', onKeyDown, { capture: true }))\n })\n\n const value: ThemeContextValue = {\n selection,\n resolvedAppearance,\n variant,\n themes: builtinThemes,\n themeAccents,\n allThemes: registry,\n preview(next) {\n setDraft(next ? { ...next } : undefined)\n },\n previewing: () => draft() !== undefined,\n accentRolesFor: (accent, variantId) =>\n accentRolesForState(accent, variantId, {\n selection: selection(),\n variant: variant(),\n registry: registry(),\n accentContrastFloor: local.accentContrastFloor,\n }),\n effectiveSurface: () => committedSurface()?.surface ?? 'opaque',\n reduceTransparencyActive: () => committedSurface()?.reduceTransparencyActive ?? false,\n zoomFonts,\n setSelection(patch) {\n const next = mergeThemeSelection(selection(), patch)\n setSelectionState(next)\n // The committed selection is now what should be showing.\n setDraft(undefined)\n writePersisted(persistence, next)\n },\n }\n\n const Context = themeContext()\n return <Context.Provider value={value}>{local.children}</Context.Provider>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"theme-provider.js","names":["catalogueThemeEngine","ThemeProviderCore","ThemeCatalogueSource","ThemeProviderProps","useOptionalTheme","useTheme","createVersionedPersistence","localStoragePersistence","ThemePersistence","ThemeStorageSchema","VersionedCodec","VersionedPersistenceOptions","ThemeSurface","EditorRolesMode","inlineScriptLiteral","themeScript","ThemeScriptOptions","defaultThemeSelection","resolvedDensity","ThemeContextValue","ThemeDensity","ThemeSelection","eagerCatalogue","engine","loaded","failed","load","Promise","resolve","ThemeProvider","props","_$createComponent","_$mergeProps","catalogue","_$createComponent","_$mergeProps"],"sources":["../../../src/components/theme/theme-provider.tsx"],"sourcesContent":["import { catalogueThemeEngine } from '#lib/theme-catalogue'\nimport {\n ThemeProviderCore,\n type ThemeCatalogueSource,\n type ThemeProviderProps,\n} from './theme-provider-core'\n\nexport { useOptionalTheme, useTheme } from './theme-context'\nexport {\n createVersionedPersistence,\n localStoragePersistence,\n type ThemePersistence,\n type ThemeStorageSchema,\n type VersionedCodec,\n type VersionedPersistenceOptions,\n} from './theme-persistence'\nexport type { ThemeSurface } from '#lib/theme-surface'\nexport type { EditorRolesMode } from '#lib/themes'\nexport type { ThemeProviderProps } from './theme-provider-core'\nexport { inlineScriptLiteral, themeScript, type ThemeScriptOptions } from './theme-script'\nexport {\n defaultThemeSelection,\n resolvedDensity,\n type ThemeContextValue,\n type ThemeDensity,\n type ThemeSelection,\n} from './theme-state-core'\n\n/** The whole catalogue, already imported: nothing to load, nothing to wait for. */\nconst eagerCatalogue: ThemeCatalogueSource = {\n engine: () => catalogueThemeEngine,\n loaded: () => true,\n failed: () => false,\n load: () => Promise.resolve(),\n}\n\n/**\n * ThemeProvider.\n *\n * Owns the appearance preference and applies it to the document. One provider near\n * the root; everything below reads it. See {@link ThemeProviderProps} for the\n * options, and `ThemeProviderCore` for how a selection is resolved and applied.\n *\n * This provider imports the whole theme catalogue (about 129 KB raw, 34 KB gzipped)\n * so that `useTheme().themes` is a complete array on the first render. A host that\n * paints its theme from a stylesheet (`palette=\"stylesheet\"`), keeps the default\n * pair, and wants the catalogue out of its start-up chunk imports the lazy provider\n * from `@adea-ai/ui/components/theme/lazy` instead; the props are the same.\n */\nexport function ThemeProvider(props: ThemeProviderProps) {\n return <ThemeProviderCore {...props} catalogue={eagerCatalogue} />\n}\n"],"mappings":";;;;;;;;;AA6BA,IAAMsB,iBAAuC;CAC3CC,cAAcvB;CACdwB,cAAc;CACdC,cAAc;CACdC,YAAYC,QAAQC,QAAQ;AAC9B;;;;;;;;;;;;;;AAeA,SAAgBC,cAAcC,OAA2B;CACvD,OAAAC,gBAAQ9B,mBAAiB+B,WAAKF,OAAK,EAAEG,WAAWX,eAAc,CAAA,CAAA;AAChE"}
@@ -1,6 +1,7 @@
1
- import { accentPresets } from "../../lib/tokens.js";
1
+ import { accentPresets } from "../../lib/accent-presets.js";
2
2
  import { isCustomAccent } from "../../lib/accent-roles.js";
3
- import { builtinThemes, defaultDarkThemeId, defaultLightThemeId } from "../../lib/themes.js";
3
+ import { THEME_INDEX } from "../../lib/theme-defaults.generated.js";
4
+ import { defaultDarkThemeId, defaultLightThemeId } from "../../lib/theme-defaults.js";
4
5
  import { safeScriptStringLiteral } from "../../lib/safe-script.js";
5
6
  import { fontSettingsBootstrapScript } from "../../lib/appearance-font-settings.js";
6
7
  import { REDUCED_TRANSPARENCY_QUERY, surfaceAlpha } from "../../lib/theme-surface.js";
@@ -42,7 +43,7 @@ function optionsFormScript(options) {
42
43
  const parts = [];
43
44
  parts.push(`(function(){try{`, `var r=document.documentElement,s=r.style;`, `var q=function(m){try{return window.matchMedia(m).matches}catch(e){return false}};`, `var get=function(k){try{return localStorage.getItem(k)}catch(e){return null}};`, `var raw=get(${literal(options.key)}),doc=null,legacy=null;`, `if(raw!==null){try{var x=JSON.parse(raw);if(x&&typeof x==='object'&&!Array.isArray(x)${versionGuard})doc=x}catch(e){}}`);
44
45
  if (options.legacyAppearanceKey !== void 0) parts.push(`else legacy=get(${literal(options.legacyAppearanceKey)});`);
45
- parts.push(`var m=doc?doc[${literal(field)}]:legacy;`, `var mode=m==='light'||m==='dark'?m:'system';`, `var dark=mode==='dark'||(mode==='system'&&q('(prefers-color-scheme: dark)'));`, `var fallback=dark?${literal(defaultDarkThemeId)}:${literal(defaultLightThemeId)};`, `var want=doc?(dark?doc.darkThemeId:doc.lightThemeId):null;`, `if(typeof want!=='string')want=fallback;`, `var ids=[${builtinThemes.map((theme) => literal(theme.id)).join(",")}];`, `var id=ids.indexOf(want)!==-1?want:null,tokens=null;`);
46
+ parts.push(`var m=doc?doc[${literal(field)}]:legacy;`, `var mode=m==='light'||m==='dark'?m:'system';`, `var dark=mode==='dark'||(mode==='system'&&q('(prefers-color-scheme: dark)'));`, `var fallback=dark?${literal(defaultDarkThemeId)}:${literal(defaultLightThemeId)};`, `var want=doc?(dark?doc.darkThemeId:doc.lightThemeId):null;`, `if(typeof want!=='string')want=fallback;`, `var ids=[${THEME_INDEX.map(([id]) => literal(id)).join(",")}];`, `var id=ids.indexOf(want)!==-1?want:null,tokens=null;`);
46
47
  if (options.libraryKey !== void 0) parts.push(`if(id===null){try{var lib=JSON.parse(get(${literal(options.libraryKey)})||'null');`, `if(lib&&typeof lib==='object'&&lib.version===${Number(options.libraryVersion ?? 1)}&&Array.isArray(lib.themes)){`, `for(var i=0;i!==lib.themes.length;i++){var t=lib.themes[i];`, `if(t&&t.id===want&&t.flatTokens&&typeof t.flatTokens==='object'){id=want;tokens=t.flatTokens;break}}}}catch(e){}}`);
47
48
  parts.push(`if(id===null)id=fallback;`, `r.classList.toggle('dark',dark);`, `s.colorScheme=dark?'dark':'light';`, `r.dataset.theme=id;`, `r.dataset.appearance=dark?'dark':'light';`);
48
49
  if (options.libraryKey !== void 0) parts.push(`if(tokens){for(var n in tokens){if(Object.prototype.hasOwnProperty.call(tokens,n)&&n.slice(0,2)==='--'&&typeof tokens[n]==='string')s.setProperty(n,tokens[n])}}`);
@@ -1 +1 @@
1
- {"version":3,"file":"theme-script.js","names":[],"sources":["../../../src/components/theme/theme-script.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Wing\n * Licensed under the MIT License.\n *\n * The options form is rewritten from Adea's accepted port of Zeron's preference\n * store (`appearanceThemeScript` in `packages/ui/src/components/appearance.ts`,\n * https://github.com/zeronsh/zeron) so the boot script reads the document the shared\n * persistence writes. See NOTICE.\n */\nimport { fontSettingsBootstrapScript } from '#lib/appearance-font-settings'\nimport { isCustomAccent } from '#lib/accent-roles'\nimport { safeScriptStringLiteral } from '#lib/safe-script'\nimport { REDUCED_TRANSPARENCY_QUERY, surfaceAlpha } from '#lib/theme-surface'\nimport { accentPresets } from '#lib/tokens'\nimport { builtinThemes, defaultDarkThemeId, defaultLightThemeId } from '#lib/themes'\nimport type { ThemeStorageSchema } from './theme-persistence'\n\n/**\n * A string as a JavaScript literal that is also safe inside an inline `<script>`.\n *\n * `JSON.stringify` produces a correct JavaScript literal, and it is **not** safe\n * here: it leaves `<` and `>` alone, so a value containing `</script>` ends the tag\n * early and everything after it is parsed as HTML. That is the classic inline-script\n * injection, and it is a real one even though the value is a developer-supplied prop\n * rather than user input — a prop can come from configuration, and a key that happens\n * to contain a closing tag should not be able to execute.\n *\n * The escape is also correct JavaScript: `\\u003c` is `<` to the parser, so the value\n * is byte-identical at runtime and inert to the HTML tokenizer. `\\u2028` and `\\u2029`\n * are escaped for the neighbouring reason — they are legal inside a JSON string and\n * were, before ES2019, line terminators to a JavaScript parser.\n *\n * CodeQL flags the unescaped form as `js/bad-code-sanitization`, which is how this\n * was found; `tests/theme-script.test.ts` pins it so it cannot come back.\n */\nexport function inlineScriptLiteral(value: string): string {\n return safeScriptStringLiteral(value)\n}\n\n/**\n * Options for the host-shaped no-flash script: the document's {@link ThemeStorageSchema}\n * plus what the script paints from it.\n */\nexport type ThemeScriptOptions = ThemeStorageSchema & {\n /**\n * The accent painted when the document has none, an unrecognised one, or the\n * legacy `theme` spelling — the host's default (adea: `violet`). Without it those\n * cases paint no `data-accent`, which is what the provider writes for `theme`.\n * Setting it means the `[data-accent]` block of the default preset paints on the\n * first frame instead of after hydration.\n */\n defaultAccent?: string\n /**\n * Also resolve the surface axis (`data-surface`, `data-reduce-transparency`,\n * `--surface-alpha`) from the document and the operating system's Reduce\n * Transparency setting. A `translucent` preference resolves to `frosted`: the\n * host's translucency capability is not known until the provider mounts, and the\n * provider re-resolves it then. Match the provider's `surface` prop.\n */\n surface?: boolean\n /**\n * Match the provider's `palette` prop. The attributes written are the same either\n * way: a catalogue variant's palette is the host stylesheet's job (`stylesheet`) or\n * the provider's, at mount (`inline`), and a stored theme's own tokens are written\n * inline in both.\n */\n palette?: 'inline' | 'stylesheet'\n /** The only `version` of the library document read. Default `1`. */\n libraryVersion?: number\n}\n\n/**\n * The no-flash script.\n *\n * Rendered inline in `<head>` before any stylesheet that matters. Without it a dark\n * preference paints the light theme first and then swaps, which is the flash every\n * themed application has to solve and the one thing a provider cannot do from\n * inside the body.\n *\n * ## The string form\n *\n * `themeScript('key')` serves a provider that stores an unversioned\n * `localStorage[key]` document. Deliberately tiny and dependency-free: it reads the\n * stored preference, resolves `system`, and sets the class. It does not apply the\n * variant's roles — the default theme in `theme.css` covers the first paint, and the\n * provider writes the rest on mount. Its output is a compatibility surface (a\n * consumer may have it in a snapshot), so it does not change.\n *\n * ## The options form\n *\n * `themeScript({ key, ... })` serves a host with a versioned document (see\n * `createVersionedPersistence`) and writes what the provider will write, so mounting\n * repaints nothing:\n *\n * - `class=\"dark\"`, `color-scheme`, `data-theme`, `data-appearance` and, for a\n * compact document, `data-density`;\n * - `data-accent`: a preset id as stored, `custom` for a `#rrggbb` (never the hex\n * itself, which is the worst possible attribute content), `defaultAccent` when the\n * document has none, nothing for a theme-derived `ansi-*` accent (it has no\n * stylesheet block; the provider paints it inline);\n * - the theme: the stored id when the catalogue or the stored library holds it, else\n * the default of the resolved appearance, exactly as the provider resolves it. A\n * library theme's own tokens are written inline, so an imported theme paints on the\n * first frame;\n * - the surface axis, when `surface` is set;\n * - the font settings, guarded to the document's `version`.\n *\n * The document's `version`, when the schema names one, must match exactly. The legacy\n * key is read only while the main key is absent: an unreadable main document is not\n * \"no preference\", so it does not resurrect an older value.\n *\n * The script reads values out of `localStorage` and writes them to `dataset` and\n * `style`, which are *property* assignments rather than code: a stored id, accent or\n * token can set an attribute and cannot execute. The values that reach the source as\n * text (keys, ids, the default accent) go through {@link inlineScriptLiteral} for the\n * reason documented there.\n */\nexport function themeScript(storageKey?: string): string\nexport function themeScript(options: ThemeScriptOptions): string\nexport function themeScript(input: string | ThemeScriptOptions = 'adea-appearance'): string {\n if (typeof input === 'string') return stringFormScript(input)\n return optionsFormScript(input)\n}\n\nfunction stringFormScript(storageKey: string): string {\n const script = `(function(){try{var s=localStorage.getItem(${inlineScriptLiteral(storageKey)});var p=s?JSON.parse(s):{};var a=p.appearance||'system';var d=a==='dark'||(a==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var r=document.documentElement;if(d)r.classList.add('dark');r.style.colorScheme=d?'dark':'light';if(p.accent&&p.accent!=='theme')r.dataset.accent=p.accent;if(p.density==='compact')r.dataset.density='compact';}catch(e){}})();`\n return `${script}${fontSettingsBootstrapScript(storageKey)}`\n}\n\nconst literal = safeScriptStringLiteral\n\nfunction optionsFormScript(options: ThemeScriptOptions): string {\n const field = options.appearanceField ?? 'appearance'\n const versionGuard =\n options.version === undefined ? '' : `&&x.version===${Number(options.version)}`\n const presets = accentPresets\n .filter((preset) => preset.id !== 'theme')\n .map((preset) => literal(preset.id))\n const defaultAccent =\n options.defaultAccent === undefined\n ? ''\n : isCustomAccent(options.defaultAccent)\n ? 'custom'\n : options.defaultAccent\n\n const parts: string[] = []\n parts.push(\n `(function(){try{`,\n `var r=document.documentElement,s=r.style;`,\n `var q=function(m){try{return window.matchMedia(m).matches}catch(e){return false}};`,\n `var get=function(k){try{return localStorage.getItem(k)}catch(e){return null}};`,\n // The main document. `raw !== null` rather than truthiness: an empty string is a\n // stored (corrupt) value, so it does not fall through to the legacy key.\n `var raw=get(${literal(options.key)}),doc=null,legacy=null;`,\n `if(raw!==null){try{var x=JSON.parse(raw);if(x&&typeof x==='object'&&!Array.isArray(x)${versionGuard})doc=x}catch(e){}}`\n )\n if (options.legacyAppearanceKey !== undefined) {\n parts.push(`else legacy=get(${literal(options.legacyAppearanceKey)});`)\n }\n parts.push(\n `var m=doc?doc[${literal(field)}]:legacy;`,\n `var mode=m==='light'||m==='dark'?m:'system';`,\n `var dark=mode==='dark'||(mode==='system'&&q('(prefers-color-scheme: dark)'));`,\n `var fallback=dark?${literal(defaultDarkThemeId)}:${literal(defaultLightThemeId)};`,\n `var want=doc?(dark?doc.darkThemeId:doc.lightThemeId):null;`,\n `if(typeof want!=='string')want=fallback;`,\n `var ids=[${builtinThemes.map((theme) => literal(theme.id)).join(',')}];`,\n `var id=ids.indexOf(want)!==-1?want:null,tokens=null;`\n )\n if (options.libraryKey !== undefined) {\n parts.push(\n `if(id===null){try{var lib=JSON.parse(get(${literal(options.libraryKey)})||'null');`,\n `if(lib&&typeof lib==='object'&&lib.version===${Number(options.libraryVersion ?? 1)}&&Array.isArray(lib.themes)){`,\n `for(var i=0;i!==lib.themes.length;i++){var t=lib.themes[i];`,\n `if(t&&t.id===want&&t.flatTokens&&typeof t.flatTokens==='object'){id=want;tokens=t.flatTokens;break}}}}catch(e){}}`\n )\n }\n parts.push(\n `if(id===null)id=fallback;`,\n `r.classList.toggle('dark',dark);`,\n `s.colorScheme=dark?'dark':'light';`,\n `r.dataset.theme=id;`,\n `r.dataset.appearance=dark?'dark':'light';`\n )\n if (options.libraryKey !== undefined) {\n parts.push(\n `if(tokens){for(var n in tokens){if(Object.prototype.hasOwnProperty.call(tokens,n)&&n.slice(0,2)==='--'&&typeof tokens[n]==='string')s.setProperty(n,tokens[n])}}`\n )\n }\n parts.push(\n `var a=doc&&typeof doc.accent==='string'?doc.accent:'';`,\n `var accent=[${presets.join(',')}].indexOf(a)!==-1?a:/^#[0-9a-fA-F]{6}$/.test(a)?'custom':a.slice(0,5)==='ansi-'?'':${literal(defaultAccent)};`,\n `if(accent)r.dataset.accent=accent;`,\n `if(doc&&doc.density==='compact')r.dataset.density='compact';`\n )\n if (options.surface) {\n parts.push(\n // Reduce Transparency forces the opaque surface; a translucent request resolves\n // to frosted because the host's capability is unknown before the provider mounts.\n `var reduce=q(${literal(REDUCED_TRANSPARENCY_QUERY)})||!!(doc&&doc.reduceTransparency===true);`,\n `var surface=reduce?'opaque':doc&&(doc.surface==='frosted'||doc.surface==='translucent')?'frosted':'opaque';`,\n `r.dataset.surface=surface;`,\n `r.dataset.reduceTransparency=reduce?'true':'false';`,\n `s.setProperty('--surface-alpha',${JSON.stringify(surfaceAlpha)}[surface]);`\n )\n }\n parts.push(`}catch(e){}})();`)\n\n return `${parts.join('')}${fontSettingsBootstrapScript(options.key, options.version)}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,oBAAoB,OAAuB;CACzD,OAAO,wBAAwB,KAAK;AACtC;AAkFA,SAAgB,YAAY,QAAqC,mBAA2B;CAC1F,IAAI,OAAO,UAAU,UAAU,OAAO,iBAAiB,KAAK;CAC5D,OAAO,kBAAkB,KAAK;AAChC;AAEA,SAAS,iBAAiB,YAA4B;CAEpD,OAAO,GAAG,8CADmD,oBAAoB,UAAU,EAAE,wXAC1E,4BAA4B,UAAU;AAC3D;AAEA,IAAM,UAAU;AAEhB,SAAS,kBAAkB,SAAqC;CAC9D,MAAM,QAAQ,QAAQ,mBAAmB;CACzC,MAAM,eACJ,QAAQ,YAAY,KAAA,IAAY,KAAK,iBAAiB,OAAO,QAAQ,OAAO;CAC9E,MAAM,UAAU,cACb,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,CACzC,KAAK,WAAW,QAAQ,OAAO,EAAE,CAAC;CACrC,MAAM,gBACJ,QAAQ,kBAAkB,KAAA,IACtB,KACA,eAAe,QAAQ,aAAa,IAClC,WACA,QAAQ;CAEhB,MAAM,QAAkB,CAAC;CACzB,MAAM,KACJ,oBACA,6CACA,sFACA,kFAGA,eAAe,QAAQ,QAAQ,GAAG,EAAE,0BACpC,wFAAwF,aAAa,mBACvG;CACA,IAAI,QAAQ,wBAAwB,KAAA,GAClC,MAAM,KAAK,mBAAmB,QAAQ,QAAQ,mBAAmB,EAAE,GAAG;CAExE,MAAM,KACJ,iBAAiB,QAAQ,KAAK,EAAE,YAChC,gDACA,iFACA,qBAAqB,QAAQ,kBAAkB,EAAE,GAAG,QAAQ,mBAAmB,EAAE,IACjF,8DACA,4CACA,YAAY,cAAc,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,KACtE,sDACF;CACA,IAAI,QAAQ,eAAe,KAAA,GACzB,MAAM,KACJ,4CAA4C,QAAQ,QAAQ,UAAU,EAAE,cACxE,gDAAgD,OAAO,QAAQ,kBAAkB,CAAC,EAAE,gCACpF,+DACA,mHACF;CAEF,MAAM,KACJ,6BACA,oCACA,sCACA,uBACA,2CACF;CACA,IAAI,QAAQ,eAAe,KAAA,GACzB,MAAM,KACJ,kKACF;CAEF,MAAM,KACJ,0DACA,eAAe,QAAQ,KAAK,GAAG,EAAE,qFAAqF,QAAQ,aAAa,EAAE,IAC7I,sCACA,8DACF;CACA,IAAI,QAAQ,SACV,MAAM,KAGJ,gBAAgB,QAAQ,0BAA0B,EAAE,6CACpD,+GACA,8BACA,uDACA,mCAAmC,KAAK,UAAU,YAAY,EAAE,YAClE;CAEF,MAAM,KAAK,kBAAkB;CAE7B,OAAO,GAAG,MAAM,KAAK,EAAE,IAAI,4BAA4B,QAAQ,KAAK,QAAQ,OAAO;AACrF"}
1
+ {"version":3,"file":"theme-script.js","names":[],"sources":["../../../src/components/theme/theme-script.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Wing\n * Licensed under the MIT License.\n *\n * The options form is rewritten from Adea's accepted port of Zeron's preference\n * store (`appearanceThemeScript` in `packages/ui/src/components/appearance.ts`,\n * https://github.com/zeronsh/zeron) so the boot script reads the document the shared\n * persistence writes. See NOTICE.\n */\nimport { fontSettingsBootstrapScript } from '#lib/appearance-font-settings'\nimport { isCustomAccent } from '#lib/accent-roles'\nimport { safeScriptStringLiteral } from '#lib/safe-script'\nimport { REDUCED_TRANSPARENCY_QUERY, surfaceAlpha } from '#lib/theme-surface'\nimport { accentPresets } from '#lib/accent-presets'\nimport { THEME_INDEX, defaultDarkThemeId, defaultLightThemeId } from '#lib/theme-defaults'\nimport type { ThemeStorageSchema } from './theme-persistence'\n\n/**\n * A string as a JavaScript literal that is also safe inside an inline `<script>`.\n *\n * `JSON.stringify` produces a correct JavaScript literal, and it is **not** safe\n * here: it leaves `<` and `>` alone, so a value containing `</script>` ends the tag\n * early and everything after it is parsed as HTML. That is the classic inline-script\n * injection, and it is a real one even though the value is a developer-supplied prop\n * rather than user input — a prop can come from configuration, and a key that happens\n * to contain a closing tag should not be able to execute.\n *\n * The escape is also correct JavaScript: `\\u003c` is `<` to the parser, so the value\n * is byte-identical at runtime and inert to the HTML tokenizer. `\\u2028` and `\\u2029`\n * are escaped for the neighbouring reason — they are legal inside a JSON string and\n * were, before ES2019, line terminators to a JavaScript parser.\n *\n * CodeQL flags the unescaped form as `js/bad-code-sanitization`, which is how this\n * was found; `tests/theme-script.test.ts` pins it so it cannot come back.\n */\nexport function inlineScriptLiteral(value: string): string {\n return safeScriptStringLiteral(value)\n}\n\n/**\n * Options for the host-shaped no-flash script: the document's {@link ThemeStorageSchema}\n * plus what the script paints from it.\n */\nexport type ThemeScriptOptions = ThemeStorageSchema & {\n /**\n * The accent painted when the document has none, an unrecognised one, or the\n * legacy `theme` spelling — the host's default (adea: `violet`). Without it those\n * cases paint no `data-accent`, which is what the provider writes for `theme`.\n * Setting it means the `[data-accent]` block of the default preset paints on the\n * first frame instead of after hydration.\n */\n defaultAccent?: string\n /**\n * Also resolve the surface axis (`data-surface`, `data-reduce-transparency`,\n * `--surface-alpha`) from the document and the operating system's Reduce\n * Transparency setting. A `translucent` preference resolves to `frosted`: the\n * host's translucency capability is not known until the provider mounts, and the\n * provider re-resolves it then. Match the provider's `surface` prop.\n */\n surface?: boolean\n /**\n * Match the provider's `palette` prop. The attributes written are the same either\n * way: a catalogue variant's palette is the host stylesheet's job (`stylesheet`) or\n * the provider's, at mount (`inline`), and a stored theme's own tokens are written\n * inline in both.\n */\n palette?: 'inline' | 'stylesheet'\n /** The only `version` of the library document read. Default `1`. */\n libraryVersion?: number\n}\n\n/**\n * The no-flash script.\n *\n * Rendered inline in `<head>` before any stylesheet that matters. Without it a dark\n * preference paints the light theme first and then swaps, which is the flash every\n * themed application has to solve and the one thing a provider cannot do from\n * inside the body.\n *\n * ## The string form\n *\n * `themeScript('key')` serves a provider that stores an unversioned\n * `localStorage[key]` document. Deliberately tiny and dependency-free: it reads the\n * stored preference, resolves `system`, and sets the class. It does not apply the\n * variant's roles — the default theme in `theme.css` covers the first paint, and the\n * provider writes the rest on mount. Its output is a compatibility surface (a\n * consumer may have it in a snapshot), so it does not change.\n *\n * ## The options form\n *\n * `themeScript({ key, ... })` serves a host with a versioned document (see\n * `createVersionedPersistence`) and writes what the provider will write, so mounting\n * repaints nothing:\n *\n * - `class=\"dark\"`, `color-scheme`, `data-theme`, `data-appearance` and, for a\n * compact document, `data-density`;\n * - `data-accent`: a preset id as stored, `custom` for a `#rrggbb` (never the hex\n * itself, which is the worst possible attribute content), `defaultAccent` when the\n * document has none, nothing for a theme-derived `ansi-*` accent (it has no\n * stylesheet block; the provider paints it inline);\n * - the theme: the stored id when the catalogue or the stored library holds it, else\n * the default of the resolved appearance, exactly as the provider resolves it. A\n * library theme's own tokens are written inline, so an imported theme paints on the\n * first frame;\n * - the surface axis, when `surface` is set;\n * - the font settings, guarded to the document's `version`.\n *\n * The document's `version`, when the schema names one, must match exactly. The legacy\n * key is read only while the main key is absent: an unreadable main document is not\n * \"no preference\", so it does not resurrect an older value.\n *\n * The script reads values out of `localStorage` and writes them to `dataset` and\n * `style`, which are *property* assignments rather than code: a stored id, accent or\n * token can set an attribute and cannot execute. The values that reach the source as\n * text (keys, ids, the default accent) go through {@link inlineScriptLiteral} for the\n * reason documented there.\n */\nexport function themeScript(storageKey?: string): string\nexport function themeScript(options: ThemeScriptOptions): string\nexport function themeScript(input: string | ThemeScriptOptions = 'adea-appearance'): string {\n if (typeof input === 'string') return stringFormScript(input)\n return optionsFormScript(input)\n}\n\nfunction stringFormScript(storageKey: string): string {\n const script = `(function(){try{var s=localStorage.getItem(${inlineScriptLiteral(storageKey)});var p=s?JSON.parse(s):{};var a=p.appearance||'system';var d=a==='dark'||(a==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var r=document.documentElement;if(d)r.classList.add('dark');r.style.colorScheme=d?'dark':'light';if(p.accent&&p.accent!=='theme')r.dataset.accent=p.accent;if(p.density==='compact')r.dataset.density='compact';}catch(e){}})();`\n return `${script}${fontSettingsBootstrapScript(storageKey)}`\n}\n\nconst literal = safeScriptStringLiteral\n\nfunction optionsFormScript(options: ThemeScriptOptions): string {\n const field = options.appearanceField ?? 'appearance'\n const versionGuard =\n options.version === undefined ? '' : `&&x.version===${Number(options.version)}`\n const presets = accentPresets\n .filter((preset) => preset.id !== 'theme')\n .map((preset) => literal(preset.id))\n const defaultAccent =\n options.defaultAccent === undefined\n ? ''\n : isCustomAccent(options.defaultAccent)\n ? 'custom'\n : options.defaultAccent\n\n const parts: string[] = []\n parts.push(\n `(function(){try{`,\n `var r=document.documentElement,s=r.style;`,\n `var q=function(m){try{return window.matchMedia(m).matches}catch(e){return false}};`,\n `var get=function(k){try{return localStorage.getItem(k)}catch(e){return null}};`,\n // The main document. `raw !== null` rather than truthiness: an empty string is a\n // stored (corrupt) value, so it does not fall through to the legacy key.\n `var raw=get(${literal(options.key)}),doc=null,legacy=null;`,\n `if(raw!==null){try{var x=JSON.parse(raw);if(x&&typeof x==='object'&&!Array.isArray(x)${versionGuard})doc=x}catch(e){}}`\n )\n if (options.legacyAppearanceKey !== undefined) {\n parts.push(`else legacy=get(${literal(options.legacyAppearanceKey)});`)\n }\n parts.push(\n `var m=doc?doc[${literal(field)}]:legacy;`,\n `var mode=m==='light'||m==='dark'?m:'system';`,\n `var dark=mode==='dark'||(mode==='system'&&q('(prefers-color-scheme: dark)'));`,\n `var fallback=dark?${literal(defaultDarkThemeId)}:${literal(defaultLightThemeId)};`,\n `var want=doc?(dark?doc.darkThemeId:doc.lightThemeId):null;`,\n `if(typeof want!=='string')want=fallback;`,\n `var ids=[${THEME_INDEX.map(([id]) => literal(id)).join(',')}];`,\n `var id=ids.indexOf(want)!==-1?want:null,tokens=null;`\n )\n if (options.libraryKey !== undefined) {\n parts.push(\n `if(id===null){try{var lib=JSON.parse(get(${literal(options.libraryKey)})||'null');`,\n `if(lib&&typeof lib==='object'&&lib.version===${Number(options.libraryVersion ?? 1)}&&Array.isArray(lib.themes)){`,\n `for(var i=0;i!==lib.themes.length;i++){var t=lib.themes[i];`,\n `if(t&&t.id===want&&t.flatTokens&&typeof t.flatTokens==='object'){id=want;tokens=t.flatTokens;break}}}}catch(e){}}`\n )\n }\n parts.push(\n `if(id===null)id=fallback;`,\n `r.classList.toggle('dark',dark);`,\n `s.colorScheme=dark?'dark':'light';`,\n `r.dataset.theme=id;`,\n `r.dataset.appearance=dark?'dark':'light';`\n )\n if (options.libraryKey !== undefined) {\n parts.push(\n `if(tokens){for(var n in tokens){if(Object.prototype.hasOwnProperty.call(tokens,n)&&n.slice(0,2)==='--'&&typeof tokens[n]==='string')s.setProperty(n,tokens[n])}}`\n )\n }\n parts.push(\n `var a=doc&&typeof doc.accent==='string'?doc.accent:'';`,\n `var accent=[${presets.join(',')}].indexOf(a)!==-1?a:/^#[0-9a-fA-F]{6}$/.test(a)?'custom':a.slice(0,5)==='ansi-'?'':${literal(defaultAccent)};`,\n `if(accent)r.dataset.accent=accent;`,\n `if(doc&&doc.density==='compact')r.dataset.density='compact';`\n )\n if (options.surface) {\n parts.push(\n // Reduce Transparency forces the opaque surface; a translucent request resolves\n // to frosted because the host's capability is unknown before the provider mounts.\n `var reduce=q(${literal(REDUCED_TRANSPARENCY_QUERY)})||!!(doc&&doc.reduceTransparency===true);`,\n `var surface=reduce?'opaque':doc&&(doc.surface==='frosted'||doc.surface==='translucent')?'frosted':'opaque';`,\n `r.dataset.surface=surface;`,\n `r.dataset.reduceTransparency=reduce?'true':'false';`,\n `s.setProperty('--surface-alpha',${JSON.stringify(surfaceAlpha)}[surface]);`\n )\n }\n parts.push(`}catch(e){}})();`)\n\n return `${parts.join('')}${fontSettingsBootstrapScript(options.key, options.version)}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,oBAAoB,OAAuB;CACzD,OAAO,wBAAwB,KAAK;AACtC;AAkFA,SAAgB,YAAY,QAAqC,mBAA2B;CAC1F,IAAI,OAAO,UAAU,UAAU,OAAO,iBAAiB,KAAK;CAC5D,OAAO,kBAAkB,KAAK;AAChC;AAEA,SAAS,iBAAiB,YAA4B;CAEpD,OAAO,GAAG,8CADmD,oBAAoB,UAAU,EAAE,wXAC1E,4BAA4B,UAAU;AAC3D;AAEA,IAAM,UAAU;AAEhB,SAAS,kBAAkB,SAAqC;CAC9D,MAAM,QAAQ,QAAQ,mBAAmB;CACzC,MAAM,eACJ,QAAQ,YAAY,KAAA,IAAY,KAAK,iBAAiB,OAAO,QAAQ,OAAO;CAC9E,MAAM,UAAU,cACb,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,CACzC,KAAK,WAAW,QAAQ,OAAO,EAAE,CAAC;CACrC,MAAM,gBACJ,QAAQ,kBAAkB,KAAA,IACtB,KACA,eAAe,QAAQ,aAAa,IAClC,WACA,QAAQ;CAEhB,MAAM,QAAkB,CAAC;CACzB,MAAM,KACJ,oBACA,6CACA,sFACA,kFAGA,eAAe,QAAQ,QAAQ,GAAG,EAAE,0BACpC,wFAAwF,aAAa,mBACvG;CACA,IAAI,QAAQ,wBAAwB,KAAA,GAClC,MAAM,KAAK,mBAAmB,QAAQ,QAAQ,mBAAmB,EAAE,GAAG;CAExE,MAAM,KACJ,iBAAiB,QAAQ,KAAK,EAAE,YAChC,gDACA,iFACA,qBAAqB,QAAQ,kBAAkB,EAAE,GAAG,QAAQ,mBAAmB,EAAE,IACjF,8DACA,4CACA,YAAY,YAAY,KAAK,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,KAC7D,sDACF;CACA,IAAI,QAAQ,eAAe,KAAA,GACzB,MAAM,KACJ,4CAA4C,QAAQ,QAAQ,UAAU,EAAE,cACxE,gDAAgD,OAAO,QAAQ,kBAAkB,CAAC,EAAE,gCACpF,+DACA,mHACF;CAEF,MAAM,KACJ,6BACA,oCACA,sCACA,uBACA,2CACF;CACA,IAAI,QAAQ,eAAe,KAAA,GACzB,MAAM,KACJ,kKACF;CAEF,MAAM,KACJ,0DACA,eAAe,QAAQ,KAAK,GAAG,EAAE,qFAAqF,QAAQ,aAAa,EAAE,IAC7I,sCACA,8DACF;CACA,IAAI,QAAQ,SACV,MAAM,KAGJ,gBAAgB,QAAQ,0BAA0B,EAAE,6CACpD,+GACA,8BACA,uDACA,mCAAmC,KAAK,UAAU,YAAY,EAAE,YAClE;CAEF,MAAM,KAAK,kBAAkB;CAE7B,OAAO,GAAG,MAAM,KAAK,EAAE,IAAI,4BAA4B,QAAQ,KAAK,QAAQ,OAAO;AACrF"}