@adea-ai/ui 0.28.0 → 0.29.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/components/appearance.d.ts +245 -0
- package/dist/components/appearance.js +768 -0
- package/dist/components/theme-provider.d.ts +35 -10
- package/dist/components/theme-provider.js +86 -55
- package/dist/components/theme-swatch.d.ts +12 -0
- package/dist/components/theme-swatch.js +66 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/package.json +2 -1
- package/src/components/appearance.ts +1055 -0
- package/src/components/theme-provider.tsx +150 -66
- package/src/components/theme-swatch.tsx +49 -0
- package/src/index.tsx +1 -0
- package/src/styles/conventional-workspace.css +63 -2
- package/src/styles/theme.css +122 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The versioned client appearance preference (Dev Runtime spec,
|
|
3
|
+
* "Appearance and App Library"). Stored as one JSON document; the legacy
|
|
4
|
+
* single `theme` key migrates into it without deleting the old value.
|
|
5
|
+
*/
|
|
6
|
+
export type AppearancePreferencesV2 = Readonly<{
|
|
7
|
+
version: 2;
|
|
8
|
+
mode: AppearanceMode;
|
|
9
|
+
lightThemeId: string;
|
|
10
|
+
darkThemeId: string;
|
|
11
|
+
/** `'theme'`, a built-in preset id, or a validated `#rrggbb` color. */
|
|
12
|
+
accent: 'theme' | string;
|
|
13
|
+
surface: 'opaque' | 'frosted' | 'translucent';
|
|
14
|
+
reduceTransparency: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
export type AppearanceMode = 'system' | 'light' | 'dark';
|
|
17
|
+
export type ResolvedAppearance = 'light' | 'dark';
|
|
18
|
+
export type SurfacePreference = 'opaque' | 'frosted' | 'translucent';
|
|
19
|
+
export declare const APPEARANCE_STORAGE_KEY = "appearance";
|
|
20
|
+
/** The pre-#425 key. Migration reads it and never deletes it. */
|
|
21
|
+
export declare const LEGACY_THEME_STORAGE_KEY = "theme";
|
|
22
|
+
export declare const DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
23
|
+
export declare const REDUCED_TRANSPARENCY_QUERY = "(prefers-reduced-transparency: reduce)";
|
|
24
|
+
export declare const defaultAppearancePreferences: AppearancePreferencesV2;
|
|
25
|
+
/**
|
|
26
|
+
* Combine the user's choice with the OS state. A pinned mode ignores the OS;
|
|
27
|
+
* `system` follows it. (Zeron `resolve`.)
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveAppearanceMode(mode: AppearanceMode, system: ResolvedAppearance): ResolvedAppearance;
|
|
30
|
+
export type RgbColor = Readonly<{
|
|
31
|
+
r: number;
|
|
32
|
+
g: number;
|
|
33
|
+
b: number;
|
|
34
|
+
a: number;
|
|
35
|
+
}>;
|
|
36
|
+
export declare class ColorParseError extends Error {
|
|
37
|
+
constructor();
|
|
38
|
+
}
|
|
39
|
+
/** Parse a hex color. Returns `undefined` for anything unsupported. */
|
|
40
|
+
export declare function parseColor(value: string): RgbColor | undefined;
|
|
41
|
+
export declare function colorToHex(color: RgbColor): string;
|
|
42
|
+
/** WCAG contrast ratio between two colors. Alpha composites over the background first. */
|
|
43
|
+
export declare function contrastRatio(foreground: RgbColor | string, background: RgbColor | string): number;
|
|
44
|
+
export type AccentPreset = Readonly<{
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
dark: string;
|
|
48
|
+
light: string;
|
|
49
|
+
}>;
|
|
50
|
+
export declare const accentPresets: readonly AccentPreset[];
|
|
51
|
+
/**
|
|
52
|
+
* Normalize a custom accent color against a variant background: unparseable
|
|
53
|
+
* input is rejected (`undefined`), and any accepted color is raised to the
|
|
54
|
+
* 3:1 interaction minimum so an accent can never ship unreadable.
|
|
55
|
+
*/
|
|
56
|
+
export declare function normalizeAccentValue(value: string, background: string): string | undefined;
|
|
57
|
+
export declare function accentPresetById(id: string): AccentPreset | undefined;
|
|
58
|
+
export type AccentRoles = Readonly<{
|
|
59
|
+
/** Interactive primary. */
|
|
60
|
+
primary: string;
|
|
61
|
+
/** Text drawn on `primary`-filled controls; ≥ 4.5:1 against it. */
|
|
62
|
+
onPrimary: string;
|
|
63
|
+
/** Hover/press emphasis derived from the primary. */
|
|
64
|
+
strong: string;
|
|
65
|
+
/** Focus ring color. */
|
|
66
|
+
ring: string;
|
|
67
|
+
/** False when the selection is the variant's own accent and no role should be overridden. */
|
|
68
|
+
overrides: boolean;
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* Derive the accent roles for a selection against a variant. `'theme'` keeps
|
|
72
|
+
* the variant's own accent; presets and custom colors are normalized to the
|
|
73
|
+
* 3:1 interaction minimum (Zeron `AccentRoles::derive`, thresholds preserved).
|
|
74
|
+
*/
|
|
75
|
+
export declare function deriveAccentRoles(selection: string, variant: ThemeVariant): AccentRoles;
|
|
76
|
+
export type EffectiveSurface = 'opaque' | 'frosted' | 'translucent';
|
|
77
|
+
export declare function resolveSurface(preference: SurfacePreference, environment: Readonly<{
|
|
78
|
+
osReducedTransparency: boolean;
|
|
79
|
+
userReducedTransparency: boolean;
|
|
80
|
+
nativeTranslucency: boolean;
|
|
81
|
+
}>): EffectiveSurface;
|
|
82
|
+
export type ThemeTerminalPalette = Readonly<{
|
|
83
|
+
background: string;
|
|
84
|
+
foreground: string;
|
|
85
|
+
cursor: string;
|
|
86
|
+
selection: string;
|
|
87
|
+
/** The 16 xterm ANSI slots, in protocol order. */
|
|
88
|
+
ansi: readonly string[];
|
|
89
|
+
}>;
|
|
90
|
+
export type ThemeEditorRoles = Readonly<{
|
|
91
|
+
keyword: string;
|
|
92
|
+
string: string;
|
|
93
|
+
number: string;
|
|
94
|
+
comment: string;
|
|
95
|
+
function: string;
|
|
96
|
+
variable: string;
|
|
97
|
+
type: string;
|
|
98
|
+
tag: string;
|
|
99
|
+
attribute: string;
|
|
100
|
+
operator: string;
|
|
101
|
+
heading: string;
|
|
102
|
+
link: string;
|
|
103
|
+
diffAdd: string;
|
|
104
|
+
diffDelete: string;
|
|
105
|
+
diffHunk: string;
|
|
106
|
+
searchMatch: string;
|
|
107
|
+
}>;
|
|
108
|
+
export type ThemeChartRoles = Readonly<{
|
|
109
|
+
chart1: string;
|
|
110
|
+
chart2: string;
|
|
111
|
+
chart3: string;
|
|
112
|
+
chart4: string;
|
|
113
|
+
chart5: string;
|
|
114
|
+
chart6: string;
|
|
115
|
+
}>;
|
|
116
|
+
export type ThemeColors = Readonly<{
|
|
117
|
+
background: string;
|
|
118
|
+
foreground: string;
|
|
119
|
+
card: string;
|
|
120
|
+
cardForeground: string;
|
|
121
|
+
popover: string;
|
|
122
|
+
popoverForeground: string;
|
|
123
|
+
primary: string;
|
|
124
|
+
primaryForeground: string;
|
|
125
|
+
secondary: string;
|
|
126
|
+
secondaryForeground: string;
|
|
127
|
+
muted: string;
|
|
128
|
+
mutedForeground: string;
|
|
129
|
+
accent: string;
|
|
130
|
+
accentForeground: string;
|
|
131
|
+
destructive: string;
|
|
132
|
+
success: string;
|
|
133
|
+
border: string;
|
|
134
|
+
input: string;
|
|
135
|
+
ring: string;
|
|
136
|
+
}>;
|
|
137
|
+
export type ThemeVariant = Readonly<{
|
|
138
|
+
id: string;
|
|
139
|
+
familyId: string;
|
|
140
|
+
familyName: string;
|
|
141
|
+
name: string;
|
|
142
|
+
appearance: ResolvedAppearance;
|
|
143
|
+
colors: ThemeColors;
|
|
144
|
+
terminal: ThemeTerminalPalette;
|
|
145
|
+
editor: ThemeEditorRoles;
|
|
146
|
+
charts: ThemeChartRoles;
|
|
147
|
+
}>;
|
|
148
|
+
/**
|
|
149
|
+
* The independent per-appearance theme selection (Zeron `ThemeSelection`,
|
|
150
|
+
* carrying the V2 preference's field names).
|
|
151
|
+
*/
|
|
152
|
+
export type ThemeSelection = Readonly<{
|
|
153
|
+
lightThemeId: string;
|
|
154
|
+
darkThemeId: string;
|
|
155
|
+
}>;
|
|
156
|
+
export declare function themeSelectionVariantId(selection: ThemeSelection, appearance: ResolvedAppearance): string;
|
|
157
|
+
export type ValidationIssue = Readonly<{
|
|
158
|
+
variantId: string;
|
|
159
|
+
severity: 'error' | 'warning';
|
|
160
|
+
message: string;
|
|
161
|
+
}>;
|
|
162
|
+
/**
|
|
163
|
+
* Registry validation, ported from Zeron `ThemeRegistry::validate`: core text
|
|
164
|
+
* and muted text must reach 4.5:1, the accent 3:1, on-accent 4.5:1, the
|
|
165
|
+
* terminal foreground 4.5:1, and every chromatic ANSI slot 3:1 (slots 0 and 8
|
|
166
|
+
* are structural black/dim colors). Provenance completeness is a Zeron
|
|
167
|
+
* library concept; M12 ships only built-ins, so the structural check here is
|
|
168
|
+
* id/non-empty-color integrity.
|
|
169
|
+
*/
|
|
170
|
+
export declare function validateThemeRegistry(registry: readonly ThemeVariant[]): ValidationIssue[];
|
|
171
|
+
/**
|
|
172
|
+
* Resolve the variant for a selection, falling back deterministically to the
|
|
173
|
+
* default variant of the same appearance when the stored id is missing or
|
|
174
|
+
* broken — a corrupt preference degrades the palette, never the UI.
|
|
175
|
+
* (Zeron `ThemeRegistry::resolve`.)
|
|
176
|
+
*/
|
|
177
|
+
export declare function resolveThemeVariant(registry: readonly ThemeVariant[], selection: ThemeSelection, appearance: ResolvedAppearance): ThemeVariant;
|
|
178
|
+
/** The `adea-light`/`adea-dark` entries mirror `styles/theme.css`. */
|
|
179
|
+
export declare const builtinThemeRegistry: readonly ThemeVariant[];
|
|
180
|
+
export type NormalizedPreferences = Readonly<{
|
|
181
|
+
/** The strict v2 document, with every unknown field corrected to defaults. */
|
|
182
|
+
value: AppearancePreferencesV2;
|
|
183
|
+
/**
|
|
184
|
+
* The raw stored document when it was not a valid v2 record. Retained so a
|
|
185
|
+
* future version (or a repaired write) never loses the user's data.
|
|
186
|
+
*/
|
|
187
|
+
retainedRaw?: unknown;
|
|
188
|
+
}>;
|
|
189
|
+
/**
|
|
190
|
+
* Parse a stored appearance document. Anything that is not a strict version-2
|
|
191
|
+
* record falls back to the defaults and keeps the raw value for retention;
|
|
192
|
+
* individual unknown values inside a v2 record are corrected field by field.
|
|
193
|
+
*/
|
|
194
|
+
export declare function normalizeAppearancePreferences(raw: unknown): NormalizedPreferences;
|
|
195
|
+
/** The legacy single-key preference, mapped into v2 during migration. */
|
|
196
|
+
export declare function migrateLegacyThemeValue(stored: string | null): AppearancePreferencesV2 | undefined;
|
|
197
|
+
type AppearanceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
|
|
198
|
+
/**
|
|
199
|
+
* Read the appearance preferences: the v2 key first, then the legacy `theme`
|
|
200
|
+
* key, then defaults. Storage failures degrade to defaults like every other
|
|
201
|
+
* blocked-storage consumer.
|
|
202
|
+
*/
|
|
203
|
+
export declare function readAppearancePreferences(storage: AppearanceStorage | undefined): AppearancePreferencesV2;
|
|
204
|
+
export declare function writeAppearancePreferences(storage: AppearanceStorage | undefined, preferences: AppearancePreferencesV2): void;
|
|
205
|
+
export type ResolvedAppearanceState = Readonly<{
|
|
206
|
+
resolvedMode: ResolvedAppearance;
|
|
207
|
+
variant: ThemeVariant;
|
|
208
|
+
accent: AccentRoles;
|
|
209
|
+
effectiveSurface: EffectiveSurface;
|
|
210
|
+
reduceTransparencyActive: boolean;
|
|
211
|
+
}>;
|
|
212
|
+
/** Resolve preferences against the environment into the applied document state. */
|
|
213
|
+
export declare function resolveAppearanceState(preferences: AppearancePreferencesV2, environment: Readonly<{
|
|
214
|
+
systemAppearance: ResolvedAppearance;
|
|
215
|
+
osReducedTransparency: boolean;
|
|
216
|
+
nativeTranslucency: boolean;
|
|
217
|
+
}>, registry?: readonly ThemeVariant[]): ResolvedAppearanceState;
|
|
218
|
+
/**
|
|
219
|
+
* The custom properties a non-default variant owns, as a flat map. This one
|
|
220
|
+
* mapping feeds both the live provider and the pre-paint no-flash script, so
|
|
221
|
+
* a restored first paint and a later live switch cannot disagree. Default
|
|
222
|
+
* variants return an empty map: `styles/theme.css` declares those tokens.
|
|
223
|
+
*/
|
|
224
|
+
export declare function flatVariantTokens(variant: ThemeVariant): Record<string, string>;
|
|
225
|
+
/**
|
|
226
|
+
* Apply a resolved state to a document: the `dark` class and color scheme for
|
|
227
|
+
* the palette, the diagnostic data attributes, the accent role overrides, and
|
|
228
|
+
* the resolved variant's semantic tokens (including terminal ANSI and editor
|
|
229
|
+
* roles). CSS custom properties cascade, so terminals and editors update live
|
|
230
|
+
* without remounting.
|
|
231
|
+
*
|
|
232
|
+
* The default `adea-light`/`adea-dark` variants skip the palette override —
|
|
233
|
+
* `styles/theme.css` declares those tokens already, and leaving them CSS-owned
|
|
234
|
+
* keeps first paint byte-identical to the pre-#425 app.
|
|
235
|
+
*/
|
|
236
|
+
export declare function applyAppearanceToDocument(document: Document, state: ResolvedAppearanceState): void;
|
|
237
|
+
/**
|
|
238
|
+
* The no-flash preload script rendered in the document head. It re-resolves
|
|
239
|
+
* the stored (or legacy) preference against the OS before first paint and
|
|
240
|
+
* applies the same palette state the provider would, so hydration never shows
|
|
241
|
+
* the wrong palette. Accent overrides land with the provider: they decorate
|
|
242
|
+
* the resolved palette and cannot produce a wrong-palette flash.
|
|
243
|
+
*/
|
|
244
|
+
export declare function appearanceThemeScript(): string;
|
|
245
|
+
export {};
|