@adea-ai/ui 0.124.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.
- package/dist/NOTICE +58 -0
- package/dist/components/theme/index.d.ts +1 -1
- package/dist/components/theme/index.d.ts.map +1 -1
- package/dist/components/theme/index.js +4 -3
- package/dist/components/theme/theme-document.d.ts +31 -1
- package/dist/components/theme/theme-document.d.ts.map +1 -1
- package/dist/components/theme/theme-document.js +25 -5
- package/dist/components/theme/theme-document.js.map +1 -1
- package/dist/components/theme/theme-persistence.d.ts +87 -0
- package/dist/components/theme/theme-persistence.d.ts.map +1 -1
- package/dist/components/theme/theme-persistence.js +65 -3
- package/dist/components/theme/theme-persistence.js.map +1 -1
- package/dist/components/theme/theme-provider.d.ts +49 -40
- package/dist/components/theme/theme-provider.d.ts.map +1 -1
- package/dist/components/theme/theme-provider.js +67 -52
- package/dist/components/theme/theme-provider.js.map +1 -1
- package/dist/components/theme/theme-script.d.ts +100 -0
- package/dist/components/theme/theme-script.d.ts.map +1 -0
- package/dist/components/theme/theme-script.js +57 -0
- package/dist/components/theme/theme-script.js.map +1 -0
- package/dist/components/theme/theme-state.d.ts +85 -2
- package/dist/components/theme/theme-state.d.ts.map +1 -1
- package/dist/components/theme/theme-state.js +41 -2
- package/dist/components/theme/theme-state.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/lib/accent-roles.d.ts +124 -0
- package/dist/lib/accent-roles.d.ts.map +1 -0
- package/dist/lib/accent-roles.js +163 -0
- package/dist/lib/accent-roles.js.map +1 -0
- package/dist/lib/font-zoom.d.ts +25 -0
- package/dist/lib/font-zoom.d.ts.map +1 -0
- package/dist/lib/font-zoom.js +45 -0
- package/dist/lib/font-zoom.js.map +1 -0
- package/dist/lib/preference-store.d.ts +126 -0
- package/dist/lib/preference-store.d.ts.map +1 -0
- package/dist/lib/preference-store.js +126 -0
- package/dist/lib/preference-store.js.map +1 -0
- package/dist/lib/theme-surface.d.ts +71 -0
- package/dist/lib/theme-surface.d.ts.map +1 -0
- package/dist/lib/theme-surface.js +62 -0
- package/dist/lib/theme-surface.js.map +1 -0
- package/dist/lib/themes.d.ts +32 -1
- package/dist/lib/themes.d.ts.map +1 -1
- package/dist/lib/themes.js +34 -13
- package/dist/lib/themes.js.map +1 -1
- package/dist/lib/tokens.d.ts.map +1 -1
- package/dist/lib/tokens.js +15 -0
- package/dist/lib/tokens.js.map +1 -1
- package/dist/r/lib.json +20 -0
- package/dist/r/registry.json +25 -0
- package/dist/r/src/components/theme/index.ts +6 -0
- package/dist/r/src/components/theme/theme-document.ts +69 -4
- package/dist/r/src/components/theme/theme-persistence.ts +172 -2
- package/dist/r/src/components/theme/theme-provider.tsx +137 -51
- package/dist/r/src/components/theme/theme-script.ts +201 -0
- package/dist/r/src/components/theme/theme-state.ts +124 -3
- package/dist/r/src/lib/accent-roles.ts +338 -0
- package/dist/r/src/lib/font-zoom.ts +74 -0
- package/dist/r/src/lib/preference-store.ts +216 -0
- package/dist/r/src/lib/theme-surface.ts +110 -0
- package/dist/r/src/lib/themes.ts +58 -19
- package/dist/r/src/lib/tokens.ts +17 -0
- package/dist/r/src/styles/theme.css +38 -0
- package/dist/r/theming.json +5 -0
- package/package.json +1 -1
- package/registry.json +25 -0
- package/src/components/theme/index.ts +6 -0
- package/src/components/theme/theme-document.ts +69 -4
- package/src/components/theme/theme-persistence.ts +172 -2
- package/src/components/theme/theme-provider.tsx +137 -51
- package/src/components/theme/theme-script.ts +201 -0
- package/src/components/theme/theme-state.ts +124 -3
- package/src/index.ts +34 -0
- package/src/lib/accent-roles.ts +338 -0
- package/src/lib/font-zoom.ts +74 -0
- package/src/lib/preference-store.ts +216 -0
- package/src/lib/theme-surface.ts +110 -0
- package/src/lib/themes.ts +58 -19
- package/src/lib/tokens.ts +17 -0
- package/src/styles/theme.css +38 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeCustomAccent } from "../../lib/accent-roles.js";
|
|
2
|
+
import { accentRolesForSelection, defaultDarkThemeId, defaultLightThemeId, resolveVariant } from "../../lib/themes.js";
|
|
2
3
|
import { DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS, normalizeAppearanceEditorFontSettings } from "../../lib/appearance-font-settings.js";
|
|
4
|
+
import { stepAppearanceFontSizes } from "../../lib/font-zoom.js";
|
|
3
5
|
//#region src/components/theme/theme-state.ts
|
|
4
6
|
/**
|
|
5
7
|
* Deliberately lists only the fields every consumer has always had. A field added
|
|
@@ -50,6 +52,7 @@ function mergeThemeSelection(base, patch) {
|
|
|
50
52
|
} else fonts = normalizeAppearanceEditorFontSettings(merged.fonts, merged.font).settings;
|
|
51
53
|
return {
|
|
52
54
|
...merged,
|
|
55
|
+
accent: normalizeCustomAccent(merged.accent) ?? merged.accent,
|
|
53
56
|
fonts,
|
|
54
57
|
font: fonts.ui.family
|
|
55
58
|
};
|
|
@@ -90,6 +93,26 @@ function resolveThemeState(selection, system, registry) {
|
|
|
90
93
|
};
|
|
91
94
|
}
|
|
92
95
|
/**
|
|
96
|
+
* The variant whose terminal palette is pinned, or `undefined` when the terminal
|
|
97
|
+
* follows the interface theme.
|
|
98
|
+
*
|
|
99
|
+
* `theme`, an absent id and an id the registry does not hold all mean the interface
|
|
100
|
+
* palette: a terminal must never resolve to a blank or half-painted palette because a
|
|
101
|
+
* theme was deleted after it was pinned.
|
|
102
|
+
*/
|
|
103
|
+
function resolveTerminalVariant(terminalThemeId, registry) {
|
|
104
|
+
if (terminalThemeId === void 0 || terminalThemeId === "theme") return void 0;
|
|
105
|
+
return registry.find((candidate) => candidate.id === terminalThemeId);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The font settings a zoom chord asks for: one pixel up or down on every axis, or
|
|
109
|
+
* the defaults for `reset`.
|
|
110
|
+
*/
|
|
111
|
+
function zoomedFontSettings(selection, intent) {
|
|
112
|
+
if (intent === "reset") return { ...DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS };
|
|
113
|
+
return stepAppearanceFontSizes(selection.fonts, intent === "in" ? 1 : -1);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
93
116
|
* The registry a provider resolves against: the catalogue, then the host's extras.
|
|
94
117
|
*
|
|
95
118
|
* The catalogue wins an id collision, because it is listed first and `resolveVariant`
|
|
@@ -98,7 +121,23 @@ function resolveThemeState(selection, system, registry) {
|
|
|
98
121
|
function mergeRegistry(builtin, extra) {
|
|
99
122
|
return extra && extra.length > 0 ? [...builtin, ...extra] : builtin;
|
|
100
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* The roles an accent paints over a variant, for {@link ThemeContextValue.accentRolesFor}.
|
|
126
|
+
*
|
|
127
|
+
* Resolved against the committed selection's theme pair, so an `ansi-*` accent means
|
|
128
|
+
* the same colour here as it does on `<html>`.
|
|
129
|
+
*/
|
|
130
|
+
function accentRolesForState(accent, variantId, state) {
|
|
131
|
+
if (accent === null) return void 0;
|
|
132
|
+
const variant = variantId === void 0 ? state.variant : state.registry.find((candidate) => candidate.id === variantId);
|
|
133
|
+
if (!variant) return void 0;
|
|
134
|
+
return accentRolesForSelection(accent, variant.appearance, state.selection, {
|
|
135
|
+
background: variant.colors.background,
|
|
136
|
+
floor: state.accentContrastFloor,
|
|
137
|
+
variantId: variant.id
|
|
138
|
+
});
|
|
139
|
+
}
|
|
101
140
|
//#endregion
|
|
102
|
-
export { defaultThemeSelection, initialThemeSelection, mergeRegistry, mergeThemeSelection, resolveAppearance, resolveThemeState, resolvedDensity, selectedThemeId };
|
|
141
|
+
export { accentRolesForState, defaultThemeSelection, initialThemeSelection, mergeRegistry, mergeThemeSelection, resolveAppearance, resolveTerminalVariant, resolveThemeState, resolvedDensity, selectedThemeId, zoomedFontSettings };
|
|
103
142
|
|
|
104
143
|
//# sourceMappingURL=theme-state.js.map
|
|
@@ -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 {\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 * or `theme` for the variant's own primary. A theme accent the current pair does\n * not offer falls back to the variant's own primary.\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\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 { ...merged, fonts, font: fonts.ui.family }\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"],"mappings":";;;;;;;;;AA4EA,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;;AAuCA,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;EAAE,GAAG;EAAQ;EAAO,MAAM,MAAM,GAAG;CAAO;AACnD;;;;;;;;;;;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"}
|
|
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
|
@@ -103,8 +103,12 @@ export * from './components/composites/about-dialog';
|
|
|
103
103
|
export * from './components/composites/help-center';
|
|
104
104
|
export * from './components/composites/workspace-mark';
|
|
105
105
|
export { cn } from './lib/utils';
|
|
106
|
+
export { isCustomAccent, normalizeCustomAccent, paintAccentRoles, resolveAccentRoles, type AccentRoles, type AccentTarget, } from './lib/accent-roles';
|
|
106
107
|
export { controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, surfaceInteractive, type ControlSize, type VariantProps, } from './lib/variants';
|
|
107
|
-
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';
|
|
108
112
|
export { keyedRows, type KeyedRow } from './lib/keyed-rows';
|
|
109
113
|
export { writeClipboardText } from './lib/clipboard';
|
|
110
114
|
export { formatBytes, formatReleaseDate, plainTextFromMarkdown } from './lib/version-notes';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,oCAAoC,CAAA;AAClD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AACpC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAG1C,cAAc,qBAAqB,CAAA;AAGnC,cAAc,kCAAkC,CAAA;AAChD,cAAc,sCAAsC,CAAA;AACpD,cAAc,kCAAkC,CAAA;AAChD,cAAc,oCAAoC,CAAA;AAClD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,0BAA0B,CAAA;AACxC,cAAc,2BAA2B,CAAA;AACzC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,wCAAwC,CAAA;AACtD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,6BAA6B,CAAA;AAG3C,cAAc,2BAA2B,CAAA;AAGzC,cAAc,oBAAoB,CAAA;AAGlC,cAAc,sCAAsC,CAAA;AACpD,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,kCAAkC,CAAA;AAChD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,kCAAkC,CAAA;AAChD,cAAc,wCAAwC,CAAA;AACtD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2CAA2C,CAAA;AACzD,cAAc,uCAAuC,CAAA;AACrD,cAAc,sCAAsC,CAAA;AACpD,cAAc,qCAAqC,CAAA;AACnD,cAAc,wCAAwC,CAAA;AAGtD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,YAAY,EACZ,GAAG,EACH,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChG,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3F,OAAO,EACL,aAAa,EACb,SAAS,EACT,WAAW,EACX,aAAa,EACb,YAAY,EACZ,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,cAAc,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -103,15 +103,20 @@ import { AttachmentCard, ThreadPanel } from "./components/conversation/thread-pa
|
|
|
103
103
|
import { PASTE_THRESHOLD_CHARS, PASTE_THRESHOLD_LINES, PASTE_TOKEN_REGEX, countLines, expandAll, findTokenRanges, formatToken, isPasteBlock, nextSeq, pruneBlocks, recollapsePastes, remapCarriedBlocks, shouldCollapse, stripTrailingBlankLines, tokenRangeAt } from "./components/conversation/paste-tokens.js";
|
|
104
104
|
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
|
+
import { isCustomAccent, normalizeCustomAccent, paintAccentRoles, resolveAccentRoles } from "./lib/accent-roles.js";
|
|
106
107
|
import { isThemeAccentId } from "./lib/theme-accents.js";
|
|
107
|
-
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";
|
|
108
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";
|
|
109
110
|
import { AppearanceFontSettingsGroup } from "./components/composites/appearance-editor/font-settings-group.js";
|
|
110
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";
|
|
111
114
|
import { useOptionalTheme, useTheme } from "./components/theme/theme-context.js";
|
|
112
115
|
import { defaultThemeSelection, resolvedDensity } from "./components/theme/theme-state.js";
|
|
113
|
-
import {
|
|
114
|
-
import {
|
|
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";
|
|
115
120
|
import { AppearancePanel } from "./components/theme/appearance-panel.js";
|
|
116
121
|
import { ThemeToggle } from "./components/theme/theme-toggle.js";
|
|
117
122
|
import { ThemeModeToggle } from "./components/theme/theme-mode-toggle.js";
|
|
@@ -136,4 +141,4 @@ import { AboutDialog } from "./components/composites/about-dialog/about-dialog.j
|
|
|
136
141
|
import { HelpCenter } from "./components/composites/help-center/help-center.js";
|
|
137
142
|
import { WorkspaceMark } from "./components/composites/workspace-mark/workspace-mark.js";
|
|
138
143
|
import { writeClipboardText } from "./lib/clipboard.js";
|
|
139
|
-
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, isPasteBlock, isThemeAccentId, itemVariants, kbdChordKeys, kbdGroupVariants, kbdVariants, keyedRows, layoutDepth, listLeaves, localStoragePersistence, monogram, motionTokens, movePane, neighborLeaf, nextSeq, normalizeAppearanceEditorFontSettings, normalizeAppearanceEditorFontSize, normalizeLayout, parseDiff, plainTextFromMarkdown, platformModifierKey, pruneBlocks, radiusTokens, recollapsePastes, remapCarriedBlocks, resizeFloatingPreview, resizePreviewMiniPlayer, resizeSplit, resolveAccentPreset, 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,124 @@
|
|
|
1
|
+
import { accentRoles, type AccentPreset, type AccentRoles as CatalogueAccentRoles } from '@adea-ai/themes';
|
|
2
|
+
/**
|
|
3
|
+
* Accent colour maths and the roles an accent paints.
|
|
4
|
+
*
|
|
5
|
+
* Three questions live here, and none of them touches the document or a signal:
|
|
6
|
+
*
|
|
7
|
+
* - **What colour is this?** `parseColor`, `colorToHex`, `contrastRatio` and
|
|
8
|
+
* `ensureContrast` are the hex helpers a host needs around an accent (an
|
|
9
|
+
* imported theme's colours, a colour field). Hex is the host preference format;
|
|
10
|
+
* the catalogue's colour engine does the maths.
|
|
11
|
+
* - **Is this accent readable on this surface?** The *contrast floor*. A preset is
|
|
12
|
+
* curated, so the catalogue already measured it against its own themes and the
|
|
13
|
+
* floor is the identity for it; a custom `#rrggbb` is anyone's choice, so it is
|
|
14
|
+
* raised until it clears the floor and can never ship unreadable. A host that
|
|
15
|
+
* imports themes (whose backgrounds the catalogue never saw) turns the floor on
|
|
16
|
+
* for presets too.
|
|
17
|
+
* - **Which custom properties does an accent own?** `AccentRoles`, written on
|
|
18
|
+
* `<html>` by the provider and onto one element by {@link paintAccentRoles}.
|
|
19
|
+
*/
|
|
20
|
+
export type RgbColor = Readonly<{
|
|
21
|
+
r: number;
|
|
22
|
+
g: number;
|
|
23
|
+
b: number;
|
|
24
|
+
a: number;
|
|
25
|
+
}>;
|
|
26
|
+
/** Parse a hex colour (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`). `undefined` for anything else. */
|
|
27
|
+
export declare function parseColor(value: string): RgbColor | undefined;
|
|
28
|
+
/** `#rrggbb`, or `#rrggbbaa` when the colour is not opaque. */
|
|
29
|
+
export declare function colorToHex(color: RgbColor): string;
|
|
30
|
+
/** WCAG contrast ratio between two colours. Alpha composites over the background first. */
|
|
31
|
+
export declare function contrastRatio(foreground: RgbColor | string, background: RgbColor | string): number;
|
|
32
|
+
/** Preserve the host hex shape while delegating contrast repair to the catalogue. */
|
|
33
|
+
export declare function ensureContrast(color: RgbColor, background: RgbColor, minimum: number): RgbColor;
|
|
34
|
+
/** The contrast an accent is held to against its surface unless the host sets another. */
|
|
35
|
+
export declare const DEFAULT_ACCENT_CONTRAST_FLOOR = 3;
|
|
36
|
+
/**
|
|
37
|
+
* Normalise a custom accent against a surface: unparseable input is rejected
|
|
38
|
+
* (`undefined`), and any accepted colour is raised to `minimum` so an accent can
|
|
39
|
+
* never ship unreadable. Accepts every CSS hex length; for the stored grammar use
|
|
40
|
+
* {@link normalizeCustomAccent}.
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizeAccentValue(value: string, background: string, minimum?: number): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* The stored form of a custom accent: `#rrggbb`, six digits, opaque, lower case.
|
|
45
|
+
*
|
|
46
|
+
* Deliberately narrower than {@link parseColor}. A stored accent is compared,
|
|
47
|
+
* deduplicated and used as an attribute, so it has one spelling; a host that
|
|
48
|
+
* accepts `#abc` in a field expands it before storing. Anything else — a name, an
|
|
49
|
+
* `rgb()`, a short or alpha form, a non-string — is not an accent and returns
|
|
50
|
+
* `undefined`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function normalizeCustomAccent(value: unknown): string | undefined;
|
|
53
|
+
/** Whether a selection's accent is the custom `#rrggbb` form (any letter case). */
|
|
54
|
+
export declare function isCustomAccent(value: unknown): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* The roles an accent paints, as CSS values: the catalogue's `AccentRoles`, so the
|
|
57
|
+
* provider, the generated stylesheet and a host's own paint cannot disagree about
|
|
58
|
+
* what a role is. `primarySubtle` is an expression over `var(--primary)`; the
|
|
59
|
+
* document writer never sets it (see {@link accentOverrideVariables}), an element
|
|
60
|
+
* paint must.
|
|
61
|
+
*/
|
|
62
|
+
export type AccentRoles = CatalogueAccentRoles;
|
|
63
|
+
/** What an accent resolves against: the surface it sits on and the theme it sits in. */
|
|
64
|
+
export type AccentTarget = {
|
|
65
|
+
/** The theme, or only its appearance. A theme gives the tint its measured strength. */
|
|
66
|
+
target: Parameters<typeof accentRoles>[1];
|
|
67
|
+
/** The surface the accent must read on. Without it nothing can be measured or repaired. */
|
|
68
|
+
background: string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* The contrast to hold the accent to against `background`. Absent, a preset is
|
|
71
|
+
* painted verbatim and a custom colour is held to {@link DEFAULT_ACCENT_CONTRAST_FLOOR}.
|
|
72
|
+
*/
|
|
73
|
+
floor?: number;
|
|
74
|
+
/**
|
|
75
|
+
* The accents the selected theme pair carries (`ansi-blue`…), for an id that is
|
|
76
|
+
* neither a preset nor a colour.
|
|
77
|
+
*/
|
|
78
|
+
themeAccents?: readonly AccentPreset[];
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* The roles for a preset (or a custom colour made into one), held to the floor.
|
|
82
|
+
*
|
|
83
|
+
* A preset that clears the floor keeps the catalogue's roles untouched. One that
|
|
84
|
+
* does not is repaired to the nearest passing colour and the whole role set is
|
|
85
|
+
* rebuilt from the repaired value, so the label, hover and ring follow it instead
|
|
86
|
+
* of describing a colour that is no longer on screen.
|
|
87
|
+
*/
|
|
88
|
+
export declare function accentRolesForPreset(preset: AccentPreset, context: AccentTarget): AccentRoles | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Resolve an accent selection to its roles, or `undefined` when it overrides nothing.
|
|
91
|
+
*
|
|
92
|
+
* `undefined` is "use the theme's own primary": the literal `theme`, an id that is
|
|
93
|
+
* neither a preset, an offered theme accent nor a colour, and a colour that does not
|
|
94
|
+
* parse. A host that wants one of those to mean something else decides before
|
|
95
|
+
* asking (Adea's codec maps a stored `theme` to its default preset when it reads).
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveAccentRoles(accent: string, context: AccentTarget): AccentRoles | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* The custom properties an accent override owns on `<html>`.
|
|
100
|
+
*
|
|
101
|
+
* `--primary-subtle` is deliberately not among them. It is a `color-mix()` over
|
|
102
|
+
* `var(--primary)`, so on the same element it follows an accent by itself, and its
|
|
103
|
+
* strength is the theme's, measured to keep body text at 4.5:1 on the tint.
|
|
104
|
+
* Writing one here would replace that measurement with the argument's.
|
|
105
|
+
*/
|
|
106
|
+
export declare function accentOverrideVariables(roles: AccentRoles): Record<string, string>;
|
|
107
|
+
/** The custom properties {@link paintAccentRoles} writes and clears. */
|
|
108
|
+
export declare const ACCENT_PAINT_PROPERTIES: readonly ["--primary", "--primary-foreground", "--primary-hover", "--primary-subtle", "--ring"];
|
|
109
|
+
/**
|
|
110
|
+
* Scope an accent to one element and its descendants.
|
|
111
|
+
*
|
|
112
|
+
* Unlike the document, an element must write `--primary-subtle` too. A custom
|
|
113
|
+
* property that uses `var()` is resolved where it is declared, so the tint inherited
|
|
114
|
+
* from `<html>` is already a mix over *`<html>`'s* primary and an element that only
|
|
115
|
+
* overrides `--primary` would keep the old tint. Declared here, the same expression
|
|
116
|
+
* resolves against this element's primary.
|
|
117
|
+
*
|
|
118
|
+
* `undefined` clears every property, and the element inherits the document's accent
|
|
119
|
+
* again.
|
|
120
|
+
*/
|
|
121
|
+
export declare function paintAccentRoles(element: {
|
|
122
|
+
style: Pick<CSSStyleDeclaration, 'setProperty' | 'removeProperty'>;
|
|
123
|
+
}, roles: AccentRoles | undefined): void;
|
|
124
|
+
//# sourceMappingURL=accent-roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accent-roles.d.ts","sourceRoot":"","sources":["../../src/lib/accent-roles.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,WAAW,EAGX,KAAK,YAAY,EACjB,KAAK,WAAW,IAAI,oBAAoB,EACzC,MAAM,iBAAiB,CAAA;AAUxB;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAI/E,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAc9D;AAMD,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAGlD;AAiBD,2FAA2F;AAC3F,wBAAgB,aAAa,CAC3B,UAAU,EAAE,QAAQ,GAAG,MAAM,EAC7B,UAAU,EAAE,QAAQ,GAAG,MAAM,GAC5B,MAAM,CASR;AAyBD,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAQ/F;AAED,0FAA0F;AAC1F,eAAO,MAAM,6BAA6B,IAAI,CAAA;AAE9C;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,MAAsC,GAC9C,MAAM,GAAG,SAAS,CAKpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIxE;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,oBAAoB,CAAA;AAE9C,wFAAwF;AACxF,MAAM,MAAM,YAAY,GAAG;IACzB,uFAAuF;IACvF,MAAM,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IACzC,2FAA2F;IAC3F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;CACvC,CAAA;AAqBD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,YAAY,GACpB,WAAW,GAAG,SAAS,CAezB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,CAgBjG;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOlF;AAED,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,iGAM1B,CAAA;AAEV;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE;IAAE,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,aAAa,GAAG,gBAAgB,CAAC,CAAA;CAAE,EAC/E,KAAK,EAAE,WAAW,GAAG,SAAS,GAC7B,IAAI,CAUN"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { ACCENTS, accentRoles, accentValue, primarySubtleCss } from "@adea-ai/themes";
|
|
2
|
+
import { contrastRatio, oklchToHex, parseColor, repairContrast } from "@adea-ai/themes/oklch";
|
|
3
|
+
//#region src/lib/accent-roles.ts
|
|
4
|
+
var WHITE_OKLCH = {
|
|
5
|
+
l: 1,
|
|
6
|
+
c: 0,
|
|
7
|
+
h: 0
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The nearest colour to `source` that reaches `minimum` against `canvas`, as hex.
|
|
11
|
+
*
|
|
12
|
+
* The catalogue's `repairContrast` is asked for a little more than the floor, in
|
|
13
|
+
* 0.001 steps up to 0.05, because the result is rounded to 8-bit hex and the
|
|
14
|
+
* rounded colour is what is measured: asking for exactly the floor can land a hair
|
|
15
|
+
* under it after rounding. If nothing in that range survives the rounding, white or
|
|
16
|
+
* black stands in, whichever clears the floor.
|
|
17
|
+
*/
|
|
18
|
+
function repairedHex(source, canvas, minimum) {
|
|
19
|
+
for (let margin = 0; margin <= .05; margin = Number((margin + .001).toFixed(3))) {
|
|
20
|
+
const repaired = repairContrast(source, canvas, minimum + margin);
|
|
21
|
+
if (!repaired.satisfied) continue;
|
|
22
|
+
const hex = oklchToHex(repaired.color);
|
|
23
|
+
const rounded = parseColor(hex);
|
|
24
|
+
if (rounded && contrastRatio(rounded, canvas) >= minimum) return hex;
|
|
25
|
+
}
|
|
26
|
+
return contrastRatio(WHITE_OKLCH, canvas) >= minimum ? "#ffffff" : "#000000";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The stored form of a custom accent: `#rrggbb`, six digits, opaque, lower case.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately narrower than {@link parseColor}. A stored accent is compared,
|
|
32
|
+
* deduplicated and used as an attribute, so it has one spelling; a host that
|
|
33
|
+
* accepts `#abc` in a field expands it before storing. Anything else — a name, an
|
|
34
|
+
* `rgb()`, a short or alpha form, a non-string — is not an accent and returns
|
|
35
|
+
* `undefined`.
|
|
36
|
+
*/
|
|
37
|
+
function normalizeCustomAccent(value) {
|
|
38
|
+
return typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value) ? value.toLowerCase() : void 0;
|
|
39
|
+
}
|
|
40
|
+
/** Whether a selection's accent is the custom `#rrggbb` form (any letter case). */
|
|
41
|
+
function isCustomAccent(value) {
|
|
42
|
+
return normalizeCustomAccent(value) !== void 0;
|
|
43
|
+
}
|
|
44
|
+
function appearanceOf(target) {
|
|
45
|
+
return typeof target === "string" ? target : target.appearance;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `value` raised to `floor` against `background` as hex, or `value` itself when it
|
|
49
|
+
* already clears the floor, is not a colour, or there is no surface to measure it on.
|
|
50
|
+
*
|
|
51
|
+
* Returning the argument itself is what makes the floor the identity for a preset
|
|
52
|
+
* that already reads: the caller compares and keeps the catalogue's own roles.
|
|
53
|
+
*/
|
|
54
|
+
function gateColor(value, background, floor) {
|
|
55
|
+
const source = parseColor(value);
|
|
56
|
+
const canvas = background ? parseColor(background) : void 0;
|
|
57
|
+
if (!source || !canvas) return value;
|
|
58
|
+
if (contrastRatio(source, canvas) >= floor) return value;
|
|
59
|
+
return repairedHex(source, canvas, floor);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The roles for a preset (or a custom colour made into one), held to the floor.
|
|
63
|
+
*
|
|
64
|
+
* A preset that clears the floor keeps the catalogue's roles untouched. One that
|
|
65
|
+
* does not is repaired to the nearest passing colour and the whole role set is
|
|
66
|
+
* rebuilt from the repaired value, so the label, hover and ring follow it instead
|
|
67
|
+
* of describing a colour that is no longer on screen.
|
|
68
|
+
*/
|
|
69
|
+
function accentRolesForPreset(preset, context) {
|
|
70
|
+
const appearance = appearanceOf(context.target);
|
|
71
|
+
const requested = accentValue(preset, appearance);
|
|
72
|
+
if (!parseColor(requested)) return void 0;
|
|
73
|
+
const floor = context.floor;
|
|
74
|
+
if (floor === void 0) return accentRoles(preset, context.target);
|
|
75
|
+
const gated = gateColor(requested, context.background, floor);
|
|
76
|
+
if (gated === requested) return accentRoles(preset, context.target);
|
|
77
|
+
return {
|
|
78
|
+
...accentRoles({
|
|
79
|
+
...preset,
|
|
80
|
+
light: gated,
|
|
81
|
+
dark: gated
|
|
82
|
+
}, context.target),
|
|
83
|
+
primarySubtle: primarySubtleCss(context.target, "--primary", [gated])
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolve an accent selection to its roles, or `undefined` when it overrides nothing.
|
|
88
|
+
*
|
|
89
|
+
* `undefined` is "use the theme's own primary": the literal `theme`, an id that is
|
|
90
|
+
* neither a preset, an offered theme accent nor a colour, and a colour that does not
|
|
91
|
+
* parse. A host that wants one of those to mean something else decides before
|
|
92
|
+
* asking (Adea's codec maps a stored `theme` to its default preset when it reads).
|
|
93
|
+
*/
|
|
94
|
+
function resolveAccentRoles(accent, context) {
|
|
95
|
+
if (accent === "theme") return void 0;
|
|
96
|
+
const custom = normalizeCustomAccent(accent);
|
|
97
|
+
if (custom) {
|
|
98
|
+
const floor = context.floor ?? 3;
|
|
99
|
+
return accentRolesForPreset({
|
|
100
|
+
id: "custom",
|
|
101
|
+
label: "Custom",
|
|
102
|
+
description: "",
|
|
103
|
+
light: custom,
|
|
104
|
+
dark: custom
|
|
105
|
+
}, {
|
|
106
|
+
...context,
|
|
107
|
+
floor
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const preset = ACCENTS.find((candidate) => candidate.id === accent) ?? context.themeAccents?.find((candidate) => candidate.id === accent);
|
|
111
|
+
return preset ? accentRolesForPreset(preset, context) : void 0;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The custom properties an accent override owns on `<html>`.
|
|
115
|
+
*
|
|
116
|
+
* `--primary-subtle` is deliberately not among them. It is a `color-mix()` over
|
|
117
|
+
* `var(--primary)`, so on the same element it follows an accent by itself, and its
|
|
118
|
+
* strength is the theme's, measured to keep body text at 4.5:1 on the tint.
|
|
119
|
+
* Writing one here would replace that measurement with the argument's.
|
|
120
|
+
*/
|
|
121
|
+
function accentOverrideVariables(roles) {
|
|
122
|
+
return {
|
|
123
|
+
"--primary": roles.primary,
|
|
124
|
+
"--primary-foreground": roles.primaryForeground,
|
|
125
|
+
"--primary-hover": roles.primaryHover,
|
|
126
|
+
"--ring": roles.ring
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** The custom properties {@link paintAccentRoles} writes and clears. */
|
|
130
|
+
var ACCENT_PAINT_PROPERTIES = [
|
|
131
|
+
"--primary",
|
|
132
|
+
"--primary-foreground",
|
|
133
|
+
"--primary-hover",
|
|
134
|
+
"--primary-subtle",
|
|
135
|
+
"--ring"
|
|
136
|
+
];
|
|
137
|
+
/**
|
|
138
|
+
* Scope an accent to one element and its descendants.
|
|
139
|
+
*
|
|
140
|
+
* Unlike the document, an element must write `--primary-subtle` too. A custom
|
|
141
|
+
* property that uses `var()` is resolved where it is declared, so the tint inherited
|
|
142
|
+
* from `<html>` is already a mix over *`<html>`'s* primary and an element that only
|
|
143
|
+
* overrides `--primary` would keep the old tint. Declared here, the same expression
|
|
144
|
+
* resolves against this element's primary.
|
|
145
|
+
*
|
|
146
|
+
* `undefined` clears every property, and the element inherits the document's accent
|
|
147
|
+
* again.
|
|
148
|
+
*/
|
|
149
|
+
function paintAccentRoles(element, roles) {
|
|
150
|
+
if (!roles) {
|
|
151
|
+
for (const property of ACCENT_PAINT_PROPERTIES) element.style.removeProperty(property);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
element.style.setProperty("--primary", roles.primary);
|
|
155
|
+
element.style.setProperty("--primary-foreground", roles.primaryForeground);
|
|
156
|
+
element.style.setProperty("--primary-hover", roles.primaryHover);
|
|
157
|
+
element.style.setProperty("--primary-subtle", roles.primarySubtle);
|
|
158
|
+
element.style.setProperty("--ring", roles.ring);
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { ACCENT_PAINT_PROPERTIES, accentOverrideVariables, accentRolesForPreset, isCustomAccent, normalizeCustomAccent, paintAccentRoles, resolveAccentRoles };
|
|
162
|
+
|
|
163
|
+
//# sourceMappingURL=accent-roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accent-roles.js","names":["parseCanonicalColor","canonicalContrastRatio"],"sources":["../../src/lib/accent-roles.ts"],"sourcesContent":["/*\n * Copyright (c) 2026 Wing\n * SPDX-License-Identifier: MIT\n *\n * The colour helpers, the contrast gate and the custom-accent grammar are\n * substantially translated from Zeron's `Color` and `AccentRoles`\n * (https://github.com/zeronsh/zeron), as Adea's accepted port of them in\n * `packages/ui/src/components/appearance.ts` expresses it. Extracted here so the\n * shared provider and Adea run one implementation; `@adea-ai/themes` owns the\n * colour engine (parsing, contrast, repair, the preset values and their roles).\n * See NOTICE.\n */\n\nimport {\n ACCENTS,\n accentRoles,\n accentValue,\n primarySubtleCss,\n type AccentPreset,\n type AccentRoles as CatalogueAccentRoles,\n} from '@adea-ai/themes'\nimport {\n contrastRatio as canonicalContrastRatio,\n oklchToHex,\n parseColor as parseCanonicalColor,\n repairContrast,\n type Oklch,\n} from '@adea-ai/themes/oklch'\nimport type { ThemeAppearance } from './themes'\n\n/**\n * Accent colour maths and the roles an accent paints.\n *\n * Three questions live here, and none of them touches the document or a signal:\n *\n * - **What colour is this?** `parseColor`, `colorToHex`, `contrastRatio` and\n * `ensureContrast` are the hex helpers a host needs around an accent (an\n * imported theme's colours, a colour field). Hex is the host preference format;\n * the catalogue's colour engine does the maths.\n * - **Is this accent readable on this surface?** The *contrast floor*. A preset is\n * curated, so the catalogue already measured it against its own themes and the\n * floor is the identity for it; a custom `#rrggbb` is anyone's choice, so it is\n * raised until it clears the floor and can never ship unreadable. A host that\n * imports themes (whose backgrounds the catalogue never saw) turns the floor on\n * for presets too.\n * - **Which custom properties does an accent own?** `AccentRoles`, written on\n * `<html>` by the provider and onto one element by {@link paintAccentRoles}.\n */\n\n/* Hex is the host preference format; the shared Themes package owns colour maths. */\n\nexport type RgbColor = Readonly<{ r: number; g: number; b: number; a: number }>\n\nconst HEX = /^#([0-9a-fA-F]+)$/\n\n/** Parse a hex colour (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`). `undefined` for anything else. */\nexport function parseColor(value: string): RgbColor | undefined {\n const match = HEX.exec(value.trim())\n const digits = match?.[1]\n if (!digits) return undefined\n if (digits.length === 3 || digits.length === 4) {\n // Each nibble is doubled: `#abc` is `#aabbcc`.\n const [r, g, b, a] = [...digits].map((digit) => Number.parseInt(digit + digit, 16))\n return { r: r!, g: g!, b: b!, a: a ?? 255 }\n }\n if (digits.length === 6 || digits.length === 8) {\n const byte = (index: number) => Number.parseInt(digits.slice(index, index + 2), 16)\n return { r: byte(0), g: byte(2), b: byte(4), a: digits.length === 8 ? byte(6) : 255 }\n }\n return undefined\n}\n\nfunction toHexByte(value: number): string {\n return value.toString(16).padStart(2, '0')\n}\n\n/** `#rrggbb`, or `#rrggbbaa` when the colour is not opaque. */\nexport function colorToHex(color: RgbColor): string {\n const opaque = `#${toHexByte(color.r)}${toHexByte(color.g)}${toHexByte(color.b)}`\n return color.a === 255 ? opaque : `${opaque}${toHexByte(color.a)}`\n}\n\nfunction blendOver(foreground: RgbColor, background: RgbColor): RgbColor {\n const alpha = foreground.a / 255\n const blend = (front: number, back: number) => Math.round(front * alpha + back * (1 - alpha))\n return {\n r: blend(foreground.r, background.r),\n g: blend(foreground.g, background.g),\n b: blend(foreground.b, background.b),\n a: 255,\n }\n}\n\nfunction toCanonicalColor(color: RgbColor): Oklch | undefined {\n return parseCanonicalColor(colorToHex({ ...color, a: 255 }))\n}\n\n/** WCAG contrast ratio between two colours. Alpha composites over the background first. */\nexport function contrastRatio(\n foreground: RgbColor | string,\n background: RgbColor | string\n): number {\n const front = typeof foreground === 'string' ? parseColor(foreground) : foreground\n const back = typeof background === 'string' ? parseColor(background) : background\n if (!front || !back) return 0\n const opaqueFront = front.a === 255 ? front : blendOver(front, back)\n const foregroundColor = toCanonicalColor(opaqueFront)\n const backgroundColor = toCanonicalColor(back)\n if (!foregroundColor || !backgroundColor) return 0\n return canonicalContrastRatio(foregroundColor, backgroundColor)\n}\n\nconst WHITE: RgbColor = { r: 255, g: 255, b: 255, a: 255 }\nconst WHITE_OKLCH: Oklch = { l: 1, c: 0, h: 0 }\n\n/**\n * The nearest colour to `source` that reaches `minimum` against `canvas`, as hex.\n *\n * The catalogue's `repairContrast` is asked for a little more than the floor, in\n * 0.001 steps up to 0.05, because the result is rounded to 8-bit hex and the\n * rounded colour is what is measured: asking for exactly the floor can land a hair\n * under it after rounding. If nothing in that range survives the rounding, white or\n * black stands in, whichever clears the floor.\n */\nfunction repairedHex(source: Oklch, canvas: Oklch, minimum: number): string {\n for (let margin = 0; margin <= 0.05; margin = Number((margin + 0.001).toFixed(3))) {\n const repaired = repairContrast(source, canvas, minimum + margin)\n if (!repaired.satisfied) continue\n const hex = oklchToHex(repaired.color)\n const rounded = parseCanonicalColor(hex)\n if (rounded && canonicalContrastRatio(rounded, canvas) >= minimum) return hex\n }\n return canonicalContrastRatio(WHITE_OKLCH, canvas) >= minimum ? '#ffffff' : '#000000'\n}\n\n/** Preserve the host hex shape while delegating contrast repair to the catalogue. */\nexport function ensureContrast(color: RgbColor, background: RgbColor, minimum: number): RgbColor {\n if (contrastRatio(color, background) >= minimum) return color\n\n const renderedColor = color.a === 255 ? color : blendOver(color, background)\n const source = toCanonicalColor(renderedColor)\n const canvas = toCanonicalColor(background)\n if (!source || !canvas) return color\n return parseColor(repairedHex(source, canvas, minimum))!\n}\n\n/** The contrast an accent is held to against its surface unless the host sets another. */\nexport const DEFAULT_ACCENT_CONTRAST_FLOOR = 3\n\n/**\n * Normalise a custom accent against a surface: unparseable input is rejected\n * (`undefined`), and any accepted colour is raised to `minimum` so an accent can\n * never ship unreadable. Accepts every CSS hex length; for the stored grammar use\n * {@link normalizeCustomAccent}.\n */\nexport function normalizeAccentValue(\n value: string,\n background: string,\n minimum: number = DEFAULT_ACCENT_CONTRAST_FLOOR\n): string | undefined {\n const parsed = parseColor(value)\n if (!parsed) return undefined\n const backdrop = parseColor(background) ?? WHITE\n return colorToHex(ensureContrast(parsed, backdrop, minimum))\n}\n\n/**\n * The stored form of a custom accent: `#rrggbb`, six digits, opaque, lower case.\n *\n * Deliberately narrower than {@link parseColor}. A stored accent is compared,\n * deduplicated and used as an attribute, so it has one spelling; a host that\n * accepts `#abc` in a field expands it before storing. Anything else — a name, an\n * `rgb()`, a short or alpha form, a non-string — is not an accent and returns\n * `undefined`.\n */\nexport function normalizeCustomAccent(value: unknown): string | undefined {\n return typeof value === 'string' && /^#[0-9a-fA-F]{6}$/.test(value)\n ? value.toLowerCase()\n : undefined\n}\n\n/** Whether a selection's accent is the custom `#rrggbb` form (any letter case). */\nexport function isCustomAccent(value: unknown): boolean {\n return normalizeCustomAccent(value) !== undefined\n}\n\n/**\n * The roles an accent paints, as CSS values: the catalogue's `AccentRoles`, so the\n * provider, the generated stylesheet and a host's own paint cannot disagree about\n * what a role is. `primarySubtle` is an expression over `var(--primary)`; the\n * document writer never sets it (see {@link accentOverrideVariables}), an element\n * paint must.\n */\nexport type AccentRoles = CatalogueAccentRoles\n\n/** What an accent resolves against: the surface it sits on and the theme it sits in. */\nexport type AccentTarget = {\n /** The theme, or only its appearance. A theme gives the tint its measured strength. */\n target: Parameters<typeof accentRoles>[1]\n /** The surface the accent must read on. Without it nothing can be measured or repaired. */\n background: string | undefined\n /**\n * The contrast to hold the accent to against `background`. Absent, a preset is\n * painted verbatim and a custom colour is held to {@link DEFAULT_ACCENT_CONTRAST_FLOOR}.\n */\n floor?: number\n /**\n * The accents the selected theme pair carries (`ansi-blue`…), for an id that is\n * neither a preset nor a colour.\n */\n themeAccents?: readonly AccentPreset[]\n}\n\nfunction appearanceOf(target: AccentTarget['target']): ThemeAppearance {\n return typeof target === 'string' ? target : target.appearance\n}\n\n/**\n * `value` raised to `floor` against `background` as hex, or `value` itself when it\n * already clears the floor, is not a colour, or there is no surface to measure it on.\n *\n * Returning the argument itself is what makes the floor the identity for a preset\n * that already reads: the caller compares and keeps the catalogue's own roles.\n */\nfunction gateColor(value: string, background: string | undefined, floor: number): string {\n const source = parseCanonicalColor(value)\n const canvas = background ? parseCanonicalColor(background) : undefined\n if (!source || !canvas) return value\n if (canonicalContrastRatio(source, canvas) >= floor) return value\n return repairedHex(source, canvas, floor)\n}\n\n/**\n * The roles for a preset (or a custom colour made into one), held to the floor.\n *\n * A preset that clears the floor keeps the catalogue's roles untouched. One that\n * does not is repaired to the nearest passing colour and the whole role set is\n * rebuilt from the repaired value, so the label, hover and ring follow it instead\n * of describing a colour that is no longer on screen.\n */\nexport function accentRolesForPreset(\n preset: AccentPreset,\n context: AccentTarget\n): AccentRoles | undefined {\n const appearance = appearanceOf(context.target)\n const requested = accentValue(preset, appearance)\n if (!parseCanonicalColor(requested)) return undefined\n\n const floor = context.floor\n if (floor === undefined) return accentRoles(preset, context.target)\n\n const gated = gateColor(requested, context.background, floor)\n if (gated === requested) return accentRoles(preset, context.target)\n\n const roles = accentRoles({ ...preset, light: gated, dark: gated }, context.target)\n // The tint's strength is measured against the primary it will sit over, and the\n // repaired primary is not one the theme's own measurement saw.\n return { ...roles, primarySubtle: primarySubtleCss(context.target, '--primary', [gated]) }\n}\n\n/**\n * Resolve an accent selection to its roles, or `undefined` when it overrides nothing.\n *\n * `undefined` is \"use the theme's own primary\": the literal `theme`, an id that is\n * neither a preset, an offered theme accent nor a colour, and a colour that does not\n * parse. A host that wants one of those to mean something else decides before\n * asking (Adea's codec maps a stored `theme` to its default preset when it reads).\n */\nexport function resolveAccentRoles(accent: string, context: AccentTarget): AccentRoles | undefined {\n if (accent === 'theme') return undefined\n\n const custom = normalizeCustomAccent(accent)\n if (custom) {\n const floor = context.floor ?? DEFAULT_ACCENT_CONTRAST_FLOOR\n return accentRolesForPreset(\n { id: 'custom', label: 'Custom', description: '', light: custom, dark: custom },\n { ...context, floor }\n )\n }\n\n const preset =\n ACCENTS.find((candidate) => candidate.id === accent) ??\n context.themeAccents?.find((candidate) => candidate.id === accent)\n return preset ? accentRolesForPreset(preset, context) : undefined\n}\n\n/**\n * The custom properties an accent override owns on `<html>`.\n *\n * `--primary-subtle` is deliberately not among them. It is a `color-mix()` over\n * `var(--primary)`, so on the same element it follows an accent by itself, and its\n * strength is the theme's, measured to keep body text at 4.5:1 on the tint.\n * Writing one here would replace that measurement with the argument's.\n */\nexport function accentOverrideVariables(roles: AccentRoles): Record<string, string> {\n return {\n '--primary': roles.primary,\n '--primary-foreground': roles.primaryForeground,\n '--primary-hover': roles.primaryHover,\n '--ring': roles.ring,\n }\n}\n\n/** The custom properties {@link paintAccentRoles} writes and clears. */\nexport const ACCENT_PAINT_PROPERTIES = [\n '--primary',\n '--primary-foreground',\n '--primary-hover',\n '--primary-subtle',\n '--ring',\n] as const\n\n/**\n * Scope an accent to one element and its descendants.\n *\n * Unlike the document, an element must write `--primary-subtle` too. A custom\n * property that uses `var()` is resolved where it is declared, so the tint inherited\n * from `<html>` is already a mix over *`<html>`'s* primary and an element that only\n * overrides `--primary` would keep the old tint. Declared here, the same expression\n * resolves against this element's primary.\n *\n * `undefined` clears every property, and the element inherits the document's accent\n * again.\n */\nexport function paintAccentRoles(\n element: { style: Pick<CSSStyleDeclaration, 'setProperty' | 'removeProperty'> },\n roles: AccentRoles | undefined\n): void {\n if (!roles) {\n for (const property of ACCENT_PAINT_PROPERTIES) element.style.removeProperty(property)\n return\n }\n element.style.setProperty('--primary', roles.primary)\n element.style.setProperty('--primary-foreground', roles.primaryForeground)\n element.style.setProperty('--primary-hover', roles.primaryHover)\n element.style.setProperty('--primary-subtle', roles.primarySubtle)\n element.style.setProperty('--ring', roles.ring)\n}\n"],"mappings":"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"}
|
|
@@ -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"}
|