@adea-ai/ui 0.125.0 → 0.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/NOTICE +40 -0
  2. package/dist/components/theme/index.d.ts +1 -1
  3. package/dist/components/theme/index.d.ts.map +1 -1
  4. package/dist/components/theme/index.js +4 -3
  5. package/dist/components/theme/theme-document.d.ts +25 -1
  6. package/dist/components/theme/theme-document.d.ts.map +1 -1
  7. package/dist/components/theme/theme-document.js +18 -3
  8. package/dist/components/theme/theme-document.js.map +1 -1
  9. package/dist/components/theme/theme-persistence.d.ts +87 -0
  10. package/dist/components/theme/theme-persistence.d.ts.map +1 -1
  11. package/dist/components/theme/theme-persistence.js +63 -2
  12. package/dist/components/theme/theme-persistence.js.map +1 -1
  13. package/dist/components/theme/theme-provider.d.ts +40 -40
  14. package/dist/components/theme/theme-provider.d.ts.map +1 -1
  15. package/dist/components/theme/theme-provider.js +57 -52
  16. package/dist/components/theme/theme-provider.js.map +1 -1
  17. package/dist/components/theme/theme-script.d.ts +100 -0
  18. package/dist/components/theme/theme-script.d.ts.map +1 -0
  19. package/dist/components/theme/theme-script.js +57 -0
  20. package/dist/components/theme/theme-script.js.map +1 -0
  21. package/dist/components/theme/theme-state.d.ts +58 -0
  22. package/dist/components/theme/theme-state.d.ts.map +1 -1
  23. package/dist/components/theme/theme-state.js +22 -1
  24. package/dist/components/theme/theme-state.js.map +1 -1
  25. package/dist/index.d.ts +4 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +8 -4
  28. package/dist/lib/font-zoom.d.ts +25 -0
  29. package/dist/lib/font-zoom.d.ts.map +1 -0
  30. package/dist/lib/font-zoom.js +45 -0
  31. package/dist/lib/font-zoom.js.map +1 -0
  32. package/dist/lib/preference-store.d.ts +126 -0
  33. package/dist/lib/preference-store.d.ts.map +1 -0
  34. package/dist/lib/preference-store.js +126 -0
  35. package/dist/lib/preference-store.js.map +1 -0
  36. package/dist/lib/theme-surface.d.ts +71 -0
  37. package/dist/lib/theme-surface.d.ts.map +1 -0
  38. package/dist/lib/theme-surface.js +62 -0
  39. package/dist/lib/theme-surface.js.map +1 -0
  40. package/dist/lib/themes.d.ts +9 -0
  41. package/dist/lib/themes.d.ts.map +1 -1
  42. package/dist/lib/themes.js +12 -1
  43. package/dist/lib/themes.js.map +1 -1
  44. package/dist/lib/tokens.d.ts.map +1 -1
  45. package/dist/lib/tokens.js +15 -0
  46. package/dist/lib/tokens.js.map +1 -1
  47. package/dist/r/lib.json +15 -0
  48. package/dist/r/registry.json +20 -0
  49. package/dist/r/src/components/theme/index.ts +6 -0
  50. package/dist/r/src/components/theme/theme-document.ts +51 -2
  51. package/dist/r/src/components/theme/theme-persistence.ts +167 -0
  52. package/dist/r/src/components/theme/theme-provider.tsx +115 -50
  53. package/dist/r/src/components/theme/theme-script.ts +201 -0
  54. package/dist/r/src/components/theme/theme-state.ts +72 -0
  55. package/dist/r/src/lib/font-zoom.ts +74 -0
  56. package/dist/r/src/lib/preference-store.ts +216 -0
  57. package/dist/r/src/lib/theme-surface.ts +110 -0
  58. package/dist/r/src/lib/themes.ts +14 -0
  59. package/dist/r/src/lib/tokens.ts +17 -0
  60. package/dist/r/src/styles/theme.css +38 -0
  61. package/dist/r/theming.json +5 -0
  62. package/package.json +1 -1
  63. package/registry.json +20 -0
  64. package/src/components/theme/index.ts +6 -0
  65. package/src/components/theme/theme-document.ts +51 -2
  66. package/src/components/theme/theme-persistence.ts +167 -0
  67. package/src/components/theme/theme-provider.tsx +115 -50
  68. package/src/components/theme/theme-script.ts +201 -0
  69. package/src/components/theme/theme-state.ts +72 -0
  70. package/src/index.ts +26 -0
  71. package/src/lib/font-zoom.ts +74 -0
  72. package/src/lib/preference-store.ts +216 -0
  73. package/src/lib/theme-surface.ts +110 -0
  74. package/src/lib/themes.ts +14 -0
  75. package/src/lib/tokens.ts +17 -0
  76. package/src/styles/theme.css +38 -0
@@ -1 +1 @@
1
- {"version":3,"file":"theme-state.js","names":[],"sources":["../../../src/components/theme/theme-state.ts"],"sourcesContent":["import {\n DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS,\n normalizeAppearanceEditorFontSettings,\n type AppearanceEditorFontSettings,\n} from '#lib/appearance-font-settings'\nimport type { AccentPreset } from '@adea-ai/themes'\nimport { normalizeCustomAccent, type AccentRoles } from '#lib/accent-roles'\nimport {\n accentRolesForSelection,\n defaultDarkThemeId,\n defaultLightThemeId,\n resolveVariant,\n type ThemeAppearance,\n type ThemeVariant,\n} from '#lib/themes'\n\n/**\n * The pure half of the theme provider: what a selection is, how two selections\n * merge, and what a selection resolves to.\n *\n * Nothing here touches the document, storage or a signal, so each rule can be\n * tested by calling it. `ThemeProvider` owns the reactivity and wires these to it;\n * `theme-document.ts` owns the writes; `theme-persistence.ts` owns the store.\n */\n\n/**\n * Four independent choices, and keeping them independent is the point:\n *\n * - **appearance** — light, dark, or follow the system.\n * - **a theme per appearance** — so a user can run Catppuccin Mocha by night and\n * Solarized Light by day without re-choosing when the system flips.\n * - **an accent** — adea's six presets, or one of the accents the selected theme\n * pair carries itself (`ansi-blue`…), which re-colour the interactive roles on\n * top of whichever theme is active.\n * - **fonts** — separate family and size choices for UI, content and code.\n */\nexport type ThemeSelection = {\n /** `system` follows `prefers-color-scheme` and updates when it changes. */\n appearance: ThemeAppearance | 'system'\n /** The variant to use when the resolved appearance is light. */\n lightThemeId: string\n /** The variant to use when the resolved appearance is dark. */\n darkThemeId: string\n /**\n * An `accentPresets` id, a theme accent id from `themeAccents()` (`ansi-blue`…),\n * `theme` for the variant's own primary, or a custom colour as `#rrggbb`. A theme\n * accent the current pair does not offer falls back to the variant's own primary.\n *\n * A custom colour is stored lower case and is raised to the contrast floor when it\n * is painted, never when it is stored: the selection keeps what the user chose.\n * Anything else that is not an id is dropped at paint, not rejected here.\n */\n accent: string\n /** UI-family alias for consumers of the original single-font preference. */\n font: string\n /** UI, content and code font settings. Omitted legacy records are migrated from `font`. */\n fonts?: AppearanceEditorFontSettings\n /**\n * `comfortable` is the system's default geometry; `compact` moves the control\n * ladder down one rung.\n *\n * Optional, and defaulted on read, so a preference stored before density existed\n * keeps working and `ThemeSelection` stays assignable without it.\n */\n density?: ThemeDensity\n}\n\n/**\n * The two density rungs.\n *\n * `comfortable` is the geometry in `theme.css`, so it is the absence of an\n * attribute rather than a value: a document that ships no `data-density` at all\n * renders correctly, which is what keeps this additive.\n */\nexport type ThemeDensity = 'comfortable' | 'compact'\n\n/**\n * Deliberately lists only the fields every consumer has always had. A field added\n * to {@link ThemeSelection} later gets no default here: `JSON.stringify` drops an\n * `undefined`, so a store written by a consumer that never sets the field stays\n * byte-identical to what it was before the field existed.\n */\nexport const defaultThemeSelection: ThemeSelection = {\n appearance: 'system',\n lightThemeId: defaultLightThemeId,\n darkThemeId: defaultDarkThemeId,\n accent: 'theme',\n font: 'system',\n fonts: DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS,\n density: 'comfortable',\n}\n\n/**\n * A stored selection's density, defaulted.\n *\n * The stored value is read through here rather than trusted, because a preference\n * written before density existed has no `density` key and a hand-edited store may\n * hold anything.\n */\nexport function resolvedDensity(selection: ThemeSelection): ThemeDensity {\n return selection.density === 'compact' ? 'compact' : 'comfortable'\n}\n\nexport type ThemeContextValue = {\n /** The preference as stored. A {@link ThemeContextValue.preview} does not change it. */\n selection: () => ThemeSelection\n /** What the preference resolves to right now, after `system`. */\n resolvedAppearance: () => ThemeAppearance\n /** The variant in use. */\n variant: () => ThemeVariant\n /** Update part of the preference. */\n setSelection: (patch: Partial<ThemeSelection>) => void\n /**\n * The built-in catalogue, so a picker does not import it separately. A plain\n * array: {@link ThemeContextValue.allThemes} is the one that follows a host's\n * extra themes.\n */\n themes: readonly ThemeVariant[]\n /**\n * The accents the selected light/dark pair carries, offered beside the presets.\n * Follows the selection: a different theme pair may offer different slots.\n */\n themeAccents: () => readonly AccentPreset[]\n /**\n * The built-in catalogue followed by the provider's `themes` prop. Reactive when\n * that prop is a function that reads signals.\n */\n allThemes: () => readonly ThemeVariant[]\n /**\n * Paint the document from `draft` laid over the stored selection, without\n * persisting it or changing what {@link ThemeContextValue.selection} returns.\n * `undefined` ends the preview. A {@link ThemeContextValue.setSelection} ends it\n * too, because the committed selection is then what should be showing.\n */\n preview: (draft: Partial<ThemeSelection> | undefined) => void\n /** True while a {@link ThemeContextValue.preview} draft is on the document. */\n previewing: () => boolean\n /**\n * The roles `accent` paints over `variantId` (default: the variant in use), held\n * to the provider's `accentContrastFloor`, for an element-scoped override with\n * `paintAccentRoles` — a workspace that carries its own colour. `undefined` for\n * `null`, for `theme`, for an id that resolves to nothing, and for a variant the\n * registry does not hold: each means \"keep the inherited accent\".\n */\n accentRolesFor: (accent: string | null, variantId?: string) => AccentRoles | undefined\n}\n\n/** A selection with the `undefined` keys of a patch left out. */\nfunction definedOnly<T extends object>(value: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined)\n ) as Partial<T>\n}\n\n/** Keep the deprecated single-font view synchronized with the UI axis. */\nexport function mergeThemeSelection(\n base: ThemeSelection,\n patch: Partial<ThemeSelection>\n): ThemeSelection {\n const merged = { ...base, ...patch }\n let fonts: AppearanceEditorFontSettings\n\n if (patch.fonts !== undefined) {\n fonts = normalizeAppearanceEditorFontSettings(patch.fonts).settings\n } else if (patch.font !== undefined) {\n const currentFonts = normalizeAppearanceEditorFontSettings(base.fonts, base.font).settings\n fonts = normalizeAppearanceEditorFontSettings({\n ...currentFonts,\n ui: { ...currentFonts.ui, family: patch.font },\n }).settings\n } else {\n fonts = normalizeAppearanceEditorFontSettings(merged.fonts, merged.font).settings\n }\n\n return {\n ...merged,\n // One spelling for a custom colour, so equal choices compare and store equal.\n accent: normalizeCustomAccent(merged.accent) ?? merged.accent,\n fonts,\n font: fonts.ui.family,\n }\n}\n\n/**\n * The selection a provider starts from: the defaults, then whatever the store\n * returned, then `initial`.\n *\n * `initial` wins over the store on every mount. That precedence is part of the\n * contract Cortana relies on (its `initial` pins `appearance` and `font`), and it\n * is why the store's answer is only the middle layer. A store that returned\n * nothing, or threw, or answered with explicit `undefined`s, leaves the defaults\n * standing for those fields.\n */\nexport function initialThemeSelection(\n stored: Partial<ThemeSelection> | undefined,\n initial: Partial<ThemeSelection> | undefined\n): ThemeSelection {\n const base = mergeThemeSelection(defaultThemeSelection, definedOnly(stored ?? {}))\n return mergeThemeSelection(base, initial ?? {})\n}\n\n/** `system` resolved against what the operating system reports. */\nexport function resolveAppearance(\n preference: ThemeSelection['appearance'],\n system: ThemeAppearance\n): ThemeAppearance {\n return preference === 'system' ? system : preference\n}\n\n/** The theme id the selection names for a resolved appearance. */\nexport function selectedThemeId(selection: ThemeSelection, appearance: ThemeAppearance): string {\n return appearance === 'dark' ? selection.darkThemeId : selection.lightThemeId\n}\n\n/**\n * What a selection paints: the resolved appearance and the variant for it.\n *\n * An id the registry does not hold resolves to the appearance's default rather than\n * being rewritten (see `resolveVariant`), so the selection a host stores is never\n * changed by being unresolvable.\n */\nexport function resolveThemeState(\n selection: ThemeSelection,\n system: ThemeAppearance,\n registry: readonly ThemeVariant[]\n): { appearance: ThemeAppearance; variant: ThemeVariant } {\n const appearance = resolveAppearance(selection.appearance, system)\n return {\n appearance,\n variant: resolveVariant(registry, selectedThemeId(selection, appearance), appearance),\n }\n}\n\n/**\n * The registry a provider resolves against: the catalogue, then the host's extras.\n *\n * The catalogue wins an id collision, because it is listed first and `resolveVariant`\n * takes the first match: a host's variant can add a theme but cannot shadow one.\n */\nexport function mergeRegistry(\n builtin: readonly ThemeVariant[],\n extra: readonly ThemeVariant[] | undefined\n): readonly ThemeVariant[] {\n return extra && extra.length > 0 ? [...builtin, ...extra] : builtin\n}\n\n/**\n * The roles an accent paints over a variant, for {@link ThemeContextValue.accentRolesFor}.\n *\n * Resolved against the committed selection's theme pair, so an `ansi-*` accent means\n * the same colour here as it does on `<html>`.\n */\nexport function accentRolesForState(\n accent: string | null,\n variantId: string | undefined,\n state: {\n selection: ThemeSelection\n variant: ThemeVariant\n registry: readonly ThemeVariant[]\n accentContrastFloor?: number\n }\n): AccentRoles | undefined {\n if (accent === null) return undefined\n const variant =\n variantId === undefined\n ? state.variant\n : state.registry.find((candidate) => candidate.id === variantId)\n if (!variant) return undefined\n return accentRolesForSelection(accent, variant.appearance, state.selection, {\n background: variant.colors.background,\n floor: state.accentContrastFloor,\n variantId: variant.id,\n })\n}\n"],"mappings":";;;;;;;;;;AAkFA,IAAa,wBAAwC;CACnD,YAAY;CACZ,cAAc;CACd,aAAa;CACb,QAAQ;CACR,MAAM;CACN,OAAO;CACP,SAAS;AACX;;;;;;;;AASA,SAAgB,gBAAgB,WAAyC;CACvE,OAAO,UAAU,YAAY,YAAY,YAAY;AACvD;;AA+CA,SAAS,YAA8B,OAAsB;CAC3D,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;;AAGA,SAAgB,oBACd,MACA,OACgB;CAChB,MAAM,SAAS;EAAE,GAAG;EAAM,GAAG;CAAM;CACnC,IAAI;CAEJ,IAAI,MAAM,UAAU,KAAA,GAClB,QAAQ,sCAAsC,MAAM,KAAK,CAAC,CAAC;MACtD,IAAI,MAAM,SAAS,KAAA,GAAW;EACnC,MAAM,eAAe,sCAAsC,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC;EAClF,QAAQ,sCAAsC;GAC5C,GAAG;GACH,IAAI;IAAE,GAAG,aAAa;IAAI,QAAQ,MAAM;GAAK;EAC/C,CAAC,CAAC,CAAC;CACL,OACE,QAAQ,sCAAsC,OAAO,OAAO,OAAO,IAAI,CAAC,CAAC;CAG3E,OAAO;EACL,GAAG;EAEH,QAAQ,sBAAsB,OAAO,MAAM,KAAK,OAAO;EACvD;EACA,MAAM,MAAM,GAAG;CACjB;AACF;;;;;;;;;;;AAYA,SAAgB,sBACd,QACA,SACgB;CAEhB,OAAO,oBADM,oBAAoB,uBAAuB,YAAY,UAAU,CAAC,CAAC,CACrD,GAAM,WAAW,CAAC,CAAC;AAChD;;AAGA,SAAgB,kBACd,YACA,QACiB;CACjB,OAAO,eAAe,WAAW,SAAS;AAC5C;;AAGA,SAAgB,gBAAgB,WAA2B,YAAqC;CAC9F,OAAO,eAAe,SAAS,UAAU,cAAc,UAAU;AACnE;;;;;;;;AASA,SAAgB,kBACd,WACA,QACA,UACwD;CACxD,MAAM,aAAa,kBAAkB,UAAU,YAAY,MAAM;CACjE,OAAO;EACL;EACA,SAAS,eAAe,UAAU,gBAAgB,WAAW,UAAU,GAAG,UAAU;CACtF;AACF;;;;;;;AAQA,SAAgB,cACd,SACA,OACyB;CACzB,OAAO,SAAS,MAAM,SAAS,IAAI,CAAC,GAAG,SAAS,GAAG,KAAK,IAAI;AAC9D;;;;;;;AAQA,SAAgB,oBACd,QACA,WACA,OAMyB;CACzB,IAAI,WAAW,MAAM,OAAO,KAAA;CAC5B,MAAM,UACJ,cAAc,KAAA,IACV,MAAM,UACN,MAAM,SAAS,MAAM,cAAc,UAAU,OAAO,SAAS;CACnE,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,OAAO,wBAAwB,QAAQ,QAAQ,YAAY,MAAM,WAAW;EAC1E,YAAY,QAAQ,OAAO;EAC3B,OAAO,MAAM;EACb,WAAW,QAAQ;CACrB,CAAC;AACH"}
1
+ {"version":3,"file":"theme-state.js","names":[],"sources":["../../../src/components/theme/theme-state.ts"],"sourcesContent":["import {\n DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS,\n normalizeAppearanceEditorFontSettings,\n type AppearanceEditorFontSettings,\n} from '#lib/appearance-font-settings'\nimport type { AccentPreset } from '@adea-ai/themes'\nimport { normalizeCustomAccent, type AccentRoles } from '#lib/accent-roles'\nimport { stepAppearanceFontSizes, type FontZoomIntent } from '#lib/font-zoom'\nimport type { ThemeSurface } from '#lib/theme-surface'\nimport {\n accentRolesForSelection,\n defaultDarkThemeId,\n defaultLightThemeId,\n resolveVariant,\n type ThemeAppearance,\n type ThemeVariant,\n} from '#lib/themes'\n\n/**\n * The pure half of the theme provider: what a selection is, how two selections\n * merge, and what a selection resolves to.\n *\n * Nothing here touches the document, storage or a signal, so each rule can be\n * tested by calling it. `ThemeProvider` owns the reactivity and wires these to it;\n * `theme-document.ts` owns the writes; `theme-persistence.ts` owns the store.\n */\n\n/**\n * Four independent choices, and keeping them independent is the point:\n *\n * - **appearance** — light, dark, or follow the system.\n * - **a theme per appearance** — so a user can run Catppuccin Mocha by night and\n * Solarized Light by day without re-choosing when the system flips.\n * - **an accent** — adea's six presets, or one of the accents the selected theme\n * pair carries itself (`ansi-blue`…), which re-colour the interactive roles on\n * top of whichever theme is active.\n * - **fonts** — separate family and size choices for UI, content and code.\n */\nexport type ThemeSelection = {\n /** `system` follows `prefers-color-scheme` and updates when it changes. */\n appearance: ThemeAppearance | 'system'\n /** The variant to use when the resolved appearance is light. */\n lightThemeId: string\n /** The variant to use when the resolved appearance is dark. */\n darkThemeId: string\n /**\n * An `accentPresets` id, a theme accent id from `themeAccents()` (`ansi-blue`…),\n * `theme` for the variant's own primary, or a custom colour as `#rrggbb`. A theme\n * accent the current pair does not offer falls back to the variant's own primary.\n *\n * A custom colour is stored lower case and is raised to the contrast floor when it\n * is painted, never when it is stored: the selection keeps what the user chose.\n * Anything else that is not an id is dropped at paint, not rejected here.\n */\n accent: string\n /** UI-family alias for consumers of the original single-font preference. */\n font: string\n /** UI, content and code font settings. Omitted legacy records are migrated from `font`. */\n fonts?: AppearanceEditorFontSettings\n /**\n * `comfortable` is the system's default geometry; `compact` moves the control\n * ladder down one rung.\n *\n * Optional, and defaulted on read, so a preference stored before density existed\n * keeps working and `ThemeSelection` stays assignable without it.\n */\n density?: ThemeDensity\n /**\n * The terminal either follows the interface theme (`theme`, or absent) or is pinned\n * to another theme's terminal palette by that theme's id. The pin is written over\n * the interface variant's own `--terminal-*` properties, so a light interface can\n * carry a dark terminal.\n *\n * An id the registry does not hold paints the interface palette and stays in the\n * selection, the same rule as an unknown theme id.\n *\n * Optional and not defaulted: see {@link defaultThemeSelection}.\n */\n terminalThemeId?: string\n /**\n * How opaque elevated panels are. Written to `<html>` only when the provider's\n * `surface` prop is on or this is set; absent, a host paints no surface axis at\n * all. Resolved against the OS and the host's capability before it is painted —\n * see `resolveSurface` — so the value stored is a preference, never the outcome.\n */\n surface?: ThemeSurface\n /**\n * The user's own Reduce Transparency switch, which forces the `opaque` surface the\n * same way the operating system's setting does.\n */\n reduceTransparency?: boolean\n}\n\n/**\n * The two density rungs.\n *\n * `comfortable` is the geometry in `theme.css`, so it is the absence of an\n * attribute rather than a value: a document that ships no `data-density` at all\n * renders correctly, which is what keeps this additive.\n */\nexport type ThemeDensity = 'comfortable' | 'compact'\n\n/**\n * Deliberately lists only the fields every consumer has always had. A field added\n * to {@link ThemeSelection} later gets no default here: `JSON.stringify` drops an\n * `undefined`, so a store written by a consumer that never sets the field stays\n * byte-identical to what it was before the field existed.\n */\nexport const defaultThemeSelection: ThemeSelection = {\n appearance: 'system',\n lightThemeId: defaultLightThemeId,\n darkThemeId: defaultDarkThemeId,\n accent: 'theme',\n font: 'system',\n fonts: DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS,\n density: 'comfortable',\n}\n\n/**\n * A stored selection's density, defaulted.\n *\n * The stored value is read through here rather than trusted, because a preference\n * written before density existed has no `density` key and a hand-edited store may\n * hold anything.\n */\nexport function resolvedDensity(selection: ThemeSelection): ThemeDensity {\n return selection.density === 'compact' ? 'compact' : 'comfortable'\n}\n\nexport type ThemeContextValue = {\n /** The preference as stored. A {@link ThemeContextValue.preview} does not change it. */\n selection: () => ThemeSelection\n /** What the preference resolves to right now, after `system`. */\n resolvedAppearance: () => ThemeAppearance\n /** The variant in use. */\n variant: () => ThemeVariant\n /** Update part of the preference. */\n setSelection: (patch: Partial<ThemeSelection>) => void\n /**\n * The built-in catalogue, so a picker does not import it separately. A plain\n * array: {@link ThemeContextValue.allThemes} is the one that follows a host's\n * extra themes.\n */\n themes: readonly ThemeVariant[]\n /**\n * The accents the selected light/dark pair carries, offered beside the presets.\n * Follows the selection: a different theme pair may offer different slots.\n */\n themeAccents: () => readonly AccentPreset[]\n /**\n * The built-in catalogue followed by the provider's `themes` prop. Reactive when\n * that prop is a function that reads signals.\n */\n allThemes: () => readonly ThemeVariant[]\n /**\n * Paint the document from `draft` laid over the stored selection, without\n * persisting it or changing what {@link ThemeContextValue.selection} returns.\n * `undefined` ends the preview. A {@link ThemeContextValue.setSelection} ends it\n * too, because the committed selection is then what should be showing.\n */\n preview: (draft: Partial<ThemeSelection> | undefined) => void\n /** True while a {@link ThemeContextValue.preview} draft is on the document. */\n previewing: () => boolean\n /**\n * The roles `accent` paints over `variantId` (default: the variant in use), held\n * to the provider's `accentContrastFloor`, for an element-scoped override with\n * `paintAccentRoles` — a workspace that carries its own colour. `undefined` for\n * `null`, for `theme`, for an id that resolves to nothing, and for a variant the\n * registry does not hold: each means \"keep the inherited accent\".\n */\n accentRolesFor: (accent: string | null, variantId?: string) => AccentRoles | undefined\n /**\n * The surface the committed selection paints, after Reduce Transparency and the\n * host's translucency capability. `opaque` when the axis is off. Follows the\n * committed selection, not a preview, like {@link ThemeContextValue.variant}.\n */\n effectiveSurface: () => ThemeSurface\n /**\n * True while the operating system or the selection's `reduceTransparency` forces\n * opaque surfaces. Always false when the surface axis is off, because nothing is\n * then listening to the operating system.\n */\n reduceTransparencyActive: () => boolean\n /**\n * Step the three text sizes together, or put them back to the defaults: what the\n * `fontZoom` prop's chords do, for a host that binds its own keys or a menu. Goes\n * through {@link ThemeContextValue.setSelection}, so it persists and ends a preview.\n */\n zoomFonts: (intent: FontZoomIntent) => void\n}\n\n/** A selection with the `undefined` keys of a patch left out. */\nfunction definedOnly<T extends object>(value: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined)\n ) as Partial<T>\n}\n\n/** Keep the deprecated single-font view synchronized with the UI axis. */\nexport function mergeThemeSelection(\n base: ThemeSelection,\n patch: Partial<ThemeSelection>\n): ThemeSelection {\n const merged = { ...base, ...patch }\n let fonts: AppearanceEditorFontSettings\n\n if (patch.fonts !== undefined) {\n fonts = normalizeAppearanceEditorFontSettings(patch.fonts).settings\n } else if (patch.font !== undefined) {\n const currentFonts = normalizeAppearanceEditorFontSettings(base.fonts, base.font).settings\n fonts = normalizeAppearanceEditorFontSettings({\n ...currentFonts,\n ui: { ...currentFonts.ui, family: patch.font },\n }).settings\n } else {\n fonts = normalizeAppearanceEditorFontSettings(merged.fonts, merged.font).settings\n }\n\n return {\n ...merged,\n // One spelling for a custom colour, so equal choices compare and store equal.\n accent: normalizeCustomAccent(merged.accent) ?? merged.accent,\n fonts,\n font: fonts.ui.family,\n }\n}\n\n/**\n * The selection a provider starts from: the defaults, then whatever the store\n * returned, then `initial`.\n *\n * `initial` wins over the store on every mount. That precedence is part of the\n * contract Cortana relies on (its `initial` pins `appearance` and `font`), and it\n * is why the store's answer is only the middle layer. A store that returned\n * nothing, or threw, or answered with explicit `undefined`s, leaves the defaults\n * standing for those fields.\n */\nexport function initialThemeSelection(\n stored: Partial<ThemeSelection> | undefined,\n initial: Partial<ThemeSelection> | undefined\n): ThemeSelection {\n const base = mergeThemeSelection(defaultThemeSelection, definedOnly(stored ?? {}))\n return mergeThemeSelection(base, initial ?? {})\n}\n\n/** `system` resolved against what the operating system reports. */\nexport function resolveAppearance(\n preference: ThemeSelection['appearance'],\n system: ThemeAppearance\n): ThemeAppearance {\n return preference === 'system' ? system : preference\n}\n\n/** The theme id the selection names for a resolved appearance. */\nexport function selectedThemeId(selection: ThemeSelection, appearance: ThemeAppearance): string {\n return appearance === 'dark' ? selection.darkThemeId : selection.lightThemeId\n}\n\n/**\n * What a selection paints: the resolved appearance and the variant for it.\n *\n * An id the registry does not hold resolves to the appearance's default rather than\n * being rewritten (see `resolveVariant`), so the selection a host stores is never\n * changed by being unresolvable.\n */\nexport function resolveThemeState(\n selection: ThemeSelection,\n system: ThemeAppearance,\n registry: readonly ThemeVariant[]\n): { appearance: ThemeAppearance; variant: ThemeVariant } {\n const appearance = resolveAppearance(selection.appearance, system)\n return {\n appearance,\n variant: resolveVariant(registry, selectedThemeId(selection, appearance), appearance),\n }\n}\n\n/**\n * The variant whose terminal palette is pinned, or `undefined` when the terminal\n * follows the interface theme.\n *\n * `theme`, an absent id and an id the registry does not hold all mean the interface\n * palette: a terminal must never resolve to a blank or half-painted palette because a\n * theme was deleted after it was pinned.\n */\nexport function resolveTerminalVariant(\n terminalThemeId: string | undefined,\n registry: readonly ThemeVariant[]\n): ThemeVariant | undefined {\n if (terminalThemeId === undefined || terminalThemeId === 'theme') return undefined\n return registry.find((candidate) => candidate.id === terminalThemeId)\n}\n\n/**\n * The font settings a zoom chord asks for: one pixel up or down on every axis, or\n * the defaults for `reset`.\n */\nexport function zoomedFontSettings(\n selection: ThemeSelection,\n intent: FontZoomIntent\n): AppearanceEditorFontSettings {\n if (intent === 'reset') return { ...DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS }\n return stepAppearanceFontSizes(selection.fonts, intent === 'in' ? 1 : -1)\n}\n\n/**\n * The registry a provider resolves against: the catalogue, then the host's extras.\n *\n * The catalogue wins an id collision, because it is listed first and `resolveVariant`\n * takes the first match: a host's variant can add a theme but cannot shadow one.\n */\nexport function mergeRegistry(\n builtin: readonly ThemeVariant[],\n extra: readonly ThemeVariant[] | undefined\n): readonly ThemeVariant[] {\n return extra && extra.length > 0 ? [...builtin, ...extra] : builtin\n}\n\n/**\n * The roles an accent paints over a variant, for {@link ThemeContextValue.accentRolesFor}.\n *\n * Resolved against the committed selection's theme pair, so an `ansi-*` accent means\n * the same colour here as it does on `<html>`.\n */\nexport function accentRolesForState(\n accent: string | null,\n variantId: string | undefined,\n state: {\n selection: ThemeSelection\n variant: ThemeVariant\n registry: readonly ThemeVariant[]\n accentContrastFloor?: number\n }\n): AccentRoles | undefined {\n if (accent === null) return undefined\n const variant =\n variantId === undefined\n ? state.variant\n : state.registry.find((candidate) => candidate.id === variantId)\n if (!variant) return undefined\n return accentRolesForSelection(accent, variant.appearance, state.selection, {\n background: variant.colors.background,\n floor: state.accentContrastFloor,\n variantId: variant.id,\n })\n}\n"],"mappings":";;;;;;;;;;;AA4GA,IAAa,wBAAwC;CACnD,YAAY;CACZ,cAAc;CACd,aAAa;CACb,QAAQ;CACR,MAAM;CACN,OAAO;CACP,SAAS;AACX;;;;;;;;AASA,SAAgB,gBAAgB,WAAyC;CACvE,OAAO,UAAU,YAAY,YAAY,YAAY;AACvD;;AAiEA,SAAS,YAA8B,OAAsB;CAC3D,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;;AAGA,SAAgB,oBACd,MACA,OACgB;CAChB,MAAM,SAAS;EAAE,GAAG;EAAM,GAAG;CAAM;CACnC,IAAI;CAEJ,IAAI,MAAM,UAAU,KAAA,GAClB,QAAQ,sCAAsC,MAAM,KAAK,CAAC,CAAC;MACtD,IAAI,MAAM,SAAS,KAAA,GAAW;EACnC,MAAM,eAAe,sCAAsC,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC;EAClF,QAAQ,sCAAsC;GAC5C,GAAG;GACH,IAAI;IAAE,GAAG,aAAa;IAAI,QAAQ,MAAM;GAAK;EAC/C,CAAC,CAAC,CAAC;CACL,OACE,QAAQ,sCAAsC,OAAO,OAAO,OAAO,IAAI,CAAC,CAAC;CAG3E,OAAO;EACL,GAAG;EAEH,QAAQ,sBAAsB,OAAO,MAAM,KAAK,OAAO;EACvD;EACA,MAAM,MAAM,GAAG;CACjB;AACF;;;;;;;;;;;AAYA,SAAgB,sBACd,QACA,SACgB;CAEhB,OAAO,oBADM,oBAAoB,uBAAuB,YAAY,UAAU,CAAC,CAAC,CACrD,GAAM,WAAW,CAAC,CAAC;AAChD;;AAGA,SAAgB,kBACd,YACA,QACiB;CACjB,OAAO,eAAe,WAAW,SAAS;AAC5C;;AAGA,SAAgB,gBAAgB,WAA2B,YAAqC;CAC9F,OAAO,eAAe,SAAS,UAAU,cAAc,UAAU;AACnE;;;;;;;;AASA,SAAgB,kBACd,WACA,QACA,UACwD;CACxD,MAAM,aAAa,kBAAkB,UAAU,YAAY,MAAM;CACjE,OAAO;EACL;EACA,SAAS,eAAe,UAAU,gBAAgB,WAAW,UAAU,GAAG,UAAU;CACtF;AACF;;;;;;;;;AAUA,SAAgB,uBACd,iBACA,UAC0B;CAC1B,IAAI,oBAAoB,KAAA,KAAa,oBAAoB,SAAS,OAAO,KAAA;CACzE,OAAO,SAAS,MAAM,cAAc,UAAU,OAAO,eAAe;AACtE;;;;;AAMA,SAAgB,mBACd,WACA,QAC8B;CAC9B,IAAI,WAAW,SAAS,OAAO,EAAE,GAAG,wCAAwC;CAC5E,OAAO,wBAAwB,UAAU,OAAO,WAAW,OAAO,IAAI,EAAE;AAC1E;;;;;;;AAQA,SAAgB,cACd,SACA,OACyB;CACzB,OAAO,SAAS,MAAM,SAAS,IAAI,CAAC,GAAG,SAAS,GAAG,KAAK,IAAI;AAC9D;;;;;;;AAQA,SAAgB,oBACd,QACA,WACA,OAMyB;CACzB,IAAI,WAAW,MAAM,OAAO,KAAA;CAC5B,MAAM,UACJ,cAAc,KAAA,IACV,MAAM,UACN,MAAM,SAAS,MAAM,cAAc,UAAU,OAAO,SAAS;CACnE,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,OAAO,wBAAwB,QAAQ,QAAQ,YAAY,MAAM,WAAW;EAC1E,YAAY,QAAQ,OAAO;EAC3B,OAAO,MAAM;EACb,WAAW,QAAQ;CACrB,CAAC;AACH"}
package/dist/index.d.ts CHANGED
@@ -105,7 +105,10 @@ export * from './components/composites/workspace-mark';
105
105
  export { cn } from './lib/utils';
106
106
  export { isCustomAccent, normalizeCustomAccent, paintAccentRoles, resolveAccentRoles, type AccentRoles, type AccentTarget, } from './lib/accent-roles';
107
107
  export { controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, surfaceInteractive, type ControlSize, type VariantProps, } from './lib/variants';
108
- export { appearancePreviewThemes, builtinThemes, contrastRatio, defaultDarkThemeId, defaultLightThemeId, isThemeAccentId, resolveAccentPreset, resolveVariant, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themesForAppearance, tokensAsHex, validateTheme, validateThemeRegistry, variantFromRecord, withAccent, type ThemeAppearance, type ThemeColors, type ThemeEditorPalette, type ThemeFinding, type ThemeProvenance, type ThemeTerminalPalette, type ThemeVariant, } from './lib/themes';
108
+ export { appearancePreviewThemes, builtinThemes, contrastRatio, defaultDarkThemeId, defaultLightThemeId, isThemeAccentId, resolveAccentPreset, resolveVariant, terminalCssVariables, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themesForAppearance, tokensAsHex, validateTheme, validateThemeRegistry, variantFromRecord, withAccent, type ThemeAppearance, type ThemeColors, type ThemeEditorPalette, type ThemeFinding, type ThemeProvenance, type ThemeTerminalPalette, type ThemeVariant, } from './lib/themes';
109
+ export { fontZoomShortcut, stepAppearanceFontSizes, type FontZoomIntent } from './lib/font-zoom';
110
+ export { REDUCED_TRANSPARENCY_QUERY, normalizeSurface, resolveSurface, resolveSurfaceState, surfaceAlpha, themeSurfaces, type SurfaceEnvironment, type SurfaceState, } from './lib/theme-surface';
111
+ export { defaultPreferenceStorage, quarantineRecord, readLegacyValue, readVersionedRecord, recoveryEnvelope, recoveryKeyFor, writeVersionedRecord, type PreferenceStorage, type RecoveryEnvelope, type RecoveryReason, type VersionedRead, type VersionedRecordSchema, } from './lib/preference-store';
109
112
  export { keyedRows, type KeyedRow } from './lib/keyed-rows';
110
113
  export { writeClipboardText } from './lib/clipboard';
111
114
  export { formatBytes, formatReleaseDate, plainTextFromMarkdown } from './lib/version-notes';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -105,14 +105,18 @@ import { fontOptions } from "./lib/font-catalog.js";
105
105
  import { accentPresets, allTokens, colorTokens, densityTokens, designTokens, elevationTokens, motionTokens, radiusTokens, typographyTokens, undocumentedTokenAliases, zIndexTokens } from "./lib/tokens.js";
106
106
  import { isCustomAccent, normalizeCustomAccent, paintAccentRoles, resolveAccentRoles } from "./lib/accent-roles.js";
107
107
  import { isThemeAccentId } from "./lib/theme-accents.js";
108
- import { appearancePreviewThemes, builtinThemes, contrastRatio, defaultDarkThemeId, defaultLightThemeId, resolveAccentPreset, resolveVariant, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themesForAppearance, tokensAsHex, validateTheme, validateThemeRegistry, variantFromRecord, withAccent } from "./lib/themes.js";
108
+ import { appearancePreviewThemes, builtinThemes, contrastRatio, defaultDarkThemeId, defaultLightThemeId, resolveAccentPreset, resolveVariant, terminalCssVariables, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themesForAppearance, tokensAsHex, validateTheme, validateThemeRegistry, variantFromRecord, withAccent } from "./lib/themes.js";
109
109
  import { APPEARANCE_EDITOR_FONT_AXES, APPEARANCE_EDITOR_FONT_SIZE_MAX, APPEARANCE_EDITOR_FONT_SIZE_MIN, DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS, applyAppearanceFontSettings, fontSettingsBootstrapScript, normalizeAppearanceEditorFontSettings, normalizeAppearanceEditorFontSize } from "./lib/appearance-font-settings.js";
110
110
  import { AppearanceFontSettingsGroup } from "./components/composites/appearance-editor/font-settings-group.js";
111
111
  import { ThemePicker, ThemePreview, ThemeSwatch } from "./components/theme/theme-picker.js";
112
+ import { fontZoomShortcut, stepAppearanceFontSizes } from "./lib/font-zoom.js";
113
+ import { REDUCED_TRANSPARENCY_QUERY, normalizeSurface, resolveSurface, resolveSurfaceState, surfaceAlpha, themeSurfaces } from "./lib/theme-surface.js";
112
114
  import { useOptionalTheme, useTheme } from "./components/theme/theme-context.js";
113
115
  import { defaultThemeSelection, resolvedDensity } from "./components/theme/theme-state.js";
114
- import { localStoragePersistence } from "./components/theme/theme-persistence.js";
115
- import { ThemeProvider, inlineScriptLiteral, themeScript } from "./components/theme/theme-provider.js";
116
+ import { defaultPreferenceStorage, quarantineRecord, readLegacyValue, readVersionedRecord, recoveryEnvelope, recoveryKeyFor, writeVersionedRecord } from "./lib/preference-store.js";
117
+ import { createVersionedPersistence, localStoragePersistence } from "./components/theme/theme-persistence.js";
118
+ import { inlineScriptLiteral, themeScript } from "./components/theme/theme-script.js";
119
+ import { ThemeProvider } from "./components/theme/theme-provider.js";
116
120
  import { AppearancePanel } from "./components/theme/appearance-panel.js";
117
121
  import { ThemeToggle } from "./components/theme/theme-toggle.js";
118
122
  import { ThemeModeToggle } from "./components/theme/theme-mode-toggle.js";
@@ -137,4 +141,4 @@ import { AboutDialog } from "./components/composites/about-dialog/about-dialog.j
137
141
  import { HelpCenter } from "./components/composites/help-center/help-center.js";
138
142
  import { WorkspaceMark } from "./components/composites/workspace-mark/workspace-mark.js";
139
143
  import { writeClipboardText } from "./lib/clipboard.js";
140
- export { APPEARANCE_EDITOR_FONT_AXES, APPEARANCE_EDITOR_FONT_SIZE_MAX, APPEARANCE_EDITOR_FONT_SIZE_MIN, AboutDialog, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, ActionButton, Alert, AlertAction, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnnotationSurface, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearanceEditorActions, AppearanceFontSettingsGroup, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, BoardCardTrigger, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, BusySendButton, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CatalogBrowser, CatalogDetail, CatalogDetailSection, ChatComposer, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandHint, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, CommandStatus, ComposerAttachmentButton, ComposerHint, ComposerMenu, ComposerMenuItem, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ContextualSidebar, ConversationAvatar, ConversationPane, ConversationSurface, DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, EntityIcon, FLOATING_PREVIEW_CORNER_RADIUS, FLOATING_PREVIEW_EDGE_GAP, Field, FieldDescription, FieldError, FieldGroup, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, FloatingPreview, FormField, Heading, HelpCenter, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputControl, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdChord, KbdGroup, Label, ListGroup, ListRow, ListRowControl, MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NO_FLOATING_PREVIEW_OBSTACLES, NO_PREVIEW_MINI_PLAYER_OBSTACLES, NativeSelect, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, NumberField, OrbitItem, OrbitLayout, PASTE_THRESHOLD_CHARS, PASTE_THRESHOLD_LINES, PASTE_TOKEN_REGEX, PREVIEW_MINI_PLAYER_CORNER_RADIUS, PREVIEW_MINI_PLAYER_EDGE_GAP, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, PixelResizeHandle, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, SceneControls, ScrollArea, SecretInputGroup, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsLayout, SettingsNavigation, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavLabel, SidebarNavResizeHandle, SidebarNavRow, SidebarNavSection, SidebarNavTitle, Skeleton, SkipLink, Slider, Spinner, SplitLayout, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, TextLink, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemeModeToggle, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, TranscriptComposition, Tree, TreeRow, TreeRowActions, UpdateChannelControl, UpdateDialog, ValueCombobox, VirtualWindow, WorkspaceMark, accentPresets, alertVariants, allTokens, appearancePreviewThemes, applyAppearanceFontSettings, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, cardVariants, clampFloatingPreviewPosition, clampPreviewMiniPlayerPosition, closePane, cn, colorTokens, computeLayoutFrames, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, countLeaves, countLines, createAppMenuItems, createLayoutState, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, expandAll, findTokenRanges, focusPane, fontOptions, fontSettingsBootstrapScript, formatBytes, formatReleaseDate, formatToken, headingElement, headingVariants, initialsFrom, inlineScriptLiteral, isCustomAccent, isPasteBlock, isThemeAccentId, itemVariants, kbdChordKeys, kbdGroupVariants, kbdVariants, keyedRows, layoutDepth, listLeaves, localStoragePersistence, monogram, motionTokens, movePane, neighborLeaf, nextSeq, normalizeAppearanceEditorFontSettings, normalizeAppearanceEditorFontSize, normalizeCustomAccent, normalizeLayout, paintAccentRoles, parseDiff, plainTextFromMarkdown, platformModifierKey, pruneBlocks, radiusTokens, recollapsePastes, remapCarriedBlocks, resizeFloatingPreview, resizePreviewMiniPlayer, resizeSplit, resolveAccentPreset, resolveAccentRoles, resolveFloatingPreviewFrame, resolvePreviewMiniPlayerFrame, resolveVariant, resolvedDensity, searchShortcutKeyshortcuts, searchShortcutLabel, sheetVariants, shouldCollapse, sidebarNavItemClass, sidebarNavItemStateClass, splitPane, splitPaneBalanced, statusDotVariants, stripTrailingBlankLines, surfaceInteractive, swapPanes, textElement, textVariants, textareaVariants, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themeScript, themesForAppearance, toast, toaster, toggleVariants, tokenRangeAt, tokensAsHex, transcriptDisclosureKey, typographyTokens, undoClosePane, undocumentedTokenAliases, useOptionalTheme, useSideRail, useTheme, validateTheme, validateThemeRegistry, variantFromRecord, withAccent, writeClipboardText, zIndexTokens };
144
+ export { APPEARANCE_EDITOR_FONT_AXES, APPEARANCE_EDITOR_FONT_SIZE_MAX, APPEARANCE_EDITOR_FONT_SIZE_MIN, AboutDialog, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, ActionButton, Alert, AlertAction, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnnotationSurface, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearanceEditorActions, AppearanceFontSettingsGroup, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, BoardCardTrigger, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, BusySendButton, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CatalogBrowser, CatalogDetail, CatalogDetailSection, ChatComposer, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandHint, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, CommandStatus, ComposerAttachmentButton, ComposerHint, ComposerMenu, ComposerMenuItem, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ContextualSidebar, ConversationAvatar, ConversationPane, ConversationSurface, DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, EntityIcon, FLOATING_PREVIEW_CORNER_RADIUS, FLOATING_PREVIEW_EDGE_GAP, Field, FieldDescription, FieldError, FieldGroup, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, FloatingPreview, FormField, Heading, HelpCenter, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputControl, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdChord, KbdGroup, Label, ListGroup, ListRow, ListRowControl, MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NO_FLOATING_PREVIEW_OBSTACLES, NO_PREVIEW_MINI_PLAYER_OBSTACLES, NativeSelect, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, NumberField, OrbitItem, OrbitLayout, PASTE_THRESHOLD_CHARS, PASTE_THRESHOLD_LINES, PASTE_TOKEN_REGEX, PREVIEW_MINI_PLAYER_CORNER_RADIUS, PREVIEW_MINI_PLAYER_EDGE_GAP, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, PixelResizeHandle, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, REDUCED_TRANSPARENCY_QUERY, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, SceneControls, ScrollArea, SecretInputGroup, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsLayout, SettingsNavigation, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavLabel, SidebarNavResizeHandle, SidebarNavRow, SidebarNavSection, SidebarNavTitle, Skeleton, SkipLink, Slider, Spinner, SplitLayout, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, TextLink, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemeModeToggle, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, TranscriptComposition, Tree, TreeRow, TreeRowActions, UpdateChannelControl, UpdateDialog, ValueCombobox, VirtualWindow, WorkspaceMark, accentPresets, alertVariants, allTokens, appearancePreviewThemes, applyAppearanceFontSettings, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, cardVariants, clampFloatingPreviewPosition, clampPreviewMiniPlayerPosition, closePane, cn, colorTokens, computeLayoutFrames, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, countLeaves, countLines, createAppMenuItems, createLayoutState, createVersionedPersistence, cva, defaultDarkThemeId, defaultLightThemeId, defaultPreferenceStorage, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, expandAll, findTokenRanges, focusPane, fontOptions, fontSettingsBootstrapScript, fontZoomShortcut, formatBytes, formatReleaseDate, formatToken, headingElement, headingVariants, initialsFrom, inlineScriptLiteral, isCustomAccent, isPasteBlock, isThemeAccentId, itemVariants, kbdChordKeys, kbdGroupVariants, kbdVariants, keyedRows, layoutDepth, listLeaves, localStoragePersistence, monogram, motionTokens, movePane, neighborLeaf, nextSeq, normalizeAppearanceEditorFontSettings, normalizeAppearanceEditorFontSize, normalizeCustomAccent, normalizeLayout, normalizeSurface, paintAccentRoles, parseDiff, plainTextFromMarkdown, platformModifierKey, pruneBlocks, quarantineRecord, radiusTokens, readLegacyValue, readVersionedRecord, recollapsePastes, recoveryEnvelope, recoveryKeyFor, remapCarriedBlocks, resizeFloatingPreview, resizePreviewMiniPlayer, resizeSplit, resolveAccentPreset, resolveAccentRoles, resolveFloatingPreviewFrame, resolvePreviewMiniPlayerFrame, resolveSurface, resolveSurfaceState, resolveVariant, resolvedDensity, searchShortcutKeyshortcuts, searchShortcutLabel, sheetVariants, shouldCollapse, sidebarNavItemClass, sidebarNavItemStateClass, splitPane, splitPaneBalanced, statusDotVariants, stepAppearanceFontSizes, stripTrailingBlankLines, surfaceAlpha, surfaceInteractive, swapPanes, terminalCssVariables, textElement, textVariants, textareaVariants, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themeScript, themeSurfaces, themesForAppearance, toast, toaster, toggleVariants, tokenRangeAt, tokensAsHex, transcriptDisclosureKey, typographyTokens, undoClosePane, undocumentedTokenAliases, useOptionalTheme, useSideRail, useTheme, validateTheme, validateThemeRegistry, variantFromRecord, withAccent, writeClipboardText, writeVersionedRecord, zIndexTokens };
@@ -0,0 +1,25 @@
1
+ import { type AppearanceEditorFontSettings } from '#lib/appearance-font-settings';
2
+ /** What the user's zoom chord asked for. */
3
+ export type FontZoomIntent = 'in' | 'out' | 'reset';
4
+ /**
5
+ * Classify the platform zoom chord: Cmd (macOS) or Ctrl (other platforms)
6
+ * with `=`/`+` zooming in, `-` zooming out, and `0` resetting to the System
7
+ * defaults. Unmodified keys, Alt-modified chords, and IME composition never
8
+ * count, so typing and text editing are untouched.
9
+ */
10
+ export declare function fontZoomShortcut(event: {
11
+ key: string;
12
+ metaKey: boolean;
13
+ ctrlKey: boolean;
14
+ altKey: boolean;
15
+ isComposing?: boolean;
16
+ }): FontZoomIntent | undefined;
17
+ /**
18
+ * Step every text tier by `delta` pixels, clamped to that range. The axes move
19
+ * together — that is what makes the chord read as zoom — and each keeps its
20
+ * own family choice.
21
+ */
22
+ export declare function stepAppearanceFontSizes(fonts: unknown, delta: 1 | -1): AppearanceEditorFontSettings;
23
+ /** Every axis name, for callers that want to report or test the stepping. */
24
+ export declare const FONT_ZOOM_AXES: readonly ["ui", "content", "code"];
25
+ //# sourceMappingURL=font-zoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-zoom.d.ts","sourceRoot":"","sources":["../../src/lib/font-zoom.ts"],"names":[],"mappings":"AAkBA,OAAO,EAKL,KAAK,4BAA4B,EAClC,MAAM,+BAA+B,CAAA;AAEtC,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,CAAA;AAEnD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,GAAG,cAAc,GAAG,SAAS,CAO7B;AAOD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,GACZ,4BAA4B,CAO9B;AAED,6EAA6E;AAC7E,eAAO,MAAM,cAAc,oCAA8B,CAAA"}
@@ -0,0 +1,45 @@
1
+ import { normalizeAppearanceEditorFontSettings } from "./appearance-font-settings.js";
2
+ //#region src/lib/font-zoom.ts
3
+ /**
4
+ * Classify the platform zoom chord: Cmd (macOS) or Ctrl (other platforms)
5
+ * with `=`/`+` zooming in, `-` zooming out, and `0` resetting to the System
6
+ * defaults. Unmodified keys, Alt-modified chords, and IME composition never
7
+ * count, so typing and text editing are untouched.
8
+ */
9
+ function fontZoomShortcut(event) {
10
+ if (event.isComposing || event.altKey) return void 0;
11
+ if (!(event.metaKey || event.ctrlKey)) return void 0;
12
+ if (event.key === "=" || event.key === "+") return "in";
13
+ if (event.key === "-") return "out";
14
+ if (event.key === "0") return "reset";
15
+ }
16
+ /** Clamp a pixel size to the shared range the appearance editor enforces. */
17
+ function clampFontSize(size) {
18
+ return Math.min(32, Math.max(10, size));
19
+ }
20
+ /**
21
+ * Step every text tier by `delta` pixels, clamped to that range. The axes move
22
+ * together — that is what makes the chord read as zoom — and each keeps its
23
+ * own family choice.
24
+ */
25
+ function stepAppearanceFontSizes(fonts, delta) {
26
+ const current = normalizeAppearanceEditorFontSettings(fonts).settings;
27
+ return {
28
+ ui: {
29
+ ...current.ui,
30
+ size: clampFontSize(current.ui.size + delta)
31
+ },
32
+ content: {
33
+ ...current.content,
34
+ size: clampFontSize(current.content.size + delta)
35
+ },
36
+ code: {
37
+ ...current.code,
38
+ size: clampFontSize(current.code.size + delta)
39
+ }
40
+ };
41
+ }
42
+ //#endregion
43
+ export { fontZoomShortcut, stepAppearanceFontSizes };
44
+
45
+ //# sourceMappingURL=font-zoom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-zoom.js","names":[],"sources":["../../src/lib/font-zoom.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Wing\n * Licensed under the MIT License.\n *\n * The platform zoom chords, mapped onto Adea's three text roles.\n *\n * macOS (and Windows/Linux) zoom shortcuts — Cmd/Ctrl with `=`, `+`, `-`, or\n * `0` — normally scale the browser's page zoom. In Adea every text element is\n * already tied to one of the three appearance font tiers (the UI tier drives\n * the whole `text-*` scale through `--font-ui-scale`; content and code roles\n * scale through their own tokens), so stepping those tiers reproduces zoom\n * while keeping the app inside its tokenized, persisted type system: the page\n * layout, menus, sheets, and terminals reflow live from the same projection\n * the appearance editor writes.\n *\n * Translated from Adea's accepted port (`packages/ui/src/components/font-zoom.ts`) so\n * the shared provider and Adea run one implementation. See NOTICE.\n */\nimport {\n APPEARANCE_EDITOR_FONT_AXES,\n APPEARANCE_EDITOR_FONT_SIZE_MAX,\n APPEARANCE_EDITOR_FONT_SIZE_MIN,\n normalizeAppearanceEditorFontSettings,\n type AppearanceEditorFontSettings,\n} from '#lib/appearance-font-settings'\n\n/** What the user's zoom chord asked for. */\nexport type FontZoomIntent = 'in' | 'out' | 'reset'\n\n/**\n * Classify the platform zoom chord: Cmd (macOS) or Ctrl (other platforms)\n * with `=`/`+` zooming in, `-` zooming out, and `0` resetting to the System\n * defaults. Unmodified keys, Alt-modified chords, and IME composition never\n * count, so typing and text editing are untouched.\n */\nexport function fontZoomShortcut(event: {\n key: string\n metaKey: boolean\n ctrlKey: boolean\n altKey: boolean\n isComposing?: boolean\n}): FontZoomIntent | undefined {\n if (event.isComposing || event.altKey) return undefined\n if (!(event.metaKey || event.ctrlKey)) return undefined\n if (event.key === '=' || event.key === '+') return 'in'\n if (event.key === '-') return 'out'\n if (event.key === '0') return 'reset'\n return undefined\n}\n\n/** Clamp a pixel size to the shared range the appearance editor enforces. */\nfunction clampFontSize(size: number): number {\n return Math.min(APPEARANCE_EDITOR_FONT_SIZE_MAX, Math.max(APPEARANCE_EDITOR_FONT_SIZE_MIN, size))\n}\n\n/**\n * Step every text tier by `delta` pixels, clamped to that range. The axes move\n * together — that is what makes the chord read as zoom — and each keeps its\n * own family choice.\n */\nexport function stepAppearanceFontSizes(\n fonts: unknown,\n delta: 1 | -1\n): AppearanceEditorFontSettings {\n const current = normalizeAppearanceEditorFontSettings(fonts).settings\n return {\n ui: { ...current.ui, size: clampFontSize(current.ui.size + delta) },\n content: { ...current.content, size: clampFontSize(current.content.size + delta) },\n code: { ...current.code, size: clampFontSize(current.code.size + delta) },\n }\n}\n\n/** Every axis name, for callers that want to report or test the stepping. */\nexport const FONT_ZOOM_AXES = APPEARANCE_EDITOR_FONT_AXES\n"],"mappings":";;;;;;;;AAmCA,SAAgB,iBAAiB,OAMF;CAC7B,IAAI,MAAM,eAAe,MAAM,QAAQ,OAAO,KAAA;CAC9C,IAAI,EAAE,MAAM,WAAW,MAAM,UAAU,OAAO,KAAA;CAC9C,IAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,KAAK,OAAO;CACnD,IAAI,MAAM,QAAQ,KAAK,OAAO;CAC9B,IAAI,MAAM,QAAQ,KAAK,OAAO;AAEhC;;AAGA,SAAS,cAAc,MAAsB;CAC3C,OAAO,KAAK,IAAA,IAAqC,KAAK,IAAA,IAAqC,IAAI,CAAC;AAClG;;;;;;AAOA,SAAgB,wBACd,OACA,OAC8B;CAC9B,MAAM,UAAU,sCAAsC,KAAK,CAAC,CAAC;CAC7D,OAAO;EACL,IAAI;GAAE,GAAG,QAAQ;GAAI,MAAM,cAAc,QAAQ,GAAG,OAAO,KAAK;EAAE;EAClE,SAAS;GAAE,GAAG,QAAQ;GAAS,MAAM,cAAc,QAAQ,QAAQ,OAAO,KAAK;EAAE;EACjF,MAAM;GAAE,GAAG,QAAQ;GAAM,MAAM,cAAc,QAAQ,KAAK,OAAO,KAAK;EAAE;CAC1E;AACF"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * A versioned preference store: read one JSON document, refuse to guess about what
3
+ * it cannot read, and keep what it refused.
4
+ *
5
+ * Nothing here knows what a preference *is*. A host hands the engine a key, a
6
+ * version and (through `createVersionedPersistence`) a codec; the engine owns only
7
+ * the part every versioned store gets wrong the same way, which is what happens to
8
+ * a document that is not the one the code expects:
9
+ *
10
+ * - A document that is not JSON, or not a record, or carries a version the code
11
+ * does not know (usually a newer build's) is **quarantined, byte for byte, into a
12
+ * recovery key** before anything else happens. The store then answers as if
13
+ * nothing were stored. Because the quarantine runs at read time, ahead of any
14
+ * write, saving valid preferences later cannot destroy the unread original.
15
+ * - A legacy key is consulted only when the main key is *absent*, never when it is
16
+ * unreadable: a corrupt main document is not "no preference", and falling back to
17
+ * an older, staler key would quietly restore a choice the user has since changed.
18
+ * The legacy key is never deleted.
19
+ * - Blocked storage (a sandboxed iframe, a privacy mode) throws on the mere access
20
+ * of the `localStorage` global. Every path here treats that as "nothing stored".
21
+ *
22
+ * The reads and writes are against a {@link PreferenceStorage}, so the rules run in a
23
+ * test without a DOM.
24
+ */
25
+ /** The part of `Storage` the engine uses. */
26
+ export type PreferenceStorage = Pick<Storage, 'getItem' | 'setItem'>;
27
+ /** Why an unread document was quarantined. */
28
+ export type RecoveryReason = 'corrupt_json' | 'unsupported_record';
29
+ /**
30
+ * What is written to the recovery key.
31
+ *
32
+ * The shape is a compatibility surface: Adea's stored envelopes already have it, and
33
+ * a tool that inspects a user's storage reads it. Do not rename a field.
34
+ */
35
+ export type RecoveryEnvelope = Readonly<{
36
+ schemaVersion: 1;
37
+ reason: RecoveryReason;
38
+ /** An ISO-8601 timestamp of the capture; refreshed by each read of the same bytes. */
39
+ capturedAt: string;
40
+ /** The document exactly as it was stored. */
41
+ raw: string;
42
+ }>;
43
+ /**
44
+ * The preference store, or nothing.
45
+ *
46
+ * The `typeof` guard sits inside the `try` for a reason: where storage is blocked,
47
+ * merely *reading* the `localStorage` global throws, so a guard in front of the `try`
48
+ * is itself the crash.
49
+ */
50
+ export declare function defaultPreferenceStorage(): PreferenceStorage | undefined;
51
+ /** The envelope for a document, stamped with `now`. */
52
+ export declare function recoveryEnvelope(raw: string, reason: RecoveryReason, now?: Date): RecoveryEnvelope;
53
+ /**
54
+ * Quarantine an unread document into `recoveryKey`.
55
+ *
56
+ * Idempotent, so re-reading the same unread value refreshes the capture without
57
+ * losing it. Best effort: a store that cannot be written leaves the caller to fail
58
+ * closed on the defaults, which is the same outcome as not having a recovery key at
59
+ * all. Never throws.
60
+ *
61
+ * Exported so a host quarantines its *other* documents (a theme library, say) with
62
+ * the same envelope. Give each document its own `recoveryKey`: two documents that
63
+ * share one overwrite each other's capture.
64
+ */
65
+ export declare function quarantineRecord(storage: PreferenceStorage, recoveryKey: string, raw: string, reason: RecoveryReason, now?: () => Date): void;
66
+ /** Where a versioned document lives and which version the code understands. */
67
+ export type VersionedRecordSchema = {
68
+ /** The storage key of the document. */
69
+ key: string;
70
+ /**
71
+ * The only `version` the code reads. Absent, the document is unversioned and any
72
+ * JSON object is accepted; present, a record whose top-level `version` is not
73
+ * exactly this number is unsupported (newer or older), not "close enough".
74
+ */
75
+ version?: number;
76
+ /** Where an unread document is kept. Default `${key}.recovery`. */
77
+ recoveryKey?: string;
78
+ };
79
+ /**
80
+ * What reading the document found.
81
+ *
82
+ * - `absent`: nothing is stored under the key.
83
+ * - `record`: a JSON object this code can read; `raw` is the stored text.
84
+ * - `quarantined`: something was stored and cannot be read. It has already been
85
+ * written to the recovery key.
86
+ * - `unavailable`: the store itself could not be read.
87
+ */
88
+ export type VersionedRead = {
89
+ status: 'absent';
90
+ } | {
91
+ status: 'record';
92
+ record: Readonly<Record<string, unknown>>;
93
+ raw: string;
94
+ } | {
95
+ status: 'quarantined';
96
+ reason: RecoveryReason;
97
+ raw: string;
98
+ } | {
99
+ status: 'unavailable';
100
+ };
101
+ /** The default recovery key for a document key. */
102
+ export declare function recoveryKeyFor(schema: VersionedRecordSchema): string;
103
+ /**
104
+ * Read the document under `schema.key`.
105
+ *
106
+ * The classification is the contract: a value that is not valid JSON, or is JSON but
107
+ * not an object (`null`, a number, a string), is `corrupt_json`; an object (or array)
108
+ * that is not a record of the supported version is `unsupported_record`. Both are
109
+ * quarantined before this returns. An empty string is a stored value, so it is
110
+ * corrupt rather than absent.
111
+ */
112
+ export declare function readVersionedRecord(storage: PreferenceStorage, schema: VersionedRecordSchema, now?: () => Date): VersionedRead;
113
+ /**
114
+ * The legacy single-key value, or `null`.
115
+ *
116
+ * Only the caller decides whether to look: the engine's rule is to ask when the main
117
+ * key is `absent`, not when it was quarantined.
118
+ */
119
+ export declare function readLegacyValue(storage: PreferenceStorage, legacyKey: string): string | null;
120
+ /**
121
+ * Write `document` to `key` as JSON. Returns whether it was stored; a full or blocked
122
+ * store returns `false` and never throws, because losing a preference must not break
123
+ * the switch that tried to save it.
124
+ */
125
+ export declare function writeVersionedRecord(storage: PreferenceStorage, key: string, document: unknown): boolean;
126
+ //# sourceMappingURL=preference-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preference-store.d.ts","sourceRoot":"","sources":["../../src/lib/preference-store.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,6CAA6C;AAC7C,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC,CAAA;AAEpE,8CAA8C;AAC9C,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,oBAAoB,CAAA;AAElE;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,aAAa,EAAE,CAAC,CAAA;IAChB,MAAM,EAAE,cAAc,CAAA;IACtB,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAA;CACZ,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,GAAG,SAAS,CAMxE;AAED,uDAAuD;AACvD,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,IAAiB,GACrB,gBAAgB,CAElB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAM,IAAuB,GACjC,IAAI,CAMN;AAED,+EAA+E;AAC/E,MAAM,MAAM,qBAAqB,GAAG;IAClC,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAE7B,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAEpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,qBAAqB,EAC7B,GAAG,CAAC,EAAE,MAAM,IAAI,GACf,aAAa,CA6Bf;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,iBAAiB,EAC1B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,OAAO,GAChB,OAAO,CAOT"}
@@ -0,0 +1,126 @@
1
+ //#region src/lib/preference-store.ts
2
+ /**
3
+ * The preference store, or nothing.
4
+ *
5
+ * The `typeof` guard sits inside the `try` for a reason: where storage is blocked,
6
+ * merely *reading* the `localStorage` global throws, so a guard in front of the `try`
7
+ * is itself the crash.
8
+ */
9
+ function defaultPreferenceStorage() {
10
+ try {
11
+ return typeof localStorage === "undefined" ? void 0 : localStorage;
12
+ } catch {
13
+ return;
14
+ }
15
+ }
16
+ /** The envelope for a document, stamped with `now`. */
17
+ function recoveryEnvelope(raw, reason, now = /* @__PURE__ */ new Date()) {
18
+ return {
19
+ schemaVersion: 1,
20
+ reason,
21
+ capturedAt: now.toISOString(),
22
+ raw
23
+ };
24
+ }
25
+ /**
26
+ * Quarantine an unread document into `recoveryKey`.
27
+ *
28
+ * Idempotent, so re-reading the same unread value refreshes the capture without
29
+ * losing it. Best effort: a store that cannot be written leaves the caller to fail
30
+ * closed on the defaults, which is the same outcome as not having a recovery key at
31
+ * all. Never throws.
32
+ *
33
+ * Exported so a host quarantines its *other* documents (a theme library, say) with
34
+ * the same envelope. Give each document its own `recoveryKey`: two documents that
35
+ * share one overwrite each other's capture.
36
+ */
37
+ function quarantineRecord(storage, recoveryKey, raw, reason, now = () => /* @__PURE__ */ new Date()) {
38
+ try {
39
+ storage.setItem(recoveryKey, JSON.stringify(recoveryEnvelope(raw, reason, now())));
40
+ } catch {}
41
+ }
42
+ /** The default recovery key for a document key. */
43
+ function recoveryKeyFor(schema) {
44
+ return schema.recoveryKey ?? `${schema.key}.recovery`;
45
+ }
46
+ /**
47
+ * Read the document under `schema.key`.
48
+ *
49
+ * The classification is the contract: a value that is not valid JSON, or is JSON but
50
+ * not an object (`null`, a number, a string), is `corrupt_json`; an object (or array)
51
+ * that is not a record of the supported version is `unsupported_record`. Both are
52
+ * quarantined before this returns. An empty string is a stored value, so it is
53
+ * corrupt rather than absent.
54
+ */
55
+ function readVersionedRecord(storage, schema, now) {
56
+ let raw;
57
+ try {
58
+ raw = storage.getItem(schema.key);
59
+ } catch {
60
+ return { status: "unavailable" };
61
+ }
62
+ if (raw === null) return { status: "absent" };
63
+ const recoveryKey = recoveryKeyFor(schema);
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(raw);
67
+ } catch {
68
+ quarantineRecord(storage, recoveryKey, raw, "corrupt_json", now);
69
+ return {
70
+ status: "quarantined",
71
+ reason: "corrupt_json",
72
+ raw
73
+ };
74
+ }
75
+ if (typeof parsed !== "object" || parsed === null) {
76
+ quarantineRecord(storage, recoveryKey, raw, "corrupt_json", now);
77
+ return {
78
+ status: "quarantined",
79
+ reason: "corrupt_json",
80
+ raw
81
+ };
82
+ }
83
+ if (Array.isArray(parsed) || schema.version !== void 0 && parsed.version !== schema.version) {
84
+ quarantineRecord(storage, recoveryKey, raw, "unsupported_record", now);
85
+ return {
86
+ status: "quarantined",
87
+ reason: "unsupported_record",
88
+ raw
89
+ };
90
+ }
91
+ return {
92
+ status: "record",
93
+ record: parsed,
94
+ raw
95
+ };
96
+ }
97
+ /**
98
+ * The legacy single-key value, or `null`.
99
+ *
100
+ * Only the caller decides whether to look: the engine's rule is to ask when the main
101
+ * key is `absent`, not when it was quarantined.
102
+ */
103
+ function readLegacyValue(storage, legacyKey) {
104
+ try {
105
+ return storage.getItem(legacyKey);
106
+ } catch {
107
+ return null;
108
+ }
109
+ }
110
+ /**
111
+ * Write `document` to `key` as JSON. Returns whether it was stored; a full or blocked
112
+ * store returns `false` and never throws, because losing a preference must not break
113
+ * the switch that tried to save it.
114
+ */
115
+ function writeVersionedRecord(storage, key, document) {
116
+ try {
117
+ storage.setItem(key, JSON.stringify(document));
118
+ return true;
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+ //#endregion
124
+ export { defaultPreferenceStorage, quarantineRecord, readLegacyValue, readVersionedRecord, recoveryEnvelope, recoveryKeyFor, writeVersionedRecord };
125
+
126
+ //# sourceMappingURL=preference-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preference-store.js","names":[],"sources":["../../src/lib/preference-store.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Wing\n * Licensed under the MIT License.\n *\n * The recovery behaviour here (quarantine before any write, the envelope shape, the\n * legacy key that is read once and never deleted) is Adea's accepted port of Zeron's\n * preference store (https://github.com/zeronsh/zeron),\n * `packages/ui/src/components/appearance.ts`, moved here so the shared provider and\n * Adea run one implementation. See NOTICE.\n */\n\n/**\n * A versioned preference store: read one JSON document, refuse to guess about what\n * it cannot read, and keep what it refused.\n *\n * Nothing here knows what a preference *is*. A host hands the engine a key, a\n * version and (through `createVersionedPersistence`) a codec; the engine owns only\n * the part every versioned store gets wrong the same way, which is what happens to\n * a document that is not the one the code expects:\n *\n * - A document that is not JSON, or not a record, or carries a version the code\n * does not know (usually a newer build's) is **quarantined, byte for byte, into a\n * recovery key** before anything else happens. The store then answers as if\n * nothing were stored. Because the quarantine runs at read time, ahead of any\n * write, saving valid preferences later cannot destroy the unread original.\n * - A legacy key is consulted only when the main key is *absent*, never when it is\n * unreadable: a corrupt main document is not \"no preference\", and falling back to\n * an older, staler key would quietly restore a choice the user has since changed.\n * The legacy key is never deleted.\n * - Blocked storage (a sandboxed iframe, a privacy mode) throws on the mere access\n * of the `localStorage` global. Every path here treats that as \"nothing stored\".\n *\n * The reads and writes are against a {@link PreferenceStorage}, so the rules run in a\n * test without a DOM.\n */\n\n/** The part of `Storage` the engine uses. */\nexport type PreferenceStorage = Pick<Storage, 'getItem' | 'setItem'>\n\n/** Why an unread document was quarantined. */\nexport type RecoveryReason = 'corrupt_json' | 'unsupported_record'\n\n/**\n * What is written to the recovery key.\n *\n * The shape is a compatibility surface: Adea's stored envelopes already have it, and\n * a tool that inspects a user's storage reads it. Do not rename a field.\n */\nexport type RecoveryEnvelope = Readonly<{\n schemaVersion: 1\n reason: RecoveryReason\n /** An ISO-8601 timestamp of the capture; refreshed by each read of the same bytes. */\n capturedAt: string\n /** The document exactly as it was stored. */\n raw: string\n}>\n\n/**\n * The preference store, or nothing.\n *\n * The `typeof` guard sits inside the `try` for a reason: where storage is blocked,\n * merely *reading* the `localStorage` global throws, so a guard in front of the `try`\n * is itself the crash.\n */\nexport function defaultPreferenceStorage(): PreferenceStorage | undefined {\n try {\n return typeof localStorage === 'undefined' ? undefined : localStorage\n } catch {\n return undefined\n }\n}\n\n/** The envelope for a document, stamped with `now`. */\nexport function recoveryEnvelope(\n raw: string,\n reason: RecoveryReason,\n now: Date = new Date()\n): RecoveryEnvelope {\n return { schemaVersion: 1, reason, capturedAt: now.toISOString(), raw }\n}\n\n/**\n * Quarantine an unread document into `recoveryKey`.\n *\n * Idempotent, so re-reading the same unread value refreshes the capture without\n * losing it. Best effort: a store that cannot be written leaves the caller to fail\n * closed on the defaults, which is the same outcome as not having a recovery key at\n * all. Never throws.\n *\n * Exported so a host quarantines its *other* documents (a theme library, say) with\n * the same envelope. Give each document its own `recoveryKey`: two documents that\n * share one overwrite each other's capture.\n */\nexport function quarantineRecord(\n storage: PreferenceStorage,\n recoveryKey: string,\n raw: string,\n reason: RecoveryReason,\n now: () => Date = () => new Date()\n): void {\n try {\n storage.setItem(recoveryKey, JSON.stringify(recoveryEnvelope(raw, reason, now())))\n } catch {\n // Quarantine is best effort; the active preference still fails closed.\n }\n}\n\n/** Where a versioned document lives and which version the code understands. */\nexport type VersionedRecordSchema = {\n /** The storage key of the document. */\n key: string\n /**\n * The only `version` the code reads. Absent, the document is unversioned and any\n * JSON object is accepted; present, a record whose top-level `version` is not\n * exactly this number is unsupported (newer or older), not \"close enough\".\n */\n version?: number\n /** Where an unread document is kept. Default `${key}.recovery`. */\n recoveryKey?: string\n}\n\n/**\n * What reading the document found.\n *\n * - `absent`: nothing is stored under the key.\n * - `record`: a JSON object this code can read; `raw` is the stored text.\n * - `quarantined`: something was stored and cannot be read. It has already been\n * written to the recovery key.\n * - `unavailable`: the store itself could not be read.\n */\nexport type VersionedRead =\n | { status: 'absent' }\n | { status: 'record'; record: Readonly<Record<string, unknown>>; raw: string }\n | { status: 'quarantined'; reason: RecoveryReason; raw: string }\n | { status: 'unavailable' }\n\n/** The default recovery key for a document key. */\nexport function recoveryKeyFor(schema: VersionedRecordSchema): string {\n return schema.recoveryKey ?? `${schema.key}.recovery`\n}\n\n/**\n * Read the document under `schema.key`.\n *\n * The classification is the contract: a value that is not valid JSON, or is JSON but\n * not an object (`null`, a number, a string), is `corrupt_json`; an object (or array)\n * that is not a record of the supported version is `unsupported_record`. Both are\n * quarantined before this returns. An empty string is a stored value, so it is\n * corrupt rather than absent.\n */\nexport function readVersionedRecord(\n storage: PreferenceStorage,\n schema: VersionedRecordSchema,\n now?: () => Date\n): VersionedRead {\n let raw: string | null\n try {\n raw = storage.getItem(schema.key)\n } catch {\n return { status: 'unavailable' }\n }\n if (raw === null) return { status: 'absent' }\n\n const recoveryKey = recoveryKeyFor(schema)\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n quarantineRecord(storage, recoveryKey, raw, 'corrupt_json', now)\n return { status: 'quarantined', reason: 'corrupt_json', raw }\n }\n if (typeof parsed !== 'object' || parsed === null) {\n quarantineRecord(storage, recoveryKey, raw, 'corrupt_json', now)\n return { status: 'quarantined', reason: 'corrupt_json', raw }\n }\n if (\n Array.isArray(parsed) ||\n (schema.version !== undefined && (parsed as { version?: unknown }).version !== schema.version)\n ) {\n quarantineRecord(storage, recoveryKey, raw, 'unsupported_record', now)\n return { status: 'quarantined', reason: 'unsupported_record', raw }\n }\n return { status: 'record', record: parsed as Record<string, unknown>, raw }\n}\n\n/**\n * The legacy single-key value, or `null`.\n *\n * Only the caller decides whether to look: the engine's rule is to ask when the main\n * key is `absent`, not when it was quarantined.\n */\nexport function readLegacyValue(storage: PreferenceStorage, legacyKey: string): string | null {\n try {\n return storage.getItem(legacyKey)\n } catch {\n return null\n }\n}\n\n/**\n * Write `document` to `key` as JSON. Returns whether it was stored; a full or blocked\n * store returns `false` and never throws, because losing a preference must not break\n * the switch that tried to save it.\n */\nexport function writeVersionedRecord(\n storage: PreferenceStorage,\n key: string,\n document: unknown\n): boolean {\n try {\n storage.setItem(key, JSON.stringify(document))\n return true\n } catch {\n return false\n }\n}\n"],"mappings":";;;;;;;;AAgEA,SAAgB,2BAA0D;CACxE,IAAI;EACF,OAAO,OAAO,iBAAiB,cAAc,KAAA,IAAY;CAC3D,QAAQ;EACN;CACF;AACF;;AAGA,SAAgB,iBACd,KACA,QACA,sBAAY,IAAI,KAAK,GACH;CAClB,OAAO;EAAE,eAAe;EAAG;EAAQ,YAAY,IAAI,YAAY;EAAG;CAAI;AACxE;;;;;;;;;;;;;AAcA,SAAgB,iBACd,SACA,aACA,KACA,QACA,4BAAwB,IAAI,KAAK,GAC3B;CACN,IAAI;EACF,QAAQ,QAAQ,aAAa,KAAK,UAAU,iBAAiB,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC;CACnF,QAAQ,CAER;AACF;;AAgCA,SAAgB,eAAe,QAAuC;CACpE,OAAO,OAAO,eAAe,GAAG,OAAO,IAAI;AAC7C;;;;;;;;;;AAWA,SAAgB,oBACd,SACA,QACA,KACe;CACf,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,QAAQ,OAAO,GAAG;CAClC,QAAQ;EACN,OAAO,EAAE,QAAQ,cAAc;CACjC;CACA,IAAI,QAAQ,MAAM,OAAO,EAAE,QAAQ,SAAS;CAE5C,MAAM,cAAc,eAAe,MAAM;CACzC,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,GAAG;CACzB,QAAQ;EACN,iBAAiB,SAAS,aAAa,KAAK,gBAAgB,GAAG;EAC/D,OAAO;GAAE,QAAQ;GAAe,QAAQ;GAAgB;EAAI;CAC9D;CACA,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM;EACjD,iBAAiB,SAAS,aAAa,KAAK,gBAAgB,GAAG;EAC/D,OAAO;GAAE,QAAQ;GAAe,QAAQ;GAAgB;EAAI;CAC9D;CACA,IACE,MAAM,QAAQ,MAAM,KACnB,OAAO,YAAY,KAAA,KAAc,OAAiC,YAAY,OAAO,SACtF;EACA,iBAAiB,SAAS,aAAa,KAAK,sBAAsB,GAAG;EACrE,OAAO;GAAE,QAAQ;GAAe,QAAQ;GAAsB;EAAI;CACpE;CACA,OAAO;EAAE,QAAQ;EAAU,QAAQ;EAAmC;CAAI;AAC5E;;;;;;;AAQA,SAAgB,gBAAgB,SAA4B,WAAkC;CAC5F,IAAI;EACF,OAAO,QAAQ,QAAQ,SAAS;CAClC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,qBACd,SACA,KACA,UACS;CACT,IAAI;EACF,QAAQ,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAC;EAC7C,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The surface axis, and the policy that decides what it paints.
3
+ *
4
+ * A surface is how opaque the window's elevated panels are: `opaque` is the plain
5
+ * canvas, `frosted` is a tokenized translucent fill over a blur (works everywhere,
6
+ * in CSS alone), and `translucent` asks for the host window's own vibrancy, which only
7
+ * a desktop shell can honour.
8
+ *
9
+ * Nothing here touches the document or a signal. The provider reads the operating
10
+ * system and the host's capability, calls {@link resolveSurface}, and
11
+ * `theme-document.ts` writes the answer; the rule itself is a function of its four
12
+ * inputs, which is what makes it testable.
13
+ */
14
+ export type ThemeSurface = 'opaque' | 'frosted' | 'translucent';
15
+ /** Every surface, in the order a picker offers them. */
16
+ export declare const themeSurfaces: readonly ThemeSurface[];
17
+ /** The media query the operating system's Reduce Transparency setting answers. */
18
+ export declare const REDUCED_TRANSPARENCY_QUERY = "(prefers-reduced-transparency: reduce)";
19
+ /**
20
+ * What the world says about transparency, as opposed to what the user prefers.
21
+ */
22
+ export type SurfaceEnvironment = Readonly<{
23
+ /** The operating system asks for less transparency. */
24
+ osReducedTransparency: boolean;
25
+ /** The user turned reduced transparency on inside the application. */
26
+ userReducedTransparency: boolean;
27
+ /** The host window can render OS translucency (vibrancy, acrylic). */
28
+ nativeTranslucency: boolean;
29
+ }>;
30
+ /**
31
+ * A stored value as a surface, or nothing.
32
+ *
33
+ * `undefined` rather than `opaque` for an unreadable value: the selection field is
34
+ * optional, and an absent field is how a store that never chose a surface stays
35
+ * byte-identical to what it was before the axis existed.
36
+ */
37
+ export declare function normalizeSurface(value: unknown): ThemeSurface | undefined;
38
+ /**
39
+ * The surface to paint: the preference, after the accessibility override and the
40
+ * host's capability.
41
+ *
42
+ * The OS setting or the user's own flag forces an accessible opaque fallback. A
43
+ * translucent request on a host without native translucency renders the tokenized
44
+ * frosted surface instead of pretending to OS vibrancy.
45
+ */
46
+ export declare function resolveSurface(preference: ThemeSurface, environment: SurfaceEnvironment): ThemeSurface;
47
+ /**
48
+ * The fill opacity each surface paints, written as `--surface-alpha`.
49
+ *
50
+ * These are the values `--surface-frost` and `--surface-translucent` declare in
51
+ * `theme.css` (the stylesheet is the readable copy; `tests/theme-surface.test.ts`
52
+ * fails when the two disagree). They are numbers rather than `var()` references so
53
+ * the property reads the same to a script, a test and the stylesheet, whichever
54
+ * wrote it.
55
+ */
56
+ export declare const surfaceAlpha: Readonly<Record<ThemeSurface, string>>;
57
+ /** What a selection paints for the surface axis. */
58
+ export type SurfaceState = Readonly<{
59
+ /** The surface after the accessibility override and capability gate. */
60
+ surface: ThemeSurface;
61
+ /** True while the OS or the user forces opaque surfaces. */
62
+ reduceTransparencyActive: boolean;
63
+ }>;
64
+ /**
65
+ * The surface state for a preference and an environment.
66
+ *
67
+ * A preference of `undefined` is `opaque`: the axis is optional on a selection, and
68
+ * a host that never set it gets the plain canvas.
69
+ */
70
+ export declare function resolveSurfaceState(preference: ThemeSurface | undefined, environment: SurfaceEnvironment): SurfaceState;
71
+ //# sourceMappingURL=theme-surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-surface.d.ts","sourceRoot":"","sources":["../../src/lib/theme-surface.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAA;AAE/D,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EAAyC,CAAA;AAE1F,kFAAkF;AAClF,eAAO,MAAM,0BAA0B,2CAA2C,CAAA;AAElF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,uDAAuD;IACvD,qBAAqB,EAAE,OAAO,CAAA;IAC9B,sEAAsE;IACtE,uBAAuB,EAAE,OAAO,CAAA;IAChC,sEAAsE;IACtE,kBAAkB,EAAE,OAAO,CAAA;CAC5B,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,YAAY,EACxB,WAAW,EAAE,kBAAkB,GAC9B,YAAY,CAId;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAI/D,CAAA;AAED,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,wEAAwE;IACxE,OAAO,EAAE,YAAY,CAAA;IACrB,4DAA4D;IAC5D,wBAAwB,EAAE,OAAO,CAAA;CAClC,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,YAAY,GAAG,SAAS,EACpC,WAAW,EAAE,kBAAkB,GAC9B,YAAY,CAMd"}