@aglyn/shared-ui-theme 1.0.0-beta.246 → 1.0.0-beta.247
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
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.247",
|
|
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.247",
|
|
29
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.247",
|
|
30
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.247",
|
|
31
31
|
"@emotion/cache": "^11.14.0",
|
|
32
32
|
"@emotion/react": "11.14.0",
|
|
33
33
|
"@swc/helpers": "0.5.23",
|
|
@@ -207,9 +207,13 @@ export declare const TOOLBAR_HEIGHT_FIELDS: {
|
|
|
207
207
|
readonly step: 1;
|
|
208
208
|
};
|
|
209
209
|
};
|
|
210
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* The site-wide look of components: the click-only fields in
|
|
212
|
+
* {@link THEME_COMPONENT_LOOK_FIELDS}, with the full override set behind
|
|
213
|
+
* them limited to the sanitizer's whitelist.
|
|
214
|
+
*/
|
|
211
215
|
export declare const COMPONENT_OVERRIDES_FIELD: {
|
|
212
|
-
readonly label: "
|
|
216
|
+
readonly label: "Style components";
|
|
213
217
|
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
218
|
};
|
|
215
219
|
/**
|
|
@@ -229,12 +233,12 @@ export interface ThemeEditorControl {
|
|
|
229
233
|
id: ThemeEditorControlId;
|
|
230
234
|
/** The label the editor shows. */
|
|
231
235
|
label: string;
|
|
232
|
-
kind: 'color' | 'select' | 'number' | '
|
|
236
|
+
kind: 'color' | 'select' | 'number' | 'components';
|
|
233
237
|
/** Edited once per scheme, under the Light and Dark tabs. */
|
|
234
238
|
perScheme: boolean;
|
|
235
239
|
/** A color control's token. */
|
|
236
240
|
token?: ThemeColorToken;
|
|
237
|
-
/** A select's values, or the components a
|
|
241
|
+
/** A select's values, or the components a components control may style. */
|
|
238
242
|
options?: readonly string[];
|
|
239
243
|
/** A number control's inclusive bounds and its step. */
|
|
240
244
|
min?: number;
|
|
@@ -255,8 +259,12 @@ export declare function themeEditorControl(id: ThemeEditorControlId): ThemeEdito
|
|
|
255
259
|
export declare function readThemeColor(theme: HostTheme | undefined, scheme: HostThemeScheme, token: ThemeColorToken): string | undefined;
|
|
256
260
|
/**
|
|
257
261
|
* Sets one color for one scheme, or clears it with `undefined` so the slot
|
|
258
|
-
* inherits again. A palette color writes `main`
|
|
259
|
-
*
|
|
262
|
+
* inherits again. A palette color writes a record of `main` alone: its
|
|
263
|
+
* `light`, `dark` and `contrastText` were derived from (or authored for) the
|
|
264
|
+
* old `main`, and the editor offers no control for them, so a kept shade
|
|
265
|
+
* would go on painting the old color (an outlined button reads `dark`) with
|
|
266
|
+
* no way for the owner to see or change it. Dropped, the theme derives them
|
|
267
|
+
* from the new `main`. Clearing it drops the record; a group member that
|
|
260
268
|
* leaves its group empty drops the group.
|
|
261
269
|
*/
|
|
262
270
|
export declare function writeThemeColor(theme: HostTheme, scheme: HostThemeScheme, token: ThemeColorToken, hex: string | undefined): HostTheme;
|
|
@@ -368,3 +376,78 @@ export interface ThemeComponentOverrideLeaf {
|
|
|
368
376
|
* and inherits the rest of the component.
|
|
369
377
|
*/
|
|
370
378
|
export declare function writeComponentOverride(theme: HostTheme, leaf: ThemeComponentOverrideLeaf): HostTheme;
|
|
379
|
+
/** Where one leaf lives, without the value it holds. */
|
|
380
|
+
export type ThemeComponentOverrideTarget = Omit<ThemeComponentOverrideLeaf, 'value'>;
|
|
381
|
+
/**
|
|
382
|
+
* Removes one leaf from the site's component overrides, and every slot,
|
|
383
|
+
* branch and component it leaves empty, so a field set back to "Theme
|
|
384
|
+
* default" stores nothing and the site inherits that property again.
|
|
385
|
+
*/
|
|
386
|
+
export declare function clearComponentOverride(theme: HostTheme, target: ThemeComponentOverrideTarget): HostTheme;
|
|
387
|
+
/** The value one override leaf holds; `undefined` when the site does not set it. */
|
|
388
|
+
export declare function readComponentOverride(theme: HostTheme | undefined, target: ThemeComponentOverrideTarget): unknown;
|
|
389
|
+
/** The components the Style components card sets a site-wide look for. */
|
|
390
|
+
export type ThemeComponentLookGroup = 'button' | 'input' | 'card';
|
|
391
|
+
export declare const THEME_COMPONENT_LOOK_GROUPS: ReadonlyArray<{
|
|
392
|
+
group: ThemeComponentLookGroup;
|
|
393
|
+
label: string;
|
|
394
|
+
}>;
|
|
395
|
+
export type ThemeComponentLookId = 'button.variant' | 'button.corners' | 'button.textCase' | 'button.weight' | 'button.padding' | 'input.variant' | 'input.corners' | 'input.background' | 'input.border' | 'card.variant' | 'card.elevation' | 'card.corners';
|
|
396
|
+
/** One choice a look field offers. */
|
|
397
|
+
export interface ThemeComponentLookOption {
|
|
398
|
+
/** What the select holds. */
|
|
399
|
+
value: string;
|
|
400
|
+
label: string;
|
|
401
|
+
/**
|
|
402
|
+
* What each of the field's targets stores, in target order; one value
|
|
403
|
+
* stands for every target.
|
|
404
|
+
*/
|
|
405
|
+
stored: string | number | ReadonlyArray<string | number>;
|
|
406
|
+
/** A theme color this choice draws in, for its swatch. */
|
|
407
|
+
token?: ThemeColorToken;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* A click-only field over the site's component overrides. Each choice writes
|
|
411
|
+
* the same leaves the Advanced overrides hold, so the two are one setting
|
|
412
|
+
* seen two ways, and anything that writes override leaves can set it.
|
|
413
|
+
*/
|
|
414
|
+
export interface ThemeComponentLookField {
|
|
415
|
+
id: ThemeComponentLookId;
|
|
416
|
+
group: ThemeComponentLookGroup;
|
|
417
|
+
label: string;
|
|
418
|
+
/** `color` choices are theme colors, shown as swatches of the scheme in view. */
|
|
419
|
+
kind: 'select' | 'color';
|
|
420
|
+
/** The leaves a choice sets, read back from the first. */
|
|
421
|
+
targets: readonly ThemeComponentOverrideTarget[];
|
|
422
|
+
/** Leaves set alongside any choice and cleared with it: what makes the choice draw right. */
|
|
423
|
+
companions?: readonly ThemeComponentOverrideLeaf[];
|
|
424
|
+
options: readonly ThemeComponentLookOption[];
|
|
425
|
+
}
|
|
426
|
+
/** The select's value for a field the site does not set: the theme's own look. */
|
|
427
|
+
export declare const THEME_LOOK_DEFAULT = "";
|
|
428
|
+
/** The select's value for a leaf set to something no choice names, by hand or by AI. */
|
|
429
|
+
export declare const THEME_LOOK_CUSTOM = "__custom__";
|
|
430
|
+
/**
|
|
431
|
+
* Every component look field, in the order the card shows them.
|
|
432
|
+
*
|
|
433
|
+
* Inputs style both of MUI's boxes: `MuiOutlinedInput` and `MuiFilledInput`,
|
|
434
|
+
* because the Input style choice picks between them and the other choices
|
|
435
|
+
* have to hold whichever is in use. A border color sets the outlined box's
|
|
436
|
+
* own border, which the Besigner's Input element draws, and the notched
|
|
437
|
+
* outline a stock text field draws.
|
|
438
|
+
*/
|
|
439
|
+
export declare const THEME_COMPONENT_LOOK_FIELDS: readonly ThemeComponentLookField[];
|
|
440
|
+
/** The catalog entry for a look field id. */
|
|
441
|
+
export declare function themeComponentLookField(id: ThemeComponentLookId): ThemeComponentLookField;
|
|
442
|
+
/**
|
|
443
|
+
* A look field as its select shows it: the choice the site's overrides hold,
|
|
444
|
+
* {@link THEME_LOOK_DEFAULT} when they set nothing, or
|
|
445
|
+
* {@link THEME_LOOK_CUSTOM} when they set a value no choice names.
|
|
446
|
+
*/
|
|
447
|
+
export declare function readComponentLook(theme: HostTheme | undefined, field: ThemeComponentLookField): string;
|
|
448
|
+
/**
|
|
449
|
+
* Sets a look field to one of its choices, or back to the theme's own look
|
|
450
|
+
* with {@link THEME_LOOK_DEFAULT}, which clears every leaf it sets. Any other
|
|
451
|
+
* value changes nothing.
|
|
452
|
+
*/
|
|
453
|
+
export declare function writeComponentLook(theme: HostTheme, field: ThemeComponentLookField, value: string): HostTheme;
|
|
@@ -424,8 +424,12 @@ export const FONT_FAMILY_FIELD = {
|
|
|
424
424
|
step: 1
|
|
425
425
|
}
|
|
426
426
|
};
|
|
427
|
-
/**
|
|
428
|
-
|
|
427
|
+
/**
|
|
428
|
+
* The site-wide look of components: the click-only fields in
|
|
429
|
+
* {@link THEME_COMPONENT_LOOK_FIELDS}, with the full override set behind
|
|
430
|
+
* them limited to the sanitizer's whitelist.
|
|
431
|
+
*/ export const COMPONENT_OVERRIDES_FIELD = {
|
|
432
|
+
label: 'Style components',
|
|
429
433
|
components: HOST_THEME_COMPONENT_WHITELIST
|
|
430
434
|
};
|
|
431
435
|
/**
|
|
@@ -506,7 +510,7 @@ export const FONT_FAMILY_FIELD = {
|
|
|
506
510
|
{
|
|
507
511
|
id: 'components',
|
|
508
512
|
label: COMPONENT_OVERRIDES_FIELD.label,
|
|
509
|
-
kind: '
|
|
513
|
+
kind: 'components',
|
|
510
514
|
perScheme: false,
|
|
511
515
|
options: COMPONENT_OVERRIDES_FIELD.components
|
|
512
516
|
}
|
|
@@ -541,8 +545,12 @@ export const FONT_FAMILY_FIELD = {
|
|
|
541
545
|
}
|
|
542
546
|
/**
|
|
543
547
|
* Sets one color for one scheme, or clears it with `undefined` so the slot
|
|
544
|
-
* inherits again. A palette color writes `main`
|
|
545
|
-
*
|
|
548
|
+
* inherits again. A palette color writes a record of `main` alone: its
|
|
549
|
+
* `light`, `dark` and `contrastText` were derived from (or authored for) the
|
|
550
|
+
* old `main`, and the editor offers no control for them, so a kept shade
|
|
551
|
+
* would go on painting the old color (an outlined button reads `dark`) with
|
|
552
|
+
* no way for the owner to see or change it. Dropped, the theme derives them
|
|
553
|
+
* from the new `main`. Clearing it drops the record; a group member that
|
|
546
554
|
* leaves its group empty drops the group.
|
|
547
555
|
*/ export function writeThemeColor(theme, scheme, token, hex) {
|
|
548
556
|
return setSchemeValue(theme, scheme, (colors)=>{
|
|
@@ -555,9 +563,9 @@ export const FONT_FAMILY_FIELD = {
|
|
|
555
563
|
const [group, key] = token.split('.');
|
|
556
564
|
if (!key) {
|
|
557
565
|
const paletteKey = group;
|
|
558
|
-
if (hex) next[paletteKey] =
|
|
566
|
+
if (hex) next[paletteKey] = {
|
|
559
567
|
main: hex
|
|
560
|
-
}
|
|
568
|
+
};
|
|
561
569
|
else delete next[paletteKey];
|
|
562
570
|
return next;
|
|
563
571
|
}
|
|
@@ -865,5 +873,520 @@ function fontEntry(choice) {
|
|
|
865
873
|
components
|
|
866
874
|
});
|
|
867
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Removes one leaf from the site's component overrides, and every slot,
|
|
878
|
+
* branch and component it leaves empty, so a field set back to "Theme
|
|
879
|
+
* default" stores nothing and the site inherits that property again.
|
|
880
|
+
*/ export function clearComponentOverride(theme, target) {
|
|
881
|
+
var _theme_components;
|
|
882
|
+
const existing = (_theme_components = theme.components) == null ? void 0 : _theme_components[target.component];
|
|
883
|
+
if (!existing) return theme;
|
|
884
|
+
const entry = _extends({}, existing);
|
|
885
|
+
if (target.target === 'defaultProps') {
|
|
886
|
+
const props = _extends({}, entry.defaultProps);
|
|
887
|
+
delete props[target.property];
|
|
888
|
+
if (Object.keys(props).length) entry.defaultProps = props;
|
|
889
|
+
else delete entry.defaultProps;
|
|
890
|
+
} else {
|
|
891
|
+
var _target_slot;
|
|
892
|
+
const branch = target.target === 'sx' ? 'sx' : 'styleOverrides';
|
|
893
|
+
const styles = _extends({}, entry[branch]);
|
|
894
|
+
const slot = (_target_slot = target.slot) != null ? _target_slot : 'root';
|
|
895
|
+
const slotStyles = _extends({}, styles[slot]);
|
|
896
|
+
if (target.media) {
|
|
897
|
+
const query = THEME_EDITOR_MEDIA_QUERIES[target.media];
|
|
898
|
+
const scoped = _extends({}, slotStyles[query]);
|
|
899
|
+
delete scoped[target.property];
|
|
900
|
+
if (Object.keys(scoped).length) slotStyles[query] = scoped;
|
|
901
|
+
else delete slotStyles[query];
|
|
902
|
+
} else {
|
|
903
|
+
delete slotStyles[target.property];
|
|
904
|
+
}
|
|
905
|
+
if (Object.keys(slotStyles).length) styles[slot] = slotStyles;
|
|
906
|
+
else delete styles[slot];
|
|
907
|
+
if (Object.keys(styles).length) {
|
|
908
|
+
if (branch === 'sx') entry.sx = styles;
|
|
909
|
+
else entry.styleOverrides = styles;
|
|
910
|
+
} else {
|
|
911
|
+
delete entry[branch];
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const components = _extends({}, theme.components);
|
|
915
|
+
if (Object.keys(entry).length) components[target.component] = entry;
|
|
916
|
+
else delete components[target.component];
|
|
917
|
+
const next = _extends({}, theme);
|
|
918
|
+
if (Object.keys(components).length) next.components = components;
|
|
919
|
+
else delete next.components;
|
|
920
|
+
return next;
|
|
921
|
+
}
|
|
922
|
+
/** The value one override leaf holds; `undefined` when the site does not set it. */ export function readComponentOverride(theme, target) {
|
|
923
|
+
var _target_slot;
|
|
924
|
+
var _theme_components, _entry_defaultProps, _slotStyles_query;
|
|
925
|
+
const entry = theme == null ? void 0 : (_theme_components = theme.components) == null ? void 0 : _theme_components[target.component];
|
|
926
|
+
if (!entry) return undefined;
|
|
927
|
+
if (target.target === 'defaultProps') return (_entry_defaultProps = entry.defaultProps) == null ? void 0 : _entry_defaultProps[target.property];
|
|
928
|
+
const styles = target.target === 'sx' ? entry.sx : entry.styleOverrides;
|
|
929
|
+
const slotStyles = styles == null ? void 0 : styles[(_target_slot = target.slot) != null ? _target_slot : 'root'];
|
|
930
|
+
if (!target.media) return slotStyles == null ? void 0 : slotStyles[target.property];
|
|
931
|
+
const query = THEME_EDITOR_MEDIA_QUERIES[target.media];
|
|
932
|
+
return slotStyles == null ? void 0 : (_slotStyles_query = slotStyles[query]) == null ? void 0 : _slotStyles_query[target.property];
|
|
933
|
+
}
|
|
934
|
+
export const THEME_COMPONENT_LOOK_GROUPS = [
|
|
935
|
+
{
|
|
936
|
+
group: 'button',
|
|
937
|
+
label: 'Buttons'
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
group: 'input',
|
|
941
|
+
label: 'Form inputs'
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
group: 'card',
|
|
945
|
+
label: 'Cards'
|
|
946
|
+
}
|
|
947
|
+
];
|
|
948
|
+
/** The select's value for a field the site does not set: the theme's own look. */ export const THEME_LOOK_DEFAULT = '';
|
|
949
|
+
/** The select's value for a leaf set to something no choice names, by hand or by AI. */ export const THEME_LOOK_CUSTOM = '__custom__';
|
|
950
|
+
function lookTarget(component, target, property, slot = target === 'defaultProps' ? null : 'root') {
|
|
951
|
+
return {
|
|
952
|
+
component,
|
|
953
|
+
target,
|
|
954
|
+
slot,
|
|
955
|
+
property,
|
|
956
|
+
media: null
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Corner choices, as multiples of the site's Border radius (an `sx`
|
|
961
|
+
* `borderRadius` number is one), so a component follows the Shape & spacing
|
|
962
|
+
* slider instead of freezing a pixel value.
|
|
963
|
+
*/ const CORNER_OPTIONS = [
|
|
964
|
+
{
|
|
965
|
+
value: 'square',
|
|
966
|
+
label: 'Square',
|
|
967
|
+
stored: 0
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
value: 'subtle',
|
|
971
|
+
label: 'Half the site radius',
|
|
972
|
+
stored: 0.5
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
value: 'site',
|
|
976
|
+
label: 'The site radius',
|
|
977
|
+
stored: 1
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
value: 'rounded',
|
|
981
|
+
label: 'Twice the site radius',
|
|
982
|
+
stored: 2
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
value: 'extra',
|
|
986
|
+
label: 'Three times the site radius',
|
|
987
|
+
stored: 3
|
|
988
|
+
}
|
|
989
|
+
];
|
|
990
|
+
const PILL_OPTION = {
|
|
991
|
+
value: 'pill',
|
|
992
|
+
label: 'Pill',
|
|
993
|
+
stored: '999px'
|
|
994
|
+
};
|
|
995
|
+
/**
|
|
996
|
+
* Every component look field, in the order the card shows them.
|
|
997
|
+
*
|
|
998
|
+
* Inputs style both of MUI's boxes: `MuiOutlinedInput` and `MuiFilledInput`,
|
|
999
|
+
* because the Input style choice picks between them and the other choices
|
|
1000
|
+
* have to hold whichever is in use. A border color sets the outlined box's
|
|
1001
|
+
* own border, which the Besigner's Input element draws, and the notched
|
|
1002
|
+
* outline a stock text field draws.
|
|
1003
|
+
*/ export const THEME_COMPONENT_LOOK_FIELDS = [
|
|
1004
|
+
{
|
|
1005
|
+
id: 'button.variant',
|
|
1006
|
+
group: 'button',
|
|
1007
|
+
label: 'Button style',
|
|
1008
|
+
kind: 'select',
|
|
1009
|
+
targets: [
|
|
1010
|
+
lookTarget('MuiButton', 'defaultProps', 'variant')
|
|
1011
|
+
],
|
|
1012
|
+
options: [
|
|
1013
|
+
{
|
|
1014
|
+
value: 'contained',
|
|
1015
|
+
label: 'Filled',
|
|
1016
|
+
stored: 'contained'
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
value: 'outlined',
|
|
1020
|
+
label: 'Outlined',
|
|
1021
|
+
stored: 'outlined'
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
value: 'text',
|
|
1025
|
+
label: 'Text only',
|
|
1026
|
+
stored: 'text'
|
|
1027
|
+
}
|
|
1028
|
+
]
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
id: 'button.corners',
|
|
1032
|
+
group: 'button',
|
|
1033
|
+
label: 'Button corners',
|
|
1034
|
+
kind: 'select',
|
|
1035
|
+
targets: [
|
|
1036
|
+
lookTarget('MuiButton', 'sx', 'borderRadius')
|
|
1037
|
+
],
|
|
1038
|
+
options: [
|
|
1039
|
+
...CORNER_OPTIONS,
|
|
1040
|
+
PILL_OPTION
|
|
1041
|
+
]
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
id: 'button.textCase',
|
|
1045
|
+
group: 'button',
|
|
1046
|
+
label: 'Button text case',
|
|
1047
|
+
kind: 'select',
|
|
1048
|
+
targets: [
|
|
1049
|
+
lookTarget('MuiButton', 'styleOverrides', 'textTransform')
|
|
1050
|
+
],
|
|
1051
|
+
options: [
|
|
1052
|
+
{
|
|
1053
|
+
value: 'none',
|
|
1054
|
+
label: 'As typed',
|
|
1055
|
+
stored: 'none'
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
value: 'uppercase',
|
|
1059
|
+
label: 'UPPERCASE',
|
|
1060
|
+
stored: 'uppercase'
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
value: 'capitalize',
|
|
1064
|
+
label: 'Capitalize Each Word',
|
|
1065
|
+
stored: 'capitalize'
|
|
1066
|
+
}
|
|
1067
|
+
]
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
id: 'button.weight',
|
|
1071
|
+
group: 'button',
|
|
1072
|
+
label: 'Button text weight',
|
|
1073
|
+
kind: 'select',
|
|
1074
|
+
targets: [
|
|
1075
|
+
lookTarget('MuiButton', 'styleOverrides', 'fontWeight')
|
|
1076
|
+
],
|
|
1077
|
+
options: [
|
|
1078
|
+
{
|
|
1079
|
+
value: '400',
|
|
1080
|
+
label: 'Regular',
|
|
1081
|
+
stored: 400
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
value: '500',
|
|
1085
|
+
label: 'Medium',
|
|
1086
|
+
stored: 500
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
value: '600',
|
|
1090
|
+
label: 'Semibold',
|
|
1091
|
+
stored: 600
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
value: '700',
|
|
1095
|
+
label: 'Bold',
|
|
1096
|
+
stored: 700
|
|
1097
|
+
}
|
|
1098
|
+
]
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
id: 'button.padding',
|
|
1102
|
+
group: 'button',
|
|
1103
|
+
label: 'Button padding',
|
|
1104
|
+
kind: 'select',
|
|
1105
|
+
// Spacing units, so padding follows the site's Spacing unit.
|
|
1106
|
+
targets: [
|
|
1107
|
+
lookTarget('MuiButton', 'sx', 'px'),
|
|
1108
|
+
lookTarget('MuiButton', 'sx', 'py')
|
|
1109
|
+
],
|
|
1110
|
+
options: [
|
|
1111
|
+
{
|
|
1112
|
+
value: 'compact',
|
|
1113
|
+
label: 'Compact',
|
|
1114
|
+
stored: [
|
|
1115
|
+
1.5,
|
|
1116
|
+
0.5
|
|
1117
|
+
]
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
value: 'comfortable',
|
|
1121
|
+
label: 'Comfortable',
|
|
1122
|
+
stored: [
|
|
1123
|
+
2,
|
|
1124
|
+
0.75
|
|
1125
|
+
]
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
value: 'roomy',
|
|
1129
|
+
label: 'Roomy',
|
|
1130
|
+
stored: [
|
|
1131
|
+
3,
|
|
1132
|
+
1
|
|
1133
|
+
]
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
value: 'wide',
|
|
1137
|
+
label: 'Wide',
|
|
1138
|
+
stored: [
|
|
1139
|
+
4,
|
|
1140
|
+
1.25
|
|
1141
|
+
]
|
|
1142
|
+
}
|
|
1143
|
+
]
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
id: 'input.variant',
|
|
1147
|
+
group: 'input',
|
|
1148
|
+
label: 'Input style',
|
|
1149
|
+
kind: 'select',
|
|
1150
|
+
targets: [
|
|
1151
|
+
lookTarget('MuiTextField', 'defaultProps', 'variant')
|
|
1152
|
+
],
|
|
1153
|
+
options: [
|
|
1154
|
+
{
|
|
1155
|
+
value: 'outlined',
|
|
1156
|
+
label: 'Outlined',
|
|
1157
|
+
stored: 'outlined'
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
value: 'filled',
|
|
1161
|
+
label: 'Filled',
|
|
1162
|
+
stored: 'filled'
|
|
1163
|
+
}
|
|
1164
|
+
]
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
id: 'input.corners',
|
|
1168
|
+
group: 'input',
|
|
1169
|
+
label: 'Input corners',
|
|
1170
|
+
kind: 'select',
|
|
1171
|
+
targets: [
|
|
1172
|
+
lookTarget('MuiOutlinedInput', 'sx', 'borderRadius'),
|
|
1173
|
+
lookTarget('MuiFilledInput', 'sx', 'borderRadius')
|
|
1174
|
+
],
|
|
1175
|
+
// A filled box's underline runs edge to edge; clipping it to the corners
|
|
1176
|
+
// keeps it inside a rounded box.
|
|
1177
|
+
companions: [
|
|
1178
|
+
_extends({}, lookTarget('MuiFilledInput', 'styleOverrides', 'overflow'), {
|
|
1179
|
+
value: 'hidden'
|
|
1180
|
+
})
|
|
1181
|
+
],
|
|
1182
|
+
options: [
|
|
1183
|
+
...CORNER_OPTIONS,
|
|
1184
|
+
PILL_OPTION
|
|
1185
|
+
]
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
id: 'input.background',
|
|
1189
|
+
group: 'input',
|
|
1190
|
+
label: 'Input background',
|
|
1191
|
+
kind: 'color',
|
|
1192
|
+
targets: [
|
|
1193
|
+
lookTarget('MuiOutlinedInput', 'sx', 'bgcolor'),
|
|
1194
|
+
lookTarget('MuiFilledInput', 'sx', 'bgcolor')
|
|
1195
|
+
],
|
|
1196
|
+
options: [
|
|
1197
|
+
{
|
|
1198
|
+
value: 'transparent',
|
|
1199
|
+
label: 'None',
|
|
1200
|
+
stored: 'transparent'
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
value: 'paper',
|
|
1204
|
+
label: 'Paper',
|
|
1205
|
+
stored: 'background.paper',
|
|
1206
|
+
token: 'background.paper'
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
value: 'background',
|
|
1210
|
+
label: 'Background',
|
|
1211
|
+
stored: 'background.default',
|
|
1212
|
+
token: 'background.default'
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
value: 'tintPrimary',
|
|
1216
|
+
label: 'Primary tint',
|
|
1217
|
+
stored: 'tint.primary',
|
|
1218
|
+
token: 'tint.primary'
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
value: 'tintSecondary',
|
|
1222
|
+
label: 'Secondary tint',
|
|
1223
|
+
stored: 'tint.secondary',
|
|
1224
|
+
token: 'tint.secondary'
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
value: 'tintTertiary',
|
|
1228
|
+
label: 'Tertiary tint',
|
|
1229
|
+
stored: 'tint.tertiary',
|
|
1230
|
+
token: 'tint.tertiary'
|
|
1231
|
+
}
|
|
1232
|
+
]
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
id: 'input.border',
|
|
1236
|
+
group: 'input',
|
|
1237
|
+
label: 'Input border color',
|
|
1238
|
+
kind: 'color',
|
|
1239
|
+
targets: [
|
|
1240
|
+
lookTarget('MuiOutlinedInput', 'sx', 'borderColor'),
|
|
1241
|
+
lookTarget('MuiOutlinedInput', 'sx', 'borderColor', 'notchedOutline')
|
|
1242
|
+
],
|
|
1243
|
+
options: [
|
|
1244
|
+
{
|
|
1245
|
+
value: 'divider',
|
|
1246
|
+
label: 'Divider',
|
|
1247
|
+
stored: 'divider',
|
|
1248
|
+
token: 'divider'
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
value: 'textSecondary',
|
|
1252
|
+
label: 'Secondary text',
|
|
1253
|
+
stored: 'text.secondary',
|
|
1254
|
+
token: 'text.secondary'
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
value: 'text',
|
|
1258
|
+
label: 'Text',
|
|
1259
|
+
stored: 'text.primary',
|
|
1260
|
+
token: 'text.primary'
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
value: 'primary',
|
|
1264
|
+
label: 'Primary',
|
|
1265
|
+
stored: 'primary.main',
|
|
1266
|
+
token: 'primary'
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
value: 'secondary',
|
|
1270
|
+
label: 'Secondary',
|
|
1271
|
+
stored: 'secondary.main',
|
|
1272
|
+
token: 'secondary'
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
value: 'tertiary',
|
|
1276
|
+
label: 'Tertiary',
|
|
1277
|
+
stored: 'tertiary.main',
|
|
1278
|
+
token: 'tertiary'
|
|
1279
|
+
}
|
|
1280
|
+
]
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
id: 'card.variant',
|
|
1284
|
+
group: 'card',
|
|
1285
|
+
label: 'Card style',
|
|
1286
|
+
kind: 'select',
|
|
1287
|
+
targets: [
|
|
1288
|
+
lookTarget('MuiCard', 'defaultProps', 'variant')
|
|
1289
|
+
],
|
|
1290
|
+
options: [
|
|
1291
|
+
{
|
|
1292
|
+
value: 'elevation',
|
|
1293
|
+
label: 'Shadow',
|
|
1294
|
+
stored: 'elevation'
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
value: 'outlined',
|
|
1298
|
+
label: 'Outline',
|
|
1299
|
+
stored: 'outlined'
|
|
1300
|
+
}
|
|
1301
|
+
]
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
id: 'card.elevation',
|
|
1305
|
+
group: 'card',
|
|
1306
|
+
label: 'Card shadow depth',
|
|
1307
|
+
kind: 'select',
|
|
1308
|
+
targets: [
|
|
1309
|
+
lookTarget('MuiCard', 'defaultProps', 'elevation')
|
|
1310
|
+
],
|
|
1311
|
+
options: [
|
|
1312
|
+
{
|
|
1313
|
+
value: '0',
|
|
1314
|
+
label: 'Flat',
|
|
1315
|
+
stored: 0
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
value: '1',
|
|
1319
|
+
label: 'Soft',
|
|
1320
|
+
stored: 1
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
value: '3',
|
|
1324
|
+
label: 'Medium',
|
|
1325
|
+
stored: 3
|
|
1326
|
+
},
|
|
1327
|
+
{
|
|
1328
|
+
value: '8',
|
|
1329
|
+
label: 'Strong',
|
|
1330
|
+
stored: 8
|
|
1331
|
+
}
|
|
1332
|
+
]
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
id: 'card.corners',
|
|
1336
|
+
group: 'card',
|
|
1337
|
+
label: 'Card corners',
|
|
1338
|
+
kind: 'select',
|
|
1339
|
+
targets: [
|
|
1340
|
+
lookTarget('MuiCard', 'sx', 'borderRadius')
|
|
1341
|
+
],
|
|
1342
|
+
options: CORNER_OPTIONS
|
|
1343
|
+
}
|
|
1344
|
+
];
|
|
1345
|
+
/** The catalog entry for a look field id. */ export function themeComponentLookField(id) {
|
|
1346
|
+
const field = THEME_COMPONENT_LOOK_FIELDS.find((entry)=>entry.id === id);
|
|
1347
|
+
if (!field) throw new Error(`unknown component look field ${id}`);
|
|
1348
|
+
return field;
|
|
1349
|
+
}
|
|
1350
|
+
function storedFor(option, index) {
|
|
1351
|
+
return Array.isArray(option.stored) ? option.stored[index] : option.stored;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* A look field as its select shows it: the choice the site's overrides hold,
|
|
1355
|
+
* {@link THEME_LOOK_DEFAULT} when they set nothing, or
|
|
1356
|
+
* {@link THEME_LOOK_CUSTOM} when they set a value no choice names.
|
|
1357
|
+
*/ export function readComponentLook(theme, field) {
|
|
1358
|
+
const values = field.targets.map((target)=>readComponentOverride(theme, target));
|
|
1359
|
+
if (values.every((value)=>value === undefined)) return THEME_LOOK_DEFAULT;
|
|
1360
|
+
const match = field.options.find((option)=>values.every((value, index)=>value === storedFor(option, index)));
|
|
1361
|
+
return match ? match.value : THEME_LOOK_CUSTOM;
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Sets a look field to one of its choices, or back to the theme's own look
|
|
1365
|
+
* with {@link THEME_LOOK_DEFAULT}, which clears every leaf it sets. Any other
|
|
1366
|
+
* value changes nothing.
|
|
1367
|
+
*/ export function writeComponentLook(theme, field, value) {
|
|
1368
|
+
var _field_companions;
|
|
1369
|
+
if (value === THEME_LOOK_DEFAULT) {
|
|
1370
|
+
var _field_companions1;
|
|
1371
|
+
let next = theme;
|
|
1372
|
+
for (const target of [
|
|
1373
|
+
...field.targets,
|
|
1374
|
+
...(_field_companions1 = field.companions) != null ? _field_companions1 : []
|
|
1375
|
+
]){
|
|
1376
|
+
next = clearComponentOverride(next, target);
|
|
1377
|
+
}
|
|
1378
|
+
return next;
|
|
1379
|
+
}
|
|
1380
|
+
const option = field.options.find((entry)=>entry.value === value);
|
|
1381
|
+
if (!option) return theme;
|
|
1382
|
+
let next = theme;
|
|
1383
|
+
field.targets.forEach((target, index)=>{
|
|
1384
|
+
next = writeComponentOverride(next, _extends({}, target, {
|
|
1385
|
+
value: storedFor(option, index)
|
|
1386
|
+
}));
|
|
1387
|
+
});
|
|
1388
|
+
for (const companion of (_field_companions = field.companions) != null ? _field_companions : [])next = writeComponentOverride(next, companion);
|
|
1389
|
+
return next;
|
|
1390
|
+
}
|
|
868
1391
|
|
|
869
1392
|
//# sourceMappingURL=theme-editor-fields.js.map
|
|
@@ -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 HostThemeFont,\n HostThemeFontCategory,\n HostThemeTypographyVariantKey,\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: HostThemeFontCategory,\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 readThemeFonts(theme).body?.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 // Through writeThemeFonts (AGL-3656), so a heading font and the site's own\n // uploaded fonts survive a body font picked from the short list or by the\n // AI theme step; replacing `fonts` wholesale dropped both.\n const current = readThemeFonts(theme)\n if (value === SYSTEM_FONT_VALUE) {\n return writeThemeFonts(theme, { body: null, heading: current.heading })\n }\n const option = GOOGLE_FONT_OPTIONS.find((entry) => entry.family === value)\n if (!option) return theme\n return writeThemeFonts(theme, {\n body: {\n family: option.family,\n category: option.category,\n weights: option.weights,\n source: 'google',\n },\n heading: current.heading,\n })\n}\n\n/**\n * The text styles a separate heading font is set on: `h1`…`h6`, and\n * `displayXl`, the platform's rung above `h1`, which a heading font left off\n * would draw in the body font — and load the body font's heaviest weight for.\n */\nexport const THEME_HEADING_VARIANTS: readonly HostThemeTypographyVariantKey[] = [\n 'displayXl',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n]\n\n/**\n * One family a theme sets text in, as the font picker shows and writes it\n * (AGL-3656): a `fonts` entry and the role it plays.\n */\nexport interface ThemeFontChoice {\n family: string\n category: HostThemeFontCategory\n /** Upright weights to load, ascending. */\n weights: number[]\n /** Italic weights to load, ascending; absent or empty for none. */\n italics?: number[]\n source: 'google' | 'custom'\n}\n\n/**\n * The theme's body font and, when it differs, its heading font. `null` is the\n * theme default: the base stack, nothing loaded.\n */\nexport interface ThemeFontSelection {\n body: ThemeFontChoice | null\n heading: ThemeFontChoice | null\n}\n\n/** The first family a CSS font-family stack names, unquoted. */\nexport function firstStackFamily(stack: unknown): string | undefined {\n if (typeof stack !== 'string') return undefined\n const first = stack.split(',')[0]?.trim().replace(/^[\"']|[\"']$/g, '').trim()\n return first || undefined\n}\n\n/** The category a stack's generic keyword names, for a font that records none. */\nfunction stackCategory(stack: unknown): HostThemeFontCategory {\n if (typeof stack !== 'string') return 'sans-serif'\n if (/\\bmonospace\\b/i.test(stack)) return 'monospace'\n if (/(^|,)\\s*serif\\b/i.test(stack)) return 'serif'\n return 'sans-serif'\n}\n\nfunction sortedWeights(values: readonly unknown[] | undefined): number[] {\n return [\n ...new Set(\n (values ?? [])\n .map((value) => Math.round(Number(value)))\n .filter((value) => Number.isFinite(value) && value >= 1 && value <= 1000),\n ),\n ].sort((a, b) => a - b)\n}\n\nfunction choiceOf(font: HostThemeFont, stack: unknown): ThemeFontChoice {\n const italics = sortedWeights(font.italics)\n return {\n family: font.family.trim(),\n category: font.category ?? stackCategory(stack),\n weights: sortedWeights(font.weights).length ? sortedWeights(font.weights) : [400],\n ...(italics.length ? { italics } : {}),\n source: font.source === 'custom' ? 'custom' : 'google',\n }\n}\n\nfunction findFont(theme: HostTheme | undefined, family: string | undefined) {\n if (!family) return undefined\n const wanted = family.toLowerCase()\n return theme?.fonts?.find(\n (font) =>\n font.family?.trim().toLowerCase() === wanted && (font.source ?? 'google') !== 'system',\n )\n}\n\n/**\n * The body and heading fonts a theme sets text in (AGL-3656).\n *\n * The body font is the family the theme-wide stack names first, when the\n * theme loads it: a font the theme lists that no stack names draws nothing,\n * which is what the theme default is. The heading font is the family `h1`\n * names, when that is a loaded font other than the body's.\n */\nexport function readThemeFonts(theme: HostTheme | undefined): ThemeFontSelection {\n const stack = theme?.typography?.fontFamily\n const bodyFont = findFont(theme, firstStackFamily(stack))\n const headingStack = theme?.typography?.variants?.h1?.fontFamily\n const headingFont = findFont(theme, firstStackFamily(headingStack))\n return {\n body: bodyFont ? choiceOf(bodyFont, stack) : null,\n heading:\n headingFont && headingFont !== bodyFont ? choiceOf(headingFont, headingStack) : null,\n }\n}\n\nfunction fontEntry(choice: ThemeFontChoice): HostThemeFont {\n const italics = sortedWeights(choice.italics)\n return {\n family: choice.family.trim(),\n weights: sortedWeights(choice.weights).length ? sortedWeights(choice.weights) : [400],\n ...(italics.length ? { italics } : {}),\n source: choice.source,\n category: choice.category,\n }\n}\n\n/**\n * Sets the theme's body and heading fonts (AGL-3656): the `fonts` entries the\n * page loads, the theme-wide stack, and the stack of each style in\n * {@link THEME_HEADING_VARIANTS} for a heading font of its own. `null` goes\n * back to the theme default for that role.\n *\n * An uploaded (`custom`) font the theme lists but no longer sets text in is\n * kept, because it is the site's own and is picked again from the list; a\n * Google font nothing sets text in any more is dropped, so the page stops\n * loading it. Everything else in the typography — sizes, weights, a variant's own\n * family that is not a heading — is left as it was.\n */\nexport function writeThemeFonts(theme: HostTheme, selection: ThemeFontSelection): HostTheme {\n const body = selection.body\n const heading =\n selection.heading &&\n (!body || selection.heading.family.toLowerCase() !== body.family.toLowerCase())\n ? selection.heading\n : null\n const previous = readThemeFonts(theme)\n const previousHeadingStack = previous.heading\n ? theme.typography?.variants?.h1?.fontFamily\n : undefined\n\n const chosen = [body, heading].filter((entry): entry is ThemeFontChoice => !!entry)\n const chosenKeys = new Set(chosen.map((entry) => entry.family.trim().toLowerCase()))\n // Families a text style other than a heading still names, by hand or by\n // an earlier editor: they keep loading, as their style keeps drawing them.\n const otherStyles = new Set(\n Object.entries(theme.typography?.variants ?? {})\n .filter(([key]) => !(THEME_HEADING_VARIANTS as readonly string[]).includes(key))\n .map(([, variant]) => firstStackFamily(variant?.fontFamily)?.toLowerCase())\n .filter((family): family is string => !!family),\n )\n const kept = (theme.fonts ?? []).filter((font) => {\n const key = font.family?.trim().toLowerCase() ?? ''\n if (!key || chosenKeys.has(key)) return false\n return font.source === 'custom' || otherStyles.has(key)\n })\n const fonts = [...chosen.map(fontEntry), ...kept]\n\n const typography = { ...theme.typography }\n if (body) typography.fontFamily = fontFamilyStack(body.family.trim(), body.category)\n else delete typography.fontFamily\n\n const variants = { ...typography.variants }\n for (const key of THEME_HEADING_VARIANTS) {\n const variant = { ...variants[key] }\n if (heading) {\n variant.fontFamily = fontFamilyStack(heading.family.trim(), heading.category)\n } else if (\n variant.fontFamily !== undefined &&\n (variant.fontFamily === previousHeadingStack ||\n !findFont({ fonts }, firstStackFamily(variant.fontFamily)))\n ) {\n // The heading font this theme had, or a family it no longer loads.\n delete variant.fontFamily\n }\n if (Object.keys(variant).length) variants[key] = variant\n else delete variants[key]\n }\n if (Object.keys(variants).length) typography.variants = variants\n else delete typography.variants\n\n const next: HostTheme = { ...theme }\n if (fonts.length) next.fonts = fonts\n else delete next.fonts\n if (Object.keys(typography).length) next.typography = typography\n else delete next.typography\n return next\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","readThemeFonts","body","writeFontFamily","current","writeThemeFonts","heading","THEME_HEADING_VARIANTS","firstStackFamily","stack","first","trim","replace","stackCategory","test","sortedWeights","values","Set","Math","round","filter","sort","a","b","choiceOf","font","italics","findFont","wanted","toLowerCase","fonts","typography","fontFamily","bodyFont","headingStack","variants","h1","headingFont","fontEntry","choice","selection","previous","previousHeadingStack","chosen","chosenKeys","otherStyles","entries","includes","variant","kept","has","writeBorderRadius","shape","borderRadius","writeSpacing","spacing","writeToolbarHeight","valid","edited","resetComponentOverrides","writeComponentOverride","leaf","component","target","defaultProps","property","slot","styles","sx","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 HostThemeFont,\n HostThemeFontCategory,\n HostThemeTypographyVariantKey,\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: HostThemeFontCategory,\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/**\n * The site-wide look of components: the click-only fields in\n * {@link THEME_COMPONENT_LOOK_FIELDS}, with the full override set behind\n * them limited to the sanitizer's whitelist.\n */\nexport const COMPONENT_OVERRIDES_FIELD = {\n label: 'Style components',\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' | 'components'\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 components control may style. */\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: 'components',\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 a record of `main` alone: its\n * `light`, `dark` and `contrastText` were derived from (or authored for) the\n * old `main`, and the editor offers no control for them, so a kept shade\n * would go on painting the old color (an outlined button reads `dark`) with\n * no way for the owner to see or change it. Dropped, the theme derives them\n * from the new `main`. 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] = { 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 readThemeFonts(theme).body?.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 // Through writeThemeFonts (AGL-3656), so a heading font and the site's own\n // uploaded fonts survive a body font picked from the short list or by the\n // AI theme step; replacing `fonts` wholesale dropped both.\n const current = readThemeFonts(theme)\n if (value === SYSTEM_FONT_VALUE) {\n return writeThemeFonts(theme, { body: null, heading: current.heading })\n }\n const option = GOOGLE_FONT_OPTIONS.find((entry) => entry.family === value)\n if (!option) return theme\n return writeThemeFonts(theme, {\n body: {\n family: option.family,\n category: option.category,\n weights: option.weights,\n source: 'google',\n },\n heading: current.heading,\n })\n}\n\n/**\n * The text styles a separate heading font is set on: `h1`…`h6`, and\n * `displayXl`, the platform's rung above `h1`, which a heading font left off\n * would draw in the body font — and load the body font's heaviest weight for.\n */\nexport const THEME_HEADING_VARIANTS: readonly HostThemeTypographyVariantKey[] = [\n 'displayXl',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n]\n\n/**\n * One family a theme sets text in, as the font picker shows and writes it\n * (AGL-3656): a `fonts` entry and the role it plays.\n */\nexport interface ThemeFontChoice {\n family: string\n category: HostThemeFontCategory\n /** Upright weights to load, ascending. */\n weights: number[]\n /** Italic weights to load, ascending; absent or empty for none. */\n italics?: number[]\n source: 'google' | 'custom'\n}\n\n/**\n * The theme's body font and, when it differs, its heading font. `null` is the\n * theme default: the base stack, nothing loaded.\n */\nexport interface ThemeFontSelection {\n body: ThemeFontChoice | null\n heading: ThemeFontChoice | null\n}\n\n/** The first family a CSS font-family stack names, unquoted. */\nexport function firstStackFamily(stack: unknown): string | undefined {\n if (typeof stack !== 'string') return undefined\n const first = stack.split(',')[0]?.trim().replace(/^[\"']|[\"']$/g, '').trim()\n return first || undefined\n}\n\n/** The category a stack's generic keyword names, for a font that records none. */\nfunction stackCategory(stack: unknown): HostThemeFontCategory {\n if (typeof stack !== 'string') return 'sans-serif'\n if (/\\bmonospace\\b/i.test(stack)) return 'monospace'\n if (/(^|,)\\s*serif\\b/i.test(stack)) return 'serif'\n return 'sans-serif'\n}\n\nfunction sortedWeights(values: readonly unknown[] | undefined): number[] {\n return [\n ...new Set(\n (values ?? [])\n .map((value) => Math.round(Number(value)))\n .filter((value) => Number.isFinite(value) && value >= 1 && value <= 1000),\n ),\n ].sort((a, b) => a - b)\n}\n\nfunction choiceOf(font: HostThemeFont, stack: unknown): ThemeFontChoice {\n const italics = sortedWeights(font.italics)\n return {\n family: font.family.trim(),\n category: font.category ?? stackCategory(stack),\n weights: sortedWeights(font.weights).length ? sortedWeights(font.weights) : [400],\n ...(italics.length ? { italics } : {}),\n source: font.source === 'custom' ? 'custom' : 'google',\n }\n}\n\nfunction findFont(theme: HostTheme | undefined, family: string | undefined) {\n if (!family) return undefined\n const wanted = family.toLowerCase()\n return theme?.fonts?.find(\n (font) =>\n font.family?.trim().toLowerCase() === wanted && (font.source ?? 'google') !== 'system',\n )\n}\n\n/**\n * The body and heading fonts a theme sets text in (AGL-3656).\n *\n * The body font is the family the theme-wide stack names first, when the\n * theme loads it: a font the theme lists that no stack names draws nothing,\n * which is what the theme default is. The heading font is the family `h1`\n * names, when that is a loaded font other than the body's.\n */\nexport function readThemeFonts(theme: HostTheme | undefined): ThemeFontSelection {\n const stack = theme?.typography?.fontFamily\n const bodyFont = findFont(theme, firstStackFamily(stack))\n const headingStack = theme?.typography?.variants?.h1?.fontFamily\n const headingFont = findFont(theme, firstStackFamily(headingStack))\n return {\n body: bodyFont ? choiceOf(bodyFont, stack) : null,\n heading:\n headingFont && headingFont !== bodyFont ? choiceOf(headingFont, headingStack) : null,\n }\n}\n\nfunction fontEntry(choice: ThemeFontChoice): HostThemeFont {\n const italics = sortedWeights(choice.italics)\n return {\n family: choice.family.trim(),\n weights: sortedWeights(choice.weights).length ? sortedWeights(choice.weights) : [400],\n ...(italics.length ? { italics } : {}),\n source: choice.source,\n category: choice.category,\n }\n}\n\n/**\n * Sets the theme's body and heading fonts (AGL-3656): the `fonts` entries the\n * page loads, the theme-wide stack, and the stack of each style in\n * {@link THEME_HEADING_VARIANTS} for a heading font of its own. `null` goes\n * back to the theme default for that role.\n *\n * An uploaded (`custom`) font the theme lists but no longer sets text in is\n * kept, because it is the site's own and is picked again from the list; a\n * Google font nothing sets text in any more is dropped, so the page stops\n * loading it. Everything else in the typography — sizes, weights, a variant's own\n * family that is not a heading — is left as it was.\n */\nexport function writeThemeFonts(theme: HostTheme, selection: ThemeFontSelection): HostTheme {\n const body = selection.body\n const heading =\n selection.heading &&\n (!body || selection.heading.family.toLowerCase() !== body.family.toLowerCase())\n ? selection.heading\n : null\n const previous = readThemeFonts(theme)\n const previousHeadingStack = previous.heading\n ? theme.typography?.variants?.h1?.fontFamily\n : undefined\n\n const chosen = [body, heading].filter((entry): entry is ThemeFontChoice => !!entry)\n const chosenKeys = new Set(chosen.map((entry) => entry.family.trim().toLowerCase()))\n // Families a text style other than a heading still names, by hand or by\n // an earlier editor: they keep loading, as their style keeps drawing them.\n const otherStyles = new Set(\n Object.entries(theme.typography?.variants ?? {})\n .filter(([key]) => !(THEME_HEADING_VARIANTS as readonly string[]).includes(key))\n .map(([, variant]) => firstStackFamily(variant?.fontFamily)?.toLowerCase())\n .filter((family): family is string => !!family),\n )\n const kept = (theme.fonts ?? []).filter((font) => {\n const key = font.family?.trim().toLowerCase() ?? ''\n if (!key || chosenKeys.has(key)) return false\n return font.source === 'custom' || otherStyles.has(key)\n })\n const fonts = [...chosen.map(fontEntry), ...kept]\n\n const typography = { ...theme.typography }\n if (body) typography.fontFamily = fontFamilyStack(body.family.trim(), body.category)\n else delete typography.fontFamily\n\n const variants = { ...typography.variants }\n for (const key of THEME_HEADING_VARIANTS) {\n const variant = { ...variants[key] }\n if (heading) {\n variant.fontFamily = fontFamilyStack(heading.family.trim(), heading.category)\n } else if (\n variant.fontFamily !== undefined &&\n (variant.fontFamily === previousHeadingStack ||\n !findFont({ fonts }, firstStackFamily(variant.fontFamily)))\n ) {\n // The heading font this theme had, or a family it no longer loads.\n delete variant.fontFamily\n }\n if (Object.keys(variant).length) variants[key] = variant\n else delete variants[key]\n }\n if (Object.keys(variants).length) typography.variants = variants\n else delete typography.variants\n\n const next: HostTheme = { ...theme }\n if (fonts.length) next.fonts = fonts\n else delete next.fonts\n if (Object.keys(typography).length) next.typography = typography\n else delete next.typography\n return next\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\n/** Where one leaf lives, without the value it holds. */\nexport type ThemeComponentOverrideTarget = Omit<ThemeComponentOverrideLeaf, 'value'>\n\n/**\n * Removes one leaf from the site's component overrides, and every slot,\n * branch and component it leaves empty, so a field set back to \"Theme\n * default\" stores nothing and the site inherits that property again.\n */\nexport function clearComponentOverride(\n theme: HostTheme,\n target: ThemeComponentOverrideTarget,\n): HostTheme {\n const existing = theme.components?.[target.component]\n if (!existing) return theme\n const entry: HostThemeComponentOverride = { ...existing }\n if (target.target === 'defaultProps') {\n const props = { ...entry.defaultProps }\n delete props[target.property]\n if (Object.keys(props).length) entry.defaultProps = props\n else delete entry.defaultProps\n } else {\n const branch = target.target === 'sx' ? 'sx' : 'styleOverrides'\n const styles: Record<string, unknown> = { ...entry[branch] }\n const slot = target.slot ?? 'root'\n const slotStyles = { ...(styles[slot] as Record<string, unknown> | undefined) }\n if (target.media) {\n const query = THEME_EDITOR_MEDIA_QUERIES[target.media]\n const scoped = { ...(slotStyles[query] as Record<string, unknown> | undefined) }\n delete scoped[target.property]\n if (Object.keys(scoped).length) slotStyles[query] = scoped\n else delete slotStyles[query]\n } else {\n delete slotStyles[target.property]\n }\n if (Object.keys(slotStyles).length) styles[slot] = slotStyles\n else delete styles[slot]\n if (Object.keys(styles).length) {\n if (branch === 'sx') entry.sx = styles as HostThemeComponentOverride['sx']\n else entry.styleOverrides = styles\n } else {\n delete entry[branch]\n }\n }\n const components = { ...theme.components }\n if (Object.keys(entry).length) components[target.component] = entry\n else delete components[target.component]\n const next: HostTheme = { ...theme }\n if (Object.keys(components).length) next.components = components\n else delete next.components\n return next\n}\n\n/** The value one override leaf holds; `undefined` when the site does not set it. */\nexport function readComponentOverride(\n theme: HostTheme | undefined,\n target: ThemeComponentOverrideTarget,\n): unknown {\n const entry = theme?.components?.[target.component]\n if (!entry) return undefined\n if (target.target === 'defaultProps') return entry.defaultProps?.[target.property]\n const styles = (target.target === 'sx' ? entry.sx : entry.styleOverrides) as\n | Record<string, unknown>\n | undefined\n const slotStyles = styles?.[target.slot ?? 'root'] as Record<string, unknown> | undefined\n if (!target.media) return slotStyles?.[target.property]\n const query = THEME_EDITOR_MEDIA_QUERIES[target.media]\n return (slotStyles?.[query] as Record<string, unknown> | undefined)?.[target.property]\n}\n\n/* ------------------------------------------------------------------------ *\n * The look of components, as click-only fields\n * ------------------------------------------------------------------------ */\n\n/** The components the Style components card sets a site-wide look for. */\nexport type ThemeComponentLookGroup = 'button' | 'input' | 'card'\n\nexport const THEME_COMPONENT_LOOK_GROUPS: ReadonlyArray<{\n group: ThemeComponentLookGroup\n label: string\n}> = [\n { group: 'button', label: 'Buttons' },\n { group: 'input', label: 'Form inputs' },\n { group: 'card', label: 'Cards' },\n]\n\nexport type ThemeComponentLookId =\n | 'button.variant'\n | 'button.corners'\n | 'button.textCase'\n | 'button.weight'\n | 'button.padding'\n | 'input.variant'\n | 'input.corners'\n | 'input.background'\n | 'input.border'\n | 'card.variant'\n | 'card.elevation'\n | 'card.corners'\n\n/** One choice a look field offers. */\nexport interface ThemeComponentLookOption {\n /** What the select holds. */\n value: string\n label: string\n /**\n * What each of the field's targets stores, in target order; one value\n * stands for every target.\n */\n stored: string | number | ReadonlyArray<string | number>\n /** A theme color this choice draws in, for its swatch. */\n token?: ThemeColorToken\n}\n\n/**\n * A click-only field over the site's component overrides. Each choice writes\n * the same leaves the Advanced overrides hold, so the two are one setting\n * seen two ways, and anything that writes override leaves can set it.\n */\nexport interface ThemeComponentLookField {\n id: ThemeComponentLookId\n group: ThemeComponentLookGroup\n label: string\n /** `color` choices are theme colors, shown as swatches of the scheme in view. */\n kind: 'select' | 'color'\n /** The leaves a choice sets, read back from the first. */\n targets: readonly ThemeComponentOverrideTarget[]\n /** Leaves set alongside any choice and cleared with it: what makes the choice draw right. */\n companions?: readonly ThemeComponentOverrideLeaf[]\n options: readonly ThemeComponentLookOption[]\n}\n\n/** The select's value for a field the site does not set: the theme's own look. */\nexport const THEME_LOOK_DEFAULT = ''\n/** The select's value for a leaf set to something no choice names, by hand or by AI. */\nexport const THEME_LOOK_CUSTOM = '__custom__'\n\nfunction lookTarget(\n component: HostThemeComponentKey,\n target: ThemeComponentOverrideLeaf['target'],\n property: string,\n slot: string | null = target === 'defaultProps' ? null : 'root',\n): ThemeComponentOverrideTarget {\n return { component, target, slot, property, media: null }\n}\n\n/**\n * Corner choices, as multiples of the site's Border radius (an `sx`\n * `borderRadius` number is one), so a component follows the Shape & spacing\n * slider instead of freezing a pixel value.\n */\nconst CORNER_OPTIONS: readonly ThemeComponentLookOption[] = [\n { value: 'square', label: 'Square', stored: 0 },\n { value: 'subtle', label: 'Half the site radius', stored: 0.5 },\n { value: 'site', label: 'The site radius', stored: 1 },\n { value: 'rounded', label: 'Twice the site radius', stored: 2 },\n { value: 'extra', label: 'Three times the site radius', stored: 3 },\n]\nconst PILL_OPTION: ThemeComponentLookOption = { value: 'pill', label: 'Pill', stored: '999px' }\n\n/**\n * Every component look field, in the order the card shows them.\n *\n * Inputs style both of MUI's boxes: `MuiOutlinedInput` and `MuiFilledInput`,\n * because the Input style choice picks between them and the other choices\n * have to hold whichever is in use. A border color sets the outlined box's\n * own border, which the Besigner's Input element draws, and the notched\n * outline a stock text field draws.\n */\nexport const THEME_COMPONENT_LOOK_FIELDS: readonly ThemeComponentLookField[] = [\n {\n id: 'button.variant',\n group: 'button',\n label: 'Button style',\n kind: 'select',\n targets: [lookTarget('MuiButton', 'defaultProps', 'variant')],\n options: [\n { value: 'contained', label: 'Filled', stored: 'contained' },\n { value: 'outlined', label: 'Outlined', stored: 'outlined' },\n { value: 'text', label: 'Text only', stored: 'text' },\n ],\n },\n {\n id: 'button.corners',\n group: 'button',\n label: 'Button corners',\n kind: 'select',\n targets: [lookTarget('MuiButton', 'sx', 'borderRadius')],\n options: [...CORNER_OPTIONS, PILL_OPTION],\n },\n {\n id: 'button.textCase',\n group: 'button',\n label: 'Button text case',\n kind: 'select',\n targets: [lookTarget('MuiButton', 'styleOverrides', 'textTransform')],\n options: [\n { value: 'none', label: 'As typed', stored: 'none' },\n { value: 'uppercase', label: 'UPPERCASE', stored: 'uppercase' },\n { value: 'capitalize', label: 'Capitalize Each Word', stored: 'capitalize' },\n ],\n },\n {\n id: 'button.weight',\n group: 'button',\n label: 'Button text weight',\n kind: 'select',\n targets: [lookTarget('MuiButton', 'styleOverrides', 'fontWeight')],\n options: [\n { value: '400', label: 'Regular', stored: 400 },\n { value: '500', label: 'Medium', stored: 500 },\n { value: '600', label: 'Semibold', stored: 600 },\n { value: '700', label: 'Bold', stored: 700 },\n ],\n },\n {\n id: 'button.padding',\n group: 'button',\n label: 'Button padding',\n kind: 'select',\n // Spacing units, so padding follows the site's Spacing unit.\n targets: [lookTarget('MuiButton', 'sx', 'px'), lookTarget('MuiButton', 'sx', 'py')],\n options: [\n { value: 'compact', label: 'Compact', stored: [1.5, 0.5] },\n { value: 'comfortable', label: 'Comfortable', stored: [2, 0.75] },\n { value: 'roomy', label: 'Roomy', stored: [3, 1] },\n { value: 'wide', label: 'Wide', stored: [4, 1.25] },\n ],\n },\n {\n id: 'input.variant',\n group: 'input',\n label: 'Input style',\n kind: 'select',\n targets: [lookTarget('MuiTextField', 'defaultProps', 'variant')],\n options: [\n { value: 'outlined', label: 'Outlined', stored: 'outlined' },\n { value: 'filled', label: 'Filled', stored: 'filled' },\n ],\n },\n {\n id: 'input.corners',\n group: 'input',\n label: 'Input corners',\n kind: 'select',\n targets: [\n lookTarget('MuiOutlinedInput', 'sx', 'borderRadius'),\n lookTarget('MuiFilledInput', 'sx', 'borderRadius'),\n ],\n // A filled box's underline runs edge to edge; clipping it to the corners\n // keeps it inside a rounded box.\n companions: [{ ...lookTarget('MuiFilledInput', 'styleOverrides', 'overflow'), value: 'hidden' }],\n options: [...CORNER_OPTIONS, PILL_OPTION],\n },\n {\n id: 'input.background',\n group: 'input',\n label: 'Input background',\n kind: 'color',\n targets: [\n lookTarget('MuiOutlinedInput', 'sx', 'bgcolor'),\n lookTarget('MuiFilledInput', 'sx', 'bgcolor'),\n ],\n options: [\n { value: 'transparent', label: 'None', stored: 'transparent' },\n { value: 'paper', label: 'Paper', stored: 'background.paper', token: 'background.paper' },\n {\n value: 'background',\n label: 'Background',\n stored: 'background.default',\n token: 'background.default',\n },\n { value: 'tintPrimary', label: 'Primary tint', stored: 'tint.primary', token: 'tint.primary' },\n {\n value: 'tintSecondary',\n label: 'Secondary tint',\n stored: 'tint.secondary',\n token: 'tint.secondary',\n },\n {\n value: 'tintTertiary',\n label: 'Tertiary tint',\n stored: 'tint.tertiary',\n token: 'tint.tertiary',\n },\n ],\n },\n {\n id: 'input.border',\n group: 'input',\n label: 'Input border color',\n kind: 'color',\n targets: [\n lookTarget('MuiOutlinedInput', 'sx', 'borderColor'),\n lookTarget('MuiOutlinedInput', 'sx', 'borderColor', 'notchedOutline'),\n ],\n options: [\n { value: 'divider', label: 'Divider', stored: 'divider', token: 'divider' },\n {\n value: 'textSecondary',\n label: 'Secondary text',\n stored: 'text.secondary',\n token: 'text.secondary',\n },\n { value: 'text', label: 'Text', stored: 'text.primary', token: 'text.primary' },\n { value: 'primary', label: 'Primary', stored: 'primary.main', token: 'primary' },\n { value: 'secondary', label: 'Secondary', stored: 'secondary.main', token: 'secondary' },\n { value: 'tertiary', label: 'Tertiary', stored: 'tertiary.main', token: 'tertiary' },\n ],\n },\n {\n id: 'card.variant',\n group: 'card',\n label: 'Card style',\n kind: 'select',\n targets: [lookTarget('MuiCard', 'defaultProps', 'variant')],\n options: [\n { value: 'elevation', label: 'Shadow', stored: 'elevation' },\n { value: 'outlined', label: 'Outline', stored: 'outlined' },\n ],\n },\n {\n id: 'card.elevation',\n group: 'card',\n label: 'Card shadow depth',\n kind: 'select',\n targets: [lookTarget('MuiCard', 'defaultProps', 'elevation')],\n options: [\n { value: '0', label: 'Flat', stored: 0 },\n { value: '1', label: 'Soft', stored: 1 },\n { value: '3', label: 'Medium', stored: 3 },\n { value: '8', label: 'Strong', stored: 8 },\n ],\n },\n {\n id: 'card.corners',\n group: 'card',\n label: 'Card corners',\n kind: 'select',\n targets: [lookTarget('MuiCard', 'sx', 'borderRadius')],\n options: CORNER_OPTIONS,\n },\n]\n\n/** The catalog entry for a look field id. */\nexport function themeComponentLookField(id: ThemeComponentLookId): ThemeComponentLookField {\n const field = THEME_COMPONENT_LOOK_FIELDS.find((entry) => entry.id === id)\n if (!field) throw new Error(`unknown component look field ${id}`)\n return field\n}\n\nfunction storedFor(option: ThemeComponentLookOption, index: number) {\n return Array.isArray(option.stored)\n ? (option.stored as ReadonlyArray<string | number>)[index]\n : (option.stored as string | number)\n}\n\n/**\n * A look field as its select shows it: the choice the site's overrides hold,\n * {@link THEME_LOOK_DEFAULT} when they set nothing, or\n * {@link THEME_LOOK_CUSTOM} when they set a value no choice names.\n */\nexport function readComponentLook(\n theme: HostTheme | undefined,\n field: ThemeComponentLookField,\n): string {\n const values = field.targets.map((target) => readComponentOverride(theme, target))\n if (values.every((value) => value === undefined)) return THEME_LOOK_DEFAULT\n const match = field.options.find((option) =>\n values.every((value, index) => value === storedFor(option, index)),\n )\n return match ? match.value : THEME_LOOK_CUSTOM\n}\n\n/**\n * Sets a look field to one of its choices, or back to the theme's own look\n * with {@link THEME_LOOK_DEFAULT}, which clears every leaf it sets. Any other\n * value changes nothing.\n */\nexport function writeComponentLook(\n theme: HostTheme,\n field: ThemeComponentLookField,\n value: string,\n): HostTheme {\n if (value === THEME_LOOK_DEFAULT) {\n let next = theme\n for (const target of [...field.targets, ...(field.companions ?? [])]) {\n next = clearComponentOverride(next, target)\n }\n return next\n }\n const option = field.options.find((entry) => entry.value === value)\n if (!option) return theme\n let next = theme\n field.targets.forEach((target, index) => {\n next = writeComponentOverride(next, { ...target, value: storedFor(option, index) })\n })\n for (const companion of field.companions ?? []) next = writeComponentOverride(next, companion)\n return next\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","readThemeFonts","body","writeFontFamily","current","writeThemeFonts","heading","THEME_HEADING_VARIANTS","firstStackFamily","stack","first","trim","replace","stackCategory","test","sortedWeights","values","Set","Math","round","filter","sort","a","b","choiceOf","font","italics","findFont","wanted","toLowerCase","fonts","typography","fontFamily","bodyFont","headingStack","variants","h1","headingFont","fontEntry","choice","selection","previous","previousHeadingStack","chosen","chosenKeys","otherStyles","entries","includes","variant","kept","has","writeBorderRadius","shape","borderRadius","writeSpacing","spacing","writeToolbarHeight","valid","edited","resetComponentOverrides","writeComponentOverride","leaf","component","target","defaultProps","property","slot","styles","sx","styleOverrides","slotStyles","media","query","clearComponentOverride","existing","props","branch","scoped","readComponentOverride","THEME_COMPONENT_LOOK_GROUPS","THEME_LOOK_DEFAULT","THEME_LOOK_CUSTOM","lookTarget","CORNER_OPTIONS","stored","PILL_OPTION","THEME_COMPONENT_LOOK_FIELDS","targets","companions","themeComponentLookField","field","storedFor","index","Array","isArray","readComponentLook","every","match","writeComponentLook","forEach","companion"],"mappings":"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"}
|