@adea-ai/ui 0.128.2 → 0.128.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/components/theme/appearance-panel.js +1 -1
  2. package/dist/components/theme/index.js +1 -1
  3. package/dist/components/theme/lazy.d.ts +15 -0
  4. package/dist/components/theme/lazy.d.ts.map +1 -0
  5. package/dist/components/theme/lazy.js +9 -0
  6. package/dist/components/theme/theme-catalogue-source.d.ts +15 -0
  7. package/dist/components/theme/theme-catalogue-source.d.ts.map +1 -0
  8. package/dist/components/theme/theme-catalogue-source.js +51 -0
  9. package/dist/components/theme/theme-catalogue-source.js.map +1 -0
  10. package/dist/components/theme/theme-document-core.d.ts +123 -0
  11. package/dist/components/theme/theme-document-core.d.ts.map +1 -0
  12. package/dist/components/theme/{theme-document.js → theme-document-core.js} +12 -12
  13. package/dist/components/theme/theme-document-core.js.map +1 -0
  14. package/dist/components/theme/theme-document.d.ts +8 -107
  15. package/dist/components/theme/theme-document.d.ts.map +1 -1
  16. package/dist/components/theme/theme-persistence.d.ts +1 -1
  17. package/dist/components/theme/theme-persistence.d.ts.map +1 -1
  18. package/dist/components/theme/theme-persistence.js +9 -6
  19. package/dist/components/theme/theme-persistence.js.map +1 -1
  20. package/dist/components/theme/theme-provider-core.d.ts +19 -0
  21. package/dist/components/theme/theme-provider-core.d.ts.map +1 -0
  22. package/dist/components/theme/theme-provider-core.js +28 -0
  23. package/dist/components/theme/theme-provider-core.js.map +1 -0
  24. package/dist/components/theme/theme-provider-lazy.d.ts +28 -0
  25. package/dist/components/theme/theme-provider-lazy.d.ts.map +1 -0
  26. package/dist/components/theme/theme-provider-lazy.js +36 -0
  27. package/dist/components/theme/theme-provider-lazy.js.map +1 -0
  28. package/dist/components/theme/theme-provider-value.d.ts +175 -0
  29. package/dist/components/theme/theme-provider-value.d.ts.map +1 -0
  30. package/dist/components/theme/theme-provider-value.js +200 -0
  31. package/dist/components/theme/theme-provider-value.js.map +1 -0
  32. package/dist/components/theme/theme-provider.d.ts +11 -136
  33. package/dist/components/theme/theme-provider.d.ts.map +1 -1
  34. package/dist/components/theme/theme-provider.js +27 -149
  35. package/dist/components/theme/theme-provider.js.map +1 -1
  36. package/dist/components/theme/theme-script.js +4 -3
  37. package/dist/components/theme/theme-script.js.map +1 -1
  38. package/dist/components/theme/theme-state-core.d.ts +248 -0
  39. package/dist/components/theme/theme-state-core.d.ts.map +1 -0
  40. package/dist/components/theme/{theme-state.js → theme-state-core.js} +8 -7
  41. package/dist/components/theme/theme-state-core.js.map +1 -0
  42. package/dist/components/theme/theme-state.d.ts +8 -214
  43. package/dist/components/theme/theme-state.d.ts.map +1 -1
  44. package/dist/components/theme/theme-toggle.js.map +1 -1
  45. package/dist/index.js +7 -4
  46. package/dist/lib/accent-presets.d.ts +26 -0
  47. package/dist/lib/accent-presets.d.ts.map +1 -0
  48. package/dist/lib/accent-presets.js +11 -0
  49. package/dist/lib/accent-presets.js.map +1 -0
  50. package/dist/lib/theme-catalogue.d.ts +3 -0
  51. package/dist/lib/theme-catalogue.d.ts.map +1 -0
  52. package/dist/lib/theme-catalogue.js +22 -0
  53. package/dist/lib/theme-catalogue.js.map +1 -0
  54. package/dist/lib/theme-css.d.ts +43 -0
  55. package/dist/lib/theme-css.d.ts.map +1 -0
  56. package/dist/lib/theme-css.js +167 -0
  57. package/dist/lib/theme-css.js.map +1 -0
  58. package/dist/lib/theme-defaults.d.ts +46 -0
  59. package/dist/lib/theme-defaults.d.ts.map +1 -0
  60. package/dist/lib/theme-defaults.generated.d.ts +13 -0
  61. package/dist/lib/theme-defaults.generated.d.ts.map +1 -0
  62. package/dist/lib/theme-defaults.generated.js +318 -0
  63. package/dist/lib/theme-defaults.generated.js.map +1 -0
  64. package/dist/lib/theme-defaults.js +47 -0
  65. package/dist/lib/theme-defaults.js.map +1 -0
  66. package/dist/lib/theme-engine-light.d.ts +3 -0
  67. package/dist/lib/theme-engine-light.d.ts.map +1 -0
  68. package/dist/lib/theme-engine-light.js +75 -0
  69. package/dist/lib/theme-engine-light.js.map +1 -0
  70. package/dist/lib/theme-engine.d.ts +69 -0
  71. package/dist/lib/theme-engine.d.ts.map +1 -0
  72. package/dist/lib/theme-engine.js +33 -0
  73. package/dist/lib/theme-engine.js.map +1 -0
  74. package/dist/lib/themes.d.ts +14 -48
  75. package/dist/lib/themes.d.ts.map +1 -1
  76. package/dist/lib/themes.js +21 -163
  77. package/dist/lib/themes.js.map +1 -1
  78. package/dist/lib/tokens.d.ts +1 -25
  79. package/dist/lib/tokens.d.ts.map +1 -1
  80. package/dist/lib/tokens.js +1 -24
  81. package/dist/lib/tokens.js.map +1 -1
  82. package/dist/r/lib.json +35 -0
  83. package/dist/r/registry.json +70 -0
  84. package/dist/r/src/components/theme/lazy.ts +35 -0
  85. package/dist/r/src/components/theme/theme-catalogue-source.ts +59 -0
  86. package/dist/r/src/components/theme/theme-document-core.ts +262 -0
  87. package/dist/r/src/components/theme/theme-document.ts +23 -240
  88. package/dist/r/src/components/theme/theme-persistence.ts +14 -6
  89. package/dist/r/src/components/theme/theme-provider-core.tsx +40 -0
  90. package/dist/r/src/components/theme/theme-provider-lazy.tsx +34 -0
  91. package/dist/r/src/components/theme/theme-provider-value.ts +445 -0
  92. package/dist/r/src/components/theme/theme-provider.tsx +23 -346
  93. package/dist/r/src/components/theme/theme-script.ts +3 -3
  94. package/dist/r/src/components/theme/theme-state-core.ts +364 -0
  95. package/dist/r/src/components/theme/theme-state.ts +10 -329
  96. package/dist/r/src/components/theme/theme-toggle.tsx +1 -1
  97. package/dist/r/src/lib/accent-presets.ts +43 -0
  98. package/dist/r/src/lib/theme-catalogue.ts +26 -0
  99. package/dist/r/src/lib/theme-css.ts +201 -0
  100. package/dist/r/src/lib/theme-defaults.generated.ts +319 -0
  101. package/dist/r/src/lib/theme-defaults.ts +79 -0
  102. package/dist/r/src/lib/theme-engine-light.ts +117 -0
  103. package/dist/r/src/lib/theme-engine.ts +111 -0
  104. package/dist/r/src/lib/themes.ts +27 -191
  105. package/dist/r/src/lib/tokens.ts +1 -35
  106. package/dist/r/theming.json +35 -0
  107. package/package.json +10 -2
  108. package/registry.json +70 -0
  109. package/src/components/theme/lazy.ts +35 -0
  110. package/src/components/theme/theme-catalogue-source.ts +59 -0
  111. package/src/components/theme/theme-document-core.ts +262 -0
  112. package/src/components/theme/theme-document.ts +23 -240
  113. package/src/components/theme/theme-persistence.ts +14 -6
  114. package/src/components/theme/theme-provider-core.tsx +40 -0
  115. package/src/components/theme/theme-provider-lazy.tsx +34 -0
  116. package/src/components/theme/theme-provider-value.ts +445 -0
  117. package/src/components/theme/theme-provider.tsx +23 -346
  118. package/src/components/theme/theme-script.ts +3 -3
  119. package/src/components/theme/theme-state-core.ts +364 -0
  120. package/src/components/theme/theme-state.ts +10 -329
  121. package/src/components/theme/theme-toggle.tsx +1 -1
  122. package/src/lib/accent-presets.ts +43 -0
  123. package/src/lib/theme-catalogue.ts +26 -0
  124. package/src/lib/theme-css.ts +201 -0
  125. package/src/lib/theme-defaults.generated.ts +319 -0
  126. package/src/lib/theme-defaults.ts +79 -0
  127. package/src/lib/theme-engine-light.ts +117 -0
  128. package/src/lib/theme-engine.ts +111 -0
  129. package/src/lib/themes.ts +27 -191
  130. package/src/lib/tokens.ts +1 -35
  131. package/dist/components/theme/theme-document.js.map +0 -1
  132. package/dist/components/theme/theme-state.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"theme-persistence.js","names":[],"sources":["../../../src/components/theme/theme-persistence.ts"],"sourcesContent":["import { normalizeCustomAccent } from '#lib/accent-roles'\nimport { accentPresets } from '#lib/tokens'\nimport { normalizeAppearanceEditorFontSettings } from '#lib/appearance-font-settings'\nimport { defaultDarkThemeId, defaultLightThemeId, isThemeAccentId, themeById } from '#lib/themes'\nimport { normalizeSurface } from '#lib/theme-surface'\nimport {\n defaultPreferenceStorage,\n readLegacyValue,\n readVersionedRecord,\n recoveryKeyFor,\n writeVersionedRecord,\n type PreferenceStorage,\n} from '#lib/preference-store'\nimport { defaultThemeSelection, resolvedDensity, type ThemeSelection } from './theme-state'\n\n/**\n * Where a selection is kept between sessions.\n *\n * The provider never touches storage itself; it asks a `ThemePersistence`. The\n * default is {@link localStoragePersistence}, which is what the provider always did.\n * A host that stores the preference in a different shape, under a versioned\n * envelope, or somewhere that is not `localStorage` supplies its own and the\n * resolution and application rules stay the shared ones.\n *\n * The contract is deliberately narrow, so that an implementation cannot make the\n * provider less safe than it was:\n *\n * - `read` is synchronous and runs once, when the provider is created. It may\n * repair or quarantine what it finds, and it must not throw: the provider\n * treats a throw as \"nothing stored\" and starts from the defaults.\n * - `write` runs only from `setSelection`. Never on mount, never from `preview`,\n * never as a side effect of reading, so the first write to a store is always a\n * user's decision. A throw is swallowed, because a full or blocked store must\n * not break the switch.\n */\nexport type ThemePersistence = {\n read(): Partial<ThemeSelection> | undefined\n write(next: ThemeSelection): void\n}\n\n/**\n * The preference store, or nothing.\n *\n * Only ever called inside a `try`: where storage is blocked — a sandboxed iframe, a\n * privacy mode, a denied site-data permission — merely *reading* the `localStorage`\n * global throws (\"Access is denied\", `SecurityError`), so even the `typeof` guard\n * has to sit inside the `try` rather than in front of it. Without a store the\n * preference lives only in the provider's signal for the session.\n */\nfunction preferenceStore(): Storage | undefined {\n return typeof localStorage === 'undefined' ? undefined : localStorage\n}\n\n/** The stored preference, validated against the catalogue. */\nfunction readSelection(storageKey: string): ThemeSelection {\n try {\n const raw = preferenceStore()?.getItem(storageKey)\n if (!raw) return defaultThemeSelection\n const parsed = JSON.parse(raw) as Partial<ThemeSelection>\n const fonts = normalizeAppearanceEditorFontSettings(parsed.fonts, parsed.font).settings\n return {\n appearance:\n parsed.appearance === 'light' ||\n parsed.appearance === 'dark' ||\n parsed.appearance === 'system'\n ? parsed.appearance\n : defaultThemeSelection.appearance,\n // An unknown id would leave the document with no theme at all, so a stale\n // preference from an older catalogue falls back rather than applying nothing.\n lightThemeId:\n themeById(parsed.lightThemeId ?? '')?.appearance === 'light'\n ? (parsed.lightThemeId as string)\n : defaultLightThemeId,\n darkThemeId:\n themeById(parsed.darkThemeId ?? '')?.appearance === 'dark'\n ? (parsed.darkThemeId as string)\n : defaultDarkThemeId,\n // A theme accent is kept whenever it names a slot, even one the stored pair\n // cannot offer: the id is role-shaped so it survives a theme switch, and an\n // unoffered slot already resolves to the theme's own primary when applied. A\n // custom `#rrggbb` is kept in its one stored spelling; its readability is\n // settled when it is painted, not when it is read.\n accent:\n typeof parsed.accent === 'string' &&\n (accentPresets.some((preset) => preset.id === parsed.accent) ||\n isThemeAccentId(parsed.accent))\n ? parsed.accent\n : (normalizeCustomAccent(parsed.accent) ?? defaultThemeSelection.accent),\n font: fonts.ui.family,\n fonts,\n // Every axis has to be read back here, or it is lost twice: the provider's\n // document effect removes the `data-density` that `themeScript` set for the first paint, and\n // the next `setSelection` rewrites the store without it.\n density: resolvedDensity(parsed as ThemeSelection),\n // The newer axes are read back only when the store holds a usable value and are\n // otherwise left `undefined`, never defaulted: `JSON.stringify` drops them, so a\n // consumer that never sets one writes the bytes it always did. An id is kept as\n // stored, known or not, for the same reason a theme id the registry lacks is.\n terminalThemeId:\n typeof parsed.terminalThemeId === 'string' && parsed.terminalThemeId.trim() !== ''\n ? parsed.terminalThemeId\n : undefined,\n surface: normalizeSurface(parsed.surface),\n reduceTransparency:\n typeof parsed.reduceTransparency === 'boolean' ? parsed.reduceTransparency : undefined,\n }\n } catch {\n return defaultThemeSelection\n }\n}\n\n/**\n * The default persistence: one JSON document in `localStorage` under `key`.\n *\n * `read` is the provider's original `readSelection`, moved here unchanged — every\n * stored id is validated against the catalogue, so a stale preference falls back to\n * a default rather than reaching the document. `key` may be a function so a\n * provider whose `storageKey` prop changes writes to the key it is now given.\n *\n * Blocked, full or absent storage is survivable in both directions: a read answers\n * with the defaults and a write is dropped, so the preference simply lasts the\n * session.\n */\nexport function localStoragePersistence(key: string | (() => string)): ThemePersistence {\n const keyOf = () => (typeof key === 'function' ? key() : key)\n return {\n read: () => readSelection(keyOf()),\n write(next) {\n try {\n preferenceStore()?.setItem(keyOf(), JSON.stringify(next))\n } catch {\n // A full, blocked or unavailable store must not break the switch: the\n // preference simply does not survive the reload.\n }\n },\n }\n}\n\n/**\n * What a persistence knows, or nothing.\n *\n * The seam's `read` must not throw, and the provider does not rely on that: a host\n * persistence is code the design system has not seen, and a theme provider that\n * fails to mount because a store misbehaved has turned a lost preference into a\n * blank application.\n */\nexport function readPersisted(persistence: ThemePersistence): Partial<ThemeSelection> | undefined {\n try {\n return persistence.read()\n } catch {\n return undefined\n }\n}\n\n/** Hand a selection to the store; a store that throws loses the preference, not the switch. */\nexport function writePersisted(persistence: ThemePersistence, next: ThemeSelection): void {\n try {\n persistence.write(next)\n } catch {\n // Same contract as a full or blocked `localStorage`: the choice holds for the\n // session and does not survive the reload.\n }\n}\n\n/**\n * Where a host's versioned preference document lives. One schema describes the\n * document to both halves of the system: {@link createVersionedPersistence} reads and\n * writes it, and `themeScript(options)` reads it before first paint, so the two\n * cannot disagree about a key or a version.\n */\nexport type ThemeStorageSchema = {\n /** The storage key of the document. */\n key: string\n /**\n * The only top-level `version` read. Absent, the document is unversioned. A record\n * of any other version is quarantined (see `recoveryKey`), not interpreted.\n */\n version?: number\n /**\n * The document field that holds the appearance preference (`system`, `light`,\n * `dark`). Read by `themeScript`; the persistence leaves field names to the codec.\n * Default `appearance`.\n */\n appearanceField?: 'appearance' | 'mode'\n /**\n * An older single-value key (`light` | `dark` | `system`) read only while `key` is\n * absent, and never deleted. The persistence hands it to the codec's `fromLegacy`.\n */\n legacyAppearanceKey?: string\n /**\n * The stored library of imported themes, for `themeScript` only: an imported\n * theme's id and verbatim tokens, so the first paint can use the right variant. The\n * persistence neither reads nor writes it.\n */\n libraryKey?: string\n /** Where an unreadable document is kept. Default `${key}.recovery`. */\n recoveryKey?: string\n}\n\n/**\n * How a host's document maps to and from the shared {@link ThemeSelection}.\n *\n * The codec is the product schema: it decides which fields a document has, how an\n * unknown value is corrected, and what a fresh install looks like. The engine decides\n * everything about *storage*: what is quarantined, when the legacy key is read, and\n * that nothing is written until `setSelection` asks.\n */\nexport type VersionedCodec<Doc> = {\n /**\n * A readable record (the right JSON shape and version) as a selection patch. Fields\n * it leaves `undefined` fall back to the provider's defaults. It may keep an id the\n * catalogue lacks; the provider resolves it, it does not rewrite it.\n */\n decode(record: Readonly<Record<string, unknown>>): Partial<ThemeSelection>\n /**\n * The legacy key's value (never `null`) as a selection patch, or `undefined` for a\n * value it does not recognise. Called only when the main key is absent.\n */\n fromLegacy?(value: string): Partial<ThemeSelection> | undefined\n /**\n * What a store with nothing readable answers: a fresh install, an unreadable\n * document, a blocked store. Absent, the provider's own defaults apply.\n */\n defaults?(): Partial<ThemeSelection> | undefined\n /**\n * The document to store for a selection. `previous` is the record last read or\n * written, so a codec can carry a field the selection does not model.\n */\n encode(next: ThemeSelection, previous?: Readonly<Record<string, unknown>>): Doc\n}\n\n/** Seams for the storage and the clock; both default to the real ones. */\nexport type VersionedPersistenceOptions = {\n /** The store, or nothing. Called per access; default is `localStorage` (blocked-safe). */\n storage?: () => PreferenceStorage | undefined\n /** The clock for the recovery envelope's `capturedAt`. */\n now?: () => Date\n}\n\n/**\n * A {@link ThemePersistence} over a versioned document and a host codec.\n *\n * `read` follows the engine's rules (`lib/preference-store.ts`): the main key first;\n * an unreadable or unsupported document is quarantined byte for byte into the\n * recovery key and the codec's `defaults` answer; the legacy key only when the main\n * key is absent. It never writes the main key, so the first write is a user's\n * decision. `write` stores `codec.encode(next, previous)` as JSON and swallows a\n * failure.\n *\n * Neither method throws. The stored bytes are exactly what the codec encodes, so a\n * host keeps its keys, field names and bytes by keeping its codec.\n */\nexport function createVersionedPersistence<Doc>(\n schema: ThemeStorageSchema,\n codec: VersionedCodec<Doc>,\n options: VersionedPersistenceOptions = {}\n): ThemePersistence {\n const storageOf = options.storage ?? defaultPreferenceStorage\n const recordSchema = {\n key: schema.key,\n version: schema.version,\n recoveryKey: recoveryKeyFor(schema),\n }\n let previous: Readonly<Record<string, unknown>> | undefined\n\n const fallback = () => {\n try {\n return codec.defaults?.()\n } catch {\n return undefined\n }\n }\n\n return {\n read() {\n const storage = storageOf()\n if (!storage) return fallback()\n try {\n const found = readVersionedRecord(storage, recordSchema, options.now)\n if (found.status === 'record') {\n previous = found.record\n return codec.decode(found.record)\n }\n if (found.status === 'absent' && schema.legacyAppearanceKey && codec.fromLegacy) {\n const legacy = readLegacyValue(storage, schema.legacyAppearanceKey)\n const migrated = legacy === null ? undefined : codec.fromLegacy(legacy)\n if (migrated) return migrated\n }\n } catch {\n // A codec that throws on a record it cannot read is an unreadable store.\n }\n return fallback()\n },\n write(next) {\n const storage = storageOf()\n if (!storage) return\n try {\n const document = codec.encode(next, previous)\n if (writeVersionedRecord(storage, schema.key, document)) {\n previous =\n typeof document === 'object' && document !== null\n ? (document as Readonly<Record<string, unknown>>)\n : undefined\n }\n } catch {\n // An encoder that throws loses the preference, not the switch.\n }\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"theme-persistence.js","names":[],"sources":["../../../src/components/theme/theme-persistence.ts"],"sourcesContent":["import { normalizeCustomAccent } from '#lib/accent-roles'\nimport { accentPresets } from '#lib/accent-presets'\nimport { normalizeAppearanceEditorFontSettings } from '#lib/appearance-font-settings'\nimport { isThemeAccentId } from '#lib/theme-accents'\nimport {\n catalogueThemeAppearance,\n defaultDarkThemeId,\n defaultLightThemeId,\n} from '#lib/theme-defaults'\nimport { normalizeSurface } from '#lib/theme-surface'\nimport {\n defaultPreferenceStorage,\n readLegacyValue,\n readVersionedRecord,\n recoveryKeyFor,\n writeVersionedRecord,\n type PreferenceStorage,\n} from '#lib/preference-store'\nimport { defaultThemeSelection, resolvedDensity, type ThemeSelection } from './theme-state-core'\n\n/**\n * Where a selection is kept between sessions.\n *\n * The provider never touches storage itself; it asks a `ThemePersistence`. The\n * default is {@link localStoragePersistence}, which is what the provider always did.\n * A host that stores the preference in a different shape, under a versioned\n * envelope, or somewhere that is not `localStorage` supplies its own and the\n * resolution and application rules stay the shared ones.\n *\n * The contract is deliberately narrow, so that an implementation cannot make the\n * provider less safe than it was:\n *\n * - `read` is synchronous and runs once, when the provider is created. It may\n * repair or quarantine what it finds, and it must not throw: the provider\n * treats a throw as \"nothing stored\" and starts from the defaults.\n * - `write` runs only from `setSelection`. Never on mount, never from `preview`,\n * never as a side effect of reading, so the first write to a store is always a\n * user's decision. A throw is swallowed, because a full or blocked store must\n * not break the switch.\n */\nexport type ThemePersistence = {\n read(): Partial<ThemeSelection> | undefined\n write(next: ThemeSelection): void\n}\n\n/**\n * The preference store, or nothing.\n *\n * Only ever called inside a `try`: where storage is blocked — a sandboxed iframe, a\n * privacy mode, a denied site-data permission — merely *reading* the `localStorage`\n * global throws (\"Access is denied\", `SecurityError`), so even the `typeof` guard\n * has to sit inside the `try` rather than in front of it. Without a store the\n * preference lives only in the provider's signal for the session.\n */\nfunction preferenceStore(): Storage | undefined {\n return typeof localStorage === 'undefined' ? undefined : localStorage\n}\n\n/**\n * The stored preference, validated against the catalogue's ids (`theme-defaults`), so\n * reading it never loads the catalogue.\n */\nfunction readSelection(storageKey: string): ThemeSelection {\n try {\n const raw = preferenceStore()?.getItem(storageKey)\n if (!raw) return defaultThemeSelection\n const parsed = JSON.parse(raw) as Partial<ThemeSelection>\n const fonts = normalizeAppearanceEditorFontSettings(parsed.fonts, parsed.font).settings\n return {\n appearance:\n parsed.appearance === 'light' ||\n parsed.appearance === 'dark' ||\n parsed.appearance === 'system'\n ? parsed.appearance\n : defaultThemeSelection.appearance,\n // An unknown id would leave the document with no theme at all, so a stale\n // preference from an older catalogue falls back rather than applying nothing.\n lightThemeId:\n catalogueThemeAppearance(parsed.lightThemeId ?? '') === 'light'\n ? (parsed.lightThemeId as string)\n : defaultLightThemeId,\n darkThemeId:\n catalogueThemeAppearance(parsed.darkThemeId ?? '') === 'dark'\n ? (parsed.darkThemeId as string)\n : defaultDarkThemeId,\n // A theme accent is kept whenever it names a slot, even one the stored pair\n // cannot offer: the id is role-shaped so it survives a theme switch, and an\n // unoffered slot already resolves to the theme's own primary when applied. A\n // custom `#rrggbb` is kept in its one stored spelling; its readability is\n // settled when it is painted, not when it is read.\n accent:\n typeof parsed.accent === 'string' &&\n (accentPresets.some((preset) => preset.id === parsed.accent) ||\n isThemeAccentId(parsed.accent))\n ? parsed.accent\n : (normalizeCustomAccent(parsed.accent) ?? defaultThemeSelection.accent),\n font: fonts.ui.family,\n fonts,\n // Every axis has to be read back here, or it is lost twice: the provider's\n // document effect removes the `data-density` that `themeScript` set for the first paint, and\n // the next `setSelection` rewrites the store without it.\n density: resolvedDensity(parsed as ThemeSelection),\n // The newer axes are read back only when the store holds a usable value and are\n // otherwise left `undefined`, never defaulted: `JSON.stringify` drops them, so a\n // consumer that never sets one writes the bytes it always did. An id is kept as\n // stored, known or not, for the same reason a theme id the registry lacks is.\n terminalThemeId:\n typeof parsed.terminalThemeId === 'string' && parsed.terminalThemeId.trim() !== ''\n ? parsed.terminalThemeId\n : undefined,\n surface: normalizeSurface(parsed.surface),\n reduceTransparency:\n typeof parsed.reduceTransparency === 'boolean' ? parsed.reduceTransparency : undefined,\n }\n } catch {\n return defaultThemeSelection\n }\n}\n\n/**\n * The default persistence: one JSON document in `localStorage` under `key`.\n *\n * `read` is the provider's original `readSelection`, moved here unchanged — every\n * stored id is validated against the catalogue, so a stale preference falls back to\n * a default rather than reaching the document. `key` may be a function so a\n * provider whose `storageKey` prop changes writes to the key it is now given.\n *\n * Blocked, full or absent storage is survivable in both directions: a read answers\n * with the defaults and a write is dropped, so the preference simply lasts the\n * session.\n */\nexport function localStoragePersistence(key: string | (() => string)): ThemePersistence {\n const keyOf = () => (typeof key === 'function' ? key() : key)\n return {\n read: () => readSelection(keyOf()),\n write(next) {\n try {\n preferenceStore()?.setItem(keyOf(), JSON.stringify(next))\n } catch {\n // A full, blocked or unavailable store must not break the switch: the\n // preference simply does not survive the reload.\n }\n },\n }\n}\n\n/**\n * What a persistence knows, or nothing.\n *\n * The seam's `read` must not throw, and the provider does not rely on that: a host\n * persistence is code the design system has not seen, and a theme provider that\n * fails to mount because a store misbehaved has turned a lost preference into a\n * blank application.\n */\nexport function readPersisted(persistence: ThemePersistence): Partial<ThemeSelection> | undefined {\n try {\n return persistence.read()\n } catch {\n return undefined\n }\n}\n\n/** Hand a selection to the store; a store that throws loses the preference, not the switch. */\nexport function writePersisted(persistence: ThemePersistence, next: ThemeSelection): void {\n try {\n persistence.write(next)\n } catch {\n // Same contract as a full or blocked `localStorage`: the choice holds for the\n // session and does not survive the reload.\n }\n}\n\n/**\n * Where a host's versioned preference document lives. One schema describes the\n * document to both halves of the system: {@link createVersionedPersistence} reads and\n * writes it, and `themeScript(options)` reads it before first paint, so the two\n * cannot disagree about a key or a version.\n */\nexport type ThemeStorageSchema = {\n /** The storage key of the document. */\n key: string\n /**\n * The only top-level `version` read. Absent, the document is unversioned. A record\n * of any other version is quarantined (see `recoveryKey`), not interpreted.\n */\n version?: number\n /**\n * The document field that holds the appearance preference (`system`, `light`,\n * `dark`). Read by `themeScript`; the persistence leaves field names to the codec.\n * Default `appearance`.\n */\n appearanceField?: 'appearance' | 'mode'\n /**\n * An older single-value key (`light` | `dark` | `system`) read only while `key` is\n * absent, and never deleted. The persistence hands it to the codec's `fromLegacy`.\n */\n legacyAppearanceKey?: string\n /**\n * The stored library of imported themes, for `themeScript` only: an imported\n * theme's id and verbatim tokens, so the first paint can use the right variant. The\n * persistence neither reads nor writes it.\n */\n libraryKey?: string\n /** Where an unreadable document is kept. Default `${key}.recovery`. */\n recoveryKey?: string\n}\n\n/**\n * How a host's document maps to and from the shared {@link ThemeSelection}.\n *\n * The codec is the product schema: it decides which fields a document has, how an\n * unknown value is corrected, and what a fresh install looks like. The engine decides\n * everything about *storage*: what is quarantined, when the legacy key is read, and\n * that nothing is written until `setSelection` asks.\n */\nexport type VersionedCodec<Doc> = {\n /**\n * A readable record (the right JSON shape and version) as a selection patch. Fields\n * it leaves `undefined` fall back to the provider's defaults. It may keep an id the\n * catalogue lacks; the provider resolves it, it does not rewrite it.\n */\n decode(record: Readonly<Record<string, unknown>>): Partial<ThemeSelection>\n /**\n * The legacy key's value (never `null`) as a selection patch, or `undefined` for a\n * value it does not recognise. Called only when the main key is absent.\n */\n fromLegacy?(value: string): Partial<ThemeSelection> | undefined\n /**\n * What a store with nothing readable answers: a fresh install, an unreadable\n * document, a blocked store. Absent, the provider's own defaults apply.\n */\n defaults?(): Partial<ThemeSelection> | undefined\n /**\n * The document to store for a selection. `previous` is the record last read or\n * written, so a codec can carry a field the selection does not model.\n */\n encode(next: ThemeSelection, previous?: Readonly<Record<string, unknown>>): Doc\n}\n\n/** Seams for the storage and the clock; both default to the real ones. */\nexport type VersionedPersistenceOptions = {\n /** The store, or nothing. Called per access; default is `localStorage` (blocked-safe). */\n storage?: () => PreferenceStorage | undefined\n /** The clock for the recovery envelope's `capturedAt`. */\n now?: () => Date\n}\n\n/**\n * A {@link ThemePersistence} over a versioned document and a host codec.\n *\n * `read` follows the engine's rules (`lib/preference-store.ts`): the main key first;\n * an unreadable or unsupported document is quarantined byte for byte into the\n * recovery key and the codec's `defaults` answer; the legacy key only when the main\n * key is absent. It never writes the main key, so the first write is a user's\n * decision. `write` stores `codec.encode(next, previous)` as JSON and swallows a\n * failure.\n *\n * Neither method throws. The stored bytes are exactly what the codec encodes, so a\n * host keeps its keys, field names and bytes by keeping its codec.\n */\nexport function createVersionedPersistence<Doc>(\n schema: ThemeStorageSchema,\n codec: VersionedCodec<Doc>,\n options: VersionedPersistenceOptions = {}\n): ThemePersistence {\n const storageOf = options.storage ?? defaultPreferenceStorage\n const recordSchema = {\n key: schema.key,\n version: schema.version,\n recoveryKey: recoveryKeyFor(schema),\n }\n let previous: Readonly<Record<string, unknown>> | undefined\n\n const fallback = () => {\n try {\n return codec.defaults?.()\n } catch {\n return undefined\n }\n }\n\n return {\n read() {\n const storage = storageOf()\n if (!storage) return fallback()\n try {\n const found = readVersionedRecord(storage, recordSchema, options.now)\n if (found.status === 'record') {\n previous = found.record\n return codec.decode(found.record)\n }\n if (found.status === 'absent' && schema.legacyAppearanceKey && codec.fromLegacy) {\n const legacy = readLegacyValue(storage, schema.legacyAppearanceKey)\n const migrated = legacy === null ? undefined : codec.fromLegacy(legacy)\n if (migrated) return migrated\n }\n } catch {\n // A codec that throws on a record it cannot read is an unreadable store.\n }\n return fallback()\n },\n write(next) {\n const storage = storageOf()\n if (!storage) return\n try {\n const document = codec.encode(next, previous)\n if (writeVersionedRecord(storage, schema.key, document)) {\n previous =\n typeof document === 'object' && document !== null\n ? (document as Readonly<Record<string, unknown>>)\n : undefined\n }\n } catch {\n // An encoder that throws loses the preference, not the switch.\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsDA,SAAS,kBAAuC;CAC9C,OAAO,OAAO,iBAAiB,cAAc,KAAA,IAAY;AAC3D;;;;;AAMA,SAAS,cAAc,YAAoC;CACzD,IAAI;EACF,MAAM,MAAM,gBAAgB,CAAC,EAAE,QAAQ,UAAU;EACjD,IAAI,CAAC,KAAK,OAAO;EACjB,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,MAAM,QAAQ,sCAAsC,OAAO,OAAO,OAAO,IAAI,CAAC,CAAC;EAC/E,OAAO;GACL,YACE,OAAO,eAAe,WACtB,OAAO,eAAe,UACtB,OAAO,eAAe,WAClB,OAAO,aACP,sBAAsB;GAG5B,cACE,yBAAyB,OAAO,gBAAgB,EAAE,MAAM,UACnD,OAAO,eACR;GACN,aACE,yBAAyB,OAAO,eAAe,EAAE,MAAM,SAClD,OAAO,cACR;GAMN,QACE,OAAO,OAAO,WAAW,aACxB,cAAc,MAAM,WAAW,OAAO,OAAO,OAAO,MAAM,KACzD,gBAAgB,OAAO,MAAM,KAC3B,OAAO,SACN,sBAAsB,OAAO,MAAM,KAAK,sBAAsB;GACrE,MAAM,MAAM,GAAG;GACf;GAIA,SAAS,gBAAgB,MAAwB;GAKjD,iBACE,OAAO,OAAO,oBAAoB,YAAY,OAAO,gBAAgB,KAAK,MAAM,KAC5E,OAAO,kBACP,KAAA;GACN,SAAS,iBAAiB,OAAO,OAAO;GACxC,oBACE,OAAO,OAAO,uBAAuB,YAAY,OAAO,qBAAqB,KAAA;EACjF;CACF,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAgB,wBAAwB,KAAgD;CACtF,MAAM,cAAe,OAAO,QAAQ,aAAa,IAAI,IAAI;CACzD,OAAO;EACL,YAAY,cAAc,MAAM,CAAC;EACjC,MAAM,MAAM;GACV,IAAI;IACF,gBAAgB,CAAC,EAAE,QAAQ,MAAM,GAAG,KAAK,UAAU,IAAI,CAAC;GAC1D,QAAQ,CAGR;EACF;CACF;AACF;;;;;;;;;AAUA,SAAgB,cAAc,aAAoE;CAChG,IAAI;EACF,OAAO,YAAY,KAAK;CAC1B,QAAQ;EACN;CACF;AACF;;AAGA,SAAgB,eAAe,aAA+B,MAA4B;CACxF,IAAI;EACF,YAAY,MAAM,IAAI;CACxB,QAAQ,CAGR;AACF;;;;;;;;;;;;;;AA0FA,SAAgB,2BACd,QACA,OACA,UAAuC,CAAC,GACtB;CAClB,MAAM,YAAY,QAAQ,WAAW;CACrC,MAAM,eAAe;EACnB,KAAK,OAAO;EACZ,SAAS,OAAO;EAChB,aAAa,eAAe,MAAM;CACpC;CACA,IAAI;CAEJ,MAAM,iBAAiB;EACrB,IAAI;GACF,OAAO,MAAM,WAAW;EAC1B,QAAQ;GACN;EACF;CACF;CAEA,OAAO;EACL,OAAO;GACL,MAAM,UAAU,UAAU;GAC1B,IAAI,CAAC,SAAS,OAAO,SAAS;GAC9B,IAAI;IACF,MAAM,QAAQ,oBAAoB,SAAS,cAAc,QAAQ,GAAG;IACpE,IAAI,MAAM,WAAW,UAAU;KAC7B,WAAW,MAAM;KACjB,OAAO,MAAM,OAAO,MAAM,MAAM;IAClC;IACA,IAAI,MAAM,WAAW,YAAY,OAAO,uBAAuB,MAAM,YAAY;KAC/E,MAAM,SAAS,gBAAgB,SAAS,OAAO,mBAAmB;KAClE,MAAM,WAAW,WAAW,OAAO,KAAA,IAAY,MAAM,WAAW,MAAM;KACtE,IAAI,UAAU,OAAO;IACvB;GACF,QAAQ,CAER;GACA,OAAO,SAAS;EAClB;EACA,MAAM,MAAM;GACV,MAAM,UAAU,UAAU;GAC1B,IAAI,CAAC,SAAS;GACd,IAAI;IACF,MAAM,WAAW,MAAM,OAAO,MAAM,QAAQ;IAC5C,IAAI,qBAAqB,SAAS,OAAO,KAAK,QAAQ,GACpD,WACE,OAAO,aAAa,YAAY,aAAa,OACxC,WACD,KAAA;GAEV,QAAQ,CAER;EACF;CACF;AACF"}
@@ -0,0 +1,19 @@
1
+ import { type ThemeCatalogueSource, type ThemeProviderProps } from './theme-provider-value';
2
+ export type { ThemeCatalogueSource, ThemeProviderProps } from './theme-provider-value';
3
+ export { createVersionedPersistence, localStoragePersistence, type ThemePersistence, type ThemeStorageSchema, type VersionedCodec, type VersionedPersistenceOptions, } from './theme-persistence';
4
+ export { useOptionalTheme, useTheme } from './theme-context';
5
+ export type { ThemeSurface } from '#lib/theme-surface';
6
+ export type { EditorRolesMode } from '#lib/themes';
7
+ export { inlineScriptLiteral, themeScript, type ThemeScriptOptions } from './theme-script';
8
+ export { defaultThemeSelection, resolvedDensity, type ThemeContextValue, type ThemeDensity, type ThemeSelection, } from './theme-state-core';
9
+ /**
10
+ * The provider, parameterised by where its catalogue comes from.
11
+ *
12
+ * `ThemeProvider` (`theme-provider.tsx`) and the lazy `ThemeProvider`
13
+ * (`theme-provider-lazy.tsx`) are this component with a source; the work is
14
+ * `createThemeContextValue`. It imports no catalogue code or data.
15
+ */
16
+ export declare function ThemeProviderCore(props: ThemeProviderProps & {
17
+ catalogue: ThemeCatalogueSource;
18
+ }): import("solid-js").JSX.Element;
19
+ //# sourceMappingURL=theme-provider-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider-core.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider-core.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAA;AAE/B,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACtF,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,2BAA2B,GACjC,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,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,oBAAoB,CAAA;AAE3B;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG;IAAE,SAAS,EAAE,oBAAoB,CAAA;CAAE,kCAIhG"}
@@ -0,0 +1,28 @@
1
+ import { themeContext, useOptionalTheme, useTheme } from "./theme-context.js";
2
+ import { defaultThemeSelection, resolvedDensity } from "./theme-state-core.js";
3
+ import { createVersionedPersistence, localStoragePersistence } from "./theme-persistence.js";
4
+ import { createThemeContextValue } from "./theme-provider-value.js";
5
+ import { inlineScriptLiteral, themeScript } from "./theme-script.js";
6
+ import { createComponent } from "solid-js/web";
7
+ //#region src/components/theme/theme-provider-core.tsx
8
+ /**
9
+ * The provider, parameterised by where its catalogue comes from.
10
+ *
11
+ * `ThemeProvider` (`theme-provider.tsx`) and the lazy `ThemeProvider`
12
+ * (`theme-provider-lazy.tsx`) are this component with a source; the work is
13
+ * `createThemeContextValue`. It imports no catalogue code or data.
14
+ */
15
+ function ThemeProviderCore(props) {
16
+ const value = createThemeContextValue(props);
17
+ const Context = themeContext();
18
+ return createComponent(Context.Provider, {
19
+ value,
20
+ get children() {
21
+ return props.children;
22
+ }
23
+ });
24
+ }
25
+ //#endregion
26
+ export { ThemeProviderCore, createVersionedPersistence, defaultThemeSelection, inlineScriptLiteral, localStoragePersistence, resolvedDensity, themeScript, useOptionalTheme, useTheme };
27
+
28
+ //# sourceMappingURL=theme-provider-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider-core.js","names":["themeContext","createThemeContextValue","ThemeCatalogueSource","ThemeProviderProps","createVersionedPersistence","localStoragePersistence","ThemePersistence","ThemeStorageSchema","VersionedCodec","VersionedPersistenceOptions","useOptionalTheme","useTheme","ThemeSurface","EditorRolesMode","inlineScriptLiteral","themeScript","ThemeScriptOptions","defaultThemeSelection","resolvedDensity","ThemeContextValue","ThemeDensity","ThemeSelection","ThemeProviderCore","props","catalogue","value","Context","_$createComponent","Provider","children","_$createComponent"],"sources":["../../../src/components/theme/theme-provider-core.tsx"],"sourcesContent":["import { themeContext } from './theme-context'\nimport {\n createThemeContextValue,\n type ThemeCatalogueSource,\n type ThemeProviderProps,\n} from './theme-provider-value'\n\nexport type { ThemeCatalogueSource, ThemeProviderProps } from './theme-provider-value'\nexport {\n createVersionedPersistence,\n localStoragePersistence,\n type ThemePersistence,\n type ThemeStorageSchema,\n type VersionedCodec,\n type VersionedPersistenceOptions,\n} from './theme-persistence'\nexport { useOptionalTheme, useTheme } from './theme-context'\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-core'\n\n/**\n * The provider, parameterised by where its catalogue comes from.\n *\n * `ThemeProvider` (`theme-provider.tsx`) and the lazy `ThemeProvider`\n * (`theme-provider-lazy.tsx`) are this component with a source; the work is\n * `createThemeContextValue`. It imports no catalogue code or data.\n */\nexport function ThemeProviderCore(props: ThemeProviderProps & { catalogue: ThemeCatalogueSource }) {\n const value = createThemeContextValue(props)\n const Context = themeContext()\n return <Context.Provider value={value}>{props.children}</Context.Provider>\n}\n"],"mappings":";;;;;;;;;;;;;;AAmCA,SAAgBsB,kBAAkBC,OAAiE;CACjG,MAAME,QAAQxB,wBAAwBsB,KAAK;CAC3C,MAAMG,UAAU1B,aAAa;CAC7B,OAAA2B,gBAAQD,QAAQE,UAAQ;EAAQH;EAAK,IAAAI,WAAA;GAAA,OAAGN,MAAMM;EAAQ;CAAA,CAAA;AACxD"}
@@ -0,0 +1,28 @@
1
+ import { type ThemeProviderProps } from './theme-provider-core';
2
+ export { loadThemeCatalogue } from './theme-catalogue-source';
3
+ /**
4
+ * ThemeProvider, with the theme catalogue out of the start-up path.
5
+ *
6
+ * Same props, same context and same behaviour as `ThemeProvider` for everything the
7
+ * shipped default pair covers: the default light and dark themes, a host's own
8
+ * `themes`, every accent preset and custom colour, the surface axis, fonts and
9
+ * density. Only the 32 other catalogue themes are not in the start-up chunk. The first
10
+ * time the selection (or a preview) names one, or a theme-derived `ansi-*` accent over
11
+ * such a pair, this provider imports the catalogue in a chunk of its own:
12
+ *
13
+ * - until it arrives the document keeps what is on it (set before first paint by
14
+ * `themeScript`, painted by the stylesheet under `palette="stylesheet"`), and
15
+ * `variant()` is the default of the appearance;
16
+ * - when it arrives the provider re-resolves and paints the stored theme, and
17
+ * `useTheme().catalogueReady()` turns true;
18
+ * - a preview of a theme that has not loaded starts the load and shows when it ends.
19
+ *
20
+ * Pair it with `palette="stylesheet"` and `themeScript`: inline palettes of catalogue
21
+ * themes need the catalogue to compute, so under the default `inline` palette every
22
+ * stored theme but the defaults waits for the import.
23
+ *
24
+ * `useTheme().themes` and `allThemes()` list only the default pair until
25
+ * `catalogueReady()`; a picker calls `useTheme().loadCatalogue()` when it mounts.
26
+ */
27
+ export declare function ThemeProvider(props: ThemeProviderProps): import("solid-js").JSX.Element;
28
+ //# sourceMappingURL=theme-provider-lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider-lazy.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider-lazy.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAElF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,kCAItD"}
@@ -0,0 +1,36 @@
1
+ import { ThemeProviderCore } from "./theme-provider-core.js";
2
+ import { createLazyCatalogue } from "./theme-catalogue-source.js";
3
+ import { createComponent, mergeProps } from "solid-js/web";
4
+ //#region src/components/theme/theme-provider-lazy.tsx
5
+ /**
6
+ * ThemeProvider, with the theme catalogue out of the start-up path.
7
+ *
8
+ * Same props, same context and same behaviour as `ThemeProvider` for everything the
9
+ * shipped default pair covers: the default light and dark themes, a host's own
10
+ * `themes`, every accent preset and custom colour, the surface axis, fonts and
11
+ * density. Only the 32 other catalogue themes are not in the start-up chunk. The first
12
+ * time the selection (or a preview) names one, or a theme-derived `ansi-*` accent over
13
+ * such a pair, this provider imports the catalogue in a chunk of its own:
14
+ *
15
+ * - until it arrives the document keeps what is on it (set before first paint by
16
+ * `themeScript`, painted by the stylesheet under `palette="stylesheet"`), and
17
+ * `variant()` is the default of the appearance;
18
+ * - when it arrives the provider re-resolves and paints the stored theme, and
19
+ * `useTheme().catalogueReady()` turns true;
20
+ * - a preview of a theme that has not loaded starts the load and shows when it ends.
21
+ *
22
+ * Pair it with `palette="stylesheet"` and `themeScript`: inline palettes of catalogue
23
+ * themes need the catalogue to compute, so under the default `inline` palette every
24
+ * stored theme but the defaults waits for the import.
25
+ *
26
+ * `useTheme().themes` and `allThemes()` list only the default pair until
27
+ * `catalogueReady()`; a picker calls `useTheme().loadCatalogue()` when it mounts.
28
+ */
29
+ function ThemeProvider(props) {
30
+ const catalogue = createLazyCatalogue();
31
+ return createComponent(ThemeProviderCore, mergeProps(props, { catalogue }));
32
+ }
33
+ //#endregion
34
+ export { ThemeProvider };
35
+
36
+ //# sourceMappingURL=theme-provider-lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider-lazy.js","names":["createLazyCatalogue","ThemeProviderCore","ThemeProviderProps","loadThemeCatalogue","ThemeProvider","props","catalogue","_$createComponent","_$mergeProps","_$createComponent","_$mergeProps"],"sources":["../../../src/components/theme/theme-provider-lazy.tsx"],"sourcesContent":["import { createLazyCatalogue } from './theme-catalogue-source'\nimport { ThemeProviderCore, type ThemeProviderProps } from './theme-provider-core'\n\nexport { loadThemeCatalogue } from './theme-catalogue-source'\n\n/**\n * ThemeProvider, with the theme catalogue out of the start-up path.\n *\n * Same props, same context and same behaviour as `ThemeProvider` for everything the\n * shipped default pair covers: the default light and dark themes, a host's own\n * `themes`, every accent preset and custom colour, the surface axis, fonts and\n * density. Only the 32 other catalogue themes are not in the start-up chunk. The first\n * time the selection (or a preview) names one, or a theme-derived `ansi-*` accent over\n * such a pair, this provider imports the catalogue in a chunk of its own:\n *\n * - until it arrives the document keeps what is on it (set before first paint by\n * `themeScript`, painted by the stylesheet under `palette=\"stylesheet\"`), and\n * `variant()` is the default of the appearance;\n * - when it arrives the provider re-resolves and paints the stored theme, and\n * `useTheme().catalogueReady()` turns true;\n * - a preview of a theme that has not loaded starts the load and shows when it ends.\n *\n * Pair it with `palette=\"stylesheet\"` and `themeScript`: inline palettes of catalogue\n * themes need the catalogue to compute, so under the default `inline` palette every\n * stored theme but the defaults waits for the import.\n *\n * `useTheme().themes` and `allThemes()` list only the default pair until\n * `catalogueReady()`; a picker calls `useTheme().loadCatalogue()` when it mounts.\n */\nexport function ThemeProvider(props: ThemeProviderProps) {\n // Hoisted: a JSX prop expression is re-evaluated on every read, and this source owns signals.\n const catalogue = createLazyCatalogue()\n return <ThemeProviderCore {...props} catalogue={catalogue} />\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgBI,cAAcC,OAA2B;CAEvD,MAAMC,YAAYN,oBAAoB;CACtC,OAAAO,gBAAQN,mBAAiBO,WAAKH,OAAK,EAAaC,UAAS,CAAA,CAAA;AAC3D"}
@@ -0,0 +1,175 @@
1
+ import type { JSX } from 'solid-js';
2
+ import { type ThemeEngine } from '#lib/theme-engine';
3
+ import type { EditorRolesMode, ThemeVariant } from '#lib/themes';
4
+ import { type ThemePersistence } from './theme-persistence';
5
+ import { type ThemeContextValue, type ThemeSelection } from './theme-state-core';
6
+ /**
7
+ * ThemeProvider.
8
+ *
9
+ * Owns the appearance preference and applies it to the document. One provider near
10
+ * the root; everything below reads it.
11
+ *
12
+ * ## What a preference is
13
+ *
14
+ * Four independent choices (`ThemeSelection`), and keeping them independent is the
15
+ * point: appearance, a theme per appearance, an accent and fonts. They compose: a
16
+ * user who wants Nord with a violet accent and the system font should not need
17
+ * someone to author that combination.
18
+ *
19
+ * ## Where the rules live
20
+ *
21
+ * This component is wiring. What a selection merges to and resolves to is
22
+ * `theme-state.ts`; what lands on `<html>` is `theme-document.ts`; where the
23
+ * preference is kept is `theme-persistence.ts`. Each is plain code a test can call,
24
+ * which is the reason none of it is inlined here.
25
+ *
26
+ * ## Why the variants are applied as inline properties
27
+ *
28
+ * The catalogue is data, and a theme is applied by writing its roles onto
29
+ * `<html>` as custom properties. That is what makes an imported palette work
30
+ * without a hand-written `[data-theme]` block per theme, and it is why a settings
31
+ * UI can show a preview by reading the same object rather than by loading a
32
+ * stylesheet. `theme.css` keeps the default theme's values so the first paint is
33
+ * correct before any script runs.
34
+ */
35
+ export type ThemeProviderProps = {
36
+ children?: JSX.Element;
37
+ /** Where the preference is persisted. Distinct per application. */
38
+ storageKey?: string;
39
+ /** Start from this instead of the stored preference. */
40
+ initial?: Partial<ThemeSelection>;
41
+ /**
42
+ * The selection, owned by the caller.
43
+ *
44
+ * For a consumer that keeps the state itself — a settings store, or a workshop
45
+ * whose selection lives in Storybook globals — rather than handing it to this
46
+ * provider's signal. Each change lands here and the document follows; nothing is
47
+ * written to the store, because the caller is the one who persists. A picker
48
+ * below the provider still works, and its change is the caller's to read back.
49
+ *
50
+ * Absent, the provider is the whole owner: it reads `storageKey` on mount,
51
+ * seeds from `initial`, and persists every `setSelection`.
52
+ */
53
+ selection?: Partial<ThemeSelection>;
54
+ /**
55
+ * Where the preference is kept, in place of `localStorage` under `storageKey`.
56
+ *
57
+ * Read once when the provider is created, so changing the prop later does not
58
+ * move the store. `storageKey` is ignored when this is given.
59
+ */
60
+ persistence?: ThemePersistence;
61
+ /**
62
+ * Variants the host adds to the built-in catalogue — themes a user imported.
63
+ *
64
+ * A function is read inside the provider's reactive scope, so one that reads a
65
+ * signal keeps the picker and the document current when the set changes. A
66
+ * stored id that names no variant, built in or added, paints the default of its
67
+ * appearance and stays in the selection untouched.
68
+ */
69
+ themes?: readonly ThemeVariant[] | (() => readonly ThemeVariant[]);
70
+ /**
71
+ * Rewrites the custom properties of each pass before they are written to
72
+ * `<html>`: the variant's roles with the accent over them. `tokensAsHex` from
73
+ * `lib/themes` is the one the system ships.
74
+ */
75
+ transformTokens?: (tokens: Record<string, string>) => Record<string, string>;
76
+ /**
77
+ * Switch CSS transitions off for the frame that carries a change, so a theme
78
+ * switch cannot animate the whole page. Off by default: it adds a `<style>`
79
+ * element to `<head>` for two frames on every change.
80
+ */
81
+ disableTransitionOnChange?: boolean;
82
+ /**
83
+ * The contrast ratio an accent is held to against the variant's background,
84
+ * repaired to the nearest passing colour when it falls short. Absent, presets
85
+ * are painted verbatim — the catalogue measured them for its own themes — and
86
+ * only a custom `#rrggbb` is held to 3:1. A host that adds themes the catalogue
87
+ * never measured passes 3 to hold the presets to it as well; for the catalogue's
88
+ * own themes the floor changes no preset.
89
+ */
90
+ accentContrastFloor?: number;
91
+ /**
92
+ * Turns the surface axis on: `selection.surface` and `selection.reduceTransparency`
93
+ * are resolved against the operating system's Reduce Transparency setting and the
94
+ * host's capability, and `data-surface`, `data-reduce-transparency` and
95
+ * `--surface-alpha` are written to `<html>` (the frost rule in `theme.css` reads
96
+ * them). Off by default: a provider that never opts in writes none of them, adds no
97
+ * media-query listener, and stores nothing new.
98
+ *
99
+ * An object also states the host's capability. `nativeTranslucency` is whether the
100
+ * window can render the OS's own translucency; without it a `translucent`
101
+ * preference paints the tokenized `frosted` surface instead of pretending to
102
+ * vibrancy. A function is read inside the provider's reactive scope, for a
103
+ * capability that is learned after mount.
104
+ *
105
+ * A selection that already carries a `surface` turns the axis on for itself, so a
106
+ * host that stores the field gets the same document without the prop.
107
+ */
108
+ surface?: boolean | {
109
+ nativeTranslucency?: boolean | (() => boolean);
110
+ };
111
+ /**
112
+ * Where a catalogue variant's palette comes from. `inline` (the default) writes
113
+ * every role onto `<html>`. `stylesheet` leaves a catalogue variant to the host's
114
+ * `[data-theme]` stylesheet and writes inline only what no stylesheet can know: a
115
+ * host's own `themes`, the accent, the pinned terminal palette, the surface alpha
116
+ * and the fonts. Switching from a host variant back to a catalogue one removes the
117
+ * properties the host variant wrote.
118
+ */
119
+ palette?: 'inline' | 'stylesheet';
120
+ /**
121
+ * The projection behind the `--editor-*` ramp. `syntax` (the default) is the
122
+ * catalogue's quiet syntax roles, a comment among them deliberately below
123
+ * body-text contrast. `floor` holds all sixteen roles to 4.5:1 on the canvas, as
124
+ * hex, for a host that renders code as content (a diff, a file view) rather than
125
+ * as decoration.
126
+ *
127
+ * Applies to catalogue variants; a host's own `themes` keep their `editor` and
128
+ * their `tokens`. With `palette="stylesheet"` it is the one palette property a
129
+ * catalogue variant still writes inline, because `theme-variants.css` is generated
130
+ * for `syntax`. Absent, nothing changes.
131
+ */
132
+ editorRoles?: EditorRolesMode;
133
+ /**
134
+ * Bind the platform zoom chords (Cmd or Ctrl with `=`, `+`, `-`, `0`) to the three
135
+ * text sizes instead of the browser's page zoom: stepping them together is zoom
136
+ * inside the persisted type system. Off by default: it adds a capturing `keydown`
137
+ * listener to `window`, and an event already handled elsewhere is left alone.
138
+ * `zoomFonts` on the context does the same from a menu or a host's own binding.
139
+ */
140
+ fontZoom?: boolean;
141
+ };
142
+ /**
143
+ * Where the provider's catalogue questions are answered, and whether the whole
144
+ * catalogue is available yet.
145
+ *
146
+ * The eager provider's source is the full catalogue and is always loaded. The lazy
147
+ * provider's source starts on the default pair that ships with it and swaps to the
148
+ * full catalogue when {@link ThemeCatalogueSource.load} resolves. Both read the same
149
+ * way in the provider: `engine()` is reactive, so everything derived from it follows
150
+ * the swap.
151
+ */
152
+ export type ThemeCatalogueSource = {
153
+ /** The engine answering catalogue questions now. Changes once, when loading completes. */
154
+ engine: () => ThemeEngine;
155
+ /** True when `engine()` is the full catalogue. */
156
+ loaded: () => boolean;
157
+ /** True when loading failed; the provider then paints what it can without the rest. */
158
+ failed: () => boolean;
159
+ /** Start loading the full catalogue (idempotent). Resolves when `loaded()` is true. */
160
+ load: () => Promise<void>;
161
+ };
162
+ /**
163
+ * Everything the provider is, as a value: the context object, with its effects and
164
+ * listeners created in the caller's reactive scope.
165
+ *
166
+ * Parameterised by where its catalogue comes from: `ThemeProvider`
167
+ * (`theme-provider.tsx`) and the lazy `ThemeProvider` (`theme-provider-lazy.tsx`) wrap
168
+ * this with a source. It imports no catalogue code or data, which is what lets the lazy
169
+ * one keep them out of a consumer's start-up chunk. It is not JSX so that the unit lane,
170
+ * which has no Solid compiler, can drive it directly.
171
+ */
172
+ export declare function createThemeContextValue(props: ThemeProviderProps & {
173
+ catalogue: ThemeCatalogueSource;
174
+ }): ThemeContextValue;
175
+ //# sourceMappingURL=theme-provider-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider-value.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider-value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInC,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACvF,OAAO,KAAK,EAAE,eAAe,EAAmB,YAAY,EAAE,MAAM,aAAa,CAAA;AAEjF,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,0FAA0F;IAC1F,MAAM,EAAE,MAAM,WAAW,CAAA;IACzB,kDAAkD;IAClD,MAAM,EAAE,MAAM,OAAO,CAAA;IACrB,uFAAuF;IACvF,MAAM,EAAE,MAAM,OAAO,CAAA;IACrB,uFAAuF;IACvF,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B,CAAA;AASD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,kBAAkB,GAAG;IAAE,SAAS,EAAE,oBAAoB,CAAA;CAAE,GAC9D,iBAAiB,CAoPnB"}
@@ -0,0 +1,200 @@
1
+ import { fontZoomShortcut } from "../../lib/font-zoom.js";
2
+ import { REDUCED_TRANSPARENCY_QUERY, resolveSurfaceState } from "../../lib/theme-surface.js";
3
+ import { CatalogueRequired, needsCatalogue } from "../../lib/theme-engine.js";
4
+ import { accentRolesForStateWith, initialThemeSelection, mergeRegistry, mergeThemeSelection, resolveTerminalVariant, resolveThemeState, zoomedFontSettings } from "./theme-state-core.js";
5
+ import { applyThemeToRootWith, applyWithoutTransitions } from "./theme-document-core.js";
6
+ import { localStoragePersistence, readPersisted, writePersisted } from "./theme-persistence.js";
7
+ import { createEffect, createMemo, createSignal, onCleanup, splitProps, untrack } from "solid-js";
8
+ //#region src/components/theme/theme-provider-value.ts
9
+ /** The operating system's Reduce Transparency query, where there is a window to ask. */
10
+ function reducedTransparencyMedia() {
11
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(REDUCED_TRANSPARENCY_QUERY) : void 0;
12
+ }
13
+ /**
14
+ * Everything the provider is, as a value: the context object, with its effects and
15
+ * listeners created in the caller's reactive scope.
16
+ *
17
+ * Parameterised by where its catalogue comes from: `ThemeProvider`
18
+ * (`theme-provider.tsx`) and the lazy `ThemeProvider` (`theme-provider-lazy.tsx`) wrap
19
+ * this with a source. It imports no catalogue code or data, which is what lets the lazy
20
+ * one keep them out of a consumer's start-up chunk. It is not JSX so that the unit lane,
21
+ * which has no Solid compiler, can drive it directly.
22
+ */
23
+ function createThemeContextValue(props) {
24
+ const [local] = splitProps(props, [
25
+ "catalogue",
26
+ "storageKey",
27
+ "initial",
28
+ "selection",
29
+ "persistence",
30
+ "themes",
31
+ "transformTokens",
32
+ "disableTransitionOnChange",
33
+ "accentContrastFloor",
34
+ "surface",
35
+ "palette",
36
+ "editorRoles",
37
+ "fontZoom"
38
+ ]);
39
+ const storageKey = () => local.storageKey ?? "adea-appearance";
40
+ const persistence = local.persistence ?? localStoragePersistence(storageKey);
41
+ const extraThemes = createMemo(() => typeof local.themes === "function" ? local.themes() : local.themes ?? []);
42
+ const source = () => local.catalogue;
43
+ const engine = () => source().engine();
44
+ const registry = createMemo(() => mergeRegistry(engine().builtinThemes, extraThemes()));
45
+ const [selection, setSelectionState] = createSignal(initialThemeSelection(readPersisted(persistence), local.initial));
46
+ createEffect(() => {
47
+ const incoming = local.selection;
48
+ if (incoming) setSelectionState((current) => mergeThemeSelection(current, incoming));
49
+ });
50
+ const [systemAppearance, setSystemAppearance] = createSignal("light");
51
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
52
+ const query = window.matchMedia("(prefers-color-scheme: dark)");
53
+ setSystemAppearance(query.matches ? "dark" : "light");
54
+ const onChange = (event) => setSystemAppearance(event.matches ? "dark" : "light");
55
+ query.addEventListener("change", onChange);
56
+ onCleanup(() => query.removeEventListener("change", onChange));
57
+ }
58
+ const committed = createMemo(() => resolveThemeState(selection(), systemAppearance(), registry()));
59
+ const resolvedAppearance = createMemo(() => committed().appearance);
60
+ const variant = createMemo(() => committed().variant);
61
+ const [draft, setDraft] = createSignal();
62
+ const shownSelection = createMemo(() => {
63
+ const preview = draft();
64
+ return preview ? mergeThemeSelection(selection(), preview) : selection();
65
+ });
66
+ const shown = createMemo(() => draft() ? resolveThemeState(shownSelection(), systemAppearance(), registry()) : committed());
67
+ const surfaceOn = (candidate) => local.surface === true || typeof local.surface === "object" || candidate.surface !== void 0;
68
+ const nativeTranslucency = () => {
69
+ const capability = typeof local.surface === "object" ? local.surface.nativeTranslucency : false;
70
+ return (typeof capability === "function" ? capability() : capability) === true;
71
+ };
72
+ const [osReducedTransparency, setOsReducedTransparency] = createSignal(surfaceOn(untrack(shownSelection)) ? reducedTransparencyMedia()?.matches ?? false : false);
73
+ const axisOn = createMemo(() => surfaceOn(shownSelection()));
74
+ createEffect(() => {
75
+ const query = axisOn() ? reducedTransparencyMedia() : void 0;
76
+ setOsReducedTransparency(query?.matches ?? false);
77
+ if (!query) return;
78
+ const onChange = (event) => setOsReducedTransparency(event.matches);
79
+ query.addEventListener("change", onChange);
80
+ onCleanup(() => query.removeEventListener("change", onChange));
81
+ });
82
+ const surfaceStateOf = (candidate) => resolveSurfaceState(candidate.surface, {
83
+ osReducedTransparency: osReducedTransparency(),
84
+ userReducedTransparency: candidate.reduceTransparency === true,
85
+ nativeTranslucency: nativeTranslucency()
86
+ });
87
+ const committedSurface = createMemo(() => surfaceOn(selection()) ? surfaceStateOf(selection()) : void 0);
88
+ const waitingFor = (candidate, appearance) => !source().loaded() && !source().failed() && needsCatalogue(candidate, appearance);
89
+ createEffect(() => {
90
+ if (needsCatalogue(selection(), committed().appearance) || needsCatalogue(shownSelection(), shown().appearance)) source().load();
91
+ });
92
+ let written;
93
+ let restoreTransitions;
94
+ createEffect(() => {
95
+ if (typeof document === "undefined") return;
96
+ const current = engine();
97
+ const display = shown();
98
+ if (waitingFor(shownSelection(), display.appearance)) return;
99
+ const state = {
100
+ selection: shownSelection(),
101
+ appearance: display.appearance,
102
+ variant: display.variant,
103
+ terminalVariant: resolveTerminalVariant(shownSelection().terminalThemeId, registry()),
104
+ surface: axisOn() ? surfaceStateOf(shownSelection()) : void 0
105
+ };
106
+ const options = {
107
+ transformTokens: local.transformTokens,
108
+ accentContrastFloor: local.accentContrastFloor,
109
+ palette: local.palette,
110
+ editorRoles: local.editorRoles
111
+ };
112
+ const apply = () => {
113
+ try {
114
+ written = applyThemeToRootWith(current, document.documentElement, state, options, written);
115
+ } catch (error) {
116
+ if (!(error instanceof CatalogueRequired)) throw error;
117
+ }
118
+ };
119
+ if (local.disableTransitionOnChange) {
120
+ restoreTransitions?.();
121
+ restoreTransitions = applyWithoutTransitions(document, apply);
122
+ } else apply();
123
+ });
124
+ onCleanup(() => restoreTransitions?.());
125
+ const themeAccents = createMemo(() => {
126
+ try {
127
+ return engine().themeAccentsFor(selection().lightThemeId, selection().darkThemeId);
128
+ } catch (error) {
129
+ if (!(error instanceof CatalogueRequired)) throw error;
130
+ return [];
131
+ }
132
+ });
133
+ const zoomFonts = (intent) => value.setSelection({ fonts: zoomedFontSettings(selection(), intent) });
134
+ const onKeyDown = (event) => {
135
+ if (event.defaultPrevented) return;
136
+ const intent = fontZoomShortcut(event);
137
+ if (!intent) return;
138
+ event.preventDefault();
139
+ zoomFonts(intent);
140
+ };
141
+ createEffect(() => {
142
+ if (!local.fontZoom || typeof window === "undefined") return;
143
+ window.addEventListener("keydown", onKeyDown, { capture: true });
144
+ onCleanup(() => window.removeEventListener("keydown", onKeyDown, { capture: true }));
145
+ });
146
+ const value = {
147
+ selection,
148
+ resolvedAppearance,
149
+ variant,
150
+ get themes() {
151
+ return engine().builtinThemes;
152
+ },
153
+ themeAccents,
154
+ allThemes: registry,
155
+ preview(next) {
156
+ setDraft(next ? { ...next } : void 0);
157
+ },
158
+ previewing: () => draft() !== void 0,
159
+ accentRolesFor(accent, variantId) {
160
+ const current = engine();
161
+ const state = {
162
+ selection: selection(),
163
+ variant: variant(),
164
+ registry: registry(),
165
+ accentContrastFloor: local.accentContrastFloor
166
+ };
167
+ try {
168
+ return accentRolesForStateWith(current, accent, variantId, state);
169
+ } catch (error) {
170
+ if (!(error instanceof CatalogueRequired)) throw error;
171
+ source().load();
172
+ try {
173
+ return accentRolesForStateWith({ accentRolesForSelection: (id, appearance, pair, options) => current.accentRolesForSelection(id, appearance, pair, {
174
+ ...options,
175
+ variantId: void 0
176
+ }) }, accent, variantId, state);
177
+ } catch (inner) {
178
+ if (!(inner instanceof CatalogueRequired)) throw inner;
179
+ return;
180
+ }
181
+ }
182
+ },
183
+ catalogueReady: () => source().loaded(),
184
+ loadCatalogue: () => source().load(),
185
+ effectiveSurface: () => committedSurface()?.surface ?? "opaque",
186
+ reduceTransparencyActive: () => committedSurface()?.reduceTransparencyActive ?? false,
187
+ zoomFonts,
188
+ setSelection(patch) {
189
+ const next = mergeThemeSelection(selection(), patch);
190
+ setSelectionState(next);
191
+ setDraft(void 0);
192
+ writePersisted(persistence, next);
193
+ }
194
+ };
195
+ return value;
196
+ }
197
+ //#endregion
198
+ export { createThemeContextValue };
199
+
200
+ //# sourceMappingURL=theme-provider-value.js.map