@adea-ai/ui 0.128.2 → 0.128.4
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/theme/appearance-panel.js +1 -1
- package/dist/components/theme/index.js +1 -1
- package/dist/components/theme/lazy.d.ts +15 -0
- package/dist/components/theme/lazy.d.ts.map +1 -0
- package/dist/components/theme/lazy.js +9 -0
- package/dist/components/theme/theme-catalogue-source.d.ts +15 -0
- package/dist/components/theme/theme-catalogue-source.d.ts.map +1 -0
- package/dist/components/theme/theme-catalogue-source.js +51 -0
- package/dist/components/theme/theme-catalogue-source.js.map +1 -0
- package/dist/components/theme/theme-document-core.d.ts +123 -0
- package/dist/components/theme/theme-document-core.d.ts.map +1 -0
- package/dist/components/theme/{theme-document.js → theme-document-core.js} +12 -12
- package/dist/components/theme/theme-document-core.js.map +1 -0
- package/dist/components/theme/theme-document.d.ts +8 -107
- package/dist/components/theme/theme-document.d.ts.map +1 -1
- package/dist/components/theme/theme-persistence.d.ts +1 -1
- package/dist/components/theme/theme-persistence.d.ts.map +1 -1
- package/dist/components/theme/theme-persistence.js +9 -6
- package/dist/components/theme/theme-persistence.js.map +1 -1
- package/dist/components/theme/theme-provider-core.d.ts +19 -0
- package/dist/components/theme/theme-provider-core.d.ts.map +1 -0
- package/dist/components/theme/theme-provider-core.js +28 -0
- package/dist/components/theme/theme-provider-core.js.map +1 -0
- package/dist/components/theme/theme-provider-lazy.d.ts +28 -0
- package/dist/components/theme/theme-provider-lazy.d.ts.map +1 -0
- package/dist/components/theme/theme-provider-lazy.js +36 -0
- package/dist/components/theme/theme-provider-lazy.js.map +1 -0
- package/dist/components/theme/theme-provider-value.d.ts +175 -0
- package/dist/components/theme/theme-provider-value.d.ts.map +1 -0
- package/dist/components/theme/theme-provider-value.js +200 -0
- package/dist/components/theme/theme-provider-value.js.map +1 -0
- package/dist/components/theme/theme-provider.d.ts +11 -136
- package/dist/components/theme/theme-provider.d.ts.map +1 -1
- package/dist/components/theme/theme-provider.js +27 -149
- package/dist/components/theme/theme-provider.js.map +1 -1
- package/dist/components/theme/theme-script.js +4 -3
- package/dist/components/theme/theme-script.js.map +1 -1
- package/dist/components/theme/theme-state-core.d.ts +248 -0
- package/dist/components/theme/theme-state-core.d.ts.map +1 -0
- package/dist/components/theme/{theme-state.js → theme-state-core.js} +8 -7
- package/dist/components/theme/theme-state-core.js.map +1 -0
- package/dist/components/theme/theme-state.d.ts +8 -214
- package/dist/components/theme/theme-state.d.ts.map +1 -1
- package/dist/components/theme/theme-toggle.js.map +1 -1
- package/dist/index.js +8 -4
- package/dist/lib/accent-presets.d.ts +35 -0
- package/dist/lib/accent-presets.d.ts.map +1 -0
- package/dist/lib/accent-presets.js +20 -0
- package/dist/lib/accent-presets.js.map +1 -0
- package/dist/lib/theme-catalogue.d.ts +3 -0
- package/dist/lib/theme-catalogue.d.ts.map +1 -0
- package/dist/lib/theme-catalogue.js +22 -0
- package/dist/lib/theme-catalogue.js.map +1 -0
- package/dist/lib/theme-css.d.ts +43 -0
- package/dist/lib/theme-css.d.ts.map +1 -0
- package/dist/lib/theme-css.js +167 -0
- package/dist/lib/theme-css.js.map +1 -0
- package/dist/lib/theme-defaults.d.ts +46 -0
- package/dist/lib/theme-defaults.d.ts.map +1 -0
- package/dist/lib/theme-defaults.generated.d.ts +13 -0
- package/dist/lib/theme-defaults.generated.d.ts.map +1 -0
- package/dist/lib/theme-defaults.generated.js +318 -0
- package/dist/lib/theme-defaults.generated.js.map +1 -0
- package/dist/lib/theme-defaults.js +47 -0
- package/dist/lib/theme-defaults.js.map +1 -0
- package/dist/lib/theme-engine-light.d.ts +3 -0
- package/dist/lib/theme-engine-light.d.ts.map +1 -0
- package/dist/lib/theme-engine-light.js +75 -0
- package/dist/lib/theme-engine-light.js.map +1 -0
- package/dist/lib/theme-engine.d.ts +69 -0
- package/dist/lib/theme-engine.d.ts.map +1 -0
- package/dist/lib/theme-engine.js +33 -0
- package/dist/lib/theme-engine.js.map +1 -0
- package/dist/lib/theme-records.d.ts +39 -0
- package/dist/lib/theme-records.d.ts.map +1 -0
- package/dist/lib/theme-records.js +178 -0
- package/dist/lib/theme-records.js.map +1 -0
- package/dist/lib/themes.d.ts +16 -60
- package/dist/lib/themes.d.ts.map +1 -1
- package/dist/lib/themes.js +23 -322
- package/dist/lib/themes.js.map +1 -1
- package/dist/lib/tokens.d.ts +1 -25
- package/dist/lib/tokens.d.ts.map +1 -1
- package/dist/lib/tokens.js +1 -24
- package/dist/lib/tokens.js.map +1 -1
- package/dist/r/lib.json +40 -0
- package/dist/r/registry.json +75 -0
- package/dist/r/src/components/theme/lazy.ts +35 -0
- package/dist/r/src/components/theme/theme-catalogue-source.ts +59 -0
- package/dist/r/src/components/theme/theme-document-core.ts +262 -0
- package/dist/r/src/components/theme/theme-document.ts +23 -240
- package/dist/r/src/components/theme/theme-persistence.ts +14 -6
- package/dist/r/src/components/theme/theme-provider-core.tsx +40 -0
- package/dist/r/src/components/theme/theme-provider-lazy.tsx +34 -0
- package/dist/r/src/components/theme/theme-provider-value.ts +445 -0
- package/dist/r/src/components/theme/theme-provider.tsx +23 -346
- package/dist/r/src/components/theme/theme-script.ts +3 -3
- package/dist/r/src/components/theme/theme-state-core.ts +364 -0
- package/dist/r/src/components/theme/theme-state.ts +10 -329
- package/dist/r/src/components/theme/theme-toggle.tsx +1 -1
- package/dist/r/src/lib/accent-presets.ts +54 -0
- package/dist/r/src/lib/theme-catalogue.ts +26 -0
- package/dist/r/src/lib/theme-css.ts +201 -0
- package/dist/r/src/lib/theme-defaults.generated.ts +319 -0
- package/dist/r/src/lib/theme-defaults.ts +79 -0
- package/dist/r/src/lib/theme-engine-light.ts +117 -0
- package/dist/r/src/lib/theme-engine.ts +111 -0
- package/dist/r/src/lib/theme-records.ts +205 -0
- package/dist/r/src/lib/themes.ts +30 -375
- package/dist/r/src/lib/tokens.ts +1 -35
- package/dist/r/theming.json +35 -0
- package/package.json +10 -2
- package/registry.json +75 -0
- package/src/components/theme/lazy.ts +35 -0
- package/src/components/theme/theme-catalogue-source.ts +59 -0
- package/src/components/theme/theme-document-core.ts +262 -0
- package/src/components/theme/theme-document.ts +23 -240
- package/src/components/theme/theme-persistence.ts +14 -6
- package/src/components/theme/theme-provider-core.tsx +40 -0
- package/src/components/theme/theme-provider-lazy.tsx +34 -0
- package/src/components/theme/theme-provider-value.ts +445 -0
- package/src/components/theme/theme-provider.tsx +23 -346
- package/src/components/theme/theme-script.ts +3 -3
- package/src/components/theme/theme-state-core.ts +364 -0
- package/src/components/theme/theme-state.ts +10 -329
- package/src/components/theme/theme-toggle.tsx +1 -1
- package/src/lib/accent-presets.ts +54 -0
- package/src/lib/theme-catalogue.ts +26 -0
- package/src/lib/theme-css.ts +201 -0
- package/src/lib/theme-defaults.generated.ts +319 -0
- package/src/lib/theme-defaults.ts +79 -0
- package/src/lib/theme-engine-light.ts +117 -0
- package/src/lib/theme-engine.ts +111 -0
- package/src/lib/theme-records.ts +205 -0
- package/src/lib/themes.ts +30 -375
- package/src/lib/tokens.ts +1 -35
- package/dist/components/theme/theme-document.js.map +0 -1
- package/dist/components/theme/theme-state.js.map +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One catalogue record, as this system's {@link ThemeVariant}.
|
|
3
|
+
*
|
|
4
|
+
* `variantFromRecord` and the lazy form the catalogue is built with live here, and not
|
|
5
|
+
* beside the 34-theme table in `lib/themes.ts`, because a bundler places a module
|
|
6
|
+
* whole: a host that imports `variantFromRecord` from the same module as the table
|
|
7
|
+
* ships the table with it. This module reads a record it is handed and imports no
|
|
8
|
+
* catalogue, so a start-up path that only turns a stored record into a variant
|
|
9
|
+
* (an imported theme, a reconstruction) stays small. `lib/themes.ts` re-exports
|
|
10
|
+
* `variantFromRecord`, so `@adea-ai/ui/lib/themes` keeps working; the lazy start-up
|
|
11
|
+
* gate (`check:theme-startup`) holds the split in place.
|
|
12
|
+
*
|
|
13
|
+
* It does import `@adea-ai/themes` for the projection helpers (`chartSeries`,
|
|
14
|
+
* `tint`, `shadcnVariables`…), none of which reads the table of themes.
|
|
15
|
+
*/
|
|
16
|
+
import {
|
|
17
|
+
chartSeries,
|
|
18
|
+
shadcnDestructiveProjection,
|
|
19
|
+
statusForeground,
|
|
20
|
+
syntaxRoles,
|
|
21
|
+
tint,
|
|
22
|
+
type AdeaThemeRecord,
|
|
23
|
+
} from '@adea-ai/themes'
|
|
24
|
+
import { shadcnVariables } from '@adea-ai/themes/adapters/shadcn'
|
|
25
|
+
import type {
|
|
26
|
+
ThemeColors,
|
|
27
|
+
ThemeEditorPalette,
|
|
28
|
+
ThemeProvenance,
|
|
29
|
+
ThemeTerminalPalette,
|
|
30
|
+
ThemeVariant,
|
|
31
|
+
} from './themes'
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The shadcn-vocabulary roles of one catalogue entry.
|
|
35
|
+
*
|
|
36
|
+
* The expensive half of a variant: `shadcnVariables` projects the whole palette
|
|
37
|
+
* (about 4 ms a theme), which is why {@link lazyVariantFromRecord} defers it.
|
|
38
|
+
*/
|
|
39
|
+
function colorsOf(theme: AdeaThemeRecord): ThemeColors {
|
|
40
|
+
const projected = shadcnVariables(theme)
|
|
41
|
+
const token = (name: string): string => {
|
|
42
|
+
const value = projected[`--${name}`]
|
|
43
|
+
if (!value) throw new Error(`${theme.id} has no shared shadcn projection for --${name}`)
|
|
44
|
+
return value
|
|
45
|
+
}
|
|
46
|
+
const destructiveAction = shadcnDestructiveProjection(theme)
|
|
47
|
+
return {
|
|
48
|
+
background: token('background'),
|
|
49
|
+
foreground: token('foreground'),
|
|
50
|
+
card: token('card'),
|
|
51
|
+
cardForeground: token('card-foreground'),
|
|
52
|
+
popover: token('popover'),
|
|
53
|
+
popoverForeground: token('popover-foreground'),
|
|
54
|
+
primary: token('primary'),
|
|
55
|
+
primaryForeground: token('primary-foreground'),
|
|
56
|
+
secondary: token('secondary'),
|
|
57
|
+
secondaryForeground: token('secondary-foreground'),
|
|
58
|
+
muted: token('muted'),
|
|
59
|
+
mutedForeground: token('muted-foreground'),
|
|
60
|
+
accent: token('accent'),
|
|
61
|
+
accentForeground: token('accent-foreground'),
|
|
62
|
+
// Preserve the UI's semantic error token. The Themes projection uses its
|
|
63
|
+
// contrast-safe solid-action fill for --destructive; UI keeps that fill in
|
|
64
|
+
// the separate destructiveAction pair below.
|
|
65
|
+
destructive: theme.colors.error,
|
|
66
|
+
destructiveForeground: statusForeground(theme, 'error'),
|
|
67
|
+
destructiveSubtle: tint(theme.colors.error, theme.colors.background),
|
|
68
|
+
destructiveAction: destructiveAction.fill,
|
|
69
|
+
destructiveActionForeground: destructiveAction.foreground,
|
|
70
|
+
success: token('success'),
|
|
71
|
+
successForeground: statusForeground(theme, 'success'),
|
|
72
|
+
successSubtle: tint(theme.colors.success, theme.colors.background),
|
|
73
|
+
warning: token('warning'),
|
|
74
|
+
warningForeground: statusForeground(theme, 'warning'),
|
|
75
|
+
warningSubtle: tint(theme.colors.warning, theme.colors.background),
|
|
76
|
+
info: token('info'),
|
|
77
|
+
infoForeground: statusForeground(theme, 'info'),
|
|
78
|
+
infoSubtle: tint(theme.colors.info, theme.colors.background),
|
|
79
|
+
border: token('border'),
|
|
80
|
+
input: token('input'),
|
|
81
|
+
ring: token('ring'),
|
|
82
|
+
sidebar: token('sidebar'),
|
|
83
|
+
sidebarForeground: token('sidebar-foreground'),
|
|
84
|
+
sidebarAccent: token('sidebar-accent'),
|
|
85
|
+
sidebarBorder: token('sidebar-border'),
|
|
86
|
+
sidebarMutedForeground: token('sidebar-muted-foreground'),
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The embedded terminal uses the theme's own canvas rather than a palette of its
|
|
91
|
+
// own. The ANSI colours came from the palette that produced the canvas, so they
|
|
92
|
+
// were chosen against it; a terminal on a different background would be the one
|
|
93
|
+
// surface in the application not lit by the active theme.
|
|
94
|
+
function terminalOf(theme: AdeaThemeRecord): ThemeTerminalPalette {
|
|
95
|
+
return {
|
|
96
|
+
background: theme.colors.background,
|
|
97
|
+
foreground: theme.colors.foreground,
|
|
98
|
+
cursor: theme.cursor,
|
|
99
|
+
selection: theme.selection,
|
|
100
|
+
...theme.ansi,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function editorOf(theme: AdeaThemeRecord): ThemeEditorPalette {
|
|
105
|
+
const syntax = syntaxRoles(theme)
|
|
106
|
+
return {
|
|
107
|
+
keyword: syntax.keyword,
|
|
108
|
+
string: syntax.string,
|
|
109
|
+
number: syntax.number,
|
|
110
|
+
comment: syntax.comment,
|
|
111
|
+
function: syntax.function,
|
|
112
|
+
variable: syntax.variable,
|
|
113
|
+
type: syntax.type,
|
|
114
|
+
tag: syntax.tag,
|
|
115
|
+
attribute: syntax.attribute,
|
|
116
|
+
operator: syntax.operator,
|
|
117
|
+
heading: syntax.heading,
|
|
118
|
+
link: syntax.link,
|
|
119
|
+
diffAdd: syntax.diffAdd,
|
|
120
|
+
diffDelete: syntax.diffDelete,
|
|
121
|
+
diffHunk: syntax.diffHunk,
|
|
122
|
+
searchMatch: syntax.searchMatch,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function provenanceOf(theme: AdeaThemeRecord): ThemeProvenance {
|
|
127
|
+
return {
|
|
128
|
+
source: theme.provenance.project,
|
|
129
|
+
url: theme.provenance.url,
|
|
130
|
+
license: theme.provenance.license,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The design system's view of one catalogue record, built in full and at once.
|
|
136
|
+
*
|
|
137
|
+
* For a host that holds a record the catalogue does not — an imported theme, a
|
|
138
|
+
* reconstruction from stored data — and wants a variant it can hand to
|
|
139
|
+
* `ThemeProvider`'s `themes`. It throws for a record the shadcn projection cannot
|
|
140
|
+
* map, here and now, rather than on first paint. The catalogue itself goes through
|
|
141
|
+
* {@link lazyVariantFromRecord} instead.
|
|
142
|
+
*/
|
|
143
|
+
export function variantFromRecord(theme: AdeaThemeRecord): ThemeVariant {
|
|
144
|
+
return {
|
|
145
|
+
id: theme.id,
|
|
146
|
+
family: theme.family,
|
|
147
|
+
familyLabel: theme.familyLabel,
|
|
148
|
+
label: theme.label,
|
|
149
|
+
appearance: theme.appearance,
|
|
150
|
+
description: theme.description,
|
|
151
|
+
colors: colorsOf(theme),
|
|
152
|
+
terminal: terminalOf(theme),
|
|
153
|
+
editor: editorOf(theme),
|
|
154
|
+
chart: chartSeries(theme),
|
|
155
|
+
provenance: provenanceOf(theme),
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Runs `build` on first call and returns that value from then on. A throw is not cached. */
|
|
160
|
+
function once<T>(build: () => T): () => T {
|
|
161
|
+
let built = false
|
|
162
|
+
let value: T
|
|
163
|
+
return () => {
|
|
164
|
+
if (!built) {
|
|
165
|
+
value = build()
|
|
166
|
+
built = true
|
|
167
|
+
}
|
|
168
|
+
return value
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const lazy = <T>(build: () => T): PropertyDescriptor => ({
|
|
173
|
+
get: once(build),
|
|
174
|
+
enumerable: true,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* {@link variantFromRecord} with the four derived palettes built on first read.
|
|
179
|
+
*
|
|
180
|
+
* Evaluating this module used to project all 34 catalogue themes up front, about
|
|
181
|
+
* 155 ms before a single one was needed, on the hydration path of every consumer.
|
|
182
|
+
* A page paints one variant, and a picker's labels and appearance are plain
|
|
183
|
+
* fields, so only a variant whose `colors`, `terminal`, `editor` or `chart` is read
|
|
184
|
+
* pays for them, once. The properties stay enumerable data-shaped accessors, so
|
|
185
|
+
* spreading, `Object.entries` and JSON see the same object they always did.
|
|
186
|
+
*/
|
|
187
|
+
export function lazyVariantFromRecord(theme: AdeaThemeRecord): ThemeVariant {
|
|
188
|
+
return Object.defineProperties(
|
|
189
|
+
{
|
|
190
|
+
id: theme.id,
|
|
191
|
+
family: theme.family,
|
|
192
|
+
familyLabel: theme.familyLabel,
|
|
193
|
+
label: theme.label,
|
|
194
|
+
appearance: theme.appearance,
|
|
195
|
+
description: theme.description,
|
|
196
|
+
provenance: provenanceOf(theme),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
colors: lazy(() => colorsOf(theme)),
|
|
200
|
+
terminal: lazy(() => terminalOf(theme)),
|
|
201
|
+
editor: lazy(() => editorOf(theme)),
|
|
202
|
+
chart: lazy(() => chartSeries(theme)),
|
|
203
|
+
}
|
|
204
|
+
) as ThemeVariant
|
|
205
|
+
}
|
package/src/lib/themes.ts
CHANGED
|
@@ -34,19 +34,12 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import {
|
|
37
|
-
ACCENTS,
|
|
38
37
|
accentForeground,
|
|
39
|
-
chartSeries,
|
|
40
38
|
editorRolesHex,
|
|
41
39
|
getAccent,
|
|
42
40
|
getTheme,
|
|
43
41
|
hasTheme,
|
|
44
|
-
shadcnDestructiveProjection,
|
|
45
|
-
statusForeground,
|
|
46
|
-
tint,
|
|
47
|
-
primaryHover,
|
|
48
42
|
primarySubtleCss,
|
|
49
|
-
syntaxRoles,
|
|
50
43
|
themes as catalogue,
|
|
51
44
|
themesByAppearance,
|
|
52
45
|
themeAccentPresets,
|
|
@@ -56,12 +49,17 @@ import {
|
|
|
56
49
|
type AdeaThemeRecord,
|
|
57
50
|
type ContrastFinding,
|
|
58
51
|
} from '@adea-ai/themes'
|
|
59
|
-
import { shadcnVariables } from '@adea-ai/themes/adapters/shadcn'
|
|
60
|
-
import { oklchToHex, parseColor } from '@adea-ai/themes/oklch'
|
|
61
52
|
import { accentOverrideVariables, resolveAccentRoles, type AccentRoles } from './accent-roles'
|
|
62
53
|
import { isThemeAccentId } from './theme-accents'
|
|
54
|
+
import { themeCssVariablesWith } from './theme-css'
|
|
55
|
+
import { defaultDarkThemeId, defaultLightThemeId } from './theme-defaults'
|
|
56
|
+
import { lazyVariantFromRecord } from './theme-records'
|
|
63
57
|
|
|
58
|
+
export { accents } from './accent-presets'
|
|
64
59
|
export { isThemeAccentId } from './theme-accents'
|
|
60
|
+
export { terminalCssVariables, tokensAsHex } from './theme-css'
|
|
61
|
+
export { defaultDarkThemeId, defaultLightThemeId, resolveVariant } from './theme-defaults'
|
|
62
|
+
export { variantFromRecord } from './theme-records'
|
|
65
63
|
|
|
66
64
|
export type ThemeAppearance = 'light' | 'dark'
|
|
67
65
|
|
|
@@ -192,180 +190,6 @@ export type ThemeVariant = {
|
|
|
192
190
|
tokens?: Readonly<Record<string, string>>
|
|
193
191
|
}
|
|
194
192
|
|
|
195
|
-
/**
|
|
196
|
-
* The shadcn-vocabulary roles of one catalogue entry.
|
|
197
|
-
*
|
|
198
|
-
* The expensive half of a variant: `shadcnVariables` projects the whole palette
|
|
199
|
-
* (about 4 ms a theme), which is why {@link lazyVariant} defers it.
|
|
200
|
-
*/
|
|
201
|
-
function colorsOf(theme: AdeaThemeRecord): ThemeColors {
|
|
202
|
-
const projected = shadcnVariables(theme)
|
|
203
|
-
const token = (name: string): string => {
|
|
204
|
-
const value = projected[`--${name}`]
|
|
205
|
-
if (!value) throw new Error(`${theme.id} has no shared shadcn projection for --${name}`)
|
|
206
|
-
return value
|
|
207
|
-
}
|
|
208
|
-
const destructiveAction = shadcnDestructiveProjection(theme)
|
|
209
|
-
return {
|
|
210
|
-
background: token('background'),
|
|
211
|
-
foreground: token('foreground'),
|
|
212
|
-
card: token('card'),
|
|
213
|
-
cardForeground: token('card-foreground'),
|
|
214
|
-
popover: token('popover'),
|
|
215
|
-
popoverForeground: token('popover-foreground'),
|
|
216
|
-
primary: token('primary'),
|
|
217
|
-
primaryForeground: token('primary-foreground'),
|
|
218
|
-
secondary: token('secondary'),
|
|
219
|
-
secondaryForeground: token('secondary-foreground'),
|
|
220
|
-
muted: token('muted'),
|
|
221
|
-
mutedForeground: token('muted-foreground'),
|
|
222
|
-
accent: token('accent'),
|
|
223
|
-
accentForeground: token('accent-foreground'),
|
|
224
|
-
// Preserve the UI's semantic error token. The Themes projection uses its
|
|
225
|
-
// contrast-safe solid-action fill for --destructive; UI keeps that fill in
|
|
226
|
-
// the separate destructiveAction pair below.
|
|
227
|
-
destructive: theme.colors.error,
|
|
228
|
-
destructiveForeground: statusForeground(theme, 'error'),
|
|
229
|
-
destructiveSubtle: tint(theme.colors.error, theme.colors.background),
|
|
230
|
-
destructiveAction: destructiveAction.fill,
|
|
231
|
-
destructiveActionForeground: destructiveAction.foreground,
|
|
232
|
-
success: token('success'),
|
|
233
|
-
successForeground: statusForeground(theme, 'success'),
|
|
234
|
-
successSubtle: tint(theme.colors.success, theme.colors.background),
|
|
235
|
-
warning: token('warning'),
|
|
236
|
-
warningForeground: statusForeground(theme, 'warning'),
|
|
237
|
-
warningSubtle: tint(theme.colors.warning, theme.colors.background),
|
|
238
|
-
info: token('info'),
|
|
239
|
-
infoForeground: statusForeground(theme, 'info'),
|
|
240
|
-
infoSubtle: tint(theme.colors.info, theme.colors.background),
|
|
241
|
-
border: token('border'),
|
|
242
|
-
input: token('input'),
|
|
243
|
-
ring: token('ring'),
|
|
244
|
-
sidebar: token('sidebar'),
|
|
245
|
-
sidebarForeground: token('sidebar-foreground'),
|
|
246
|
-
sidebarAccent: token('sidebar-accent'),
|
|
247
|
-
sidebarBorder: token('sidebar-border'),
|
|
248
|
-
sidebarMutedForeground: token('sidebar-muted-foreground'),
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// The embedded terminal uses the theme's own canvas rather than a palette of its
|
|
253
|
-
// own. The ANSI colours came from the palette that produced the canvas, so they
|
|
254
|
-
// were chosen against it; a terminal on a different background would be the one
|
|
255
|
-
// surface in the application not lit by the active theme.
|
|
256
|
-
function terminalOf(theme: AdeaThemeRecord): ThemeTerminalPalette {
|
|
257
|
-
return {
|
|
258
|
-
background: theme.colors.background,
|
|
259
|
-
foreground: theme.colors.foreground,
|
|
260
|
-
cursor: theme.cursor,
|
|
261
|
-
selection: theme.selection,
|
|
262
|
-
...theme.ansi,
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function editorOf(theme: AdeaThemeRecord): ThemeEditorPalette {
|
|
267
|
-
const syntax = syntaxRoles(theme)
|
|
268
|
-
return {
|
|
269
|
-
keyword: syntax.keyword,
|
|
270
|
-
string: syntax.string,
|
|
271
|
-
number: syntax.number,
|
|
272
|
-
comment: syntax.comment,
|
|
273
|
-
function: syntax.function,
|
|
274
|
-
variable: syntax.variable,
|
|
275
|
-
type: syntax.type,
|
|
276
|
-
tag: syntax.tag,
|
|
277
|
-
attribute: syntax.attribute,
|
|
278
|
-
operator: syntax.operator,
|
|
279
|
-
heading: syntax.heading,
|
|
280
|
-
link: syntax.link,
|
|
281
|
-
diffAdd: syntax.diffAdd,
|
|
282
|
-
diffDelete: syntax.diffDelete,
|
|
283
|
-
diffHunk: syntax.diffHunk,
|
|
284
|
-
searchMatch: syntax.searchMatch,
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function provenanceOf(theme: AdeaThemeRecord): ThemeProvenance {
|
|
289
|
-
return {
|
|
290
|
-
source: theme.provenance.project,
|
|
291
|
-
url: theme.provenance.url,
|
|
292
|
-
license: theme.provenance.license,
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* The design system's view of one catalogue record, built in full and at once.
|
|
298
|
-
*
|
|
299
|
-
* For a host that holds a record the catalogue does not — an imported theme, a
|
|
300
|
-
* reconstruction from stored data — and wants a variant it can hand to
|
|
301
|
-
* `ThemeProvider`'s `themes`. It throws for a record the shadcn projection cannot
|
|
302
|
-
* map, here and now, rather than on first paint. The catalogue itself goes through
|
|
303
|
-
* {@link lazyVariant} instead.
|
|
304
|
-
*/
|
|
305
|
-
export function variantFromRecord(theme: AdeaThemeRecord): ThemeVariant {
|
|
306
|
-
return {
|
|
307
|
-
id: theme.id,
|
|
308
|
-
family: theme.family,
|
|
309
|
-
familyLabel: theme.familyLabel,
|
|
310
|
-
label: theme.label,
|
|
311
|
-
appearance: theme.appearance,
|
|
312
|
-
description: theme.description,
|
|
313
|
-
colors: colorsOf(theme),
|
|
314
|
-
terminal: terminalOf(theme),
|
|
315
|
-
editor: editorOf(theme),
|
|
316
|
-
chart: chartSeries(theme),
|
|
317
|
-
provenance: provenanceOf(theme),
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** Runs `build` on first call and returns that value from then on. A throw is not cached. */
|
|
322
|
-
function once<T>(build: () => T): () => T {
|
|
323
|
-
let built = false
|
|
324
|
-
let value: T
|
|
325
|
-
return () => {
|
|
326
|
-
if (!built) {
|
|
327
|
-
value = build()
|
|
328
|
-
built = true
|
|
329
|
-
}
|
|
330
|
-
return value
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const lazy = <T>(build: () => T): PropertyDescriptor => ({
|
|
335
|
-
get: once(build),
|
|
336
|
-
enumerable: true,
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* {@link variantFromRecord} with the four derived palettes built on first read.
|
|
341
|
-
*
|
|
342
|
-
* Evaluating this module used to project all 34 catalogue themes up front, about
|
|
343
|
-
* 155 ms before a single one was needed, on the hydration path of every consumer.
|
|
344
|
-
* A page paints one variant, and a picker's labels and appearance are plain
|
|
345
|
-
* fields, so only a variant whose `colors`, `terminal`, `editor` or `chart` is read
|
|
346
|
-
* pays for them, once. The properties stay enumerable data-shaped accessors, so
|
|
347
|
-
* spreading, `Object.entries` and JSON see the same object they always did.
|
|
348
|
-
*/
|
|
349
|
-
function lazyVariant(theme: AdeaThemeRecord): ThemeVariant {
|
|
350
|
-
return Object.defineProperties(
|
|
351
|
-
{
|
|
352
|
-
id: theme.id,
|
|
353
|
-
family: theme.family,
|
|
354
|
-
familyLabel: theme.familyLabel,
|
|
355
|
-
label: theme.label,
|
|
356
|
-
appearance: theme.appearance,
|
|
357
|
-
description: theme.description,
|
|
358
|
-
provenance: provenanceOf(theme),
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
colors: lazy(() => colorsOf(theme)),
|
|
362
|
-
terminal: lazy(() => terminalOf(theme)),
|
|
363
|
-
editor: lazy(() => editorOf(theme)),
|
|
364
|
-
chart: lazy(() => chartSeries(theme)),
|
|
365
|
-
}
|
|
366
|
-
) as ThemeVariant
|
|
367
|
-
}
|
|
368
|
-
|
|
369
193
|
/**
|
|
370
194
|
* A variant's `--primary-subtle`: the tint over `var(--primary)` at the strength its
|
|
371
195
|
* catalogue record measures (`primarySubtleAlpha`). A variant that is not in the
|
|
@@ -376,46 +200,22 @@ export function primarySubtleFor(theme: Pick<ThemeVariant, 'id' | 'appearance'>)
|
|
|
376
200
|
return record ? primarySubtleCss(record) : primarySubtleCss(theme.appearance)
|
|
377
201
|
}
|
|
378
202
|
|
|
379
|
-
/**
|
|
380
|
-
|
|
203
|
+
/**
|
|
204
|
+
* The catalogue, in catalogue order.
|
|
205
|
+
*
|
|
206
|
+
* The `@__PURE__` marks let a bundler drop the whole table, and with it the catalogue,
|
|
207
|
+
* from a consumer that imports only this module's catalogue-free helpers
|
|
208
|
+
* (`tokensAsHex`, `isThemeAccentId`, `resolveVariant`…) and never reads it.
|
|
209
|
+
*/
|
|
210
|
+
export const builtinThemes: readonly ThemeVariant[] = /* @__PURE__ */ Object.freeze(
|
|
211
|
+
/* @__PURE__ */ catalogue.map(lazyVariantFromRecord)
|
|
212
|
+
)
|
|
381
213
|
|
|
382
214
|
export function themeById(id: string): ThemeVariant | undefined {
|
|
383
215
|
if (!hasTheme(id)) return undefined
|
|
384
216
|
return builtinThemes.find((theme) => theme.id === id)
|
|
385
217
|
}
|
|
386
218
|
|
|
387
|
-
/**
|
|
388
|
-
* The variant a stored id means, or the default of the same appearance.
|
|
389
|
-
*
|
|
390
|
-
* An id the registry does not hold — an imported theme that was deleted, a
|
|
391
|
-
* catalogue entry that was renamed — must still paint *something of the appearance
|
|
392
|
-
* being shown*. Falling back to whichever variant happens to be first in the
|
|
393
|
-
* registry painted a dark palette under a light `color-scheme` for a light id, so
|
|
394
|
-
* the fallback is the appearance's own default, then any variant of that
|
|
395
|
-
* appearance, then the registry's first entry only when it holds nothing else.
|
|
396
|
-
*
|
|
397
|
-
* `registry` is the builtin catalogue plus whatever extras the host supplies; the
|
|
398
|
-
* stored id is never rewritten, only resolved, so a host that keeps an unknown id
|
|
399
|
-
* keeps resolving it if the theme comes back.
|
|
400
|
-
*
|
|
401
|
-
* The fallback order is translated from Zeron's `ThemeRegistry::resolve` (MIT,
|
|
402
|
-
* Copyright (c) 2026 Wing); see NOTICE.
|
|
403
|
-
*/
|
|
404
|
-
export function resolveVariant(
|
|
405
|
-
registry: readonly ThemeVariant[],
|
|
406
|
-
id: string,
|
|
407
|
-
appearance: ThemeAppearance
|
|
408
|
-
): ThemeVariant {
|
|
409
|
-
const found = registry.find((variant) => variant.id === id)
|
|
410
|
-
if (found) return found
|
|
411
|
-
const defaultId = appearance === 'dark' ? defaultDarkThemeId : defaultLightThemeId
|
|
412
|
-
return (
|
|
413
|
-
registry.find((variant) => variant.id === defaultId) ??
|
|
414
|
-
registry.find((variant) => variant.appearance === appearance) ??
|
|
415
|
-
registry[0]!
|
|
416
|
-
)
|
|
417
|
-
}
|
|
418
|
-
|
|
419
219
|
/** The variants of one appearance, which is what a picker for that mode shows. */
|
|
420
220
|
export function themesForAppearance(appearance: ThemeAppearance): readonly ThemeVariant[] {
|
|
421
221
|
const ids = new Set(themesByAppearance(appearance).map((theme) => theme.id))
|
|
@@ -437,9 +237,6 @@ export function themeFamilies(): { family: string; label: string; themes: ThemeV
|
|
|
437
237
|
return [...families.values()]
|
|
438
238
|
}
|
|
439
239
|
|
|
440
|
-
/** The accent presets, in the order a picker should show them. */
|
|
441
|
-
export const accents = ACCENTS
|
|
442
|
-
|
|
443
240
|
/**
|
|
444
241
|
* The canonical catalogue records, in catalogue order.
|
|
445
242
|
*
|
|
@@ -708,166 +505,27 @@ export function editorPaletteFor(
|
|
|
708
505
|
/**
|
|
709
506
|
* The roles a variant sets, as CSS custom properties.
|
|
710
507
|
*
|
|
711
|
-
* The
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
* Absent it, the result is what it has always been; the generated `theme-variants.css`
|
|
718
|
-
* and the design-system artifact are built without it.
|
|
508
|
+
* The mapping itself is `themeCssVariablesWith` in `theme-css.ts`; this binds it to
|
|
509
|
+
* the catalogue, which measures a theme's `--primary-subtle` strength
|
|
510
|
+
* ({@link primarySubtleFor}) and projects its floor editor ramp
|
|
511
|
+
* ({@link editorPaletteFor}). Absent `options`, the result is what it has always
|
|
512
|
+
* been; the generated `theme-variants.css` and the design-system artifact are built
|
|
513
|
+
* without it.
|
|
719
514
|
*/
|
|
720
515
|
export function themeCssVariables(
|
|
721
516
|
theme: ThemeVariant,
|
|
722
517
|
options: ThemeCssVariableOptions = {}
|
|
723
518
|
): Record<string, string> {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
'--card-foreground': c.cardForeground,
|
|
732
|
-
'--popover': c.popover,
|
|
733
|
-
'--popover-foreground': c.popoverForeground,
|
|
734
|
-
'--primary': c.primary,
|
|
735
|
-
'--primary-foreground': c.primaryForeground,
|
|
736
|
-
/*
|
|
737
|
-
* The two tokens derived from the primary, and they are here rather than left to
|
|
738
|
-
* `theme.css` because they must follow `--primary` — which this function writes
|
|
739
|
-
* for *any* theme in the catalogue, not just the two defaults `theme.css` carries.
|
|
740
|
-
*
|
|
741
|
-
* `--primary-hover` is a resolved colour, not a `color-mix()`: a uniform hover
|
|
742
|
-
* step cannot be expressed as a mix (see `primaryHover`). Left out, a theme switch
|
|
743
|
-
* would keep the previous theme's hover on the new theme's primary.
|
|
744
|
-
*
|
|
745
|
-
* `--primary-subtle` is a mix over `--primary`, so it follows an accent on its
|
|
746
|
-
* own, but its *strength* is per theme: `primarySubtleCss(record)` lowers the
|
|
747
|
-
* appearance ceiling only as far as this theme needs for body text to clear 4.5:1
|
|
748
|
-
* on the tint, over every surface, for every accent it offers. It has to be
|
|
749
|
-
* written per theme for that reason — the appearance-wide value is not legible on
|
|
750
|
-
* every palette.
|
|
751
|
-
*/
|
|
752
|
-
'--primary-hover': primaryHover(c.primary, theme.appearance),
|
|
753
|
-
'--primary-subtle': primarySubtleFor(theme),
|
|
754
|
-
'--secondary': c.secondary,
|
|
755
|
-
'--secondary-foreground': c.secondaryForeground,
|
|
756
|
-
'--muted': c.muted,
|
|
757
|
-
'--muted-foreground': c.mutedForeground,
|
|
758
|
-
'--accent': c.accent,
|
|
759
|
-
'--accent-foreground': c.accentForeground,
|
|
760
|
-
'--destructive': c.destructive,
|
|
761
|
-
'--destructive-foreground': c.destructiveForeground,
|
|
762
|
-
'--destructive-subtle': c.destructiveSubtle,
|
|
763
|
-
'--destructive-action': c.destructiveAction,
|
|
764
|
-
'--destructive-action-foreground': c.destructiveActionForeground,
|
|
765
|
-
'--success': c.success,
|
|
766
|
-
'--success-foreground': c.successForeground,
|
|
767
|
-
'--success-subtle': c.successSubtle,
|
|
768
|
-
'--warning': c.warning,
|
|
769
|
-
'--warning-foreground': c.warningForeground,
|
|
770
|
-
'--warning-subtle': c.warningSubtle,
|
|
771
|
-
'--info': c.info,
|
|
772
|
-
'--info-foreground': c.infoForeground,
|
|
773
|
-
'--info-subtle': c.infoSubtle,
|
|
774
|
-
'--border': c.border,
|
|
775
|
-
'--input': c.input,
|
|
776
|
-
'--ring': c.ring,
|
|
777
|
-
'--sidebar': c.sidebar,
|
|
778
|
-
'--sidebar-foreground': c.sidebarForeground,
|
|
779
|
-
'--sidebar-accent': c.sidebarAccent,
|
|
780
|
-
'--sidebar-border': c.sidebarBorder,
|
|
781
|
-
'--sidebar-primary': c.primary,
|
|
782
|
-
'--sidebar-primary-foreground': c.primaryForeground,
|
|
783
|
-
'--sidebar-ring': c.ring,
|
|
784
|
-
'--sidebar-muted-foreground': c.sidebarMutedForeground,
|
|
785
|
-
'--surface-sunken': c.muted,
|
|
786
|
-
'--surface-hover': c.accent,
|
|
787
|
-
'--surface-active': c.border,
|
|
788
|
-
'--chrome': c.background,
|
|
789
|
-
'--terminal-background': t.background,
|
|
790
|
-
'--terminal-foreground': t.foreground,
|
|
791
|
-
'--terminal-cursor': t.cursor,
|
|
792
|
-
'--terminal-selection': t.selection,
|
|
793
|
-
'--terminal-ansi-black': t.black,
|
|
794
|
-
'--terminal-ansi-red': t.red,
|
|
795
|
-
'--terminal-ansi-green': t.green,
|
|
796
|
-
'--terminal-ansi-yellow': t.yellow,
|
|
797
|
-
'--terminal-ansi-blue': t.blue,
|
|
798
|
-
'--terminal-ansi-magenta': t.magenta,
|
|
799
|
-
'--terminal-ansi-cyan': t.cyan,
|
|
800
|
-
'--terminal-ansi-white': t.white,
|
|
801
|
-
'--terminal-ansi-bright-black': t.brightBlack,
|
|
802
|
-
'--terminal-ansi-bright-red': t.brightRed,
|
|
803
|
-
'--terminal-ansi-bright-green': t.brightGreen,
|
|
804
|
-
'--terminal-ansi-bright-yellow': t.brightYellow,
|
|
805
|
-
'--terminal-ansi-bright-blue': t.brightBlue,
|
|
806
|
-
'--terminal-ansi-bright-magenta': t.brightMagenta,
|
|
807
|
-
'--terminal-ansi-bright-cyan': t.brightCyan,
|
|
808
|
-
'--terminal-ansi-bright-white': t.brightWhite,
|
|
809
|
-
'--editor-keyword': e.keyword,
|
|
810
|
-
'--editor-string': e.string,
|
|
811
|
-
'--editor-number': e.number,
|
|
812
|
-
'--editor-comment': e.comment,
|
|
813
|
-
'--editor-function': e.function,
|
|
814
|
-
'--editor-variable': e.variable,
|
|
815
|
-
'--editor-type': e.type,
|
|
816
|
-
'--editor-tag': e.tag,
|
|
817
|
-
'--editor-attribute': e.attribute,
|
|
818
|
-
'--editor-operator': e.operator,
|
|
819
|
-
'--editor-heading': e.heading,
|
|
820
|
-
'--editor-link': e.link,
|
|
821
|
-
'--editor-diff-add': e.diffAdd,
|
|
822
|
-
'--editor-diff-delete': e.diffDelete,
|
|
823
|
-
'--editor-diff-hunk': e.diffHunk,
|
|
824
|
-
'--editor-search-match': e.searchMatch,
|
|
825
|
-
'--chart-1': theme.chart[0] ?? c.primary,
|
|
826
|
-
'--chart-2': theme.chart[1] ?? c.info,
|
|
827
|
-
'--chart-3': theme.chart[2] ?? c.success,
|
|
828
|
-
'--chart-4': theme.chart[3] ?? c.warning,
|
|
829
|
-
'--chart-5': theme.chart[4] ?? c.destructive,
|
|
830
|
-
'--chart-6': theme.chart[5] ?? c.mutedForeground,
|
|
831
|
-
}
|
|
832
|
-
return theme.tokens ? { ...derived, ...theme.tokens } : derived
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
/**
|
|
836
|
-
* The `--terminal-*` properties of a variant: the palette a terminal paints.
|
|
837
|
-
*
|
|
838
|
-
* What a pinned terminal theme writes over the interface variant's own. Taken from
|
|
839
|
-
* {@link themeCssVariables} rather than rebuilt, so the names, and a custom
|
|
840
|
-
* variant's verbatim `tokens`, can never drift from what the interface variant
|
|
841
|
-
* would have written for the same palette.
|
|
842
|
-
*/
|
|
843
|
-
export function terminalCssVariables(theme: ThemeVariant): Record<string, string> {
|
|
844
|
-
return Object.fromEntries(
|
|
845
|
-
Object.entries(themeCssVariables(theme)).filter(([name]) => name.startsWith('--terminal-'))
|
|
519
|
+
return themeCssVariablesWith(
|
|
520
|
+
{
|
|
521
|
+
primarySubtle: primarySubtleFor,
|
|
522
|
+
editor: (variant, mode) => editorPaletteFor(variant, mode),
|
|
523
|
+
},
|
|
524
|
+
theme,
|
|
525
|
+
options
|
|
846
526
|
)
|
|
847
527
|
}
|
|
848
528
|
|
|
849
|
-
/**
|
|
850
|
-
* Token values with every `oklch()` colour written as `#rrggbb`.
|
|
851
|
-
*
|
|
852
|
-
* For a host whose stylesheet, tests or native shell speak hex: the catalogue's
|
|
853
|
-
* roles are `oklch()` strings, and this is the one conversion, gamut-mapped by the
|
|
854
|
-
* catalogue's own `oklchToHex`. A value that is already hex, carries alpha
|
|
855
|
-
* (`rgba()`), or is not a plain colour (`color-mix()`, `var()`) is left byte for
|
|
856
|
-
* byte, because converting would either lose the alpha or be a guess. Suitable as
|
|
857
|
-
* `ThemeProvider`'s `transformTokens`.
|
|
858
|
-
*/
|
|
859
|
-
export function tokensAsHex(tokens: Readonly<Record<string, string>>): Record<string, string> {
|
|
860
|
-
const converted: Record<string, string> = {}
|
|
861
|
-
for (const [name, value] of Object.entries(tokens)) {
|
|
862
|
-
const trimmed = value.trim()
|
|
863
|
-
// `parseOklch` discards alpha, so a translucent oklch() stays as written.
|
|
864
|
-
const opaque = trimmed.startsWith('oklch(') && !trimmed.includes('/')
|
|
865
|
-
const parsed = opaque ? parseColor(trimmed) : undefined
|
|
866
|
-
converted[name] = parsed ? oklchToHex(parsed) : value
|
|
867
|
-
}
|
|
868
|
-
return converted
|
|
869
|
-
}
|
|
870
|
-
|
|
871
529
|
/* ---------------------------------------------------------------------------
|
|
872
530
|
* Validation
|
|
873
531
|
*
|
|
@@ -914,8 +572,5 @@ export function validateThemeRegistry(
|
|
|
914
572
|
return [...orphaned, ...validateCatalogue(records).map(toFinding)]
|
|
915
573
|
}
|
|
916
574
|
|
|
917
|
-
export const defaultLightThemeId = 'adea-light'
|
|
918
|
-
export const defaultDarkThemeId = 'adea-dark'
|
|
919
|
-
|
|
920
575
|
/** Re-exported so a consumer measuring a colour does not need a second import. */
|
|
921
576
|
export { contrastRatio } from '@adea-ai/themes'
|