@reopt-ai/opt-ui 1.4.1 → 1.5.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/COMPONENT_CATALOG.md +291 -46
- package/dist/{chunk-N4NDU5ET.cjs → chunk-3QFYBBL6.cjs} +66 -52
- package/dist/{chunk-QWBHD54V.js → chunk-3XD4HIFL.js} +26 -3
- package/dist/{chunk-X5WCXJAF.cjs → chunk-NXZJIHEZ.cjs} +26 -3
- package/dist/{chunk-ELWICXYY.js → chunk-VBGPEW45.js} +69 -55
- package/dist/core/index.cjs +2 -2
- package/dist/core/index.d.cts +2 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +1 -1
- package/dist/docs/02-components/01-core.md +156 -18
- package/dist/docs/02-components/03-shells.md +137 -30
- package/dist/docs/03-recipes/03-layouts.md +4 -0
- package/dist/docs/04-theming.md +3 -1
- package/dist/id-registry.cjs +8 -8
- package/dist/id-registry.js +8 -8
- package/dist/id-registry.json +16 -16
- package/dist/{index-DlAcuvQp.d.cts → index-DWyqDIkH.d.cts} +14 -11
- package/dist/{index-Slu5hOj1.d.ts → index-Uyijm14M.d.ts} +14 -11
- package/dist/index.cjs +282 -105
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +254 -77
- package/dist/meta.cjs +120 -10
- package/dist/meta.js +120 -10
- package/dist/shells/index.cjs +2 -2
- package/dist/shells/index.js +1 -1
- package/dist/tailwind.css +5 -5
- package/dist/theme/presets/default.css +12 -12
- package/dist/theme/server.cjs +281 -0
- package/dist/theme/server.d.cts +129 -0
- package/dist/theme/server.d.ts +129 -0
- package/dist/theme/server.js +242 -0
- package/package.json +14 -3
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/theme-server.ts
|
|
21
|
+
var theme_server_exports = {};
|
|
22
|
+
__export(theme_server_exports, {
|
|
23
|
+
ALL_COMPOUND_THEMES: () => ALL_COMPOUND_THEMES,
|
|
24
|
+
DARK_MQ: () => DARK_MQ,
|
|
25
|
+
FORCE_LIGHT_PRESETS: () => FORCE_LIGHT_PRESETS,
|
|
26
|
+
LEGACY_THEME_STORAGE_KEY: () => LEGACY_THEME_STORAGE_KEY,
|
|
27
|
+
THEME_MODE_STORAGE_KEY: () => THEME_MODE_STORAGE_KEY,
|
|
28
|
+
THEME_MODE_VALUES: () => THEME_MODE_VALUES,
|
|
29
|
+
THEME_PRESET_STORAGE_KEY: () => THEME_PRESET_STORAGE_KEY,
|
|
30
|
+
THEME_PRESET_VALUES: () => THEME_PRESET_VALUES,
|
|
31
|
+
coerceThemeMode: () => coerceThemeMode,
|
|
32
|
+
createThemeBootScript: () => createThemeBootScript,
|
|
33
|
+
getCompoundTheme: () => getCompoundTheme,
|
|
34
|
+
isThemeMode: () => isThemeMode,
|
|
35
|
+
isThemePreset: () => isThemePreset,
|
|
36
|
+
readStoredTheme: () => readStoredTheme,
|
|
37
|
+
resolveThemeMode: () => resolveThemeMode
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(theme_server_exports);
|
|
40
|
+
|
|
41
|
+
// src/lib/theme/presets/index.ts
|
|
42
|
+
var DEFAULT_DESCRIPTOR = {
|
|
43
|
+
id: "default",
|
|
44
|
+
label: "Default",
|
|
45
|
+
description: "Blue accent, balanced radius & shadow",
|
|
46
|
+
palette: {
|
|
47
|
+
seed: "#2563eb",
|
|
48
|
+
harmony: "split-complementary",
|
|
49
|
+
radiusScale: "medium",
|
|
50
|
+
shadowIntensity: "subtle",
|
|
51
|
+
neutralTemperature: "cool"
|
|
52
|
+
},
|
|
53
|
+
switcherSwatches: ["hsl(221 83% 53%)", "hsl(142 71% 45%)", "hsl(38 92% 50%)"]
|
|
54
|
+
};
|
|
55
|
+
var MINIMAL_DESCRIPTOR = {
|
|
56
|
+
id: "minimal",
|
|
57
|
+
label: "Minimal",
|
|
58
|
+
description: "Monochrome, no shadow, sharp edges",
|
|
59
|
+
palette: {
|
|
60
|
+
seed: "#333333",
|
|
61
|
+
harmony: "analogous",
|
|
62
|
+
radiusScale: "sharp",
|
|
63
|
+
shadowIntensity: "none",
|
|
64
|
+
neutralTemperature: "cool"
|
|
65
|
+
},
|
|
66
|
+
switcherSwatches: ["hsl(0 0% 20%)", "hsl(0 0% 45%)", "hsl(0 0% 70%)"]
|
|
67
|
+
};
|
|
68
|
+
var NATURAL_DESCRIPTOR = {
|
|
69
|
+
id: "natural",
|
|
70
|
+
label: "Natural",
|
|
71
|
+
description: "Always light \u2014 sage accent, warm cream",
|
|
72
|
+
forceLightOnly: true,
|
|
73
|
+
palette: {
|
|
74
|
+
seed: "#5a9376",
|
|
75
|
+
harmony: "complementary",
|
|
76
|
+
radiusScale: "medium",
|
|
77
|
+
shadowIntensity: "subtle",
|
|
78
|
+
neutralTemperature: "warm"
|
|
79
|
+
},
|
|
80
|
+
switcherSwatches: ["hsl(155 38% 45%)", "hsl(25 65% 55%)", "hsl(45 75% 50%)"]
|
|
81
|
+
};
|
|
82
|
+
var PRO_DESCRIPTOR = {
|
|
83
|
+
id: "pro",
|
|
84
|
+
label: "Pro",
|
|
85
|
+
description: "Neon blue accent, mono headings, ultra-dense",
|
|
86
|
+
palette: {
|
|
87
|
+
seed: "#0080ff",
|
|
88
|
+
harmony: "tetradic",
|
|
89
|
+
radiusScale: "sharp",
|
|
90
|
+
shadowIntensity: "subtle",
|
|
91
|
+
neutralTemperature: "cool"
|
|
92
|
+
},
|
|
93
|
+
switcherSwatches: [
|
|
94
|
+
"hsl(210 100% 55%)",
|
|
95
|
+
"hsl(170 90% 48%)",
|
|
96
|
+
"hsl(290 85% 60%)"
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
var MONO_DARK_DESCRIPTOR = {
|
|
100
|
+
id: "mono-dark",
|
|
101
|
+
label: "Mono Dark",
|
|
102
|
+
description: "OLED-friendly black, neon cyan accent, mono headings",
|
|
103
|
+
palette: {
|
|
104
|
+
seed: "#00d4ff",
|
|
105
|
+
harmony: "tetradic",
|
|
106
|
+
radiusScale: "sharp",
|
|
107
|
+
shadowIntensity: "subtle",
|
|
108
|
+
neutralTemperature: "cool"
|
|
109
|
+
},
|
|
110
|
+
switcherSwatches: ["hsl(190 100% 50%)", "hsl(0 0% 8%)", "hsl(280 90% 65%)"]
|
|
111
|
+
};
|
|
112
|
+
var THEME_REGISTRY = {
|
|
113
|
+
default: DEFAULT_DESCRIPTOR,
|
|
114
|
+
minimal: MINIMAL_DESCRIPTOR,
|
|
115
|
+
natural: NATURAL_DESCRIPTOR,
|
|
116
|
+
pro: PRO_DESCRIPTOR,
|
|
117
|
+
"mono-dark": MONO_DARK_DESCRIPTOR
|
|
118
|
+
};
|
|
119
|
+
var THEME_PRESET_IDS = Object.keys(THEME_REGISTRY);
|
|
120
|
+
function isThemePresetId(id) {
|
|
121
|
+
return id in THEME_REGISTRY;
|
|
122
|
+
}
|
|
123
|
+
var FORCE_LIGHT_PRESET_IDS = new Set(
|
|
124
|
+
THEME_PRESET_IDS.filter((id) => THEME_REGISTRY[id].forceLightOnly)
|
|
125
|
+
);
|
|
126
|
+
var DEPRECATED_PRESET_MIGRATIONS = Object.freeze({
|
|
127
|
+
corporate: "default",
|
|
128
|
+
playful: "default"
|
|
129
|
+
});
|
|
130
|
+
function migrateDeprecatedPresetId(id) {
|
|
131
|
+
if (!id) return null;
|
|
132
|
+
if (isThemePresetId(id)) return id;
|
|
133
|
+
if (id in DEPRECATED_PRESET_MIGRATIONS) {
|
|
134
|
+
return DEPRECATED_PRESET_MIGRATIONS[id];
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/lib/theme/theme-serialize.ts
|
|
140
|
+
var import_opt_palette = require("@reopt-ai/opt-palette");
|
|
141
|
+
var GLOBAL_THEME_STYLE_ID = "opt-global-theme";
|
|
142
|
+
var THEME_SPEC_STORAGE_KEY = "opt-theme-spec";
|
|
143
|
+
var THEME_TOKENS_STORAGE_KEY = "opt-theme-tokens";
|
|
144
|
+
|
|
145
|
+
// src/lib/theme/theme-resolution.ts
|
|
146
|
+
var THEME_PRESET_VALUES = THEME_PRESET_IDS;
|
|
147
|
+
var THEME_MODE_VALUES = ["light", "dark", "system"];
|
|
148
|
+
var LEGACY_THEME_STORAGE_KEY = "opt-theme";
|
|
149
|
+
var THEME_PRESET_STORAGE_KEY = "opt-preset";
|
|
150
|
+
var THEME_MODE_STORAGE_KEY = "opt-mode";
|
|
151
|
+
var DARK_MQ = "(prefers-color-scheme: dark)";
|
|
152
|
+
var FORCE_LIGHT_PRESETS = FORCE_LIGHT_PRESET_IDS;
|
|
153
|
+
var ALL_COMPOUND_THEMES = THEME_PRESET_IDS.flatMap(
|
|
154
|
+
(id) => FORCE_LIGHT_PRESET_IDS.has(id) ? [id] : [id, `${id}-dark`]
|
|
155
|
+
);
|
|
156
|
+
function hasStorageMutation(storage) {
|
|
157
|
+
return typeof storage.setItem === "function" && typeof storage.removeItem === "function";
|
|
158
|
+
}
|
|
159
|
+
function isThemePreset(value) {
|
|
160
|
+
return typeof value === "string" && THEME_PRESET_VALUES.includes(value);
|
|
161
|
+
}
|
|
162
|
+
function isThemeMode(value) {
|
|
163
|
+
return typeof value === "string" && THEME_MODE_VALUES.includes(value);
|
|
164
|
+
}
|
|
165
|
+
function getCompoundTheme(preset, resolvedMode) {
|
|
166
|
+
if (FORCE_LIGHT_PRESETS.has(preset)) return preset;
|
|
167
|
+
return resolvedMode === "dark" ? `${preset}-dark` : preset;
|
|
168
|
+
}
|
|
169
|
+
function resolveThemeMode(preset, mode, systemDark) {
|
|
170
|
+
if (FORCE_LIGHT_PRESETS.has(preset)) return "light";
|
|
171
|
+
if (mode === "system") return systemDark ? "dark" : "light";
|
|
172
|
+
return mode;
|
|
173
|
+
}
|
|
174
|
+
function readStoredTheme(storage, defaultPreset, defaultMode, options = {}) {
|
|
175
|
+
const legacy = storage.getItem(LEGACY_THEME_STORAGE_KEY);
|
|
176
|
+
const storedPreset = storage.getItem(THEME_PRESET_STORAGE_KEY);
|
|
177
|
+
const storedMode = storage.getItem(THEME_MODE_STORAGE_KEY);
|
|
178
|
+
let preset = defaultPreset;
|
|
179
|
+
if (typeof legacy === "string") {
|
|
180
|
+
const migratedLegacy = migrateDeprecatedPresetId(legacy);
|
|
181
|
+
if (migratedLegacy) {
|
|
182
|
+
if (options.migrateLegacy && hasStorageMutation(storage)) {
|
|
183
|
+
storage.setItem(THEME_PRESET_STORAGE_KEY, migratedLegacy);
|
|
184
|
+
storage.removeItem(LEGACY_THEME_STORAGE_KEY);
|
|
185
|
+
}
|
|
186
|
+
preset = migratedLegacy;
|
|
187
|
+
}
|
|
188
|
+
} else if (typeof storedPreset === "string") {
|
|
189
|
+
const migrated = migrateDeprecatedPresetId(storedPreset);
|
|
190
|
+
if (migrated) {
|
|
191
|
+
preset = migrated;
|
|
192
|
+
if (hasStorageMutation(storage) && migrated !== storedPreset && storedPreset in DEPRECATED_PRESET_MIGRATIONS) {
|
|
193
|
+
storage.setItem(THEME_PRESET_STORAGE_KEY, migrated);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const mode = isThemeMode(storedMode) ? storedMode : defaultMode;
|
|
198
|
+
return { preset, mode };
|
|
199
|
+
}
|
|
200
|
+
function coerceThemeMode(mode, allowedModes, fallback = "system") {
|
|
201
|
+
var _a;
|
|
202
|
+
if (!allowedModes || allowedModes.length === 0) return mode;
|
|
203
|
+
if (allowedModes.includes(mode)) return mode;
|
|
204
|
+
if (allowedModes.includes(fallback)) return fallback;
|
|
205
|
+
return (_a = allowedModes[0]) != null ? _a : fallback;
|
|
206
|
+
}
|
|
207
|
+
function createThemeBootScript(defaultPreset = "default", defaultMode = "system", options = {}) {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
const presetValues = JSON.stringify(THEME_PRESET_VALUES);
|
|
210
|
+
const modeValues = JSON.stringify(THEME_MODE_VALUES);
|
|
211
|
+
const allowedModes = ((_a = options.allowedModes) == null ? void 0 : _a.length) ? options.allowedModes : THEME_MODE_VALUES;
|
|
212
|
+
const allowedModeValues = JSON.stringify(allowedModes);
|
|
213
|
+
const forceLightPresets = JSON.stringify(Array.from(FORCE_LIGHT_PRESETS));
|
|
214
|
+
const deprecatedMap = JSON.stringify(DEPRECATED_PRESET_MIGRATIONS);
|
|
215
|
+
const lockedPreset = options.lockedPreset;
|
|
216
|
+
const resolvedDefaultPreset = lockedPreset != null ? lockedPreset : defaultPreset;
|
|
217
|
+
const defaultPresetLiteral = JSON.stringify(resolvedDefaultPreset);
|
|
218
|
+
const defaultModeLiteral = JSON.stringify(
|
|
219
|
+
coerceThemeMode(defaultMode, allowedModes, (_b = allowedModes[0]) != null ? _b : "system")
|
|
220
|
+
);
|
|
221
|
+
const lockedPresetLiteral = JSON.stringify(lockedPreset != null ? lockedPreset : null);
|
|
222
|
+
const styleId = JSON.stringify(GLOBAL_THEME_STYLE_ID);
|
|
223
|
+
const specKey = JSON.stringify(THEME_SPEC_STORAGE_KEY);
|
|
224
|
+
const tokensKey = JSON.stringify(THEME_TOKENS_STORAGE_KEY);
|
|
225
|
+
const presetKey = JSON.stringify(THEME_PRESET_STORAGE_KEY);
|
|
226
|
+
const modeKey = JSON.stringify(THEME_MODE_STORAGE_KEY);
|
|
227
|
+
const legacyKey = JSON.stringify(LEGACY_THEME_STORAGE_KEY);
|
|
228
|
+
const darkMq = JSON.stringify(DARK_MQ);
|
|
229
|
+
const source = `
|
|
230
|
+
try {
|
|
231
|
+
var pv=${presetValues},mv=${modeValues},am=${allowedModeValues},fp=${forceLightPresets},dm=${deprecatedMap},lk=${lockedPresetLiteral};
|
|
232
|
+
function mp(v){if(typeof v!=="string")return null;if(pv.indexOf(v)>=0)return v;return Object.prototype.hasOwnProperty.call(dm,v)?dm[v]:null;}
|
|
233
|
+
var p=${defaultPresetLiteral};
|
|
234
|
+
if(!lk){
|
|
235
|
+
var lp=localStorage.getItem(${legacyKey}),sp=localStorage.getItem(${presetKey});
|
|
236
|
+
var lpm=mp(lp),spm=mp(sp);
|
|
237
|
+
p=lpm||spm||${defaultPresetLiteral};
|
|
238
|
+
if(lpm){localStorage.setItem(${presetKey},lpm);localStorage.removeItem(${legacyKey});}
|
|
239
|
+
else if(sp&&spm&&sp!==spm){localStorage.setItem(${presetKey},spm);}
|
|
240
|
+
}
|
|
241
|
+
var sm=localStorage.getItem(${modeKey});
|
|
242
|
+
var m=(typeof sm==="string"&&mv.indexOf(sm)>=0&&am.indexOf(sm)>=0)?sm:${defaultModeLiteral};
|
|
243
|
+
var spec=null,tk=null;
|
|
244
|
+
if(!lk){try{var sj=localStorage.getItem(${specKey});if(sj)spec=JSON.parse(sj);}catch(e){}}
|
|
245
|
+
if(!lk){try{var tj=localStorage.getItem(${tokensKey});if(tj)tk=JSON.parse(tj);}catch(e){}}
|
|
246
|
+
if(!lk&&spec&&spec.kind==="generated"&&tk&&typeof tk.id==="string"&&tk.light&&typeof tk.light==="object"){
|
|
247
|
+
var fld=spec.forceLightOnly===true;
|
|
248
|
+
var dk=!fld&&(m==="dark"||(m==="system"&&matchMedia(${darkMq}).matches));
|
|
249
|
+
var tt=(dk&&tk.dark)?tk.dark:tk.light;
|
|
250
|
+
var cid=(dk&&tk.dark)?(tk.id+"-dark"):tk.id;
|
|
251
|
+
var css="";
|
|
252
|
+
for(var k in tt){if(Object.prototype.hasOwnProperty.call(tt,k))css+=k+":"+tt[k]+";"}
|
|
253
|
+
var st=document.getElementById(${styleId});
|
|
254
|
+
if(!st){st=document.createElement("style");st.id=${styleId};document.head.appendChild(st);}
|
|
255
|
+
st.textContent='[data-theme="'+cid+'"]{'+css+'}';
|
|
256
|
+
document.documentElement.setAttribute("data-theme",cid);
|
|
257
|
+
} else {
|
|
258
|
+
var d=fp.indexOf(p)<0&&(m==="dark"||(m==="system"&&matchMedia(${darkMq}).matches));
|
|
259
|
+
document.documentElement.setAttribute("data-theme",d?p+"-dark":p);
|
|
260
|
+
}
|
|
261
|
+
} catch(e){}`;
|
|
262
|
+
return source.replace(/\n\s*/g, "");
|
|
263
|
+
}
|
|
264
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
265
|
+
0 && (module.exports = {
|
|
266
|
+
ALL_COMPOUND_THEMES,
|
|
267
|
+
DARK_MQ,
|
|
268
|
+
FORCE_LIGHT_PRESETS,
|
|
269
|
+
LEGACY_THEME_STORAGE_KEY,
|
|
270
|
+
THEME_MODE_STORAGE_KEY,
|
|
271
|
+
THEME_MODE_VALUES,
|
|
272
|
+
THEME_PRESET_STORAGE_KEY,
|
|
273
|
+
THEME_PRESET_VALUES,
|
|
274
|
+
coerceThemeMode,
|
|
275
|
+
createThemeBootScript,
|
|
276
|
+
getCompoundTheme,
|
|
277
|
+
isThemeMode,
|
|
278
|
+
isThemePreset,
|
|
279
|
+
readStoredTheme,
|
|
280
|
+
resolveThemeMode
|
|
281
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { PaletteConfig } from '@reopt-ai/opt-palette';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* presets/index.ts
|
|
5
|
+
*
|
|
6
|
+
* Single registry of theme preset metadata. Each entry pairs a stable id
|
|
7
|
+
* (used in `<html data-theme>` and localStorage) with display metadata and an
|
|
8
|
+
* optional `PaletteConfig` that approximates the preset's seed for the
|
|
9
|
+
* ThemeSwitcher swatch row and "clone to custom" workflow.
|
|
10
|
+
*
|
|
11
|
+
* Token values themselves currently live in the sibling `*.css` files. A
|
|
12
|
+
* follow-up sub-phase will migrate the full token tables into this file and
|
|
13
|
+
* have a codegen step emit the CSS, but the metadata layer is enough to
|
|
14
|
+
* unblock dynamic ThemeSwitcher rendering and type-safe preset id unions.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface ThemeDescriptor {
|
|
18
|
+
/** Stable id used in `<html data-theme="...">` and localStorage. */
|
|
19
|
+
readonly id: string;
|
|
20
|
+
/** Human-readable label shown in ThemeSwitcher. */
|
|
21
|
+
readonly label: string;
|
|
22
|
+
/** Short description shown under the label. */
|
|
23
|
+
readonly description: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional engine input that approximates this preset. Used by
|
|
26
|
+
* ThemeSwitcher to derive swatches when `switcherSwatches` is omitted, and
|
|
27
|
+
* as the starting point when a user clones a preset into a custom palette.
|
|
28
|
+
* Codegen does NOT consume this in the current phase — the static `*.css`
|
|
29
|
+
* file remains the source of token values.
|
|
30
|
+
*/
|
|
31
|
+
readonly palette?: PaletteConfig;
|
|
32
|
+
/**
|
|
33
|
+
* Three CSS color strings for the ThemeSwitcher swatch row. Falls back to
|
|
34
|
+
* the live `--opt-accent` / `--opt-surface` / `--opt-text` tokens when
|
|
35
|
+
* omitted.
|
|
36
|
+
*/
|
|
37
|
+
readonly switcherSwatches?: readonly [string, string, string];
|
|
38
|
+
/** Disable dark variant generation entirely. */
|
|
39
|
+
readonly forceLightOnly?: boolean;
|
|
40
|
+
}
|
|
41
|
+
declare const THEME_REGISTRY: {
|
|
42
|
+
readonly default: ThemeDescriptor;
|
|
43
|
+
readonly minimal: ThemeDescriptor;
|
|
44
|
+
readonly natural: ThemeDescriptor;
|
|
45
|
+
readonly pro: ThemeDescriptor;
|
|
46
|
+
readonly "mono-dark": ThemeDescriptor;
|
|
47
|
+
};
|
|
48
|
+
/** Compile-time-derived union of registered preset ids. */
|
|
49
|
+
type ThemePresetId = keyof typeof THEME_REGISTRY;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Registered preset id list. Preferred for runtime iteration.
|
|
53
|
+
* @deprecated Prefer `THEME_PRESET_IDS` from `./presets`.
|
|
54
|
+
*/
|
|
55
|
+
declare const THEME_PRESET_VALUES: readonly ThemePresetId[];
|
|
56
|
+
/** Theme preset id — alias of `ThemePresetId` from the registry. */
|
|
57
|
+
type ThemePreset = ThemePresetId;
|
|
58
|
+
declare const THEME_MODE_VALUES: readonly ["light", "dark", "system"];
|
|
59
|
+
/** Type definition for theme mode. */
|
|
60
|
+
type ThemeMode = (typeof THEME_MODE_VALUES)[number];
|
|
61
|
+
/** Runtime constraints for apps that expose only a subset of opt-ui theming. */
|
|
62
|
+
interface ThemeRuntimeOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Force the root theme to one preset regardless of stored preset/generated
|
|
65
|
+
* theme state. Mode is still resolved normally unless constrained.
|
|
66
|
+
*/
|
|
67
|
+
lockedPreset?: ThemePreset;
|
|
68
|
+
/**
|
|
69
|
+
* Restrict mode reads/writes to a smaller set, e.g. `["light", "dark"]`.
|
|
70
|
+
* The first value becomes the fallback when the stored/default mode is not
|
|
71
|
+
* allowed.
|
|
72
|
+
*/
|
|
73
|
+
allowedModes?: readonly ThemeMode[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compound theme value applied to `<html data-theme>`. Derived from the
|
|
77
|
+
* registered preset ids: `<id>` (light) or `<id>-dark` (dark). Force-light
|
|
78
|
+
* presets only emit the bare id form.
|
|
79
|
+
*/
|
|
80
|
+
type CompoundTheme = `${ThemePreset}` | `${ThemePreset}-dark`;
|
|
81
|
+
declare const LEGACY_THEME_STORAGE_KEY = "opt-theme";
|
|
82
|
+
declare const THEME_PRESET_STORAGE_KEY = "opt-preset";
|
|
83
|
+
declare const THEME_MODE_STORAGE_KEY = "opt-mode";
|
|
84
|
+
declare const DARK_MQ = "(prefers-color-scheme: dark)";
|
|
85
|
+
/**
|
|
86
|
+
* Presets that force light mode regardless of system dark/light preference.
|
|
87
|
+
* Mirrors `FORCE_LIGHT_PRESET_IDS` from `./presets` — kept as alias for
|
|
88
|
+
* existing imports.
|
|
89
|
+
*/
|
|
90
|
+
declare const FORCE_LIGHT_PRESETS: ReadonlySet<ThemePreset>;
|
|
91
|
+
/** All valid compound theme names. Useful for static enumeration. */
|
|
92
|
+
declare const ALL_COMPOUND_THEMES: CompoundTheme[];
|
|
93
|
+
interface ThemeStorageReader {
|
|
94
|
+
getItem(key: string): string | null;
|
|
95
|
+
}
|
|
96
|
+
/** Handles is theme preset. */
|
|
97
|
+
declare function isThemePreset(value: string | null): value is ThemePreset;
|
|
98
|
+
/** Handles is theme mode. */
|
|
99
|
+
declare function isThemeMode(value: string | null): value is ThemeMode;
|
|
100
|
+
/** Compute compound theme name from preset + resolved mode. */
|
|
101
|
+
declare function getCompoundTheme(preset: ThemePreset, resolvedMode: "light" | "dark"): CompoundTheme;
|
|
102
|
+
/** Handles resolve theme mode. */
|
|
103
|
+
declare function resolveThemeMode(preset: ThemePreset, mode: ThemeMode, systemDark: boolean): "light" | "dark";
|
|
104
|
+
/** Handles read stored theme. */
|
|
105
|
+
declare function readStoredTheme(storage: ThemeStorageReader, defaultPreset: ThemePreset, defaultMode: ThemeMode, options?: {
|
|
106
|
+
migrateLegacy?: boolean;
|
|
107
|
+
}): {
|
|
108
|
+
preset: ThemePreset;
|
|
109
|
+
mode: ThemeMode;
|
|
110
|
+
};
|
|
111
|
+
/** Normalize a mode against an optional allowed-mode list. */
|
|
112
|
+
declare function coerceThemeMode(mode: ThemeMode, allowedModes: readonly ThemeMode[] | undefined, fallback?: ThemeMode): ThemeMode;
|
|
113
|
+
/**
|
|
114
|
+
* Build the inline `<head>` boot script that paints the correct theme before
|
|
115
|
+
* React mounts. Two paths:
|
|
116
|
+
*
|
|
117
|
+
* 1. **Generated** — when both `opt-theme-spec` (kind: "generated") and
|
|
118
|
+
* `opt-theme-tokens` are present and well-formed, a `<style id="…">` tag
|
|
119
|
+
* is inserted with the cached tokens and `<html data-theme>` is set to the
|
|
120
|
+
* generated id (with optional `-dark` suffix). This bypasses opt-palette
|
|
121
|
+
* at startup so first paint is instant.
|
|
122
|
+
* 2. **Preset (legacy)** — falls through to the existing preset selector,
|
|
123
|
+
* including legacy `opt-theme` → `opt-preset` migration.
|
|
124
|
+
*
|
|
125
|
+
* Returned as a single minified line; intended for `dangerouslySetInnerHTML`.
|
|
126
|
+
*/
|
|
127
|
+
declare function createThemeBootScript(defaultPreset?: ThemePreset, defaultMode?: ThemeMode, options?: ThemeRuntimeOptions): string;
|
|
128
|
+
|
|
129
|
+
export { ALL_COMPOUND_THEMES, type CompoundTheme, DARK_MQ, FORCE_LIGHT_PRESETS, LEGACY_THEME_STORAGE_KEY, THEME_MODE_STORAGE_KEY, THEME_MODE_VALUES, THEME_PRESET_STORAGE_KEY, THEME_PRESET_VALUES, type ThemeMode, type ThemePreset, type ThemeRuntimeOptions, coerceThemeMode, createThemeBootScript, getCompoundTheme, isThemeMode, isThemePreset, readStoredTheme, resolveThemeMode };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { PaletteConfig } from '@reopt-ai/opt-palette';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* presets/index.ts
|
|
5
|
+
*
|
|
6
|
+
* Single registry of theme preset metadata. Each entry pairs a stable id
|
|
7
|
+
* (used in `<html data-theme>` and localStorage) with display metadata and an
|
|
8
|
+
* optional `PaletteConfig` that approximates the preset's seed for the
|
|
9
|
+
* ThemeSwitcher swatch row and "clone to custom" workflow.
|
|
10
|
+
*
|
|
11
|
+
* Token values themselves currently live in the sibling `*.css` files. A
|
|
12
|
+
* follow-up sub-phase will migrate the full token tables into this file and
|
|
13
|
+
* have a codegen step emit the CSS, but the metadata layer is enough to
|
|
14
|
+
* unblock dynamic ThemeSwitcher rendering and type-safe preset id unions.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface ThemeDescriptor {
|
|
18
|
+
/** Stable id used in `<html data-theme="...">` and localStorage. */
|
|
19
|
+
readonly id: string;
|
|
20
|
+
/** Human-readable label shown in ThemeSwitcher. */
|
|
21
|
+
readonly label: string;
|
|
22
|
+
/** Short description shown under the label. */
|
|
23
|
+
readonly description: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional engine input that approximates this preset. Used by
|
|
26
|
+
* ThemeSwitcher to derive swatches when `switcherSwatches` is omitted, and
|
|
27
|
+
* as the starting point when a user clones a preset into a custom palette.
|
|
28
|
+
* Codegen does NOT consume this in the current phase — the static `*.css`
|
|
29
|
+
* file remains the source of token values.
|
|
30
|
+
*/
|
|
31
|
+
readonly palette?: PaletteConfig;
|
|
32
|
+
/**
|
|
33
|
+
* Three CSS color strings for the ThemeSwitcher swatch row. Falls back to
|
|
34
|
+
* the live `--opt-accent` / `--opt-surface` / `--opt-text` tokens when
|
|
35
|
+
* omitted.
|
|
36
|
+
*/
|
|
37
|
+
readonly switcherSwatches?: readonly [string, string, string];
|
|
38
|
+
/** Disable dark variant generation entirely. */
|
|
39
|
+
readonly forceLightOnly?: boolean;
|
|
40
|
+
}
|
|
41
|
+
declare const THEME_REGISTRY: {
|
|
42
|
+
readonly default: ThemeDescriptor;
|
|
43
|
+
readonly minimal: ThemeDescriptor;
|
|
44
|
+
readonly natural: ThemeDescriptor;
|
|
45
|
+
readonly pro: ThemeDescriptor;
|
|
46
|
+
readonly "mono-dark": ThemeDescriptor;
|
|
47
|
+
};
|
|
48
|
+
/** Compile-time-derived union of registered preset ids. */
|
|
49
|
+
type ThemePresetId = keyof typeof THEME_REGISTRY;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Registered preset id list. Preferred for runtime iteration.
|
|
53
|
+
* @deprecated Prefer `THEME_PRESET_IDS` from `./presets`.
|
|
54
|
+
*/
|
|
55
|
+
declare const THEME_PRESET_VALUES: readonly ThemePresetId[];
|
|
56
|
+
/** Theme preset id — alias of `ThemePresetId` from the registry. */
|
|
57
|
+
type ThemePreset = ThemePresetId;
|
|
58
|
+
declare const THEME_MODE_VALUES: readonly ["light", "dark", "system"];
|
|
59
|
+
/** Type definition for theme mode. */
|
|
60
|
+
type ThemeMode = (typeof THEME_MODE_VALUES)[number];
|
|
61
|
+
/** Runtime constraints for apps that expose only a subset of opt-ui theming. */
|
|
62
|
+
interface ThemeRuntimeOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Force the root theme to one preset regardless of stored preset/generated
|
|
65
|
+
* theme state. Mode is still resolved normally unless constrained.
|
|
66
|
+
*/
|
|
67
|
+
lockedPreset?: ThemePreset;
|
|
68
|
+
/**
|
|
69
|
+
* Restrict mode reads/writes to a smaller set, e.g. `["light", "dark"]`.
|
|
70
|
+
* The first value becomes the fallback when the stored/default mode is not
|
|
71
|
+
* allowed.
|
|
72
|
+
*/
|
|
73
|
+
allowedModes?: readonly ThemeMode[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compound theme value applied to `<html data-theme>`. Derived from the
|
|
77
|
+
* registered preset ids: `<id>` (light) or `<id>-dark` (dark). Force-light
|
|
78
|
+
* presets only emit the bare id form.
|
|
79
|
+
*/
|
|
80
|
+
type CompoundTheme = `${ThemePreset}` | `${ThemePreset}-dark`;
|
|
81
|
+
declare const LEGACY_THEME_STORAGE_KEY = "opt-theme";
|
|
82
|
+
declare const THEME_PRESET_STORAGE_KEY = "opt-preset";
|
|
83
|
+
declare const THEME_MODE_STORAGE_KEY = "opt-mode";
|
|
84
|
+
declare const DARK_MQ = "(prefers-color-scheme: dark)";
|
|
85
|
+
/**
|
|
86
|
+
* Presets that force light mode regardless of system dark/light preference.
|
|
87
|
+
* Mirrors `FORCE_LIGHT_PRESET_IDS` from `./presets` — kept as alias for
|
|
88
|
+
* existing imports.
|
|
89
|
+
*/
|
|
90
|
+
declare const FORCE_LIGHT_PRESETS: ReadonlySet<ThemePreset>;
|
|
91
|
+
/** All valid compound theme names. Useful for static enumeration. */
|
|
92
|
+
declare const ALL_COMPOUND_THEMES: CompoundTheme[];
|
|
93
|
+
interface ThemeStorageReader {
|
|
94
|
+
getItem(key: string): string | null;
|
|
95
|
+
}
|
|
96
|
+
/** Handles is theme preset. */
|
|
97
|
+
declare function isThemePreset(value: string | null): value is ThemePreset;
|
|
98
|
+
/** Handles is theme mode. */
|
|
99
|
+
declare function isThemeMode(value: string | null): value is ThemeMode;
|
|
100
|
+
/** Compute compound theme name from preset + resolved mode. */
|
|
101
|
+
declare function getCompoundTheme(preset: ThemePreset, resolvedMode: "light" | "dark"): CompoundTheme;
|
|
102
|
+
/** Handles resolve theme mode. */
|
|
103
|
+
declare function resolveThemeMode(preset: ThemePreset, mode: ThemeMode, systemDark: boolean): "light" | "dark";
|
|
104
|
+
/** Handles read stored theme. */
|
|
105
|
+
declare function readStoredTheme(storage: ThemeStorageReader, defaultPreset: ThemePreset, defaultMode: ThemeMode, options?: {
|
|
106
|
+
migrateLegacy?: boolean;
|
|
107
|
+
}): {
|
|
108
|
+
preset: ThemePreset;
|
|
109
|
+
mode: ThemeMode;
|
|
110
|
+
};
|
|
111
|
+
/** Normalize a mode against an optional allowed-mode list. */
|
|
112
|
+
declare function coerceThemeMode(mode: ThemeMode, allowedModes: readonly ThemeMode[] | undefined, fallback?: ThemeMode): ThemeMode;
|
|
113
|
+
/**
|
|
114
|
+
* Build the inline `<head>` boot script that paints the correct theme before
|
|
115
|
+
* React mounts. Two paths:
|
|
116
|
+
*
|
|
117
|
+
* 1. **Generated** — when both `opt-theme-spec` (kind: "generated") and
|
|
118
|
+
* `opt-theme-tokens` are present and well-formed, a `<style id="…">` tag
|
|
119
|
+
* is inserted with the cached tokens and `<html data-theme>` is set to the
|
|
120
|
+
* generated id (with optional `-dark` suffix). This bypasses opt-palette
|
|
121
|
+
* at startup so first paint is instant.
|
|
122
|
+
* 2. **Preset (legacy)** — falls through to the existing preset selector,
|
|
123
|
+
* including legacy `opt-theme` → `opt-preset` migration.
|
|
124
|
+
*
|
|
125
|
+
* Returned as a single minified line; intended for `dangerouslySetInnerHTML`.
|
|
126
|
+
*/
|
|
127
|
+
declare function createThemeBootScript(defaultPreset?: ThemePreset, defaultMode?: ThemeMode, options?: ThemeRuntimeOptions): string;
|
|
128
|
+
|
|
129
|
+
export { ALL_COMPOUND_THEMES, type CompoundTheme, DARK_MQ, FORCE_LIGHT_PRESETS, LEGACY_THEME_STORAGE_KEY, THEME_MODE_STORAGE_KEY, THEME_MODE_VALUES, THEME_PRESET_STORAGE_KEY, THEME_PRESET_VALUES, type ThemeMode, type ThemePreset, type ThemeRuntimeOptions, coerceThemeMode, createThemeBootScript, getCompoundTheme, isThemeMode, isThemePreset, readStoredTheme, resolveThemeMode };
|