@aglyn/shared-ui-theme 1.0.0-beta.214 → 1.0.0-beta.215
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/package.json +4 -4
- package/src/lib/util/host-theme.d.ts +8 -1
- package/src/lib/util/host-theme.js +183 -1
- package/src/lib/util/host-theme.js.map +1 -1
- package/src/lib/util/theme-editor-fields.d.ts +7 -3
- package/src/lib/util/theme-editor-fields.js +6 -2
- package/src/lib/util/theme-editor-fields.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/shared-ui-theme",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.215",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
28
|
+
"@aglyn/shared-data-types": "1.0.0-beta.215",
|
|
29
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.215",
|
|
30
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.215",
|
|
31
31
|
"@emotion/cache": "^11.14.0",
|
|
32
32
|
"@emotion/react": "11.14.0",
|
|
33
33
|
"@swc/helpers": "0.5.23",
|
|
@@ -21,8 +21,15 @@ import type { ThemeOptions } from '../../vendor/mui';
|
|
|
21
21
|
* anything outside this list is dropped by {@link sanitizeHostTheme} so a
|
|
22
22
|
* tampered document can't restyle console-internal or portal-critical
|
|
23
23
|
* components.
|
|
24
|
+
*
|
|
25
|
+
* The list is what a site actually renders — its elements, the forms, booking
|
|
26
|
+
* and commerce surfaces, and the dialogs and menus they open — so a theme can
|
|
27
|
+
* restyle a whole site rather than its buttons alone (AGL-3403). Layout
|
|
28
|
+
* primitives (`MuiContainer`, `MuiGrid`, `MuiStack`, `MuiBox`) are absent on
|
|
29
|
+
* purpose: they carry the page structure, and a theme that reflowed them
|
|
30
|
+
* would break layouts the Besigner drew.
|
|
24
31
|
*/
|
|
25
|
-
export declare const HOST_THEME_COMPONENT_WHITELIST: readonly ["MuiAppBar", "MuiAvatar", "MuiBadge", "MuiButton", "MuiButtonBase", "MuiCard", "MuiCardContent", "MuiCheckbox", "MuiChip", "MuiCircularProgress", "MuiDivider", "MuiIconButton", "MuiLinearProgress", "MuiLink", "MuiList", "MuiListItem", "MuiMenu", "MuiPaper", "MuiRadio", "MuiSlider", "MuiSwitch", "MuiTab", "MuiTabs", "MuiTextField", "MuiToolbar", "MuiTooltip", "MuiTypography"];
|
|
32
|
+
export declare const HOST_THEME_COMPONENT_WHITELIST: readonly ["MuiAccordion", "MuiAccordionDetails", "MuiAccordionSummary", "MuiAlert", "MuiAlertTitle", "MuiAppBar", "MuiAvatar", "MuiBadge", "MuiBreadcrumbs", "MuiButton", "MuiButtonBase", "MuiButtonGroup", "MuiCard", "MuiCardActions", "MuiCardContent", "MuiCardHeader", "MuiCheckbox", "MuiChip", "MuiCircularProgress", "MuiDialog", "MuiDialogActions", "MuiDialogContent", "MuiDialogTitle", "MuiDivider", "MuiDrawer", "MuiFab", "MuiFilledInput", "MuiFormControlLabel", "MuiFormHelperText", "MuiFormLabel", "MuiIconButton", "MuiInput", "MuiInputBase", "MuiInputLabel", "MuiLinearProgress", "MuiLink", "MuiList", "MuiListItem", "MuiListItemButton", "MuiMenu", "MuiMenuItem", "MuiOutlinedInput", "MuiPagination", "MuiPaginationItem", "MuiPaper", "MuiRadio", "MuiRating", "MuiSelect", "MuiSlider", "MuiSnackbarContent", "MuiSwitch", "MuiTab", "MuiTable", "MuiTableCell", "MuiTableHead", "MuiTableRow", "MuiTabs", "MuiTextField", "MuiToggleButton", "MuiToggleButtonGroup", "MuiToolbar", "MuiTooltip", "MuiTypography"];
|
|
26
33
|
export type HostThemeComponentKey = (typeof HOST_THEME_COMPONENT_WHITELIST)[number];
|
|
27
34
|
/**
|
|
28
35
|
* Strips unknown component overrides and empty branches from a persisted
|
|
@@ -20,31 +20,74 @@ import { objectDeepMergeReplaceArrays } from "@aglyn/shared-util-vendor";
|
|
|
20
20
|
* anything outside this list is dropped by {@link sanitizeHostTheme} so a
|
|
21
21
|
* tampered document can't restyle console-internal or portal-critical
|
|
22
22
|
* components.
|
|
23
|
+
*
|
|
24
|
+
* The list is what a site actually renders — its elements, the forms, booking
|
|
25
|
+
* and commerce surfaces, and the dialogs and menus they open — so a theme can
|
|
26
|
+
* restyle a whole site rather than its buttons alone (AGL-3403). Layout
|
|
27
|
+
* primitives (`MuiContainer`, `MuiGrid`, `MuiStack`, `MuiBox`) are absent on
|
|
28
|
+
* purpose: they carry the page structure, and a theme that reflowed them
|
|
29
|
+
* would break layouts the Besigner drew.
|
|
23
30
|
*/ export const HOST_THEME_COMPONENT_WHITELIST = [
|
|
31
|
+
'MuiAccordion',
|
|
32
|
+
'MuiAccordionDetails',
|
|
33
|
+
'MuiAccordionSummary',
|
|
34
|
+
'MuiAlert',
|
|
35
|
+
'MuiAlertTitle',
|
|
24
36
|
'MuiAppBar',
|
|
25
37
|
'MuiAvatar',
|
|
26
38
|
'MuiBadge',
|
|
39
|
+
'MuiBreadcrumbs',
|
|
27
40
|
'MuiButton',
|
|
28
41
|
'MuiButtonBase',
|
|
42
|
+
'MuiButtonGroup',
|
|
29
43
|
'MuiCard',
|
|
44
|
+
'MuiCardActions',
|
|
30
45
|
'MuiCardContent',
|
|
46
|
+
'MuiCardHeader',
|
|
31
47
|
'MuiCheckbox',
|
|
32
48
|
'MuiChip',
|
|
33
49
|
'MuiCircularProgress',
|
|
50
|
+
'MuiDialog',
|
|
51
|
+
'MuiDialogActions',
|
|
52
|
+
'MuiDialogContent',
|
|
53
|
+
'MuiDialogTitle',
|
|
34
54
|
'MuiDivider',
|
|
55
|
+
'MuiDrawer',
|
|
56
|
+
'MuiFab',
|
|
57
|
+
'MuiFilledInput',
|
|
58
|
+
'MuiFormControlLabel',
|
|
59
|
+
'MuiFormHelperText',
|
|
60
|
+
'MuiFormLabel',
|
|
35
61
|
'MuiIconButton',
|
|
62
|
+
'MuiInput',
|
|
63
|
+
'MuiInputBase',
|
|
64
|
+
'MuiInputLabel',
|
|
36
65
|
'MuiLinearProgress',
|
|
37
66
|
'MuiLink',
|
|
38
67
|
'MuiList',
|
|
39
68
|
'MuiListItem',
|
|
69
|
+
'MuiListItemButton',
|
|
40
70
|
'MuiMenu',
|
|
71
|
+
'MuiMenuItem',
|
|
72
|
+
'MuiOutlinedInput',
|
|
73
|
+
'MuiPagination',
|
|
74
|
+
'MuiPaginationItem',
|
|
41
75
|
'MuiPaper',
|
|
42
76
|
'MuiRadio',
|
|
77
|
+
'MuiRating',
|
|
78
|
+
'MuiSelect',
|
|
43
79
|
'MuiSlider',
|
|
80
|
+
'MuiSnackbarContent',
|
|
44
81
|
'MuiSwitch',
|
|
45
82
|
'MuiTab',
|
|
83
|
+
'MuiTable',
|
|
84
|
+
'MuiTableCell',
|
|
85
|
+
'MuiTableHead',
|
|
86
|
+
'MuiTableRow',
|
|
46
87
|
'MuiTabs',
|
|
47
88
|
'MuiTextField',
|
|
89
|
+
'MuiToggleButton',
|
|
90
|
+
'MuiToggleButtonGroup',
|
|
48
91
|
'MuiToolbar',
|
|
49
92
|
'MuiTooltip',
|
|
50
93
|
'MuiTypography'
|
|
@@ -120,10 +163,99 @@ function sanitizeComponents(components) {
|
|
|
120
163
|
const entry = {};
|
|
121
164
|
if (override.defaultProps) entry.defaultProps = override.defaultProps;
|
|
122
165
|
if (override.styleOverrides) entry.styleOverrides = override.styleOverrides;
|
|
166
|
+
const sx = sanitizeSlotStyles(override.sx);
|
|
167
|
+
if (sx) entry.sx = sx;
|
|
168
|
+
const variants = sanitizeVariants(override.variants);
|
|
169
|
+
if (variants) entry.variants = variants;
|
|
123
170
|
if (Object.keys(entry).length) sanitized[key] = entry;
|
|
124
171
|
}
|
|
125
172
|
return Object.keys(sanitized).length ? sanitized : undefined;
|
|
126
173
|
}
|
|
174
|
+
/** `sx` slots: each a style object, anything else dropped. */ function sanitizeSlotStyles(slots) {
|
|
175
|
+
if (!isPlainObject(slots)) return undefined;
|
|
176
|
+
const kept = {};
|
|
177
|
+
for (const [slot, style] of Object.entries(slots)){
|
|
178
|
+
if (isPlainObject(style)) kept[slot] = style;
|
|
179
|
+
}
|
|
180
|
+
return Object.keys(kept).length ? kept : undefined;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Variants that can match and style something: a props object of scalars
|
|
184
|
+
* and a style or an `sx`. A malformed entry is dropped rather than handed to
|
|
185
|
+
* MUI, whose variant loop reads `props` and would throw on a missing one.
|
|
186
|
+
*/ function sanitizeVariants(variants) {
|
|
187
|
+
if (!Array.isArray(variants)) return undefined;
|
|
188
|
+
const kept = [];
|
|
189
|
+
for (const variant of variants){
|
|
190
|
+
if (!isPlainObject(variant) || !isPlainObject(variant['props'])) continue;
|
|
191
|
+
const props = variant['props'];
|
|
192
|
+
if (Object.values(props).some((value)=>value !== null && ![
|
|
193
|
+
'string',
|
|
194
|
+
'number',
|
|
195
|
+
'boolean'
|
|
196
|
+
].includes(typeof value))) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const entry = {
|
|
200
|
+
props: props
|
|
201
|
+
};
|
|
202
|
+
if (isPlainObject(variant['style'])) entry.style = variant['style'];
|
|
203
|
+
if (isPlainObject(variant['sx'])) entry.sx = variant['sx'];
|
|
204
|
+
if (entry.style || entry.sx) kept.push(entry);
|
|
205
|
+
}
|
|
206
|
+
return kept.length ? kept : undefined;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* An `sx` object as a style function of the theme it renders under.
|
|
210
|
+
*
|
|
211
|
+
* `components` are read against the BUILT theme of each scheme, so a palette
|
|
212
|
+
* path resolves to that scheme's color: `borderColor: 'divider'` is the light
|
|
213
|
+
* divider on light and the dark one on dark, which is the thing a literal
|
|
214
|
+
* override cannot say.
|
|
215
|
+
*/ function sxStyle(sx) {
|
|
216
|
+
return ({ theme })=>(theme == null ? void 0 : theme.unstable_sx) ? theme.unstable_sx(sx) : sx;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The stored (JSON) components as MUI reads them: each `sx` slot joins its
|
|
220
|
+
* literal `styleOverrides` slot, literal first, and each variant's `sx`
|
|
221
|
+
* becomes a style function.
|
|
222
|
+
*/ function componentsToThemeOptions(components) {
|
|
223
|
+
const converted = {};
|
|
224
|
+
for (const [key, override] of Object.entries(components)){
|
|
225
|
+
var _override_styleOverrides, _override_sx;
|
|
226
|
+
var _override_variants;
|
|
227
|
+
const entry = {};
|
|
228
|
+
if (override.defaultProps) entry['defaultProps'] = override.defaultProps;
|
|
229
|
+
const slots = new Set([
|
|
230
|
+
...Object.keys((_override_styleOverrides = override.styleOverrides) != null ? _override_styleOverrides : {}),
|
|
231
|
+
...Object.keys((_override_sx = override.sx) != null ? _override_sx : {})
|
|
232
|
+
]);
|
|
233
|
+
if (slots.size) {
|
|
234
|
+
const styleOverrides = {};
|
|
235
|
+
for (const slot of slots){
|
|
236
|
+
var _override_styleOverrides1, _override_sx1;
|
|
237
|
+
const literal = (_override_styleOverrides1 = override.styleOverrides) == null ? void 0 : _override_styleOverrides1[slot];
|
|
238
|
+
const sx = (_override_sx1 = override.sx) == null ? void 0 : _override_sx1[slot];
|
|
239
|
+
styleOverrides[slot] = sx && literal !== undefined ? [
|
|
240
|
+
literal,
|
|
241
|
+
sxStyle(sx)
|
|
242
|
+
] : sx ? sxStyle(sx) : literal;
|
|
243
|
+
}
|
|
244
|
+
entry['styleOverrides'] = styleOverrides;
|
|
245
|
+
}
|
|
246
|
+
if ((_override_variants = override.variants) == null ? void 0 : _override_variants.length) {
|
|
247
|
+
entry['variants'] = override.variants.map(({ props, style, sx })=>({
|
|
248
|
+
props,
|
|
249
|
+
style: sx ? (context)=>[
|
|
250
|
+
style != null ? style : {},
|
|
251
|
+
sxStyle(sx)(context)
|
|
252
|
+
] : style
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
converted[key] = entry;
|
|
256
|
+
}
|
|
257
|
+
return converted;
|
|
258
|
+
}
|
|
127
259
|
/**
|
|
128
260
|
* Strips unknown component overrides and empty branches from a persisted
|
|
129
261
|
* host theme. Returns a new object; the input is never mutated.
|
|
@@ -250,7 +382,9 @@ function sanitizeComponents(components) {
|
|
|
250
382
|
options.spacing = sanitized.spacing;
|
|
251
383
|
}
|
|
252
384
|
if (sanitized.components) {
|
|
253
|
-
|
|
385
|
+
// Ordered BEFORE conversion: an `sx` object ends up inside a closure,
|
|
386
|
+
// where the pass over the finished options below cannot reach it.
|
|
387
|
+
options.components = componentsToThemeOptions(orderMediaWidths(sanitized.components));
|
|
254
388
|
}
|
|
255
389
|
// Toolbar height is only reachable here (AGL-1242). MUI derives the
|
|
256
390
|
// Toolbar's `regular` variant style from `mixins.toolbar` and applies it
|
|
@@ -309,8 +443,56 @@ function sanitizeComponents(components) {
|
|
|
309
443
|
// back even in principle. Deep-merging means you override the leaf you
|
|
310
444
|
// named and inherit everything else, functions included.
|
|
311
445
|
merged.components = objectDeepMergeReplaceArrays((_base_components = base.components) != null ? _base_components : {}, (_overrides_components = overrides.components) != null ? _overrides_components : {});
|
|
446
|
+
composeComponentStyles(merged.components, base.components, overrides.components);
|
|
312
447
|
return merged;
|
|
313
448
|
}
|
|
449
|
+
/** A slot's styles as a list MUI resolves in order (its `processStyle` flattens arrays). */ function styleList(style) {
|
|
450
|
+
return Array.isArray(style) ? style : [
|
|
451
|
+
style
|
|
452
|
+
];
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Where a deep merge would REPLACE styling rather than add to it, composes
|
|
456
|
+
* the two instead (AGL-3403).
|
|
457
|
+
*
|
|
458
|
+
* A deep merge merges two style OBJECTS key by key, which is right. But the
|
|
459
|
+
* base styles several slots with a FUNCTION of the theme — `MuiButton.root`
|
|
460
|
+
* carries the accent-text contrast fix — and merging an object onto a
|
|
461
|
+
* function keeps only the object. So a theme that set one property on the
|
|
462
|
+
* button root silently removed the fix, and nothing JSON can say could put it
|
|
463
|
+
* back. MUI accepts a list per slot and applies it in order, so a function on
|
|
464
|
+
* either side makes the slot `[base, override]`: the override still wins
|
|
465
|
+
* every property it names, and everything else the base did still happens.
|
|
466
|
+
*
|
|
467
|
+
* `variants` concatenate for the same reason: the base's outlined
|
|
468
|
+
* `IconButton` must not disappear because a theme styled its `small` size.
|
|
469
|
+
*/ function composeComponentStyles(merged, base, overrides) {
|
|
470
|
+
if (!merged || !base || !overrides) return;
|
|
471
|
+
const target = merged;
|
|
472
|
+
for (const [name, override] of Object.entries(overrides)){
|
|
473
|
+
var _override_styleOverrides;
|
|
474
|
+
const from = base[name];
|
|
475
|
+
if (!from || !override) continue;
|
|
476
|
+
for (const [slot, style] of Object.entries((_override_styleOverrides = override.styleOverrides) != null ? _override_styleOverrides : {})){
|
|
477
|
+
var _from_styleOverrides;
|
|
478
|
+
const baseStyle = (_from_styleOverrides = from.styleOverrides) == null ? void 0 : _from_styleOverrides[slot];
|
|
479
|
+
if (baseStyle === undefined) continue;
|
|
480
|
+
if (isPlainObject(baseStyle) && isPlainObject(style)) continue;
|
|
481
|
+
target[name].styleOverrides = _extends({}, target[name].styleOverrides, {
|
|
482
|
+
[slot]: [
|
|
483
|
+
...styleList(baseStyle),
|
|
484
|
+
...styleList(style)
|
|
485
|
+
]
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
if (Array.isArray(from.variants) && Array.isArray(override.variants)) {
|
|
489
|
+
target[name].variants = [
|
|
490
|
+
...from.variants,
|
|
491
|
+
...override.variants
|
|
492
|
+
];
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
314
496
|
/** Plain data object — not an array, function, or class instance. */ function isPlainObject(value) {
|
|
315
497
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
316
498
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/host-theme.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeFont,\n HostThemePaletteColor,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport { objectDeepMergeReplaceArrays } from '@aglyn/shared-util-vendor'\nimport type { PaletteOptions, ThemeOptions } from '../../vendor/mui'\n\n/**\n * Components a host theme may override. Persisted overrides are plain JSON;\n * anything outside this list is dropped by {@link sanitizeHostTheme} so a\n * tampered document can't restyle console-internal or portal-critical\n * components.\n */\nexport const HOST_THEME_COMPONENT_WHITELIST = [\n 'MuiAppBar',\n 'MuiAvatar',\n 'MuiBadge',\n 'MuiButton',\n 'MuiButtonBase',\n 'MuiCard',\n 'MuiCardContent',\n 'MuiCheckbox',\n 'MuiChip',\n 'MuiCircularProgress',\n 'MuiDivider',\n 'MuiIconButton',\n 'MuiLinearProgress',\n 'MuiLink',\n 'MuiList',\n 'MuiListItem',\n 'MuiMenu',\n 'MuiPaper',\n 'MuiRadio',\n 'MuiSlider',\n 'MuiSwitch',\n 'MuiTab',\n 'MuiTabs',\n 'MuiTextField',\n 'MuiToolbar',\n 'MuiTooltip',\n 'MuiTypography',\n] as const\n\nexport type HostThemeComponentKey =\n (typeof HOST_THEME_COMPONENT_WHITELIST)[number]\n\nconst componentWhitelist: ReadonlySet<string> = new Set(\n HOST_THEME_COMPONENT_WHITELIST,\n)\n\nfunction pickPaletteColor(color: HostThemePaletteColor | undefined) {\n if (!color?.main) return undefined\n const picked: HostThemePaletteColor = { main: color.main }\n if (color.light) picked.light = color.light\n if (color.dark) picked.dark = color.dark\n if (color.contrastText) picked.contrastText = color.contrastText\n return picked\n}\n\nfunction schemeColorsToPaletteOptions(\n scheme: HostThemeScheme,\n colors: HostThemeSchemeColors | undefined,\n): PaletteOptions {\n const palette: PaletteOptions = { mode: scheme }\n if (!colors) return palette\n\n const colorKeys = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'surface',\n 'error',\n 'warning',\n 'info',\n 'success',\n ] as const\n for (const key of colorKeys) {\n const color = pickPaletteColor(colors[key])\n if (color) (palette as Record<string, unknown>)[key] = color\n }\n\n if (colors.background?.default || colors.background?.paper) {\n palette.background = {\n ...(colors.background.default && { default: colors.background.default }),\n ...(colors.background.paper && { paper: colors.background.paper }),\n }\n }\n if (\n colors.text?.primary ||\n colors.text?.secondary ||\n colors.text?.disabled\n ) {\n palette.text = {\n ...(colors.text.primary && { primary: colors.text.primary }),\n ...(colors.text.secondary && { secondary: colors.text.secondary }),\n ...(colors.text.disabled && { disabled: colors.text.disabled }),\n }\n }\n // Tints are string leaves, not a PaletteColor, so they pass through the\n // same \"copy what was set\" path as `background`/`text` rather than\n // `pickPaletteColor` — which requires a `main` a tint does not have\n // (AGL-1244).\n if (colors.tint?.primary || colors.tint?.secondary || colors.tint?.tertiary) {\n palette.tint = {\n ...(colors.tint.primary && { primary: colors.tint.primary }),\n ...(colors.tint.secondary && { secondary: colors.tint.secondary }),\n ...(colors.tint.tertiary && { tertiary: colors.tint.tertiary }),\n }\n }\n if (colors.divider) palette.divider = colors.divider\n\n return palette\n}\n\nfunction sanitizeComponents(\n components: Record<string, HostThemeComponentOverride> | undefined,\n) {\n if (!components) return undefined\n const sanitized: Record<string, HostThemeComponentOverride> = {}\n for (const [key, override] of Object.entries(components)) {\n if (!componentWhitelist.has(key) || !override) continue\n const entry: HostThemeComponentOverride = {}\n if (override.defaultProps) entry.defaultProps = override.defaultProps\n if (override.styleOverrides) entry.styleOverrides = override.styleOverrides\n if (Object.keys(entry).length) sanitized[key] = entry\n }\n return Object.keys(sanitized).length ? sanitized : undefined\n}\n\n/**\n * Strips unknown component overrides and empty branches from a persisted\n * host theme. Returns a new object; the input is never mutated.\n */\nexport function sanitizeHostTheme(theme: HostTheme | undefined): HostTheme {\n if (!theme) return {}\n const sanitized: HostTheme = { ...theme }\n const components = sanitizeComponents(theme.components)\n if (components) sanitized.components = components\n else delete sanitized.components\n // `mixins.toolbar` must be a CSS object; a scalar would reach\n // `createTheme` and throw while building the Toolbar variant.\n if (isPlainObject(theme.mixins?.toolbar)) {\n sanitized.mixins = { toolbar: theme.mixins.toolbar }\n } else {\n delete sanitized.mixins\n }\n // Absent already means \"follows the visitor\", so only the opt-out is worth\n // persisting; anything else (a stale `'auto'`, junk) is dropped.\n if (theme.darkScheme !== 'off') delete sanitized.darkScheme\n return sanitized\n}\n\n/** An `@media` key that names a `min-width`, and the width it names. */\nconst MIN_WIDTH_AT_RULE = /^@media\\b[^{]*\\bmin-width\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*px/\n\n/**\n * Re-orders breakpoint at-rules ascending, so STORED key order cannot decide\n * which rule wins (AGL-3146).\n *\n * Two `min-width` rules of equal specificity both match a wide window, so the\n * later one wins — which makes key order a rendering decision. Firestore does\n * not preserve it: one stored toolbar mixin came back sm, landscape, base to\n * the server reader and landscape, base, sm to the browser, so aglyn.com's\n * nav drew 48px live and 72px on the besigner canvas from a single saved\n * document. The editor and the page disagreed about a value neither of them\n * had changed.\n *\n * Ascending is the order MUI writes its own breakpoint styles in and the\n * order the theme editor writes a toolbar mixin in: the wider query is the\n * more specific answer, so it belongs last.\n *\n * Only two things move. Plain declarations are hoisted ahead of the blocks,\n * keeping their order among themselves — which is where the style engine\n * emits them anyway, since a nested block becomes a rule of its own after the\n * base one, and where the shorthand/longhand hazard lives. Breakpoint rules\n * are sorted into the slots the blocks already occupy, so a nested selector\n * or a condition naming no width never changes position against one another.\n *\n * Returns its input by identity when no order changes, which is every theme\n * with at most one breakpoint rule per object.\n */\nexport function orderMediaWidths<T>(value: T): T {\n if (Array.isArray(value)) {\n let moved = false\n const next = value.map((entry) => {\n const ordered = orderMediaWidths(entry)\n if (ordered !== entry) moved = true\n return ordered\n })\n return (moved ? next : value) as T\n }\n if (!isPlainObject(value)) return value\n\n const source = value as Record<string, unknown>\n const keys = Object.keys(source)\n const declarations = keys.filter((key) => !isPlainObject(source[key]))\n const blocks = keys.filter((key) => isPlainObject(source[key]))\n const widthSlots: number[] = []\n const widths: Array<{ key: string; width: number }> = []\n blocks.forEach((key, index) => {\n const match = MIN_WIDTH_AT_RULE.exec(key)\n if (!match) return\n widthSlots.push(index)\n widths.push({ key, width: Number.parseFloat(match[1]) })\n })\n // A stable sort, so two rules at the same width keep the order they were\n // stored in — there is nothing to prefer between them.\n const sorted = [...widths].sort((a, b) => a.width - b.width)\n widthSlots.forEach((slot, position) => {\n blocks[slot] = sorted[position].key\n })\n\n const order = [...declarations, ...blocks]\n let moved = order.some((key, index) => key !== keys[index])\n const next: Record<string, unknown> = {}\n for (const key of order) {\n const child = orderMediaWidths(source[key])\n if (child !== source[key]) moved = true\n next[key] = child\n }\n return (moved ? next : value) as T\n}\n\n/**\n * Converts a persisted {@link HostTheme} document into MUI `ThemeOptions`\n * for one color scheme. The result is meant to be passed through\n * `createResponsiveTheme` (or `createTheme`) by the consumer; shade and\n * contrast-text derivation for partial palettes is MUI's job, so only\n * explicitly set values are forwarded.\n */\nexport function hostThemeToThemeOptions(\n theme: HostTheme | undefined,\n scheme: HostThemeScheme,\n): ThemeOptions {\n const sanitized = sanitizeHostTheme(theme)\n const options: ThemeOptions = {\n palette: schemeColorsToPaletteOptions(\n scheme,\n sanitized.colorSchemes?.[scheme],\n ),\n }\n\n const { typography } = sanitized\n if (typography?.fontFamily || typography?.variants) {\n // HostThemeTypographyVariant is a sanitized subset of MUI's\n // TypographyStyleOptions; the missing index signature is by design.\n options.typography = {\n ...(typography.fontFamily && { fontFamily: typography.fontFamily }),\n ...typography.variants,\n } as ThemeOptions['typography']\n }\n\n if (typeof sanitized.shape?.borderRadius === 'number') {\n options.shape = { borderRadius: sanitized.shape.borderRadius }\n }\n if (typeof sanitized.spacing === 'number') {\n options.spacing = sanitized.spacing\n }\n if (sanitized.components) {\n options.components = sanitized.components as ThemeOptions['components']\n }\n // Toolbar height is only reachable here (AGL-1242). MUI derives the\n // Toolbar's `regular` variant style from `mixins.toolbar` and applies it\n // AFTER `components.MuiToolbar.styleOverrides`, so the slot override loses\n // every time — its nested media queries do not even emit.\n if (sanitized.mixins?.toolbar) {\n options.mixins = { toolbar: sanitized.mixins.toolbar }\n }\n\n // Every surface that renders a site's theme arrives here — the tenant's\n // provider, the besigner canvas, Preview, the theme editor — so this is\n // where a stored document stops being able to resolve differently for\n // different readers (AGL-3146).\n return orderMediaWidths(options)\n}\n\n/**\n * Layers a host's overrides ONTO a base set of theme options (AGL-1180).\n *\n * `hostThemeToThemeOptions` deliberately emits only what the host explicitly\n * set, so building a theme from it alone leaves every other slot to MUI's\n * stock palette. Consumers used to switch — console theme when the document\n * was empty, host document when it was not — which meant setting a single\n * value (the spec's own example is `{ spacing: 8 }`) silently repainted\n * secondary, tertiary, surface, info, success, warning, error, background\n * and paper in MUI blue/purple. Merging instead of switching keeps the brand\n * as the floor no matter how much the host customizes.\n *\n * `palette` merges one level deep so overriding `primary` cannot drop\n * `secondary`. Within a single colour the override replaces the whole record\n * — MUI derives shades and contrast text from `main`, which is exactly the\n * partial-palette behaviour the converter is written for.\n */\nexport function mergeThemeOptions(\n base: ThemeOptions,\n overrides: ThemeOptions,\n): ThemeOptions {\n const merged: ThemeOptions = { ...base, ...overrides }\n\n // Palette merges ONE level: overriding a colour replaces its whole record\n // so MUI re-derives light/dark/contrastText from the new `main`, which is\n // the partial-palette behaviour the converter is written for. Overriding\n // `primary` still must not disturb `secondary`, hence the level.\n merged.palette = { ...base.palette, ...overrides.palette }\n merged.shape = { ...base.shape, ...overrides.shape }\n // Same reasoning as `palette`: setting `toolbar` must not drop any other\n // mixin the base defines.\n merged.mixins = { ...base.mixins, ...overrides.mixins }\n\n // `typography` is an object in every base we ship, but MUI's type also\n // allows a function of the palette — merging into that would silently drop\n // the base, so prefer the override wholesale in that case.\n if (\n isPlainObject(base.typography) &&\n isPlainObject(overrides.typography)\n ) {\n merged.typography = objectDeepMergeReplaceArrays(\n base.typography,\n overrides.typography,\n ) as ThemeOptions['typography']\n }\n\n // Components merge DEEPLY. A host override names one component, and often\n // one property inside it — `MuiButton.defaultProps.color`. A shallow merge\n // would swap out the entire `MuiButton` entry and take the brand's\n // `styleOverrides` with it, and those styles are frequently FUNCTIONS of\n // the theme that JSON cannot express, so the editor could not put them\n // back even in principle. Deep-merging means you override the leaf you\n // named and inherit everything else, functions included.\n merged.components = objectDeepMergeReplaceArrays(\n base.components ?? {},\n overrides.components ?? {},\n ) as ThemeOptions['components']\n\n return merged\n}\n\n/** Plain data object — not an array, function, or class instance. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value)\n )\n}\n\n/** True when the document customizes anything, i.e. consumers should build a theme from it rather than using their default. */\nexport function hasHostTheme(theme: HostTheme | undefined): theme is HostTheme {\n return !!theme && Object.keys(theme).length > 0\n}\n\n/**\n * Builds a Google Fonts CSS2 stylesheet URL for the theme's loadable fonts.\n * Returns undefined when nothing needs loading (system/absent fonts).\n */\nexport function getGoogleFontsUrl(fonts: Array<HostThemeFont> | undefined) {\n const families = (fonts ?? [])\n .filter((font) => font.family && (font.source ?? 'google') === 'google')\n .map((font) => {\n const family = font.family.trim().replace(/\\s+/g, '+')\n const weights = font.weights?.length\n ? `:wght@${[...font.weights].sort((a, b) => a - b).join(';')}`\n : ''\n return `family=${family}${weights}`\n })\n if (!families.length) return undefined\n return `https://fonts.googleapis.com/css2?${families.join('&')}&display=swap`\n}\n\nexport default hostThemeToThemeOptions\n"],"names":["objectDeepMergeReplaceArrays","HOST_THEME_COMPONENT_WHITELIST","componentWhitelist","Set","pickPaletteColor","color","main","undefined","picked","light","dark","contrastText","schemeColorsToPaletteOptions","scheme","colors","palette","mode","colorKeys","key","background","default","paper","text","primary","secondary","disabled","tint","tertiary","divider","sanitizeComponents","components","sanitized","override","Object","entries","has","entry","defaultProps","styleOverrides","keys","length","sanitizeHostTheme","theme","isPlainObject","mixins","toolbar","darkScheme","MIN_WIDTH_AT_RULE","orderMediaWidths","value","Array","isArray","moved","next","map","ordered","source","declarations","filter","blocks","widthSlots","widths","forEach","index","match","exec","push","width","Number","parseFloat","sorted","sort","a","b","slot","position","order","some","child","hostThemeToThemeOptions","options","colorSchemes","typography","fontFamily","variants","shape","borderRadius","spacing","mergeThemeOptions","base","overrides","merged","hasHostTheme","getGoogleFontsUrl","fonts","families","font","family","trim","replace","weights","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/host-theme.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeComponentVariant,\n HostThemeFont,\n HostThemePaletteColor,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport { objectDeepMergeReplaceArrays } from '@aglyn/shared-util-vendor'\nimport type { PaletteOptions, ThemeOptions } from '../../vendor/mui'\n\n/**\n * Components a host theme may override. Persisted overrides are plain JSON;\n * anything outside this list is dropped by {@link sanitizeHostTheme} so a\n * tampered document can't restyle console-internal or portal-critical\n * components.\n *\n * The list is what a site actually renders — its elements, the forms, booking\n * and commerce surfaces, and the dialogs and menus they open — so a theme can\n * restyle a whole site rather than its buttons alone (AGL-3403). Layout\n * primitives (`MuiContainer`, `MuiGrid`, `MuiStack`, `MuiBox`) are absent on\n * purpose: they carry the page structure, and a theme that reflowed them\n * would break layouts the Besigner drew.\n */\nexport const HOST_THEME_COMPONENT_WHITELIST = [\n 'MuiAccordion',\n 'MuiAccordionDetails',\n 'MuiAccordionSummary',\n 'MuiAlert',\n 'MuiAlertTitle',\n 'MuiAppBar',\n 'MuiAvatar',\n 'MuiBadge',\n 'MuiBreadcrumbs',\n 'MuiButton',\n 'MuiButtonBase',\n 'MuiButtonGroup',\n 'MuiCard',\n 'MuiCardActions',\n 'MuiCardContent',\n 'MuiCardHeader',\n 'MuiCheckbox',\n 'MuiChip',\n 'MuiCircularProgress',\n 'MuiDialog',\n 'MuiDialogActions',\n 'MuiDialogContent',\n 'MuiDialogTitle',\n 'MuiDivider',\n 'MuiDrawer',\n 'MuiFab',\n 'MuiFilledInput',\n 'MuiFormControlLabel',\n 'MuiFormHelperText',\n 'MuiFormLabel',\n 'MuiIconButton',\n 'MuiInput',\n 'MuiInputBase',\n 'MuiInputLabel',\n 'MuiLinearProgress',\n 'MuiLink',\n 'MuiList',\n 'MuiListItem',\n 'MuiListItemButton',\n 'MuiMenu',\n 'MuiMenuItem',\n 'MuiOutlinedInput',\n 'MuiPagination',\n 'MuiPaginationItem',\n 'MuiPaper',\n 'MuiRadio',\n 'MuiRating',\n 'MuiSelect',\n 'MuiSlider',\n 'MuiSnackbarContent',\n 'MuiSwitch',\n 'MuiTab',\n 'MuiTable',\n 'MuiTableCell',\n 'MuiTableHead',\n 'MuiTableRow',\n 'MuiTabs',\n 'MuiTextField',\n 'MuiToggleButton',\n 'MuiToggleButtonGroup',\n 'MuiToolbar',\n 'MuiTooltip',\n 'MuiTypography',\n] as const\n\nexport type HostThemeComponentKey =\n (typeof HOST_THEME_COMPONENT_WHITELIST)[number]\n\nconst componentWhitelist: ReadonlySet<string> = new Set(\n HOST_THEME_COMPONENT_WHITELIST,\n)\n\nfunction pickPaletteColor(color: HostThemePaletteColor | undefined) {\n if (!color?.main) return undefined\n const picked: HostThemePaletteColor = { main: color.main }\n if (color.light) picked.light = color.light\n if (color.dark) picked.dark = color.dark\n if (color.contrastText) picked.contrastText = color.contrastText\n return picked\n}\n\nfunction schemeColorsToPaletteOptions(\n scheme: HostThemeScheme,\n colors: HostThemeSchemeColors | undefined,\n): PaletteOptions {\n const palette: PaletteOptions = { mode: scheme }\n if (!colors) return palette\n\n const colorKeys = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'surface',\n 'error',\n 'warning',\n 'info',\n 'success',\n ] as const\n for (const key of colorKeys) {\n const color = pickPaletteColor(colors[key])\n if (color) (palette as Record<string, unknown>)[key] = color\n }\n\n if (colors.background?.default || colors.background?.paper) {\n palette.background = {\n ...(colors.background.default && { default: colors.background.default }),\n ...(colors.background.paper && { paper: colors.background.paper }),\n }\n }\n if (\n colors.text?.primary ||\n colors.text?.secondary ||\n colors.text?.disabled\n ) {\n palette.text = {\n ...(colors.text.primary && { primary: colors.text.primary }),\n ...(colors.text.secondary && { secondary: colors.text.secondary }),\n ...(colors.text.disabled && { disabled: colors.text.disabled }),\n }\n }\n // Tints are string leaves, not a PaletteColor, so they pass through the\n // same \"copy what was set\" path as `background`/`text` rather than\n // `pickPaletteColor` — which requires a `main` a tint does not have\n // (AGL-1244).\n if (colors.tint?.primary || colors.tint?.secondary || colors.tint?.tertiary) {\n palette.tint = {\n ...(colors.tint.primary && { primary: colors.tint.primary }),\n ...(colors.tint.secondary && { secondary: colors.tint.secondary }),\n ...(colors.tint.tertiary && { tertiary: colors.tint.tertiary }),\n }\n }\n if (colors.divider) palette.divider = colors.divider\n\n return palette\n}\n\nfunction sanitizeComponents(\n components: Record<string, HostThemeComponentOverride> | undefined,\n) {\n if (!components) return undefined\n const sanitized: Record<string, HostThemeComponentOverride> = {}\n for (const [key, override] of Object.entries(components)) {\n if (!componentWhitelist.has(key) || !override) continue\n const entry: HostThemeComponentOverride = {}\n if (override.defaultProps) entry.defaultProps = override.defaultProps\n if (override.styleOverrides) entry.styleOverrides = override.styleOverrides\n const sx = sanitizeSlotStyles(override.sx)\n if (sx) entry.sx = sx\n const variants = sanitizeVariants(override.variants)\n if (variants) entry.variants = variants\n if (Object.keys(entry).length) sanitized[key] = entry\n }\n return Object.keys(sanitized).length ? sanitized : undefined\n}\n\n/** `sx` slots: each a style object, anything else dropped. */\nfunction sanitizeSlotStyles(\n slots: unknown,\n): Record<string, Record<string, unknown>> | undefined {\n if (!isPlainObject(slots)) return undefined\n const kept: Record<string, Record<string, unknown>> = {}\n for (const [slot, style] of Object.entries(slots)) {\n if (isPlainObject(style)) kept[slot] = style\n }\n return Object.keys(kept).length ? kept : undefined\n}\n\n/**\n * Variants that can match and style something: a props object of scalars\n * and a style or an `sx`. A malformed entry is dropped rather than handed to\n * MUI, whose variant loop reads `props` and would throw on a missing one.\n */\nfunction sanitizeVariants(\n variants: unknown,\n): HostThemeComponentVariant[] | undefined {\n if (!Array.isArray(variants)) return undefined\n const kept: HostThemeComponentVariant[] = []\n for (const variant of variants) {\n if (!isPlainObject(variant) || !isPlainObject(variant['props'])) continue\n const props = variant['props'] as Record<string, unknown>\n if (\n Object.values(props).some(\n (value) =>\n value !== null &&\n !['string', 'number', 'boolean'].includes(typeof value),\n )\n ) {\n continue\n }\n const entry: HostThemeComponentVariant = {\n props: props as HostThemeComponentVariant['props'],\n }\n if (isPlainObject(variant['style'])) entry.style = variant['style']\n if (isPlainObject(variant['sx'])) entry.sx = variant['sx']\n if (entry.style || entry.sx) kept.push(entry)\n }\n return kept.length ? kept : undefined\n}\n\n/** The theme a style function is handed, as far as `sx` needs it. */\ntype SxTheme = { unstable_sx?: (style: unknown) => unknown }\n\n/**\n * An `sx` object as a style function of the theme it renders under.\n *\n * `components` are read against the BUILT theme of each scheme, so a palette\n * path resolves to that scheme's color: `borderColor: 'divider'` is the light\n * divider on light and the dark one on dark, which is the thing a literal\n * override cannot say.\n */\nfunction sxStyle(sx: Record<string, unknown>) {\n return ({ theme }: { theme: SxTheme }) =>\n theme?.unstable_sx ? theme.unstable_sx(sx) : sx\n}\n\n/**\n * The stored (JSON) components as MUI reads them: each `sx` slot joins its\n * literal `styleOverrides` slot, literal first, and each variant's `sx`\n * becomes a style function.\n */\nfunction componentsToThemeOptions(\n components: Record<string, HostThemeComponentOverride>,\n): NonNullable<ThemeOptions['components']> {\n const converted: Record<string, Record<string, unknown>> = {}\n for (const [key, override] of Object.entries(components)) {\n const entry: Record<string, unknown> = {}\n if (override.defaultProps) entry['defaultProps'] = override.defaultProps\n const slots = new Set([\n ...Object.keys(override.styleOverrides ?? {}),\n ...Object.keys(override.sx ?? {}),\n ])\n if (slots.size) {\n const styleOverrides: Record<string, unknown> = {}\n for (const slot of slots) {\n const literal = override.styleOverrides?.[slot]\n const sx = override.sx?.[slot]\n styleOverrides[slot] =\n sx && literal !== undefined\n ? [literal, sxStyle(sx)]\n : sx\n ? sxStyle(sx)\n : literal\n }\n entry['styleOverrides'] = styleOverrides\n }\n if (override.variants?.length) {\n entry['variants'] = override.variants.map(({ props, style, sx }) => ({\n props,\n style: sx\n ? (context: { theme: SxTheme }) => [\n style ?? {},\n sxStyle(sx)(context),\n ]\n : style,\n }))\n }\n converted[key] = entry\n }\n return converted as NonNullable<ThemeOptions['components']>\n}\n\n/**\n * Strips unknown component overrides and empty branches from a persisted\n * host theme. Returns a new object; the input is never mutated.\n */\nexport function sanitizeHostTheme(theme: HostTheme | undefined): HostTheme {\n if (!theme) return {}\n const sanitized: HostTheme = { ...theme }\n const components = sanitizeComponents(theme.components)\n if (components) sanitized.components = components\n else delete sanitized.components\n // `mixins.toolbar` must be a CSS object; a scalar would reach\n // `createTheme` and throw while building the Toolbar variant.\n if (isPlainObject(theme.mixins?.toolbar)) {\n sanitized.mixins = { toolbar: theme.mixins.toolbar }\n } else {\n delete sanitized.mixins\n }\n // Absent already means \"follows the visitor\", so only the opt-out is worth\n // persisting; anything else (a stale `'auto'`, junk) is dropped.\n if (theme.darkScheme !== 'off') delete sanitized.darkScheme\n return sanitized\n}\n\n/** An `@media` key that names a `min-width`, and the width it names. */\nconst MIN_WIDTH_AT_RULE = /^@media\\b[^{]*\\bmin-width\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*px/\n\n/**\n * Re-orders breakpoint at-rules ascending, so STORED key order cannot decide\n * which rule wins (AGL-3146).\n *\n * Two `min-width` rules of equal specificity both match a wide window, so the\n * later one wins — which makes key order a rendering decision. Firestore does\n * not preserve it: one stored toolbar mixin came back sm, landscape, base to\n * the server reader and landscape, base, sm to the browser, so aglyn.com's\n * nav drew 48px live and 72px on the besigner canvas from a single saved\n * document. The editor and the page disagreed about a value neither of them\n * had changed.\n *\n * Ascending is the order MUI writes its own breakpoint styles in and the\n * order the theme editor writes a toolbar mixin in: the wider query is the\n * more specific answer, so it belongs last.\n *\n * Only two things move. Plain declarations are hoisted ahead of the blocks,\n * keeping their order among themselves — which is where the style engine\n * emits them anyway, since a nested block becomes a rule of its own after the\n * base one, and where the shorthand/longhand hazard lives. Breakpoint rules\n * are sorted into the slots the blocks already occupy, so a nested selector\n * or a condition naming no width never changes position against one another.\n *\n * Returns its input by identity when no order changes, which is every theme\n * with at most one breakpoint rule per object.\n */\nexport function orderMediaWidths<T>(value: T): T {\n if (Array.isArray(value)) {\n let moved = false\n const next = value.map((entry) => {\n const ordered = orderMediaWidths(entry)\n if (ordered !== entry) moved = true\n return ordered\n })\n return (moved ? next : value) as T\n }\n if (!isPlainObject(value)) return value\n\n const source = value as Record<string, unknown>\n const keys = Object.keys(source)\n const declarations = keys.filter((key) => !isPlainObject(source[key]))\n const blocks = keys.filter((key) => isPlainObject(source[key]))\n const widthSlots: number[] = []\n const widths: Array<{ key: string; width: number }> = []\n blocks.forEach((key, index) => {\n const match = MIN_WIDTH_AT_RULE.exec(key)\n if (!match) return\n widthSlots.push(index)\n widths.push({ key, width: Number.parseFloat(match[1]) })\n })\n // A stable sort, so two rules at the same width keep the order they were\n // stored in — there is nothing to prefer between them.\n const sorted = [...widths].sort((a, b) => a.width - b.width)\n widthSlots.forEach((slot, position) => {\n blocks[slot] = sorted[position].key\n })\n\n const order = [...declarations, ...blocks]\n let moved = order.some((key, index) => key !== keys[index])\n const next: Record<string, unknown> = {}\n for (const key of order) {\n const child = orderMediaWidths(source[key])\n if (child !== source[key]) moved = true\n next[key] = child\n }\n return (moved ? next : value) as T\n}\n\n/**\n * Converts a persisted {@link HostTheme} document into MUI `ThemeOptions`\n * for one color scheme. The result is meant to be passed through\n * `createResponsiveTheme` (or `createTheme`) by the consumer; shade and\n * contrast-text derivation for partial palettes is MUI's job, so only\n * explicitly set values are forwarded.\n */\nexport function hostThemeToThemeOptions(\n theme: HostTheme | undefined,\n scheme: HostThemeScheme,\n): ThemeOptions {\n const sanitized = sanitizeHostTheme(theme)\n const options: ThemeOptions = {\n palette: schemeColorsToPaletteOptions(\n scheme,\n sanitized.colorSchemes?.[scheme],\n ),\n }\n\n const { typography } = sanitized\n if (typography?.fontFamily || typography?.variants) {\n // HostThemeTypographyVariant is a sanitized subset of MUI's\n // TypographyStyleOptions; the missing index signature is by design.\n options.typography = {\n ...(typography.fontFamily && { fontFamily: typography.fontFamily }),\n ...typography.variants,\n } as ThemeOptions['typography']\n }\n\n if (typeof sanitized.shape?.borderRadius === 'number') {\n options.shape = { borderRadius: sanitized.shape.borderRadius }\n }\n if (typeof sanitized.spacing === 'number') {\n options.spacing = sanitized.spacing\n }\n if (sanitized.components) {\n // Ordered BEFORE conversion: an `sx` object ends up inside a closure,\n // where the pass over the finished options below cannot reach it.\n options.components = componentsToThemeOptions(\n orderMediaWidths(sanitized.components),\n )\n }\n // Toolbar height is only reachable here (AGL-1242). MUI derives the\n // Toolbar's `regular` variant style from `mixins.toolbar` and applies it\n // AFTER `components.MuiToolbar.styleOverrides`, so the slot override loses\n // every time — its nested media queries do not even emit.\n if (sanitized.mixins?.toolbar) {\n options.mixins = { toolbar: sanitized.mixins.toolbar }\n }\n\n // Every surface that renders a site's theme arrives here — the tenant's\n // provider, the besigner canvas, Preview, the theme editor — so this is\n // where a stored document stops being able to resolve differently for\n // different readers (AGL-3146).\n return orderMediaWidths(options)\n}\n\n/**\n * Layers a host's overrides ONTO a base set of theme options (AGL-1180).\n *\n * `hostThemeToThemeOptions` deliberately emits only what the host explicitly\n * set, so building a theme from it alone leaves every other slot to MUI's\n * stock palette. Consumers used to switch — console theme when the document\n * was empty, host document when it was not — which meant setting a single\n * value (the spec's own example is `{ spacing: 8 }`) silently repainted\n * secondary, tertiary, surface, info, success, warning, error, background\n * and paper in MUI blue/purple. Merging instead of switching keeps the brand\n * as the floor no matter how much the host customizes.\n *\n * `palette` merges one level deep so overriding `primary` cannot drop\n * `secondary`. Within a single colour the override replaces the whole record\n * — MUI derives shades and contrast text from `main`, which is exactly the\n * partial-palette behaviour the converter is written for.\n */\nexport function mergeThemeOptions(\n base: ThemeOptions,\n overrides: ThemeOptions,\n): ThemeOptions {\n const merged: ThemeOptions = { ...base, ...overrides }\n\n // Palette merges ONE level: overriding a colour replaces its whole record\n // so MUI re-derives light/dark/contrastText from the new `main`, which is\n // the partial-palette behaviour the converter is written for. Overriding\n // `primary` still must not disturb `secondary`, hence the level.\n merged.palette = { ...base.palette, ...overrides.palette }\n merged.shape = { ...base.shape, ...overrides.shape }\n // Same reasoning as `palette`: setting `toolbar` must not drop any other\n // mixin the base defines.\n merged.mixins = { ...base.mixins, ...overrides.mixins }\n\n // `typography` is an object in every base we ship, but MUI's type also\n // allows a function of the palette — merging into that would silently drop\n // the base, so prefer the override wholesale in that case.\n if (\n isPlainObject(base.typography) &&\n isPlainObject(overrides.typography)\n ) {\n merged.typography = objectDeepMergeReplaceArrays(\n base.typography,\n overrides.typography,\n ) as ThemeOptions['typography']\n }\n\n // Components merge DEEPLY. A host override names one component, and often\n // one property inside it — `MuiButton.defaultProps.color`. A shallow merge\n // would swap out the entire `MuiButton` entry and take the brand's\n // `styleOverrides` with it, and those styles are frequently FUNCTIONS of\n // the theme that JSON cannot express, so the editor could not put them\n // back even in principle. Deep-merging means you override the leaf you\n // named and inherit everything else, functions included.\n merged.components = objectDeepMergeReplaceArrays(\n base.components ?? {},\n overrides.components ?? {},\n ) as ThemeOptions['components']\n composeComponentStyles(merged.components, base.components, overrides.components)\n\n return merged\n}\n\ntype ComponentOptions = {\n styleOverrides?: Record<string, unknown>\n variants?: unknown[]\n}\n\n/** A slot's styles as a list MUI resolves in order (its `processStyle` flattens arrays). */\nfunction styleList(style: unknown): unknown[] {\n return Array.isArray(style) ? style : [style]\n}\n\n/**\n * Where a deep merge would REPLACE styling rather than add to it, composes\n * the two instead (AGL-3403).\n *\n * A deep merge merges two style OBJECTS key by key, which is right. But the\n * base styles several slots with a FUNCTION of the theme — `MuiButton.root`\n * carries the accent-text contrast fix — and merging an object onto a\n * function keeps only the object. So a theme that set one property on the\n * button root silently removed the fix, and nothing JSON can say could put it\n * back. MUI accepts a list per slot and applies it in order, so a function on\n * either side makes the slot `[base, override]`: the override still wins\n * every property it names, and everything else the base did still happens.\n *\n * `variants` concatenate for the same reason: the base's outlined\n * `IconButton` must not disappear because a theme styled its `small` size.\n */\nfunction composeComponentStyles(\n merged: ThemeOptions['components'],\n base: ThemeOptions['components'],\n overrides: ThemeOptions['components'],\n): void {\n if (!merged || !base || !overrides) return\n const target = merged as Record<string, ComponentOptions>\n for (const [name, override] of Object.entries(\n overrides as Record<string, ComponentOptions>,\n )) {\n const from = (base as Record<string, ComponentOptions>)[name]\n if (!from || !override) continue\n for (const [slot, style] of Object.entries(override.styleOverrides ?? {})) {\n const baseStyle = from.styleOverrides?.[slot]\n if (baseStyle === undefined) continue\n if (isPlainObject(baseStyle) && isPlainObject(style)) continue\n target[name].styleOverrides = {\n ...target[name].styleOverrides,\n [slot]: [...styleList(baseStyle), ...styleList(style)],\n }\n }\n if (Array.isArray(from.variants) && Array.isArray(override.variants)) {\n target[name].variants = [...from.variants, ...override.variants]\n }\n }\n}\n\n/** Plain data object — not an array, function, or class instance. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value)\n )\n}\n\n/** True when the document customizes anything, i.e. consumers should build a theme from it rather than using their default. */\nexport function hasHostTheme(theme: HostTheme | undefined): theme is HostTheme {\n return !!theme && Object.keys(theme).length > 0\n}\n\n/**\n * Builds a Google Fonts CSS2 stylesheet URL for the theme's loadable fonts.\n * Returns undefined when nothing needs loading (system/absent fonts).\n */\nexport function getGoogleFontsUrl(fonts: Array<HostThemeFont> | undefined) {\n const families = (fonts ?? [])\n .filter((font) => font.family && (font.source ?? 'google') === 'google')\n .map((font) => {\n const family = font.family.trim().replace(/\\s+/g, '+')\n const weights = font.weights?.length\n ? `:wght@${[...font.weights].sort((a, b) => a - b).join(';')}`\n : ''\n return `family=${family}${weights}`\n })\n if (!families.length) return undefined\n return `https://fonts.googleapis.com/css2?${families.join('&')}&display=swap`\n}\n\nexport default hostThemeToThemeOptions\n"],"names":["objectDeepMergeReplaceArrays","HOST_THEME_COMPONENT_WHITELIST","componentWhitelist","Set","pickPaletteColor","color","main","undefined","picked","light","dark","contrastText","schemeColorsToPaletteOptions","scheme","colors","palette","mode","colorKeys","key","background","default","paper","text","primary","secondary","disabled","tint","tertiary","divider","sanitizeComponents","components","sanitized","override","Object","entries","has","entry","defaultProps","styleOverrides","sx","sanitizeSlotStyles","variants","sanitizeVariants","keys","length","slots","isPlainObject","kept","slot","style","Array","isArray","variant","props","values","some","value","includes","push","sxStyle","theme","unstable_sx","componentsToThemeOptions","converted","size","literal","map","context","sanitizeHostTheme","mixins","toolbar","darkScheme","MIN_WIDTH_AT_RULE","orderMediaWidths","moved","next","ordered","source","declarations","filter","blocks","widthSlots","widths","forEach","index","match","exec","width","Number","parseFloat","sorted","sort","a","b","position","order","child","hostThemeToThemeOptions","options","colorSchemes","typography","fontFamily","shape","borderRadius","spacing","mergeThemeOptions","base","overrides","merged","composeComponentStyles","styleList","target","name","from","baseStyle","hasHostTheme","getGoogleFontsUrl","fonts","families","font","family","trim","replace","weights","join"],"mappings":"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"}
|
|
@@ -210,7 +210,7 @@ export declare const TOOLBAR_HEIGHT_FIELDS: {
|
|
|
210
210
|
/** The raw-JSON component overrides, limited to the sanitizer's whitelist. */
|
|
211
211
|
export declare const COMPONENT_OVERRIDES_FIELD: {
|
|
212
212
|
readonly label: "Component overrides";
|
|
213
|
-
readonly components: readonly ["MuiAppBar", "MuiAvatar", "MuiBadge", "MuiButton", "MuiButtonBase", "MuiCard", "MuiCardContent", "MuiCheckbox", "MuiChip", "MuiCircularProgress", "MuiDivider", "MuiIconButton", "MuiLinearProgress", "MuiLink", "MuiList", "MuiListItem", "MuiMenu", "MuiPaper", "MuiRadio", "MuiSlider", "MuiSwitch", "MuiTab", "MuiTabs", "MuiTextField", "MuiToolbar", "MuiTooltip", "MuiTypography"];
|
|
213
|
+
readonly components: readonly ["MuiAccordion", "MuiAccordionDetails", "MuiAccordionSummary", "MuiAlert", "MuiAlertTitle", "MuiAppBar", "MuiAvatar", "MuiBadge", "MuiBreadcrumbs", "MuiButton", "MuiButtonBase", "MuiButtonGroup", "MuiCard", "MuiCardActions", "MuiCardContent", "MuiCardHeader", "MuiCheckbox", "MuiChip", "MuiCircularProgress", "MuiDialog", "MuiDialogActions", "MuiDialogContent", "MuiDialogTitle", "MuiDivider", "MuiDrawer", "MuiFab", "MuiFilledInput", "MuiFormControlLabel", "MuiFormHelperText", "MuiFormLabel", "MuiIconButton", "MuiInput", "MuiInputBase", "MuiInputLabel", "MuiLinearProgress", "MuiLink", "MuiList", "MuiListItem", "MuiListItemButton", "MuiMenu", "MuiMenuItem", "MuiOutlinedInput", "MuiPagination", "MuiPaginationItem", "MuiPaper", "MuiRadio", "MuiRating", "MuiSelect", "MuiSlider", "MuiSnackbarContent", "MuiSwitch", "MuiTab", "MuiTable", "MuiTableCell", "MuiTableHead", "MuiTableRow", "MuiTabs", "MuiTextField", "MuiToggleButton", "MuiToggleButtonGroup", "MuiToolbar", "MuiTooltip", "MuiTypography"];
|
|
214
214
|
};
|
|
215
215
|
/**
|
|
216
216
|
* The media queries a component override may scope a style to: either side
|
|
@@ -296,8 +296,12 @@ export declare function resetComponentOverrides(theme: HostTheme): HostTheme;
|
|
|
296
296
|
/** One leaf of a component override, as a writer names it. */
|
|
297
297
|
export interface ThemeComponentOverrideLeaf {
|
|
298
298
|
component: HostThemeComponentKey;
|
|
299
|
-
/**
|
|
300
|
-
|
|
299
|
+
/**
|
|
300
|
+
* `styleOverrides` styles a slot in literal CSS; `sx` styles it in theme
|
|
301
|
+
* terms, so a value may name a palette path (`primary.main`) or a spacing
|
|
302
|
+
* unit (AGL-3403); `defaultProps` sets a prop's default.
|
|
303
|
+
*/
|
|
304
|
+
target: 'styleOverrides' | 'sx' | 'defaultProps';
|
|
301
305
|
/** The style slot (`root`, `contained`, `h1`); `null` for a default prop. */
|
|
302
306
|
slot: string | null;
|
|
303
307
|
/** A camelCase CSS property, or the prop's name. */
|
|
@@ -692,7 +692,7 @@ export const FONT_FAMILY_FIELD = {
|
|
|
692
692
|
} else {
|
|
693
693
|
var _leaf_slot;
|
|
694
694
|
const slot = (_leaf_slot = leaf.slot) != null ? _leaf_slot : 'root';
|
|
695
|
-
const styles = _extends({}, entry.styleOverrides);
|
|
695
|
+
const styles = _extends({}, leaf.target === 'sx' ? entry.sx : entry.styleOverrides);
|
|
696
696
|
const slotStyles = _extends({}, styles[slot]);
|
|
697
697
|
if (leaf.media) {
|
|
698
698
|
const query = THEME_EDITOR_MEDIA_QUERIES[leaf.media];
|
|
@@ -703,7 +703,11 @@ export const FONT_FAMILY_FIELD = {
|
|
|
703
703
|
slotStyles[leaf.property] = leaf.value;
|
|
704
704
|
}
|
|
705
705
|
styles[slot] = slotStyles;
|
|
706
|
-
|
|
706
|
+
if (leaf.target === 'sx') {
|
|
707
|
+
entry.sx = styles;
|
|
708
|
+
} else {
|
|
709
|
+
entry.styleOverrides = styles;
|
|
710
|
+
}
|
|
707
711
|
}
|
|
708
712
|
components[leaf.component] = entry;
|
|
709
713
|
return _extends({}, theme, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/theme-editor-fields.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport {\n HOST_THEME_COMPONENT_WHITELIST,\n type HostThemeComponentKey,\n} from './host-theme'\n\n/**\n * The host theme editor's field catalog (AGL-2938): every control the editor\n * renders, the range each one accepts, the token it writes, and the write\n * itself.\n *\n * It lives here rather than beside the editor because the editor is not the\n * only thing that changes a site's theme. Anything that proposes a theme\n * change has to offer exactly the controls the editor offers, with the same\n * bounds, and has to write each one the way the editor writes it. A copy of\n * this list anywhere else is a second answer to \"what can a theme hold\" that\n * drifts from the first the day a control is added, so the editor renders\n * from this module and every other writer reads it.\n *\n * Pure data and pure functions over `HostTheme`: no MUI, no React and no\n * brand theme. What a control resolves to when the site sets nothing is the\n * brand theme's business, in `theme-editor-defaults.ts`.\n *\n * Deliberately NOT re-exported from this library's index: the index reaches\n * every published page, and nothing a visitor renders needs the catalog.\n * Reach it as `@aglyn/shared-ui-theme/util/theme-editor-fields`.\n */\n\n/**\n * MUI's own toolbar breakpoint. `mixins.toolbar` has to carry this exact\n * query, because the rule it competes with is MUI's `@media (min-width:600px)\n * { min-height: 64px }` (AGL-1242).\n */\nexport const TOOLBAR_SM_MIN_WIDTH = 600\nexport const TOOLBAR_SM_QUERY = `@media (min-width:${TOOLBAR_SM_MIN_WIDTH}px)`\n/** MUI's stock Toolbar heights, shown when the host has set none. */\nexport const DEFAULT_TOOLBAR_XS = 56\nexport const DEFAULT_TOOLBAR_SM = 64\n/**\n * `createMixins` spreads `...mixins` AFTER its default, so anything we write\n * REPLACES the stock toolbar wholesale — including its short-landscape rule.\n * Carrying it forward keeps that behavior instead of dropping it silently.\n */\nexport const TOOLBAR_LANDSCAPE_QUERY = '@media (min-width:0px)'\nexport const TOOLBAR_LANDSCAPE_RULE = {\n '@media (orientation: landscape)': { minHeight: 48 },\n}\n\n/** Reads a px `minHeight` out of `mixins.toolbar` for one breakpoint. */\nexport function readToolbarHeight(theme: HostTheme, breakpoint: 'xs' | 'sm') {\n const toolbar = theme.mixins?.toolbar\n if (!toolbar) return undefined\n const raw =\n breakpoint === 'xs'\n ? toolbar.minHeight\n : (toolbar[TOOLBAR_SM_QUERY] as { minHeight?: unknown } | undefined)\n ?.minHeight\n const value = parseFloat(String(raw ?? ''))\n return Number.isFinite(value) ? value : undefined\n}\n\n/**\n * Builds a COMPLETE `mixins.toolbar`, in the order MUI emits it.\n *\n * Two things this exists to get right, both because `mixins.toolbar` replaces\n * MUI's default wholesale rather than merging into it (AGL-1242):\n *\n * - **Completeness.** Anything omitted is simply gone. Writing only a desktop\n * height left portrait phones with no `min-height` at all, so unset\n * breakpoints fall back to MUI's own 56 / 64.\n * - **Key order.** These land in one CSS rule, so the last matching\n * declaration wins. MUI emits the landscape clause BEFORE the sm height;\n * emitting it after makes a wide landscape window — i.e. every desktop —\n * 48px tall.\n */\nexport function buildToolbarMixin(xs: number | undefined, sm: number | undefined) {\n return {\n minHeight: `${xs ?? DEFAULT_TOOLBAR_XS}px`,\n [TOOLBAR_LANDSCAPE_QUERY]: TOOLBAR_LANDSCAPE_RULE,\n [TOOLBAR_SM_QUERY]: { minHeight: `${sm ?? DEFAULT_TOOLBAR_SM}px` },\n }\n}\n\n/**\n * Serializes the branches whose KEY ORDER changes what renders.\n *\n * `deepEqual` is order-INsensitive on purpose (AGL-56: Firestore hands the\n * palette back in a different key order than the editor builds it, and a\n * string compare left Save enabled forever), so on its own it cannot see a\n * reorder of `mixins.toolbar` — a change that really does alter the CSS.\n *\n * Deliberately narrow: only `mixins`. `components.*.styleOverrides` are CSS\n * objects too, but widening this would re-expose the AGL-56 failure for every\n * host with component overrides, and order-sensitivity has only actually been\n * demonstrated here.\n *\n * Firestore does NOT hand a map back in the order it was written, and it\n * hands different readers different orders (AGL-3146) — which is why what\n * renders is canonicalized in `hostThemeToThemeOptions` rather than trusted\n * from storage. A freshly-loaded document is still not dirty here, because\n * the draft is seeded from the loaded document itself: both sides of this\n * comparison carry whatever order THIS reader was given.\n */\nexport function orderSensitiveKey(theme: HostTheme): string {\n return JSON.stringify(theme.mixins ?? {})\n}\n\nexport type PaletteColorKey =\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'surface'\n | 'error'\n | 'warning'\n | 'info'\n | 'success'\n\nexport const PALETTE_COLOR_FIELDS: Array<{\n key: PaletteColorKey\n label: string\n}> = [\n { key: 'primary', label: 'Primary' },\n { key: 'secondary', label: 'Secondary' },\n { key: 'tertiary', label: 'Tertiary' },\n { key: 'surface', label: 'Surface' },\n { key: 'error', label: 'Error' },\n { key: 'warning', label: 'Warning' },\n { key: 'info', label: 'Info' },\n { key: 'success', label: 'Success' },\n]\n\nexport type SurfaceColorPath =\n | ['background', 'default']\n | ['background', 'paper']\n | ['text', 'primary']\n | ['text', 'secondary']\n | ['text', 'disabled']\n | ['tint', 'primary']\n | ['tint', 'secondary']\n | ['tint', 'tertiary']\n\nexport const SURFACE_COLOR_FIELDS: Array<{\n path: SurfaceColorPath\n label: string\n}> = [\n { path: ['background', 'default'], label: 'Background' },\n { path: ['background', 'paper'], label: 'Paper' },\n { path: ['text', 'primary'], label: 'Text' },\n { path: ['text', 'secondary'], label: 'Secondary text' },\n { path: ['text', 'disabled'], label: 'Disabled text' },\n]\n\n/**\n * Pale accent washes (AGL-1244).\n *\n * Separate from {@link PALETTE_COLOR_FIELDS} because a tint is a STRING LEAF,\n * not a `{ main }` record — the palette fields all write `main`, and a tint has\n * none. It rides the same `SurfaceColorPath` machinery as `background`/`text`\n * for exactly that reason, and is listed apart only so the editor can head it\n * \"Tints\" instead of filing it under \"Background & text\".\n */\nexport const TINT_COLOR_FIELDS: Array<{\n path: SurfaceColorPath\n label: string\n}> = [\n { path: ['tint', 'primary'], label: 'Primary tint' },\n { path: ['tint', 'secondary'], label: 'Secondary tint' },\n { path: ['tint', 'tertiary'], label: 'Tertiary tint' },\n]\n\n/** The one color leaf that is neither a palette color nor a member of a group. */\nexport const DIVIDER_COLOR_FIELD = { key: 'divider', label: 'Divider' } as const\n\n/** Curated Google Fonts choices for the font family selector. */\nexport const GOOGLE_FONT_OPTIONS: Array<{\n family: string\n category: 'sans-serif' | 'serif' | 'monospace' | 'display'\n weights: Array<number>\n}> = [\n { family: 'Inter', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Roboto', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Open Sans', category: 'sans-serif', weights: [400, 600, 700] },\n { family: 'Lato', category: 'sans-serif', weights: [400, 700] },\n { family: 'Montserrat', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Poppins', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Nunito', category: 'sans-serif', weights: [400, 600, 700] },\n { family: 'Work Sans', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Raleway', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Merriweather', category: 'serif', weights: [400, 700] },\n { family: 'Playfair Display', category: 'serif', weights: [400, 700] },\n { family: 'Lora', category: 'serif', weights: [400, 700] },\n { family: 'Source Serif 4', category: 'serif', weights: [400, 700] },\n { family: 'JetBrains Mono', category: 'monospace', weights: [400, 700] },\n { family: 'Bebas Neue', category: 'display', weights: [400] },\n]\n\nexport function fontFamilyStack(\n family: string,\n category: (typeof GOOGLE_FONT_OPTIONS)[number]['category'],\n) {\n const fallback = category === 'monospace' ? 'monospace' : category === 'serif' ? 'serif' : 'sans-serif'\n return `\"${family}\", ${fallback}`\n}\n\nexport function getSchemeColor(\n colors: HostThemeSchemeColors | undefined,\n path: SurfaceColorPath,\n): string | undefined {\n const [group, key] = path\n return (colors?.[group] as Record<string, string> | undefined)?.[key]\n}\n\n/* ------------------------------------------------------------------------ *\n * The controls, as one catalog\n * ------------------------------------------------------------------------ */\n\n/** The two color schemes the editor edits, in tab order. */\nexport const THEME_EDITOR_SCHEMES: readonly HostThemeScheme[] = ['light', 'dark']\n\n/**\n * Every color the editor edits per scheme, named the way a diff names it: a\n * palette key (`primary`), a group path joined with a dot\n * (`background.default`, `tint.primary`), or `divider`.\n */\nexport type ThemeColorToken =\n | PaletteColorKey\n | 'background.default'\n | 'background.paper'\n | 'text.primary'\n | 'text.secondary'\n | 'text.disabled'\n | 'tint.primary'\n | 'tint.secondary'\n | 'tint.tertiary'\n | 'divider'\n\n/** Where a color control sits in the editor's Color scheme card. */\nexport type ThemeColorGroup = 'palette' | 'surface' | 'tint' | 'divider'\n\n/** Every color field the editor renders, in the order it renders them. */\nexport const THEME_COLOR_FIELDS: ReadonlyArray<{\n token: ThemeColorToken\n label: string\n group: ThemeColorGroup\n}> = [\n ...PALETTE_COLOR_FIELDS.map(({ key, label }) => ({\n token: key,\n label,\n group: 'palette' as const,\n })),\n ...SURFACE_COLOR_FIELDS.map(({ path, label }) => ({\n token: path.join('.') as ThemeColorToken,\n label,\n group: 'surface' as const,\n })),\n ...TINT_COLOR_FIELDS.map(({ path, label }) => ({\n token: path.join('.') as ThemeColorToken,\n label,\n group: 'tint' as const,\n })),\n {\n token: DIVIDER_COLOR_FIELD.key,\n label: DIVIDER_COLOR_FIELD.label,\n group: 'divider' as const,\n },\n]\n\n/** Whether visitors get a dark scheme; absent from the document means `auto`. */\nexport type ThemeDarkSchemeValue = 'auto' | 'off'\n\nexport const DARK_SCHEME_FIELD = {\n label: 'Dark scheme',\n options: [\n { value: 'auto', label: 'Follows the visitor' },\n { value: 'off', label: 'Off — always light' },\n ],\n} as const\n\n/**\n * The font select's value for \"no font of the site's own\": the theme's\n * default stack, with nothing to load.\n */\nexport const SYSTEM_FONT_VALUE = '__system__'\n\nexport const FONT_FAMILY_FIELD = { label: 'Font family' } as const\n\n/** The corner radius slider, in px. */\nexport const BORDER_RADIUS_FIELD = {\n label: 'Border radius',\n min: 0,\n max: 24,\n step: 1,\n} as const\n\n/** The spacing unit MUI multiplies every `spacing(n)` by, in px. */\nexport const SPACING_FIELD = {\n label: 'Spacing unit (px)',\n min: 2,\n max: 16,\n step: 1,\n} as const\n\n/** The two nav heights, either side of MUI's toolbar breakpoint, in px. */\nexport const TOOLBAR_HEIGHT_FIELDS = {\n xs: { label: 'Nav height, mobile (px)', min: 40, max: 160, step: 1 },\n sm: { label: 'Nav height, desktop (px)', min: 40, max: 160, step: 1 },\n} as const\n\n/** The raw-JSON component overrides, limited to the sanitizer's whitelist. */\nexport const COMPONENT_OVERRIDES_FIELD = {\n label: 'Component overrides',\n components: HOST_THEME_COMPONENT_WHITELIST,\n} as const\n\n/**\n * The media queries a component override may scope a style to: either side\n * of MUI's own toolbar breakpoint, the one breakpoint the editor already\n * names. MUI's `down('sm')` stops a twentieth of a pixel short of it.\n */\nexport const THEME_EDITOR_MEDIA_QUERIES = {\n mobile: `@media (max-width:${TOOLBAR_SM_MIN_WIDTH - 0.05}px)`,\n desktop: TOOLBAR_SM_QUERY,\n} as const\n\nexport type ThemeEditorMedia = keyof typeof THEME_EDITOR_MEDIA_QUERIES\n\nexport type ThemeEditorControlId =\n | `color.${ThemeColorToken}`\n | 'darkScheme'\n | 'fontFamily'\n | 'borderRadius'\n | 'spacing'\n | 'navHeight.xs'\n | 'navHeight.sm'\n | 'components'\n\n/** One control the editor renders, described well enough to be offered elsewhere. */\nexport interface ThemeEditorControl {\n /** Stable id: what a diff row, a proposal and a spec name the control by. */\n id: ThemeEditorControlId\n /** The label the editor shows. */\n label: string\n kind: 'color' | 'select' | 'number' | 'json'\n /** Edited once per scheme, under the Light and Dark tabs. */\n perScheme: boolean\n /** A color control's token. */\n token?: ThemeColorToken\n /** A select's values, or the components a JSON control accepts. */\n options?: readonly string[]\n /** A number control's inclusive bounds and its step. */\n min?: number\n max?: number\n step?: number\n}\n\n/**\n * Every value-bearing control the editor renders, in the order it renders\n * them. The buttons that act on these values rather than hold one — Copy\n * from light, Reset to default, Discard, Save — are not controls: a reset is\n * a control set back to nothing, and a copy is every color control of one\n * scheme written from the other.\n */\nexport const THEME_EDITOR_CONTROLS: readonly ThemeEditorControl[] = [\n {\n id: 'darkScheme',\n label: DARK_SCHEME_FIELD.label,\n kind: 'select',\n perScheme: false,\n options: DARK_SCHEME_FIELD.options.map((option) => option.value),\n },\n ...THEME_COLOR_FIELDS.map(({ token, label }) => ({\n id: `color.${token}` as const,\n label,\n kind: 'color' as const,\n perScheme: true,\n token,\n })),\n {\n id: 'fontFamily',\n label: FONT_FAMILY_FIELD.label,\n kind: 'select',\n perScheme: false,\n options: [SYSTEM_FONT_VALUE, ...GOOGLE_FONT_OPTIONS.map((option) => option.family)],\n },\n {\n id: 'borderRadius',\n label: BORDER_RADIUS_FIELD.label,\n kind: 'number',\n perScheme: false,\n min: BORDER_RADIUS_FIELD.min,\n max: BORDER_RADIUS_FIELD.max,\n step: BORDER_RADIUS_FIELD.step,\n },\n {\n id: 'spacing',\n label: SPACING_FIELD.label,\n kind: 'number',\n perScheme: false,\n min: SPACING_FIELD.min,\n max: SPACING_FIELD.max,\n step: SPACING_FIELD.step,\n },\n {\n id: 'navHeight.xs',\n label: TOOLBAR_HEIGHT_FIELDS.xs.label,\n kind: 'number',\n perScheme: false,\n min: TOOLBAR_HEIGHT_FIELDS.xs.min,\n max: TOOLBAR_HEIGHT_FIELDS.xs.max,\n step: TOOLBAR_HEIGHT_FIELDS.xs.step,\n },\n {\n id: 'navHeight.sm',\n label: TOOLBAR_HEIGHT_FIELDS.sm.label,\n kind: 'number',\n perScheme: false,\n min: TOOLBAR_HEIGHT_FIELDS.sm.min,\n max: TOOLBAR_HEIGHT_FIELDS.sm.max,\n step: TOOLBAR_HEIGHT_FIELDS.sm.step,\n },\n {\n id: 'components',\n label: COMPONENT_OVERRIDES_FIELD.label,\n kind: 'json',\n perScheme: false,\n options: COMPONENT_OVERRIDES_FIELD.components,\n },\n]\n\n/** The catalog entry for a control id. */\nexport function themeEditorControl(id: ThemeEditorControlId): ThemeEditorControl {\n const control = THEME_EDITOR_CONTROLS.find((entry) => entry.id === id)\n if (!control) throw new Error(`unknown theme editor control ${id}`)\n return control\n}\n\n/* ------------------------------------------------------------------------ *\n * Reads and writes, exactly as the editor performs them\n * ------------------------------------------------------------------------ */\n\nfunction setSchemeValue(\n theme: HostTheme,\n scheme: HostThemeScheme,\n update: (colors: HostThemeSchemeColors) => HostThemeSchemeColors,\n): HostTheme {\n const colors = theme.colorSchemes?.[scheme] ?? {}\n return {\n ...theme,\n colorSchemes: { ...theme.colorSchemes, [scheme]: update(colors) },\n }\n}\n\n/** A color the site set for one scheme; `undefined` when the slot inherits. */\nexport function readThemeColor(\n theme: HostTheme | undefined,\n scheme: HostThemeScheme,\n token: ThemeColorToken,\n): string | undefined {\n const colors = theme?.colorSchemes?.[scheme]\n if (token === 'divider') return colors?.divider\n const [group, key] = token.split('.')\n if (!key) return colors?.[group as PaletteColorKey]?.main\n return getSchemeColor(colors, [group, key] as SurfaceColorPath)\n}\n\n/**\n * Sets one color for one scheme, or clears it with `undefined` so the slot\n * inherits again. A palette color writes `main` and keeps whatever else its\n * record carries, and clearing it drops the record; a group member that\n * leaves its group empty drops the group.\n */\nexport function writeThemeColor(\n theme: HostTheme,\n scheme: HostThemeScheme,\n token: ThemeColorToken,\n hex: string | undefined,\n): HostTheme {\n return setSchemeValue(theme, scheme, (colors) => {\n const next: HostThemeSchemeColors = { ...colors }\n if (token === 'divider') {\n if (hex) next.divider = hex\n else delete next.divider\n return next\n }\n const [group, key] = token.split('.')\n if (!key) {\n const paletteKey = group as PaletteColorKey\n if (hex) next[paletteKey] = { ...next[paletteKey], main: hex }\n else delete next[paletteKey]\n return next\n }\n const groupKey = group as 'background' | 'text' | 'tint'\n const groupValue = {\n ...(colors[groupKey] as Record<string, string> | undefined),\n }\n if (hex) groupValue[key] = hex\n else delete groupValue[key]\n const grouped = { ...next, [groupKey]: groupValue } as HostThemeSchemeColors\n if (!Object.keys(groupValue).length) delete grouped[groupKey]\n return grouped\n })\n}\n\n/** Replaces one scheme's colors with a copy of the other's. */\nexport function copyThemeSchemeColors(\n theme: HostTheme,\n from: HostThemeScheme,\n to: HostThemeScheme,\n): HostTheme {\n const source = theme.colorSchemes?.[from]\n if (!source) return theme\n return {\n ...theme,\n colorSchemes: {\n ...theme.colorSchemes,\n [to]: JSON.parse(JSON.stringify(source)),\n },\n }\n}\n\n/** The dark scheme switch as the select shows it. */\nexport function readDarkScheme(theme: HostTheme | undefined): ThemeDarkSchemeValue {\n return theme?.darkScheme === 'off' ? 'off' : 'auto'\n}\n\n/**\n * Absent means \"follows the visitor\"; only the opt-out is written, so the\n * saved document stays empty for the common case.\n */\nexport function writeDarkScheme(\n theme: HostTheme,\n value: ThemeDarkSchemeValue,\n): HostTheme {\n const next: HostTheme = { ...theme }\n if (value === 'off') next.darkScheme = 'off'\n else delete next.darkScheme\n return next\n}\n\n/** The font select's value: the site's font, or {@link SYSTEM_FONT_VALUE}. */\nexport function readFontFamily(theme: HostTheme | undefined): string {\n return theme?.fonts?.[0]?.family ?? SYSTEM_FONT_VALUE\n}\n\n/**\n * Picks a font from {@link GOOGLE_FONT_OPTIONS}, loading it and naming it as\n * the typography's stack, or {@link SYSTEM_FONT_VALUE} to go back to the\n * theme default. A family outside the curated list changes nothing.\n */\nexport function writeFontFamily(theme: HostTheme, value: string): HostTheme {\n if (value === SYSTEM_FONT_VALUE) {\n const next = { ...theme }\n delete next.fonts\n const typography = { ...next.typography }\n delete typography.fontFamily\n if (Object.keys(typography).length) next.typography = typography\n else delete next.typography\n return next\n }\n const option = GOOGLE_FONT_OPTIONS.find((entry) => entry.family === value)\n if (!option) return theme\n return {\n ...theme,\n fonts: [\n {\n family: option.family,\n weights: option.weights,\n source: 'google',\n },\n ],\n typography: {\n ...theme.typography,\n fontFamily: fontFamilyStack(option.family, option.category),\n },\n }\n}\n\n/** Sets the corner radius, or clears it with `undefined` so it inherits. */\nexport function writeBorderRadius(\n theme: HostTheme,\n value: number | undefined,\n): HostTheme {\n if (value !== undefined && Number.isFinite(value)) {\n return { ...theme, shape: { ...theme.shape, borderRadius: value } }\n }\n const next = { ...theme }\n const shape = { ...next.shape }\n delete shape.borderRadius\n if (Object.keys(shape).length) next.shape = shape\n else delete next.shape\n return next\n}\n\n/** Sets the spacing unit; anything but a positive number clears it. */\nexport function writeSpacing(theme: HostTheme, value: number | undefined): HostTheme {\n const next = { ...theme }\n if (value !== undefined && Number.isFinite(value) && value > 0) next.spacing = value\n else delete next.spacing\n return next\n}\n\n/**\n * Sets one nav height — anything but a positive number clears it — and\n * rebuilds the whole toolbar mixin around both, because `mixins.toolbar`\n * replaces MUI's default rather than merging into it (AGL-1242).\n */\nexport function writeToolbarHeight(\n theme: HostTheme,\n breakpoint: 'xs' | 'sm',\n value: number | undefined,\n): HostTheme {\n const valid = value !== undefined && Number.isFinite(value) && value > 0\n const edited = valid ? value : undefined\n const xs = breakpoint === 'xs' ? edited : readToolbarHeight(theme, 'xs')\n const sm = breakpoint === 'sm' ? edited : readToolbarHeight(theme, 'sm')\n const next = { ...theme }\n if (xs === undefined && sm === undefined) {\n delete next.mixins\n return next\n }\n next.mixins = { toolbar: buildToolbarMixin(xs, sm) }\n return next\n}\n\n/**\n * Drops the site's component overrides. With nothing stored the site renders\n * the brand's own component styles, which is what the editor's \"Reset to\n * theme defaults\" means.\n */\nexport function resetComponentOverrides(theme: HostTheme): HostTheme {\n const next = { ...theme }\n delete next.components\n return next\n}\n\n/** One leaf of a component override, as a writer names it. */\nexport interface ThemeComponentOverrideLeaf {\n component: HostThemeComponentKey\n /** `styleOverrides` styles a slot; `defaultProps` sets a prop's default. */\n target: 'styleOverrides' | 'defaultProps'\n /** The style slot (`root`, `contained`, `h1`); `null` for a default prop. */\n slot: string | null\n /** A camelCase CSS property, or the prop's name. */\n property: string\n /** Scopes a style to one side of the toolbar breakpoint; `null` for every width. */\n media: ThemeEditorMedia | null\n value: string | number | boolean\n}\n\n/**\n * Sets one leaf inside the site's component overrides and keeps every other\n * override the site has. The stored overrides are deep-merged over the\n * brand's own at render time, so naming one property changes that property\n * and inherits the rest of the component.\n */\nexport function writeComponentOverride(\n theme: HostTheme,\n leaf: ThemeComponentOverrideLeaf,\n): HostTheme {\n const components = { ...theme.components }\n const entry: HostThemeComponentOverride = { ...components[leaf.component] }\n if (leaf.target === 'defaultProps') {\n entry.defaultProps = { ...entry.defaultProps, [leaf.property]: leaf.value }\n } else {\n const slot = leaf.slot ?? 'root'\n const styles = { ...entry.styleOverrides }\n const slotStyles = { ...(styles[slot] as Record<string, unknown> | undefined) }\n if (leaf.media) {\n const query = THEME_EDITOR_MEDIA_QUERIES[leaf.media]\n slotStyles[query] = {\n ...(slotStyles[query] as Record<string, unknown> | undefined),\n [leaf.property]: leaf.value,\n }\n } else {\n slotStyles[leaf.property] = leaf.value\n }\n styles[slot] = slotStyles\n entry.styleOverrides = styles\n }\n components[leaf.component] = entry\n return { ...theme, components }\n}\n"],"names":["HOST_THEME_COMPONENT_WHITELIST","TOOLBAR_SM_MIN_WIDTH","TOOLBAR_SM_QUERY","DEFAULT_TOOLBAR_XS","DEFAULT_TOOLBAR_SM","TOOLBAR_LANDSCAPE_QUERY","TOOLBAR_LANDSCAPE_RULE","minHeight","readToolbarHeight","theme","breakpoint","toolbar","mixins","undefined","raw","value","parseFloat","String","Number","isFinite","buildToolbarMixin","xs","sm","orderSensitiveKey","JSON","stringify","PALETTE_COLOR_FIELDS","key","label","SURFACE_COLOR_FIELDS","path","TINT_COLOR_FIELDS","DIVIDER_COLOR_FIELD","GOOGLE_FONT_OPTIONS","family","category","weights","fontFamilyStack","fallback","getSchemeColor","colors","group","THEME_EDITOR_SCHEMES","THEME_COLOR_FIELDS","map","token","join","DARK_SCHEME_FIELD","options","SYSTEM_FONT_VALUE","FONT_FAMILY_FIELD","BORDER_RADIUS_FIELD","min","max","step","SPACING_FIELD","TOOLBAR_HEIGHT_FIELDS","COMPONENT_OVERRIDES_FIELD","components","THEME_EDITOR_MEDIA_QUERIES","mobile","desktop","THEME_EDITOR_CONTROLS","id","kind","perScheme","option","themeEditorControl","control","find","entry","Error","setSchemeValue","scheme","update","colorSchemes","readThemeColor","divider","split","main","writeThemeColor","hex","next","paletteKey","groupKey","groupValue","grouped","Object","keys","length","copyThemeSchemeColors","from","to","source","parse","readDarkScheme","darkScheme","writeDarkScheme","readFontFamily","fonts","writeFontFamily","typography","fontFamily","writeBorderRadius","shape","borderRadius","writeSpacing","spacing","writeToolbarHeight","valid","edited","resetComponentOverrides","writeComponentOverride","leaf","component","target","defaultProps","property","slot","styles","styleOverrides","slotStyles","media","query"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/theme-editor-fields.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport {\n HOST_THEME_COMPONENT_WHITELIST,\n type HostThemeComponentKey,\n} from './host-theme'\n\n/**\n * The host theme editor's field catalog (AGL-2938): every control the editor\n * renders, the range each one accepts, the token it writes, and the write\n * itself.\n *\n * It lives here rather than beside the editor because the editor is not the\n * only thing that changes a site's theme. Anything that proposes a theme\n * change has to offer exactly the controls the editor offers, with the same\n * bounds, and has to write each one the way the editor writes it. A copy of\n * this list anywhere else is a second answer to \"what can a theme hold\" that\n * drifts from the first the day a control is added, so the editor renders\n * from this module and every other writer reads it.\n *\n * Pure data and pure functions over `HostTheme`: no MUI, no React and no\n * brand theme. What a control resolves to when the site sets nothing is the\n * brand theme's business, in `theme-editor-defaults.ts`.\n *\n * Deliberately NOT re-exported from this library's index: the index reaches\n * every published page, and nothing a visitor renders needs the catalog.\n * Reach it as `@aglyn/shared-ui-theme/util/theme-editor-fields`.\n */\n\n/**\n * MUI's own toolbar breakpoint. `mixins.toolbar` has to carry this exact\n * query, because the rule it competes with is MUI's `@media (min-width:600px)\n * { min-height: 64px }` (AGL-1242).\n */\nexport const TOOLBAR_SM_MIN_WIDTH = 600\nexport const TOOLBAR_SM_QUERY = `@media (min-width:${TOOLBAR_SM_MIN_WIDTH}px)`\n/** MUI's stock Toolbar heights, shown when the host has set none. */\nexport const DEFAULT_TOOLBAR_XS = 56\nexport const DEFAULT_TOOLBAR_SM = 64\n/**\n * `createMixins` spreads `...mixins` AFTER its default, so anything we write\n * REPLACES the stock toolbar wholesale — including its short-landscape rule.\n * Carrying it forward keeps that behavior instead of dropping it silently.\n */\nexport const TOOLBAR_LANDSCAPE_QUERY = '@media (min-width:0px)'\nexport const TOOLBAR_LANDSCAPE_RULE = {\n '@media (orientation: landscape)': { minHeight: 48 },\n}\n\n/** Reads a px `minHeight` out of `mixins.toolbar` for one breakpoint. */\nexport function readToolbarHeight(theme: HostTheme, breakpoint: 'xs' | 'sm') {\n const toolbar = theme.mixins?.toolbar\n if (!toolbar) return undefined\n const raw =\n breakpoint === 'xs'\n ? toolbar.minHeight\n : (toolbar[TOOLBAR_SM_QUERY] as { minHeight?: unknown } | undefined)\n ?.minHeight\n const value = parseFloat(String(raw ?? ''))\n return Number.isFinite(value) ? value : undefined\n}\n\n/**\n * Builds a COMPLETE `mixins.toolbar`, in the order MUI emits it.\n *\n * Two things this exists to get right, both because `mixins.toolbar` replaces\n * MUI's default wholesale rather than merging into it (AGL-1242):\n *\n * - **Completeness.** Anything omitted is simply gone. Writing only a desktop\n * height left portrait phones with no `min-height` at all, so unset\n * breakpoints fall back to MUI's own 56 / 64.\n * - **Key order.** These land in one CSS rule, so the last matching\n * declaration wins. MUI emits the landscape clause BEFORE the sm height;\n * emitting it after makes a wide landscape window — i.e. every desktop —\n * 48px tall.\n */\nexport function buildToolbarMixin(xs: number | undefined, sm: number | undefined) {\n return {\n minHeight: `${xs ?? DEFAULT_TOOLBAR_XS}px`,\n [TOOLBAR_LANDSCAPE_QUERY]: TOOLBAR_LANDSCAPE_RULE,\n [TOOLBAR_SM_QUERY]: { minHeight: `${sm ?? DEFAULT_TOOLBAR_SM}px` },\n }\n}\n\n/**\n * Serializes the branches whose KEY ORDER changes what renders.\n *\n * `deepEqual` is order-INsensitive on purpose (AGL-56: Firestore hands the\n * palette back in a different key order than the editor builds it, and a\n * string compare left Save enabled forever), so on its own it cannot see a\n * reorder of `mixins.toolbar` — a change that really does alter the CSS.\n *\n * Deliberately narrow: only `mixins`. `components.*.styleOverrides` are CSS\n * objects too, but widening this would re-expose the AGL-56 failure for every\n * host with component overrides, and order-sensitivity has only actually been\n * demonstrated here.\n *\n * Firestore does NOT hand a map back in the order it was written, and it\n * hands different readers different orders (AGL-3146) — which is why what\n * renders is canonicalized in `hostThemeToThemeOptions` rather than trusted\n * from storage. A freshly-loaded document is still not dirty here, because\n * the draft is seeded from the loaded document itself: both sides of this\n * comparison carry whatever order THIS reader was given.\n */\nexport function orderSensitiveKey(theme: HostTheme): string {\n return JSON.stringify(theme.mixins ?? {})\n}\n\nexport type PaletteColorKey =\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'surface'\n | 'error'\n | 'warning'\n | 'info'\n | 'success'\n\nexport const PALETTE_COLOR_FIELDS: Array<{\n key: PaletteColorKey\n label: string\n}> = [\n { key: 'primary', label: 'Primary' },\n { key: 'secondary', label: 'Secondary' },\n { key: 'tertiary', label: 'Tertiary' },\n { key: 'surface', label: 'Surface' },\n { key: 'error', label: 'Error' },\n { key: 'warning', label: 'Warning' },\n { key: 'info', label: 'Info' },\n { key: 'success', label: 'Success' },\n]\n\nexport type SurfaceColorPath =\n | ['background', 'default']\n | ['background', 'paper']\n | ['text', 'primary']\n | ['text', 'secondary']\n | ['text', 'disabled']\n | ['tint', 'primary']\n | ['tint', 'secondary']\n | ['tint', 'tertiary']\n\nexport const SURFACE_COLOR_FIELDS: Array<{\n path: SurfaceColorPath\n label: string\n}> = [\n { path: ['background', 'default'], label: 'Background' },\n { path: ['background', 'paper'], label: 'Paper' },\n { path: ['text', 'primary'], label: 'Text' },\n { path: ['text', 'secondary'], label: 'Secondary text' },\n { path: ['text', 'disabled'], label: 'Disabled text' },\n]\n\n/**\n * Pale accent washes (AGL-1244).\n *\n * Separate from {@link PALETTE_COLOR_FIELDS} because a tint is a STRING LEAF,\n * not a `{ main }` record — the palette fields all write `main`, and a tint has\n * none. It rides the same `SurfaceColorPath` machinery as `background`/`text`\n * for exactly that reason, and is listed apart only so the editor can head it\n * \"Tints\" instead of filing it under \"Background & text\".\n */\nexport const TINT_COLOR_FIELDS: Array<{\n path: SurfaceColorPath\n label: string\n}> = [\n { path: ['tint', 'primary'], label: 'Primary tint' },\n { path: ['tint', 'secondary'], label: 'Secondary tint' },\n { path: ['tint', 'tertiary'], label: 'Tertiary tint' },\n]\n\n/** The one color leaf that is neither a palette color nor a member of a group. */\nexport const DIVIDER_COLOR_FIELD = { key: 'divider', label: 'Divider' } as const\n\n/** Curated Google Fonts choices for the font family selector. */\nexport const GOOGLE_FONT_OPTIONS: Array<{\n family: string\n category: 'sans-serif' | 'serif' | 'monospace' | 'display'\n weights: Array<number>\n}> = [\n { family: 'Inter', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Roboto', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Open Sans', category: 'sans-serif', weights: [400, 600, 700] },\n { family: 'Lato', category: 'sans-serif', weights: [400, 700] },\n { family: 'Montserrat', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Poppins', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Nunito', category: 'sans-serif', weights: [400, 600, 700] },\n { family: 'Work Sans', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Raleway', category: 'sans-serif', weights: [400, 500, 700] },\n { family: 'Merriweather', category: 'serif', weights: [400, 700] },\n { family: 'Playfair Display', category: 'serif', weights: [400, 700] },\n { family: 'Lora', category: 'serif', weights: [400, 700] },\n { family: 'Source Serif 4', category: 'serif', weights: [400, 700] },\n { family: 'JetBrains Mono', category: 'monospace', weights: [400, 700] },\n { family: 'Bebas Neue', category: 'display', weights: [400] },\n]\n\nexport function fontFamilyStack(\n family: string,\n category: (typeof GOOGLE_FONT_OPTIONS)[number]['category'],\n) {\n const fallback = category === 'monospace' ? 'monospace' : category === 'serif' ? 'serif' : 'sans-serif'\n return `\"${family}\", ${fallback}`\n}\n\nexport function getSchemeColor(\n colors: HostThemeSchemeColors | undefined,\n path: SurfaceColorPath,\n): string | undefined {\n const [group, key] = path\n return (colors?.[group] as Record<string, string> | undefined)?.[key]\n}\n\n/* ------------------------------------------------------------------------ *\n * The controls, as one catalog\n * ------------------------------------------------------------------------ */\n\n/** The two color schemes the editor edits, in tab order. */\nexport const THEME_EDITOR_SCHEMES: readonly HostThemeScheme[] = ['light', 'dark']\n\n/**\n * Every color the editor edits per scheme, named the way a diff names it: a\n * palette key (`primary`), a group path joined with a dot\n * (`background.default`, `tint.primary`), or `divider`.\n */\nexport type ThemeColorToken =\n | PaletteColorKey\n | 'background.default'\n | 'background.paper'\n | 'text.primary'\n | 'text.secondary'\n | 'text.disabled'\n | 'tint.primary'\n | 'tint.secondary'\n | 'tint.tertiary'\n | 'divider'\n\n/** Where a color control sits in the editor's Color scheme card. */\nexport type ThemeColorGroup = 'palette' | 'surface' | 'tint' | 'divider'\n\n/** Every color field the editor renders, in the order it renders them. */\nexport const THEME_COLOR_FIELDS: ReadonlyArray<{\n token: ThemeColorToken\n label: string\n group: ThemeColorGroup\n}> = [\n ...PALETTE_COLOR_FIELDS.map(({ key, label }) => ({\n token: key,\n label,\n group: 'palette' as const,\n })),\n ...SURFACE_COLOR_FIELDS.map(({ path, label }) => ({\n token: path.join('.') as ThemeColorToken,\n label,\n group: 'surface' as const,\n })),\n ...TINT_COLOR_FIELDS.map(({ path, label }) => ({\n token: path.join('.') as ThemeColorToken,\n label,\n group: 'tint' as const,\n })),\n {\n token: DIVIDER_COLOR_FIELD.key,\n label: DIVIDER_COLOR_FIELD.label,\n group: 'divider' as const,\n },\n]\n\n/** Whether visitors get a dark scheme; absent from the document means `auto`. */\nexport type ThemeDarkSchemeValue = 'auto' | 'off'\n\nexport const DARK_SCHEME_FIELD = {\n label: 'Dark scheme',\n options: [\n { value: 'auto', label: 'Follows the visitor' },\n { value: 'off', label: 'Off — always light' },\n ],\n} as const\n\n/**\n * The font select's value for \"no font of the site's own\": the theme's\n * default stack, with nothing to load.\n */\nexport const SYSTEM_FONT_VALUE = '__system__'\n\nexport const FONT_FAMILY_FIELD = { label: 'Font family' } as const\n\n/** The corner radius slider, in px. */\nexport const BORDER_RADIUS_FIELD = {\n label: 'Border radius',\n min: 0,\n max: 24,\n step: 1,\n} as const\n\n/** The spacing unit MUI multiplies every `spacing(n)` by, in px. */\nexport const SPACING_FIELD = {\n label: 'Spacing unit (px)',\n min: 2,\n max: 16,\n step: 1,\n} as const\n\n/** The two nav heights, either side of MUI's toolbar breakpoint, in px. */\nexport const TOOLBAR_HEIGHT_FIELDS = {\n xs: { label: 'Nav height, mobile (px)', min: 40, max: 160, step: 1 },\n sm: { label: 'Nav height, desktop (px)', min: 40, max: 160, step: 1 },\n} as const\n\n/** The raw-JSON component overrides, limited to the sanitizer's whitelist. */\nexport const COMPONENT_OVERRIDES_FIELD = {\n label: 'Component overrides',\n components: HOST_THEME_COMPONENT_WHITELIST,\n} as const\n\n/**\n * The media queries a component override may scope a style to: either side\n * of MUI's own toolbar breakpoint, the one breakpoint the editor already\n * names. MUI's `down('sm')` stops a twentieth of a pixel short of it.\n */\nexport const THEME_EDITOR_MEDIA_QUERIES = {\n mobile: `@media (max-width:${TOOLBAR_SM_MIN_WIDTH - 0.05}px)`,\n desktop: TOOLBAR_SM_QUERY,\n} as const\n\nexport type ThemeEditorMedia = keyof typeof THEME_EDITOR_MEDIA_QUERIES\n\nexport type ThemeEditorControlId =\n | `color.${ThemeColorToken}`\n | 'darkScheme'\n | 'fontFamily'\n | 'borderRadius'\n | 'spacing'\n | 'navHeight.xs'\n | 'navHeight.sm'\n | 'components'\n\n/** One control the editor renders, described well enough to be offered elsewhere. */\nexport interface ThemeEditorControl {\n /** Stable id: what a diff row, a proposal and a spec name the control by. */\n id: ThemeEditorControlId\n /** The label the editor shows. */\n label: string\n kind: 'color' | 'select' | 'number' | 'json'\n /** Edited once per scheme, under the Light and Dark tabs. */\n perScheme: boolean\n /** A color control's token. */\n token?: ThemeColorToken\n /** A select's values, or the components a JSON control accepts. */\n options?: readonly string[]\n /** A number control's inclusive bounds and its step. */\n min?: number\n max?: number\n step?: number\n}\n\n/**\n * Every value-bearing control the editor renders, in the order it renders\n * them. The buttons that act on these values rather than hold one — Copy\n * from light, Reset to default, Discard, Save — are not controls: a reset is\n * a control set back to nothing, and a copy is every color control of one\n * scheme written from the other.\n */\nexport const THEME_EDITOR_CONTROLS: readonly ThemeEditorControl[] = [\n {\n id: 'darkScheme',\n label: DARK_SCHEME_FIELD.label,\n kind: 'select',\n perScheme: false,\n options: DARK_SCHEME_FIELD.options.map((option) => option.value),\n },\n ...THEME_COLOR_FIELDS.map(({ token, label }) => ({\n id: `color.${token}` as const,\n label,\n kind: 'color' as const,\n perScheme: true,\n token,\n })),\n {\n id: 'fontFamily',\n label: FONT_FAMILY_FIELD.label,\n kind: 'select',\n perScheme: false,\n options: [SYSTEM_FONT_VALUE, ...GOOGLE_FONT_OPTIONS.map((option) => option.family)],\n },\n {\n id: 'borderRadius',\n label: BORDER_RADIUS_FIELD.label,\n kind: 'number',\n perScheme: false,\n min: BORDER_RADIUS_FIELD.min,\n max: BORDER_RADIUS_FIELD.max,\n step: BORDER_RADIUS_FIELD.step,\n },\n {\n id: 'spacing',\n label: SPACING_FIELD.label,\n kind: 'number',\n perScheme: false,\n min: SPACING_FIELD.min,\n max: SPACING_FIELD.max,\n step: SPACING_FIELD.step,\n },\n {\n id: 'navHeight.xs',\n label: TOOLBAR_HEIGHT_FIELDS.xs.label,\n kind: 'number',\n perScheme: false,\n min: TOOLBAR_HEIGHT_FIELDS.xs.min,\n max: TOOLBAR_HEIGHT_FIELDS.xs.max,\n step: TOOLBAR_HEIGHT_FIELDS.xs.step,\n },\n {\n id: 'navHeight.sm',\n label: TOOLBAR_HEIGHT_FIELDS.sm.label,\n kind: 'number',\n perScheme: false,\n min: TOOLBAR_HEIGHT_FIELDS.sm.min,\n max: TOOLBAR_HEIGHT_FIELDS.sm.max,\n step: TOOLBAR_HEIGHT_FIELDS.sm.step,\n },\n {\n id: 'components',\n label: COMPONENT_OVERRIDES_FIELD.label,\n kind: 'json',\n perScheme: false,\n options: COMPONENT_OVERRIDES_FIELD.components,\n },\n]\n\n/** The catalog entry for a control id. */\nexport function themeEditorControl(id: ThemeEditorControlId): ThemeEditorControl {\n const control = THEME_EDITOR_CONTROLS.find((entry) => entry.id === id)\n if (!control) throw new Error(`unknown theme editor control ${id}`)\n return control\n}\n\n/* ------------------------------------------------------------------------ *\n * Reads and writes, exactly as the editor performs them\n * ------------------------------------------------------------------------ */\n\nfunction setSchemeValue(\n theme: HostTheme,\n scheme: HostThemeScheme,\n update: (colors: HostThemeSchemeColors) => HostThemeSchemeColors,\n): HostTheme {\n const colors = theme.colorSchemes?.[scheme] ?? {}\n return {\n ...theme,\n colorSchemes: { ...theme.colorSchemes, [scheme]: update(colors) },\n }\n}\n\n/** A color the site set for one scheme; `undefined` when the slot inherits. */\nexport function readThemeColor(\n theme: HostTheme | undefined,\n scheme: HostThemeScheme,\n token: ThemeColorToken,\n): string | undefined {\n const colors = theme?.colorSchemes?.[scheme]\n if (token === 'divider') return colors?.divider\n const [group, key] = token.split('.')\n if (!key) return colors?.[group as PaletteColorKey]?.main\n return getSchemeColor(colors, [group, key] as SurfaceColorPath)\n}\n\n/**\n * Sets one color for one scheme, or clears it with `undefined` so the slot\n * inherits again. A palette color writes `main` and keeps whatever else its\n * record carries, and clearing it drops the record; a group member that\n * leaves its group empty drops the group.\n */\nexport function writeThemeColor(\n theme: HostTheme,\n scheme: HostThemeScheme,\n token: ThemeColorToken,\n hex: string | undefined,\n): HostTheme {\n return setSchemeValue(theme, scheme, (colors) => {\n const next: HostThemeSchemeColors = { ...colors }\n if (token === 'divider') {\n if (hex) next.divider = hex\n else delete next.divider\n return next\n }\n const [group, key] = token.split('.')\n if (!key) {\n const paletteKey = group as PaletteColorKey\n if (hex) next[paletteKey] = { ...next[paletteKey], main: hex }\n else delete next[paletteKey]\n return next\n }\n const groupKey = group as 'background' | 'text' | 'tint'\n const groupValue = {\n ...(colors[groupKey] as Record<string, string> | undefined),\n }\n if (hex) groupValue[key] = hex\n else delete groupValue[key]\n const grouped = { ...next, [groupKey]: groupValue } as HostThemeSchemeColors\n if (!Object.keys(groupValue).length) delete grouped[groupKey]\n return grouped\n })\n}\n\n/** Replaces one scheme's colors with a copy of the other's. */\nexport function copyThemeSchemeColors(\n theme: HostTheme,\n from: HostThemeScheme,\n to: HostThemeScheme,\n): HostTheme {\n const source = theme.colorSchemes?.[from]\n if (!source) return theme\n return {\n ...theme,\n colorSchemes: {\n ...theme.colorSchemes,\n [to]: JSON.parse(JSON.stringify(source)),\n },\n }\n}\n\n/** The dark scheme switch as the select shows it. */\nexport function readDarkScheme(theme: HostTheme | undefined): ThemeDarkSchemeValue {\n return theme?.darkScheme === 'off' ? 'off' : 'auto'\n}\n\n/**\n * Absent means \"follows the visitor\"; only the opt-out is written, so the\n * saved document stays empty for the common case.\n */\nexport function writeDarkScheme(\n theme: HostTheme,\n value: ThemeDarkSchemeValue,\n): HostTheme {\n const next: HostTheme = { ...theme }\n if (value === 'off') next.darkScheme = 'off'\n else delete next.darkScheme\n return next\n}\n\n/** The font select's value: the site's font, or {@link SYSTEM_FONT_VALUE}. */\nexport function readFontFamily(theme: HostTheme | undefined): string {\n return theme?.fonts?.[0]?.family ?? SYSTEM_FONT_VALUE\n}\n\n/**\n * Picks a font from {@link GOOGLE_FONT_OPTIONS}, loading it and naming it as\n * the typography's stack, or {@link SYSTEM_FONT_VALUE} to go back to the\n * theme default. A family outside the curated list changes nothing.\n */\nexport function writeFontFamily(theme: HostTheme, value: string): HostTheme {\n if (value === SYSTEM_FONT_VALUE) {\n const next = { ...theme }\n delete next.fonts\n const typography = { ...next.typography }\n delete typography.fontFamily\n if (Object.keys(typography).length) next.typography = typography\n else delete next.typography\n return next\n }\n const option = GOOGLE_FONT_OPTIONS.find((entry) => entry.family === value)\n if (!option) return theme\n return {\n ...theme,\n fonts: [\n {\n family: option.family,\n weights: option.weights,\n source: 'google',\n },\n ],\n typography: {\n ...theme.typography,\n fontFamily: fontFamilyStack(option.family, option.category),\n },\n }\n}\n\n/** Sets the corner radius, or clears it with `undefined` so it inherits. */\nexport function writeBorderRadius(\n theme: HostTheme,\n value: number | undefined,\n): HostTheme {\n if (value !== undefined && Number.isFinite(value)) {\n return { ...theme, shape: { ...theme.shape, borderRadius: value } }\n }\n const next = { ...theme }\n const shape = { ...next.shape }\n delete shape.borderRadius\n if (Object.keys(shape).length) next.shape = shape\n else delete next.shape\n return next\n}\n\n/** Sets the spacing unit; anything but a positive number clears it. */\nexport function writeSpacing(theme: HostTheme, value: number | undefined): HostTheme {\n const next = { ...theme }\n if (value !== undefined && Number.isFinite(value) && value > 0) next.spacing = value\n else delete next.spacing\n return next\n}\n\n/**\n * Sets one nav height — anything but a positive number clears it — and\n * rebuilds the whole toolbar mixin around both, because `mixins.toolbar`\n * replaces MUI's default rather than merging into it (AGL-1242).\n */\nexport function writeToolbarHeight(\n theme: HostTheme,\n breakpoint: 'xs' | 'sm',\n value: number | undefined,\n): HostTheme {\n const valid = value !== undefined && Number.isFinite(value) && value > 0\n const edited = valid ? value : undefined\n const xs = breakpoint === 'xs' ? edited : readToolbarHeight(theme, 'xs')\n const sm = breakpoint === 'sm' ? edited : readToolbarHeight(theme, 'sm')\n const next = { ...theme }\n if (xs === undefined && sm === undefined) {\n delete next.mixins\n return next\n }\n next.mixins = { toolbar: buildToolbarMixin(xs, sm) }\n return next\n}\n\n/**\n * Drops the site's component overrides. With nothing stored the site renders\n * the brand's own component styles, which is what the editor's \"Reset to\n * theme defaults\" means.\n */\nexport function resetComponentOverrides(theme: HostTheme): HostTheme {\n const next = { ...theme }\n delete next.components\n return next\n}\n\n/** One leaf of a component override, as a writer names it. */\nexport interface ThemeComponentOverrideLeaf {\n component: HostThemeComponentKey\n /**\n * `styleOverrides` styles a slot in literal CSS; `sx` styles it in theme\n * terms, so a value may name a palette path (`primary.main`) or a spacing\n * unit (AGL-3403); `defaultProps` sets a prop's default.\n */\n target: 'styleOverrides' | 'sx' | 'defaultProps'\n /** The style slot (`root`, `contained`, `h1`); `null` for a default prop. */\n slot: string | null\n /** A camelCase CSS property, or the prop's name. */\n property: string\n /** Scopes a style to one side of the toolbar breakpoint; `null` for every width. */\n media: ThemeEditorMedia | null\n value: string | number | boolean\n}\n\n/**\n * Sets one leaf inside the site's component overrides and keeps every other\n * override the site has. The stored overrides are deep-merged over the\n * brand's own at render time, so naming one property changes that property\n * and inherits the rest of the component.\n */\nexport function writeComponentOverride(\n theme: HostTheme,\n leaf: ThemeComponentOverrideLeaf,\n): HostTheme {\n const components = { ...theme.components }\n const entry: HostThemeComponentOverride = { ...components[leaf.component] }\n if (leaf.target === 'defaultProps') {\n entry.defaultProps = { ...entry.defaultProps, [leaf.property]: leaf.value }\n } else {\n const slot = leaf.slot ?? 'root'\n const styles: Record<string, unknown> = {\n ...(leaf.target === 'sx' ? entry.sx : entry.styleOverrides),\n }\n const slotStyles = { ...(styles[slot] as Record<string, unknown> | undefined) }\n if (leaf.media) {\n const query = THEME_EDITOR_MEDIA_QUERIES[leaf.media]\n slotStyles[query] = {\n ...(slotStyles[query] as Record<string, unknown> | undefined),\n [leaf.property]: leaf.value,\n }\n } else {\n slotStyles[leaf.property] = leaf.value\n }\n styles[slot] = slotStyles\n if (leaf.target === 'sx') {\n entry.sx = styles as HostThemeComponentOverride['sx']\n } else {\n entry.styleOverrides = styles\n }\n }\n components[leaf.component] = entry\n return { ...theme, components }\n}\n"],"names":["HOST_THEME_COMPONENT_WHITELIST","TOOLBAR_SM_MIN_WIDTH","TOOLBAR_SM_QUERY","DEFAULT_TOOLBAR_XS","DEFAULT_TOOLBAR_SM","TOOLBAR_LANDSCAPE_QUERY","TOOLBAR_LANDSCAPE_RULE","minHeight","readToolbarHeight","theme","breakpoint","toolbar","mixins","undefined","raw","value","parseFloat","String","Number","isFinite","buildToolbarMixin","xs","sm","orderSensitiveKey","JSON","stringify","PALETTE_COLOR_FIELDS","key","label","SURFACE_COLOR_FIELDS","path","TINT_COLOR_FIELDS","DIVIDER_COLOR_FIELD","GOOGLE_FONT_OPTIONS","family","category","weights","fontFamilyStack","fallback","getSchemeColor","colors","group","THEME_EDITOR_SCHEMES","THEME_COLOR_FIELDS","map","token","join","DARK_SCHEME_FIELD","options","SYSTEM_FONT_VALUE","FONT_FAMILY_FIELD","BORDER_RADIUS_FIELD","min","max","step","SPACING_FIELD","TOOLBAR_HEIGHT_FIELDS","COMPONENT_OVERRIDES_FIELD","components","THEME_EDITOR_MEDIA_QUERIES","mobile","desktop","THEME_EDITOR_CONTROLS","id","kind","perScheme","option","themeEditorControl","control","find","entry","Error","setSchemeValue","scheme","update","colorSchemes","readThemeColor","divider","split","main","writeThemeColor","hex","next","paletteKey","groupKey","groupValue","grouped","Object","keys","length","copyThemeSchemeColors","from","to","source","parse","readDarkScheme","darkScheme","writeDarkScheme","readFontFamily","fonts","writeFontFamily","typography","fontFamily","writeBorderRadius","shape","borderRadius","writeSpacing","spacing","writeToolbarHeight","valid","edited","resetComponentOverrides","writeComponentOverride","leaf","component","target","defaultProps","property","slot","styles","sx","styleOverrides","slotStyles","media","query"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAQD,SACEA,8BAA8B,QAEzB,kBAAc;AAErB;;;;;;;;;;;;;;;;;;;;CAoBC,GAED;;;;CAIC,GACD,OAAO,MAAMC,uBAAuB,IAAG;AACvC,OAAO,MAAMC,mBAAmB,CAAC,kBAAkB,EAAED,qBAAqB,GAAG,CAAC,CAAA;AAC9E,mEAAmE,GACnE,OAAO,MAAME,qBAAqB,GAAE;AACpC,OAAO,MAAMC,qBAAqB,GAAE;AACpC;;;;CAIC,GACD,OAAO,MAAMC,0BAA0B,yBAAwB;AAC/D,OAAO,MAAMC,yBAAyB;IACpC,mCAAmC;QAAEC,WAAW;IAAG;AACrD,EAAC;AAED,uEAAuE,GACvE,OAAO,SAASC,kBAAkBC,KAAgB,EAAEC,UAAuB;QACzDD,eAKTE;IALP,MAAMA,WAAUF,gBAAAA,MAAMG,MAAM,qBAAZH,cAAcE,OAAO;IACrC,IAAI,CAACA,SAAS,OAAOE;IACrB,MAAMC,MACJJ,eAAe,OACXC,QAAQJ,SAAS,IAChBI,4BAAAA,OAAO,CAACT,iBAAiB,qBAA1B,AAACS,0BACGJ,SAAS;IACnB,MAAMQ,QAAQC,WAAWC,OAAOH,cAAAA,MAAO;IACvC,OAAOI,OAAOC,QAAQ,CAACJ,SAASA,QAAQF;AAC1C;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASO,kBAAkBC,EAAsB,EAAEC,EAAsB;IAC9E,OAAO;QACLf,WAAW,GAAGc,aAAAA,KAAMlB,mBAAmB,EAAE,CAAC;QAC1C,CAACE,wBAAwB,EAAEC;QAC3B,CAACJ,iBAAiB,EAAE;YAAEK,WAAW,GAAGe,aAAAA,KAAMlB,mBAAmB,EAAE,CAAC;QAAC;IACnE;AACF;AAEA;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,SAASmB,kBAAkBd,KAAgB;QAC1BA;IAAtB,OAAOe,KAAKC,SAAS,EAAChB,gBAAAA,MAAMG,MAAM,YAAZH,gBAAgB,CAAC;AACzC;AAYA,OAAO,MAAMiB,uBAGR;IACH;QAAEC,KAAK;QAAWC,OAAO;IAAU;IACnC;QAAED,KAAK;QAAaC,OAAO;IAAY;IACvC;QAAED,KAAK;QAAYC,OAAO;IAAW;IACrC;QAAED,KAAK;QAAWC,OAAO;IAAU;IACnC;QAAED,KAAK;QAASC,OAAO;IAAQ;IAC/B;QAAED,KAAK;QAAWC,OAAO;IAAU;IACnC;QAAED,KAAK;QAAQC,OAAO;IAAO;IAC7B;QAAED,KAAK;QAAWC,OAAO;IAAU;CACpC,CAAA;AAYD,OAAO,MAAMC,uBAGR;IACH;QAAEC,MAAM;YAAC;YAAc;SAAU;QAAEF,OAAO;IAAa;IACvD;QAAEE,MAAM;YAAC;YAAc;SAAQ;QAAEF,OAAO;IAAQ;IAChD;QAAEE,MAAM;YAAC;YAAQ;SAAU;QAAEF,OAAO;IAAO;IAC3C;QAAEE,MAAM;YAAC;YAAQ;SAAY;QAAEF,OAAO;IAAiB;IACvD;QAAEE,MAAM;YAAC;YAAQ;SAAW;QAAEF,OAAO;IAAgB;CACtD,CAAA;AAED;;;;;;;;CAQC,GACD,OAAO,MAAMG,oBAGR;IACH;QAAED,MAAM;YAAC;YAAQ;SAAU;QAAEF,OAAO;IAAe;IACnD;QAAEE,MAAM;YAAC;YAAQ;SAAY;QAAEF,OAAO;IAAiB;IACvD;QAAEE,MAAM;YAAC;YAAQ;SAAW;QAAEF,OAAO;IAAgB;CACtD,CAAA;AAED,gFAAgF,GAChF,OAAO,MAAMI,sBAAsB;IAAEL,KAAK;IAAWC,OAAO;AAAU,EAAU;AAEhF,+DAA+D,GAC/D,OAAO,MAAMK,sBAIR;IACH;QAAEC,QAAQ;QAASC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACpE;QAAEF,QAAQ;QAAUC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACrE;QAAEF,QAAQ;QAAaC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACxE;QAAEF,QAAQ;QAAQC,UAAU;QAAcC,SAAS;YAAC;YAAK;SAAI;IAAC;IAC9D;QAAEF,QAAQ;QAAcC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACzE;QAAEF,QAAQ;QAAWC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACtE;QAAEF,QAAQ;QAAUC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACrE;QAAEF,QAAQ;QAAaC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACxE;QAAEF,QAAQ;QAAWC,UAAU;QAAcC,SAAS;YAAC;YAAK;YAAK;SAAI;IAAC;IACtE;QAAEF,QAAQ;QAAgBC,UAAU;QAASC,SAAS;YAAC;YAAK;SAAI;IAAC;IACjE;QAAEF,QAAQ;QAAoBC,UAAU;QAASC,SAAS;YAAC;YAAK;SAAI;IAAC;IACrE;QAAEF,QAAQ;QAAQC,UAAU;QAASC,SAAS;YAAC;YAAK;SAAI;IAAC;IACzD;QAAEF,QAAQ;QAAkBC,UAAU;QAASC,SAAS;YAAC;YAAK;SAAI;IAAC;IACnE;QAAEF,QAAQ;QAAkBC,UAAU;QAAaC,SAAS;YAAC;YAAK;SAAI;IAAC;IACvE;QAAEF,QAAQ;QAAcC,UAAU;QAAWC,SAAS;YAAC;SAAI;IAAC;CAC7D,CAAA;AAED,OAAO,SAASC,gBACdH,MAAc,EACdC,QAA0D;IAE1D,MAAMG,WAAWH,aAAa,cAAc,cAAcA,aAAa,UAAU,UAAU;IAC3F,OAAO,CAAC,CAAC,EAAED,OAAO,GAAG,EAAEI,UAAU;AACnC;AAEA,OAAO,SAASC,eACdC,MAAyC,EACzCV,IAAsB;QAGdU;IADR,MAAM,CAACC,OAAOd,IAAI,GAAGG;IACrB,OAAQU,2BAAAA,gBAAAA,MAAQ,CAACC,MAAM,qBAAhB,AAACD,aAAwD,CAACb,IAAI;AACvE;AAEA;;4EAE4E,GAE5E,0DAA0D,GAC1D,OAAO,MAAMe,uBAAmD;IAAC;IAAS;CAAO,CAAA;AAsBjF,wEAAwE,GACxE,OAAO,MAAMC,qBAIR;OACAjB,qBAAqBkB,GAAG,CAAC,CAAC,EAAEjB,GAAG,EAAEC,KAAK,EAAE,GAAM,CAAA;YAC/CiB,OAAOlB;YACPC;YACAa,OAAO;QACT,CAAA;OACGZ,qBAAqBe,GAAG,CAAC,CAAC,EAAEd,IAAI,EAAEF,KAAK,EAAE,GAAM,CAAA;YAChDiB,OAAOf,KAAKgB,IAAI,CAAC;YACjBlB;YACAa,OAAO;QACT,CAAA;OACGV,kBAAkBa,GAAG,CAAC,CAAC,EAAEd,IAAI,EAAEF,KAAK,EAAE,GAAM,CAAA;YAC7CiB,OAAOf,KAAKgB,IAAI,CAAC;YACjBlB;YACAa,OAAO;QACT,CAAA;IACA;QACEI,OAAOb,oBAAoBL,GAAG;QAC9BC,OAAOI,oBAAoBJ,KAAK;QAChCa,OAAO;IACT;CACD,CAAA;AAKD,OAAO,MAAMM,oBAAoB;IAC/BnB,OAAO;IACPoB,SAAS;QACP;YAAEjC,OAAO;YAAQa,OAAO;QAAsB;QAC9C;YAAEb,OAAO;YAAOa,OAAO;QAAqB;KAC7C;AACH,EAAU;AAEV;;;CAGC,GACD,OAAO,MAAMqB,oBAAoB,aAAY;AAE7C,OAAO,MAAMC,oBAAoB;IAAEtB,OAAO;AAAc,EAAU;AAElE,qCAAqC,GACrC,OAAO,MAAMuB,sBAAsB;IACjCvB,OAAO;IACPwB,KAAK;IACLC,KAAK;IACLC,MAAM;AACR,EAAU;AAEV,kEAAkE,GAClE,OAAO,MAAMC,gBAAgB;IAC3B3B,OAAO;IACPwB,KAAK;IACLC,KAAK;IACLC,MAAM;AACR,EAAU;AAEV,yEAAyE,GACzE,OAAO,MAAME,wBAAwB;IACnCnC,IAAI;QAAEO,OAAO;QAA2BwB,KAAK;QAAIC,KAAK;QAAKC,MAAM;IAAE;IACnEhC,IAAI;QAAEM,OAAO;QAA4BwB,KAAK;QAAIC,KAAK;QAAKC,MAAM;IAAE;AACtE,EAAU;AAEV,4EAA4E,GAC5E,OAAO,MAAMG,4BAA4B;IACvC7B,OAAO;IACP8B,YAAY1D;AACd,EAAU;AAEV;;;;CAIC,GACD,OAAO,MAAM2D,6BAA6B;IACxCC,QAAQ,CAAC,kBAAkB,EAAE3D,uBAAuB,KAAK,GAAG,CAAC;IAC7D4D,SAAS3D;AACX,EAAU;AAiCV;;;;;;CAMC,GACD,OAAO,MAAM4D,wBAAuD;IAClE;QACEC,IAAI;QACJnC,OAAOmB,kBAAkBnB,KAAK;QAC9BoC,MAAM;QACNC,WAAW;QACXjB,SAASD,kBAAkBC,OAAO,CAACJ,GAAG,CAAC,CAACsB,SAAWA,OAAOnD,KAAK;IACjE;OACG4B,mBAAmBC,GAAG,CAAC,CAAC,EAAEC,KAAK,EAAEjB,KAAK,EAAE,GAAM,CAAA;YAC/CmC,IAAI,CAAC,MAAM,EAAElB,OAAO;YACpBjB;YACAoC,MAAM;YACNC,WAAW;YACXpB;QACF,CAAA;IACA;QACEkB,IAAI;QACJnC,OAAOsB,kBAAkBtB,KAAK;QAC9BoC,MAAM;QACNC,WAAW;QACXjB,SAAS;YAACC;eAAsBhB,oBAAoBW,GAAG,CAAC,CAACsB,SAAWA,OAAOhC,MAAM;SAAE;IACrF;IACA;QACE6B,IAAI;QACJnC,OAAOuB,oBAAoBvB,KAAK;QAChCoC,MAAM;QACNC,WAAW;QACXb,KAAKD,oBAAoBC,GAAG;QAC5BC,KAAKF,oBAAoBE,GAAG;QAC5BC,MAAMH,oBAAoBG,IAAI;IAChC;IACA;QACES,IAAI;QACJnC,OAAO2B,cAAc3B,KAAK;QAC1BoC,MAAM;QACNC,WAAW;QACXb,KAAKG,cAAcH,GAAG;QACtBC,KAAKE,cAAcF,GAAG;QACtBC,MAAMC,cAAcD,IAAI;IAC1B;IACA;QACES,IAAI;QACJnC,OAAO4B,sBAAsBnC,EAAE,CAACO,KAAK;QACrCoC,MAAM;QACNC,WAAW;QACXb,KAAKI,sBAAsBnC,EAAE,CAAC+B,GAAG;QACjCC,KAAKG,sBAAsBnC,EAAE,CAACgC,GAAG;QACjCC,MAAME,sBAAsBnC,EAAE,CAACiC,IAAI;IACrC;IACA;QACES,IAAI;QACJnC,OAAO4B,sBAAsBlC,EAAE,CAACM,KAAK;QACrCoC,MAAM;QACNC,WAAW;QACXb,KAAKI,sBAAsBlC,EAAE,CAAC8B,GAAG;QACjCC,KAAKG,sBAAsBlC,EAAE,CAAC+B,GAAG;QACjCC,MAAME,sBAAsBlC,EAAE,CAACgC,IAAI;IACrC;IACA;QACES,IAAI;QACJnC,OAAO6B,0BAA0B7B,KAAK;QACtCoC,MAAM;QACNC,WAAW;QACXjB,SAASS,0BAA0BC,UAAU;IAC/C;CACD,CAAA;AAED,wCAAwC,GACxC,OAAO,SAASS,mBAAmBJ,EAAwB;IACzD,MAAMK,UAAUN,sBAAsBO,IAAI,CAAC,CAACC,QAAUA,MAAMP,EAAE,KAAKA;IACnE,IAAI,CAACK,SAAS,MAAM,IAAIG,MAAM,CAAC,6BAA6B,EAAER,IAAI;IAClE,OAAOK;AACT;AAEA;;4EAE4E,GAE5E,SAASI,eACP/D,KAAgB,EAChBgE,MAAuB,EACvBC,MAAgE;;QAEjDjE;IAAf,MAAM+B,kBAAS/B,sBAAAA,MAAMkE,YAAY,qBAAlBlE,mBAAoB,CAACgE,OAAO,mBAAI,CAAC;IAChD,OAAO,aACFhE;QACHkE,cAAc,aAAKlE,MAAMkE,YAAY;YAAE,CAACF,OAAO,EAAEC,OAAOlC;;;AAE5D;AAEA,6EAA6E,GAC7E,OAAO,SAASoC,eACdnE,KAA4B,EAC5BgE,MAAuB,EACvB5B,KAAsB;QAEPpC,qBAGE+B;IAHjB,MAAMA,SAAS/B,0BAAAA,sBAAAA,MAAOkE,YAAY,qBAAnBlE,mBAAqB,CAACgE,OAAO;IAC5C,IAAI5B,UAAU,WAAW,OAAOL,0BAAAA,OAAQqC,OAAO;IAC/C,MAAM,CAACpC,OAAOd,IAAI,GAAGkB,MAAMiC,KAAK,CAAC;IACjC,IAAI,CAACnD,KAAK,OAAOa,2BAAAA,gBAAAA,MAAQ,CAACC,MAAyB,qBAAlCD,cAAoCuC,IAAI;IACzD,OAAOxC,eAAeC,QAAQ;QAACC;QAAOd;KAAI;AAC5C;AAEA;;;;;CAKC,GACD,OAAO,SAASqD,gBACdvE,KAAgB,EAChBgE,MAAuB,EACvB5B,KAAsB,EACtBoC,GAAuB;IAEvB,OAAOT,eAAe/D,OAAOgE,QAAQ,CAACjC;QACpC,MAAM0C,OAA8B,aAAK1C;QACzC,IAAIK,UAAU,WAAW;YACvB,IAAIoC,KAAKC,KAAKL,OAAO,GAAGI;iBACnB,OAAOC,KAAKL,OAAO;YACxB,OAAOK;QACT;QACA,MAAM,CAACzC,OAAOd,IAAI,GAAGkB,MAAMiC,KAAK,CAAC;QACjC,IAAI,CAACnD,KAAK;YACR,MAAMwD,aAAa1C;YACnB,IAAIwC,KAAKC,IAAI,CAACC,WAAW,GAAG,aAAKD,IAAI,CAACC,WAAW;gBAAEJ,MAAME;;iBACpD,OAAOC,IAAI,CAACC,WAAW;YAC5B,OAAOD;QACT;QACA,MAAME,WAAW3C;QACjB,MAAM4C,aAAa,aACb7C,MAAM,CAAC4C,SAAS;QAEtB,IAAIH,KAAKI,UAAU,CAAC1D,IAAI,GAAGsD;aACtB,OAAOI,UAAU,CAAC1D,IAAI;QAC3B,MAAM2D,UAAU,aAAKJ;YAAM,CAACE,SAAS,EAAEC;;QACvC,IAAI,CAACE,OAAOC,IAAI,CAACH,YAAYI,MAAM,EAAE,OAAOH,OAAO,CAACF,SAAS;QAC7D,OAAOE;IACT;AACF;AAEA,6DAA6D,GAC7D,OAAO,SAASI,sBACdjF,KAAgB,EAChBkF,IAAqB,EACrBC,EAAmB;QAEJnF;IAAf,MAAMoF,UAASpF,sBAAAA,MAAMkE,YAAY,qBAAlBlE,mBAAoB,CAACkF,KAAK;IACzC,IAAI,CAACE,QAAQ,OAAOpF;IACpB,OAAO,aACFA;QACHkE,cAAc,aACTlE,MAAMkE,YAAY;YACrB,CAACiB,GAAG,EAAEpE,KAAKsE,KAAK,CAACtE,KAAKC,SAAS,CAACoE;;;AAGtC;AAEA,mDAAmD,GACnD,OAAO,SAASE,eAAetF,KAA4B;IACzD,OAAOA,CAAAA,yBAAAA,MAAOuF,UAAU,MAAK,QAAQ,QAAQ;AAC/C;AAEA;;;CAGC,GACD,OAAO,SAASC,gBACdxF,KAAgB,EAChBM,KAA2B;IAE3B,MAAMmE,OAAkB,aAAKzE;IAC7B,IAAIM,UAAU,OAAOmE,KAAKc,UAAU,GAAG;SAClC,OAAOd,KAAKc,UAAU;IAC3B,OAAOd;AACT;AAEA,4EAA4E,GAC5E,OAAO,SAASgB,eAAezF,KAA4B;;QAClDA,eAAAA;IAAP,eAAOA,0BAAAA,eAAAA,MAAO0F,KAAK,sBAAZ1F,gBAAAA,YAAc,CAAC,EAAE,qBAAjBA,cAAmByB,MAAM,mBAAIe;AACtC;AAEA;;;;CAIC,GACD,OAAO,SAASmD,gBAAgB3F,KAAgB,EAAEM,KAAa;IAC7D,IAAIA,UAAUkC,mBAAmB;QAC/B,MAAMiC,OAAO,aAAKzE;QAClB,OAAOyE,KAAKiB,KAAK;QACjB,MAAME,aAAa,aAAKnB,KAAKmB,UAAU;QACvC,OAAOA,WAAWC,UAAU;QAC5B,IAAIf,OAAOC,IAAI,CAACa,YAAYZ,MAAM,EAAEP,KAAKmB,UAAU,GAAGA;aACjD,OAAOnB,KAAKmB,UAAU;QAC3B,OAAOnB;IACT;IACA,MAAMhB,SAASjC,oBAAoBoC,IAAI,CAAC,CAACC,QAAUA,MAAMpC,MAAM,KAAKnB;IACpE,IAAI,CAACmD,QAAQ,OAAOzD;IACpB,OAAO,aACFA;QACH0F,OAAO;YACL;gBACEjE,QAAQgC,OAAOhC,MAAM;gBACrBE,SAAS8B,OAAO9B,OAAO;gBACvByD,QAAQ;YACV;SACD;QACDQ,YAAY,aACP5F,MAAM4F,UAAU;YACnBC,YAAYjE,gBAAgB6B,OAAOhC,MAAM,EAAEgC,OAAO/B,QAAQ;;;AAGhE;AAEA,0EAA0E,GAC1E,OAAO,SAASoE,kBACd9F,KAAgB,EAChBM,KAAyB;IAEzB,IAAIA,UAAUF,aAAaK,OAAOC,QAAQ,CAACJ,QAAQ;QACjD,OAAO,aAAKN;YAAO+F,OAAO,aAAK/F,MAAM+F,KAAK;gBAAEC,cAAc1F;;;IAC5D;IACA,MAAMmE,OAAO,aAAKzE;IAClB,MAAM+F,QAAQ,aAAKtB,KAAKsB,KAAK;IAC7B,OAAOA,MAAMC,YAAY;IACzB,IAAIlB,OAAOC,IAAI,CAACgB,OAAOf,MAAM,EAAEP,KAAKsB,KAAK,GAAGA;SACvC,OAAOtB,KAAKsB,KAAK;IACtB,OAAOtB;AACT;AAEA,qEAAqE,GACrE,OAAO,SAASwB,aAAajG,KAAgB,EAAEM,KAAyB;IACtE,MAAMmE,OAAO,aAAKzE;IAClB,IAAIM,UAAUF,aAAaK,OAAOC,QAAQ,CAACJ,UAAUA,QAAQ,GAAGmE,KAAKyB,OAAO,GAAG5F;SAC1E,OAAOmE,KAAKyB,OAAO;IACxB,OAAOzB;AACT;AAEA;;;;CAIC,GACD,OAAO,SAAS0B,mBACdnG,KAAgB,EAChBC,UAAuB,EACvBK,KAAyB;IAEzB,MAAM8F,QAAQ9F,UAAUF,aAAaK,OAAOC,QAAQ,CAACJ,UAAUA,QAAQ;IACvE,MAAM+F,SAASD,QAAQ9F,QAAQF;IAC/B,MAAMQ,KAAKX,eAAe,OAAOoG,SAAStG,kBAAkBC,OAAO;IACnE,MAAMa,KAAKZ,eAAe,OAAOoG,SAAStG,kBAAkBC,OAAO;IACnE,MAAMyE,OAAO,aAAKzE;IAClB,IAAIY,OAAOR,aAAaS,OAAOT,WAAW;QACxC,OAAOqE,KAAKtE,MAAM;QAClB,OAAOsE;IACT;IACAA,KAAKtE,MAAM,GAAG;QAAED,SAASS,kBAAkBC,IAAIC;IAAI;IACnD,OAAO4D;AACT;AAEA;;;;CAIC,GACD,OAAO,SAAS6B,wBAAwBtG,KAAgB;IACtD,MAAMyE,OAAO,aAAKzE;IAClB,OAAOyE,KAAKxB,UAAU;IACtB,OAAOwB;AACT;AAoBA;;;;;CAKC,GACD,OAAO,SAAS8B,uBACdvG,KAAgB,EAChBwG,IAAgC;IAEhC,MAAMvD,aAAa,aAAKjD,MAAMiD,UAAU;IACxC,MAAMY,QAAoC,aAAKZ,UAAU,CAACuD,KAAKC,SAAS,CAAC;IACzE,IAAID,KAAKE,MAAM,KAAK,gBAAgB;QAClC7C,MAAM8C,YAAY,GAAG,aAAK9C,MAAM8C,YAAY;YAAE,CAACH,KAAKI,QAAQ,CAAC,EAAEJ,KAAKlG,KAAK;;IAC3E,OAAO;YACQkG;QAAb,MAAMK,QAAOL,aAAAA,KAAKK,IAAI,YAATL,aAAa;QAC1B,MAAMM,SAAkC,aAClCN,KAAKE,MAAM,KAAK,OAAO7C,MAAMkD,EAAE,GAAGlD,MAAMmD,cAAc;QAE5D,MAAMC,aAAa,aAAMH,MAAM,CAACD,KAAK;QACrC,IAAIL,KAAKU,KAAK,EAAE;YACd,MAAMC,QAAQjE,0BAA0B,CAACsD,KAAKU,KAAK,CAAC;YACpDD,UAAU,CAACE,MAAM,GAAG,aACdF,UAAU,CAACE,MAAM;gBACrB,CAACX,KAAKI,QAAQ,CAAC,EAAEJ,KAAKlG,KAAK;;QAE/B,OAAO;YACL2G,UAAU,CAACT,KAAKI,QAAQ,CAAC,GAAGJ,KAAKlG,KAAK;QACxC;QACAwG,MAAM,CAACD,KAAK,GAAGI;QACf,IAAIT,KAAKE,MAAM,KAAK,MAAM;YACxB7C,MAAMkD,EAAE,GAAGD;QACb,OAAO;YACLjD,MAAMmD,cAAc,GAAGF;QACzB;IACF;IACA7D,UAAU,CAACuD,KAAKC,SAAS,CAAC,GAAG5C;IAC7B,OAAO,aAAK7D;QAAOiD;;AACrB"}
|