@aglyn/shared-ui-theme 1.0.0-beta.246 → 1.0.0-beta.249
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/lib/util/host-theme.d.ts +6 -1
- package/src/lib/util/host-theme.js +35 -0
- package/src/lib/util/host-theme.js.map +1 -1
- package/src/lib/util/theme-editor-fields.d.ts +89 -6
- package/src/lib/util/theme-editor-fields.js +539 -7
- package/src/lib/util/theme-editor-fields.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/shared-ui-theme",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.249",
|
|
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.249",
|
|
29
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.249",
|
|
30
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.249",
|
|
31
31
|
"@emotion/cache": "^11.14.0",
|
|
32
32
|
"@emotion/react": "11.14.0",
|
|
33
33
|
"@swc/helpers": "0.5.23",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import type { HostTheme, HostThemeFont, HostThemeScheme } from '@aglyn/shared-data-types';
|
|
18
|
-
import type
|
|
18
|
+
import { type PaletteOptions, type ThemeOptions } from '../../vendor/mui';
|
|
19
19
|
/**
|
|
20
20
|
* Components a host theme may override. Persisted overrides are plain JSON;
|
|
21
21
|
* anything outside this list is dropped by {@link sanitizeHostTheme} so a
|
|
@@ -63,6 +63,11 @@ export declare function sanitizeHostTheme(theme: HostTheme | undefined): HostThe
|
|
|
63
63
|
* with at most one breakpoint rule per object.
|
|
64
64
|
*/
|
|
65
65
|
export declare function orderMediaWidths<T>(value: T): T;
|
|
66
|
+
/**
|
|
67
|
+
* A tint is a wash of its accent, so it is derived from `main` the way MUI
|
|
68
|
+
* derives `light` and `dark`: with its own `lighten`/`darken`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function accentTint(main: string, mode: PaletteOptions['mode']): string;
|
|
66
71
|
/**
|
|
67
72
|
* Converts a persisted {@link HostTheme} document into MUI `ThemeOptions`
|
|
68
73
|
* for one color scheme. The result is meant to be passed through
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
17
|
import { objectDeepMergeReplaceArrays } from "@aglyn/shared-util-vendor";
|
|
18
|
+
import { darken, lighten } from "../../vendor/mui.js";
|
|
18
19
|
/**
|
|
19
20
|
* Components a host theme may override. Persisted overrides are plain JSON;
|
|
20
21
|
* anything outside this list is dropped by {@link sanitizeHostTheme} so a
|
|
@@ -353,6 +354,38 @@ function sanitizeComponents(components) {
|
|
|
353
354
|
}
|
|
354
355
|
return moved ? next : value;
|
|
355
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* A tint is a wash of its accent, so it is derived from `main` the way MUI
|
|
359
|
+
* derives `light` and `dark`: with its own `lighten`/`darken`.
|
|
360
|
+
*/ export function accentTint(main, mode) {
|
|
361
|
+
return mode === 'dark' ? darken(main, 0.7) : lighten(main, 0.9);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* The tints a site renders: the base's, each replaced by the site's own where
|
|
365
|
+
* it sets one, or washed from the site's accent where it sets the accent
|
|
366
|
+
* without one, so a recolored brand never keeps the theme's old wash.
|
|
367
|
+
*/ function mergeTints(base, overrides) {
|
|
368
|
+
if (!(overrides == null ? void 0 : overrides.tint) && !(overrides == null ? void 0 : overrides.primary) && !(overrides == null ? void 0 : overrides.secondary) && !(overrides == null ? void 0 : overrides.tertiary)) {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
const tint = _extends({}, base == null ? void 0 : base.tint, overrides.tint);
|
|
372
|
+
for (const key of [
|
|
373
|
+
'primary',
|
|
374
|
+
'secondary',
|
|
375
|
+
'tertiary'
|
|
376
|
+
]){
|
|
377
|
+
var _overrides_key, _overrides_tint;
|
|
378
|
+
const main = (_overrides_key = overrides[key]) == null ? void 0 : _overrides_key.main;
|
|
379
|
+
if (((_overrides_tint = overrides.tint) == null ? void 0 : _overrides_tint[key]) || typeof main !== 'string') continue;
|
|
380
|
+
try {
|
|
381
|
+
var _overrides_mode;
|
|
382
|
+
tint[key] = accentTint(main, (_overrides_mode = overrides.mode) != null ? _overrides_mode : base == null ? void 0 : base.mode);
|
|
383
|
+
} catch (unused) {
|
|
384
|
+
// Not a color MUI can parse (a CSS variable): keep the inherited tint.
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return tint;
|
|
388
|
+
}
|
|
356
389
|
/**
|
|
357
390
|
* Converts a persisted {@link HostTheme} document into MUI `ThemeOptions`
|
|
358
391
|
* for one color scheme. The result is meant to be passed through
|
|
@@ -425,6 +458,8 @@ function sanitizeComponents(components) {
|
|
|
425
458
|
// the partial-palette behaviour the converter is written for. Overriding
|
|
426
459
|
// `primary` still must not disturb `secondary`, hence the level.
|
|
427
460
|
merged.palette = _extends({}, base.palette, overrides.palette);
|
|
461
|
+
const tint = mergeTints(base.palette, overrides.palette);
|
|
462
|
+
if (tint) merged.palette.tint = tint;
|
|
428
463
|
merged.shape = _extends({}, base.shape, overrides.shape);
|
|
429
464
|
// Same reasoning as `palette`: setting `toolbar` must not drop any other
|
|
430
465
|
// mixin the base defines.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/host-theme.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeComponentVariant,\n HostThemeFont,\n HostThemePaletteColor,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport { objectDeepMergeReplaceArrays } from '@aglyn/shared-util-vendor'\nimport type { PaletteOptions, ThemeOptions } from '../../vendor/mui'\n\n/**\n * Components a host theme may override. Persisted overrides are plain JSON;\n * anything outside this list is dropped by {@link sanitizeHostTheme} so a\n * tampered document can't restyle console-internal or portal-critical\n * components.\n *\n * The list is what a site actually renders — its elements, the forms, booking\n * and commerce surfaces, and the dialogs and menus they open — so a theme can\n * restyle a whole site rather than its buttons alone (AGL-3403). Layout\n * primitives (`MuiContainer`, `MuiGrid`, `MuiStack`, `MuiBox`) are absent on\n * purpose: they carry the page structure, and a theme that reflowed them\n * would break layouts the Besigner drew.\n */\nexport const HOST_THEME_COMPONENT_WHITELIST = [\n 'MuiAccordion',\n 'MuiAccordionDetails',\n 'MuiAccordionSummary',\n 'MuiAlert',\n 'MuiAlertTitle',\n 'MuiAppBar',\n 'MuiAvatar',\n 'MuiBadge',\n 'MuiBreadcrumbs',\n 'MuiButton',\n 'MuiButtonBase',\n 'MuiButtonGroup',\n 'MuiCard',\n 'MuiCardActions',\n 'MuiCardContent',\n 'MuiCardHeader',\n 'MuiCheckbox',\n 'MuiChip',\n 'MuiCircularProgress',\n 'MuiDialog',\n 'MuiDialogActions',\n 'MuiDialogContent',\n 'MuiDialogTitle',\n 'MuiDivider',\n 'MuiDrawer',\n 'MuiFab',\n 'MuiFilledInput',\n 'MuiFormControlLabel',\n 'MuiFormHelperText',\n 'MuiFormLabel',\n 'MuiIconButton',\n 'MuiInput',\n 'MuiInputBase',\n 'MuiInputLabel',\n 'MuiLinearProgress',\n 'MuiLink',\n 'MuiList',\n 'MuiListItem',\n 'MuiListItemButton',\n 'MuiMenu',\n 'MuiMenuItem',\n 'MuiOutlinedInput',\n 'MuiPagination',\n 'MuiPaginationItem',\n 'MuiPaper',\n 'MuiRadio',\n 'MuiRating',\n 'MuiSelect',\n 'MuiSlider',\n 'MuiSnackbarContent',\n 'MuiSwitch',\n 'MuiTab',\n 'MuiTable',\n 'MuiTableCell',\n 'MuiTableHead',\n 'MuiTableRow',\n 'MuiTabs',\n 'MuiTextField',\n 'MuiToggleButton',\n 'MuiToggleButtonGroup',\n 'MuiToolbar',\n 'MuiTooltip',\n 'MuiTypography',\n] as const\n\nexport type HostThemeComponentKey =\n (typeof HOST_THEME_COMPONENT_WHITELIST)[number]\n\nconst componentWhitelist: ReadonlySet<string> = new Set(\n HOST_THEME_COMPONENT_WHITELIST,\n)\n\nfunction pickPaletteColor(color: HostThemePaletteColor | undefined) {\n if (!color?.main) return undefined\n const picked: HostThemePaletteColor = { main: color.main }\n if (color.light) picked.light = color.light\n if (color.dark) picked.dark = color.dark\n if (color.contrastText) picked.contrastText = color.contrastText\n return picked\n}\n\nfunction schemeColorsToPaletteOptions(\n scheme: HostThemeScheme,\n colors: HostThemeSchemeColors | undefined,\n): PaletteOptions {\n const palette: PaletteOptions = { mode: scheme }\n if (!colors) return palette\n\n const colorKeys = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'surface',\n 'error',\n 'warning',\n 'info',\n 'success',\n ] as const\n for (const key of colorKeys) {\n const color = pickPaletteColor(colors[key])\n if (color) (palette as Record<string, unknown>)[key] = color\n }\n\n if (colors.background?.default || colors.background?.paper) {\n palette.background = {\n ...(colors.background.default && { default: colors.background.default }),\n ...(colors.background.paper && { paper: colors.background.paper }),\n }\n }\n if (\n colors.text?.primary ||\n colors.text?.secondary ||\n colors.text?.disabled\n ) {\n palette.text = {\n ...(colors.text.primary && { primary: colors.text.primary }),\n ...(colors.text.secondary && { secondary: colors.text.secondary }),\n ...(colors.text.disabled && { disabled: colors.text.disabled }),\n }\n }\n // Tints are string leaves, not a PaletteColor, so they pass through the\n // same \"copy what was set\" path as `background`/`text` rather than\n // `pickPaletteColor` — which requires a `main` a tint does not have\n // (AGL-1244).\n if (colors.tint?.primary || colors.tint?.secondary || colors.tint?.tertiary) {\n palette.tint = {\n ...(colors.tint.primary && { primary: colors.tint.primary }),\n ...(colors.tint.secondary && { secondary: colors.tint.secondary }),\n ...(colors.tint.tertiary && { tertiary: colors.tint.tertiary }),\n }\n }\n if (colors.divider) palette.divider = colors.divider\n\n return palette\n}\n\nfunction sanitizeComponents(\n components: Record<string, HostThemeComponentOverride> | undefined,\n) {\n if (!components) return undefined\n const sanitized: Record<string, HostThemeComponentOverride> = {}\n for (const [key, override] of Object.entries(components)) {\n if (!componentWhitelist.has(key) || !override) continue\n const entry: HostThemeComponentOverride = {}\n if (override.defaultProps) entry.defaultProps = override.defaultProps\n if (override.styleOverrides) entry.styleOverrides = override.styleOverrides\n const sx = sanitizeSlotStyles(override.sx)\n if (sx) entry.sx = sx\n const variants = sanitizeVariants(override.variants)\n if (variants) entry.variants = variants\n if (Object.keys(entry).length) sanitized[key] = entry\n }\n return Object.keys(sanitized).length ? sanitized : undefined\n}\n\n/** `sx` slots: each a style object, anything else dropped. */\nfunction sanitizeSlotStyles(\n slots: unknown,\n): Record<string, Record<string, unknown>> | undefined {\n if (!isPlainObject(slots)) return undefined\n const kept: Record<string, Record<string, unknown>> = {}\n for (const [slot, style] of Object.entries(slots)) {\n if (isPlainObject(style)) kept[slot] = style\n }\n return Object.keys(kept).length ? kept : undefined\n}\n\n/**\n * Variants that can match and style something: a props object of scalars\n * and a style or an `sx`. A malformed entry is dropped rather than handed to\n * MUI, whose variant loop reads `props` and would throw on a missing one.\n */\nfunction sanitizeVariants(\n variants: unknown,\n): HostThemeComponentVariant[] | undefined {\n if (!Array.isArray(variants)) return undefined\n const kept: HostThemeComponentVariant[] = []\n for (const variant of variants) {\n if (!isPlainObject(variant) || !isPlainObject(variant['props'])) continue\n const props = variant['props'] as Record<string, unknown>\n if (\n Object.values(props).some(\n (value) =>\n value !== null &&\n !['string', 'number', 'boolean'].includes(typeof value),\n )\n ) {\n continue\n }\n const entry: HostThemeComponentVariant = {\n props: props as HostThemeComponentVariant['props'],\n }\n if (isPlainObject(variant['style'])) entry.style = variant['style']\n if (isPlainObject(variant['sx'])) entry.sx = variant['sx']\n if (entry.style || entry.sx) kept.push(entry)\n }\n return kept.length ? kept : undefined\n}\n\n/** The theme a style function is handed, as far as `sx` needs it. */\ntype SxTheme = { unstable_sx?: (style: unknown) => unknown }\n\n/**\n * An `sx` object as a style function of the theme it renders under.\n *\n * `components` are read against the BUILT theme of each scheme, so a palette\n * path resolves to that scheme's color: `borderColor: 'divider'` is the light\n * divider on light and the dark one on dark, which is the thing a literal\n * override cannot say.\n */\nfunction sxStyle(sx: Record<string, unknown>) {\n return ({ theme }: { theme: SxTheme }) =>\n theme?.unstable_sx ? theme.unstable_sx(sx) : sx\n}\n\n/**\n * The stored (JSON) components as MUI reads them: each `sx` slot joins its\n * literal `styleOverrides` slot, literal first, and each variant's `sx`\n * becomes a style function.\n */\nfunction componentsToThemeOptions(\n components: Record<string, HostThemeComponentOverride>,\n): NonNullable<ThemeOptions['components']> {\n const converted: Record<string, Record<string, unknown>> = {}\n for (const [key, override] of Object.entries(components)) {\n const entry: Record<string, unknown> = {}\n if (override.defaultProps) entry['defaultProps'] = override.defaultProps\n const slots = new Set([\n ...Object.keys(override.styleOverrides ?? {}),\n ...Object.keys(override.sx ?? {}),\n ])\n if (slots.size) {\n const styleOverrides: Record<string, unknown> = {}\n for (const slot of slots) {\n const literal = override.styleOverrides?.[slot]\n const sx = override.sx?.[slot]\n styleOverrides[slot] =\n sx && literal !== undefined\n ? [literal, sxStyle(sx)]\n : sx\n ? sxStyle(sx)\n : literal\n }\n entry['styleOverrides'] = styleOverrides\n }\n if (override.variants?.length) {\n entry['variants'] = override.variants.map(({ props, style, sx }) => ({\n props,\n style: sx\n ? (context: { theme: SxTheme }) => [\n style ?? {},\n sxStyle(sx)(context),\n ]\n : style,\n }))\n }\n converted[key] = entry\n }\n return converted as NonNullable<ThemeOptions['components']>\n}\n\n/**\n * Strips unknown component overrides and empty branches from a persisted\n * host theme. Returns a new object; the input is never mutated.\n */\nexport function sanitizeHostTheme(theme: HostTheme | undefined): HostTheme {\n if (!theme) return {}\n const sanitized: HostTheme = { ...theme }\n const components = sanitizeComponents(theme.components)\n if (components) sanitized.components = components\n else delete sanitized.components\n // `mixins.toolbar` must be a CSS object; a scalar would reach\n // `createTheme` and throw while building the Toolbar variant.\n if (isPlainObject(theme.mixins?.toolbar)) {\n sanitized.mixins = { toolbar: theme.mixins.toolbar }\n } else {\n delete sanitized.mixins\n }\n // Absent already means \"follows the visitor\", so only the opt-out is worth\n // persisting; anything else (a stale `'auto'`, junk) is dropped.\n if (theme.darkScheme !== 'off') delete sanitized.darkScheme\n return sanitized\n}\n\n/** An `@media` key that names a `min-width`, and the width it names. */\nconst MIN_WIDTH_AT_RULE = /^@media\\b[^{]*\\bmin-width\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*px/\n\n/**\n * Re-orders breakpoint at-rules ascending, so STORED key order cannot decide\n * which rule wins (AGL-3146).\n *\n * Two `min-width` rules of equal specificity both match a wide window, so the\n * later one wins — which makes key order a rendering decision. Firestore does\n * not preserve it: one stored toolbar mixin came back sm, landscape, base to\n * the server reader and landscape, base, sm to the browser, so aglyn.com's\n * nav drew 48px live and 72px on the besigner canvas from a single saved\n * document. The editor and the page disagreed about a value neither of them\n * had changed.\n *\n * Ascending is the order MUI writes its own breakpoint styles in and the\n * order the theme editor writes a toolbar mixin in: the wider query is the\n * more specific answer, so it belongs last.\n *\n * Only two things move. Plain declarations are hoisted ahead of the blocks,\n * keeping their order among themselves — which is where the style engine\n * emits them anyway, since a nested block becomes a rule of its own after the\n * base one, and where the shorthand/longhand hazard lives. Breakpoint rules\n * are sorted into the slots the blocks already occupy, so a nested selector\n * or a condition naming no width never changes position against one another.\n *\n * Returns its input by identity when no order changes, which is every theme\n * with at most one breakpoint rule per object.\n */\nexport function orderMediaWidths<T>(value: T): T {\n if (Array.isArray(value)) {\n let moved = false\n const next = value.map((entry) => {\n const ordered = orderMediaWidths(entry)\n if (ordered !== entry) moved = true\n return ordered\n })\n return (moved ? next : value) as T\n }\n if (!isPlainObject(value)) return value\n\n const source = value as Record<string, unknown>\n const keys = Object.keys(source)\n const declarations = keys.filter((key) => !isPlainObject(source[key]))\n const blocks = keys.filter((key) => isPlainObject(source[key]))\n const widthSlots: number[] = []\n const widths: Array<{ key: string; width: number }> = []\n blocks.forEach((key, index) => {\n const match = MIN_WIDTH_AT_RULE.exec(key)\n if (!match) return\n widthSlots.push(index)\n widths.push({ key, width: Number.parseFloat(match[1]) })\n })\n // A stable sort, so two rules at the same width keep the order they were\n // stored in — there is nothing to prefer between them.\n const sorted = [...widths].sort((a, b) => a.width - b.width)\n widthSlots.forEach((slot, position) => {\n blocks[slot] = sorted[position].key\n })\n\n const order = [...declarations, ...blocks]\n let moved = order.some((key, index) => key !== keys[index])\n const next: Record<string, unknown> = {}\n for (const key of order) {\n const child = orderMediaWidths(source[key])\n if (child !== source[key]) moved = true\n next[key] = child\n }\n return (moved ? next : value) as T\n}\n\n/**\n * Converts a persisted {@link HostTheme} document into MUI `ThemeOptions`\n * for one color scheme. The result is meant to be passed through\n * `createResponsiveTheme` (or `createTheme`) by the consumer; shade and\n * contrast-text derivation for partial palettes is MUI's job, so only\n * explicitly set values are forwarded.\n */\nexport function hostThemeToThemeOptions(\n theme: HostTheme | undefined,\n scheme: HostThemeScheme,\n): ThemeOptions {\n const sanitized = sanitizeHostTheme(theme)\n const options: ThemeOptions = {\n palette: schemeColorsToPaletteOptions(\n scheme,\n sanitized.colorSchemes?.[scheme],\n ),\n }\n\n const { typography } = sanitized\n if (typography?.fontFamily || typography?.variants) {\n // HostThemeTypographyVariant is a sanitized subset of MUI's\n // TypographyStyleOptions; the missing index signature is by design.\n options.typography = {\n ...(typography.fontFamily && { fontFamily: typography.fontFamily }),\n ...typography.variants,\n } as ThemeOptions['typography']\n }\n\n if (typeof sanitized.shape?.borderRadius === 'number') {\n options.shape = { borderRadius: sanitized.shape.borderRadius }\n }\n if (typeof sanitized.spacing === 'number') {\n options.spacing = sanitized.spacing\n }\n if (sanitized.components) {\n // Ordered BEFORE conversion: an `sx` object ends up inside a closure,\n // where the pass over the finished options below cannot reach it.\n options.components = componentsToThemeOptions(\n orderMediaWidths(sanitized.components),\n )\n }\n // Toolbar height is only reachable here (AGL-1242). MUI derives the\n // Toolbar's `regular` variant style from `mixins.toolbar` and applies it\n // AFTER `components.MuiToolbar.styleOverrides`, so the slot override loses\n // every time — its nested media queries do not even emit.\n if (sanitized.mixins?.toolbar) {\n options.mixins = { toolbar: sanitized.mixins.toolbar }\n }\n\n // Every surface that renders a site's theme arrives here — the tenant's\n // provider, the besigner canvas, Preview, the theme editor — so this is\n // where a stored document stops being able to resolve differently for\n // different readers (AGL-3146).\n return orderMediaWidths(options)\n}\n\n/**\n * Layers a host's overrides ONTO a base set of theme options (AGL-1180).\n *\n * `hostThemeToThemeOptions` deliberately emits only what the host explicitly\n * set, so building a theme from it alone leaves every other slot to MUI's\n * stock palette. Consumers used to switch — console theme when the document\n * was empty, host document when it was not — which meant setting a single\n * value (the spec's own example is `{ spacing: 8 }`) silently repainted\n * secondary, tertiary, surface, info, success, warning, error, background\n * and paper in MUI blue/purple. Merging instead of switching keeps the brand\n * as the floor no matter how much the host customizes.\n *\n * `palette` merges one level deep so overriding `primary` cannot drop\n * `secondary`. Within a single colour the override replaces the whole record\n * — MUI derives shades and contrast text from `main`, which is exactly the\n * partial-palette behaviour the converter is written for.\n */\nexport function mergeThemeOptions(\n base: ThemeOptions,\n overrides: ThemeOptions,\n): ThemeOptions {\n const merged: ThemeOptions = { ...base, ...overrides }\n\n // Palette merges ONE level: overriding a colour replaces its whole record\n // so MUI re-derives light/dark/contrastText from the new `main`, which is\n // the partial-palette behaviour the converter is written for. Overriding\n // `primary` still must not disturb `secondary`, hence the level.\n merged.palette = { ...base.palette, ...overrides.palette }\n merged.shape = { ...base.shape, ...overrides.shape }\n // Same reasoning as `palette`: setting `toolbar` must not drop any other\n // mixin the base defines.\n merged.mixins = { ...base.mixins, ...overrides.mixins }\n\n // `typography` is an object in every base we ship, but MUI's type also\n // allows a function of the palette — merging into that would silently drop\n // the base, so prefer the override wholesale in that case.\n if (\n isPlainObject(base.typography) &&\n isPlainObject(overrides.typography)\n ) {\n merged.typography = objectDeepMergeReplaceArrays(\n base.typography,\n overrides.typography,\n ) as ThemeOptions['typography']\n }\n\n // Components merge DEEPLY. A host override names one component, and often\n // one property inside it — `MuiButton.defaultProps.color`. A shallow merge\n // would swap out the entire `MuiButton` entry and take the brand's\n // `styleOverrides` with it, and those styles are frequently FUNCTIONS of\n // the theme that JSON cannot express, so the editor could not put them\n // back even in principle. Deep-merging means you override the leaf you\n // named and inherit everything else, functions included.\n merged.components = objectDeepMergeReplaceArrays(\n base.components ?? {},\n overrides.components ?? {},\n ) as ThemeOptions['components']\n composeComponentStyles(merged.components, base.components, overrides.components)\n\n return merged\n}\n\ntype ComponentOptions = {\n styleOverrides?: Record<string, unknown>\n variants?: unknown[]\n}\n\n/** A slot's styles as a list MUI resolves in order (its `processStyle` flattens arrays). */\nfunction styleList(style: unknown): unknown[] {\n return Array.isArray(style) ? style : [style]\n}\n\n/**\n * Where a deep merge would REPLACE styling rather than add to it, composes\n * the two instead (AGL-3403).\n *\n * A deep merge merges two style OBJECTS key by key, which is right. But the\n * base styles several slots with a FUNCTION of the theme — `MuiButton.root`\n * carries the accent-text contrast fix — and merging an object onto a\n * function keeps only the object. So a theme that set one property on the\n * button root silently removed the fix, and nothing JSON can say could put it\n * back. MUI accepts a list per slot and applies it in order, so a function on\n * either side makes the slot `[base, override]`: the override still wins\n * every property it names, and everything else the base did still happens.\n *\n * `variants` concatenate for the same reason: the base's outlined\n * `IconButton` must not disappear because a theme styled its `small` size.\n */\nfunction composeComponentStyles(\n merged: ThemeOptions['components'],\n base: ThemeOptions['components'],\n overrides: ThemeOptions['components'],\n): void {\n if (!merged || !base || !overrides) return\n const target = merged as Record<string, ComponentOptions>\n for (const [name, override] of Object.entries(\n overrides as Record<string, ComponentOptions>,\n )) {\n const from = (base as Record<string, ComponentOptions>)[name]\n if (!from || !override) continue\n for (const [slot, style] of Object.entries(override.styleOverrides ?? {})) {\n const baseStyle = from.styleOverrides?.[slot]\n if (baseStyle === undefined) continue\n if (isPlainObject(baseStyle) && isPlainObject(style)) continue\n target[name].styleOverrides = {\n ...target[name].styleOverrides,\n [slot]: [...styleList(baseStyle), ...styleList(style)],\n }\n }\n if (Array.isArray(from.variants) && Array.isArray(override.variants)) {\n target[name].variants = [...from.variants, ...override.variants]\n }\n }\n}\n\n/** Plain data object — not an array, function, or class instance. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value)\n )\n}\n\n/** True when the document customizes anything, i.e. consumers should build a theme from it rather than using their default. */\nexport function hasHostTheme(theme: HostTheme | undefined): theme is HostTheme {\n return !!theme && Object.keys(theme).length > 0\n}\n\n/**\n * Builds a Google Fonts CSS2 stylesheet URL for the theme's loadable fonts.\n * Returns undefined when nothing needs loading (system/absent fonts).\n */\nexport function getGoogleFontsUrl(fonts: Array<HostThemeFont> | undefined) {\n const families = (fonts ?? [])\n .filter((font) => font.family && (font.source ?? 'google') === 'google')\n .map((font) => {\n const family = font.family.trim().replace(/\\s+/g, '+')\n const weights = font.weights?.length\n ? `:wght@${[...font.weights].sort((a, b) => a - b).join(';')}`\n : ''\n return `family=${family}${weights}`\n })\n if (!families.length) return undefined\n return `https://fonts.googleapis.com/css2?${families.join('&')}&display=swap`\n}\n\nexport default hostThemeToThemeOptions\n"],"names":["objectDeepMergeReplaceArrays","HOST_THEME_COMPONENT_WHITELIST","componentWhitelist","Set","pickPaletteColor","color","main","undefined","picked","light","dark","contrastText","schemeColorsToPaletteOptions","scheme","colors","palette","mode","colorKeys","key","background","default","paper","text","primary","secondary","disabled","tint","tertiary","divider","sanitizeComponents","components","sanitized","override","Object","entries","has","entry","defaultProps","styleOverrides","sx","sanitizeSlotStyles","variants","sanitizeVariants","keys","length","slots","isPlainObject","kept","slot","style","Array","isArray","variant","props","values","some","value","includes","push","sxStyle","theme","unstable_sx","componentsToThemeOptions","converted","size","literal","map","context","sanitizeHostTheme","mixins","toolbar","darkScheme","MIN_WIDTH_AT_RULE","orderMediaWidths","moved","next","ordered","source","declarations","filter","blocks","widthSlots","widths","forEach","index","match","exec","width","Number","parseFloat","sorted","sort","a","b","position","order","child","hostThemeToThemeOptions","options","colorSchemes","typography","fontFamily","shape","borderRadius","spacing","mergeThemeOptions","base","overrides","merged","composeComponentStyles","styleList","target","name","from","baseStyle","hasHostTheme","getGoogleFontsUrl","fonts","families","font","family","trim","replace","weights","join"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAWD,SAASA,4BAA4B,QAAQ,4BAA2B;AAGxE;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC,iCAAiC;IAC5C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAKV,MAAMC,qBAA0C,IAAIC,IAClDF;AAGF,SAASG,iBAAiBC,KAAwC;IAChE,IAAI,EAACA,yBAAAA,MAAOC,IAAI,GAAE,OAAOC;IACzB,MAAMC,SAAgC;QAAEF,MAAMD,MAAMC,IAAI;IAAC;IACzD,IAAID,MAAMI,KAAK,EAAED,OAAOC,KAAK,GAAGJ,MAAMI,KAAK;IAC3C,IAAIJ,MAAMK,IAAI,EAAEF,OAAOE,IAAI,GAAGL,MAAMK,IAAI;IACxC,IAAIL,MAAMM,YAAY,EAAEH,OAAOG,YAAY,GAAGN,MAAMM,YAAY;IAChE,OAAOH;AACT;AAEA,SAASI,6BACPC,MAAuB,EACvBC,MAAyC;QAoBrCA,oBAA8BA,qBAOhCA,cACAA,eACAA,eAYEA,cAAwBA,eAA0BA;IAvCtD,MAAMC,UAA0B;QAAEC,MAAMH;IAAO;IAC/C,IAAI,CAACC,QAAQ,OAAOC;IAEpB,MAAME,YAAY;QAChB;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD;IACD,KAAK,MAAMC,OAAOD,UAAW;QAC3B,MAAMZ,QAAQD,iBAAiBU,MAAM,CAACI,IAAI;QAC1C,IAAIb,OAAO,AAACU,OAAmC,CAACG,IAAI,GAAGb;IACzD;IAEA,IAAIS,EAAAA,qBAAAA,OAAOK,UAAU,qBAAjBL,mBAAmBM,OAAO,OAAIN,sBAAAA,OAAOK,UAAU,qBAAjBL,oBAAmBO,KAAK,GAAE;QAC1DN,QAAQI,UAAU,GAAG,aACfL,OAAOK,UAAU,CAACC,OAAO,IAAI;YAAEA,SAASN,OAAOK,UAAU,CAACC,OAAO;QAAC,GAClEN,OAAOK,UAAU,CAACE,KAAK,IAAI;YAAEA,OAAOP,OAAOK,UAAU,CAACE,KAAK;QAAC;IAEpE;IACA,IACEP,EAAAA,eAAAA,OAAOQ,IAAI,qBAAXR,aAAaS,OAAO,OACpBT,gBAAAA,OAAOQ,IAAI,qBAAXR,cAAaU,SAAS,OACtBV,gBAAAA,OAAOQ,IAAI,qBAAXR,cAAaW,QAAQ,GACrB;QACAV,QAAQO,IAAI,GAAG,aACTR,OAAOQ,IAAI,CAACC,OAAO,IAAI;YAAEA,SAAST,OAAOQ,IAAI,CAACC,OAAO;QAAC,GACtDT,OAAOQ,IAAI,CAACE,SAAS,IAAI;YAAEA,WAAWV,OAAOQ,IAAI,CAACE,SAAS;QAAC,GAC5DV,OAAOQ,IAAI,CAACG,QAAQ,IAAI;YAAEA,UAAUX,OAAOQ,IAAI,CAACG,QAAQ;QAAC;IAEjE;IACA,wEAAwE;IACxE,mEAAmE;IACnE,oEAAoE;IACpE,cAAc;IACd,IAAIX,EAAAA,eAAAA,OAAOY,IAAI,qBAAXZ,aAAaS,OAAO,OAAIT,gBAAAA,OAAOY,IAAI,qBAAXZ,cAAaU,SAAS,OAAIV,gBAAAA,OAAOY,IAAI,qBAAXZ,cAAaa,QAAQ,GAAE;QAC3EZ,QAAQW,IAAI,GAAG,aACTZ,OAAOY,IAAI,CAACH,OAAO,IAAI;YAAEA,SAAST,OAAOY,IAAI,CAACH,OAAO;QAAC,GACtDT,OAAOY,IAAI,CAACF,SAAS,IAAI;YAAEA,WAAWV,OAAOY,IAAI,CAACF,SAAS;QAAC,GAC5DV,OAAOY,IAAI,CAACC,QAAQ,IAAI;YAAEA,UAAUb,OAAOY,IAAI,CAACC,QAAQ;QAAC;IAEjE;IACA,IAAIb,OAAOc,OAAO,EAAEb,QAAQa,OAAO,GAAGd,OAAOc,OAAO;IAEpD,OAAOb;AACT;AAEA,SAASc,mBACPC,UAAkE;IAElE,IAAI,CAACA,YAAY,OAAOvB;IACxB,MAAMwB,YAAwD,CAAC;IAC/D,KAAK,MAAM,CAACb,KAAKc,SAAS,IAAIC,OAAOC,OAAO,CAACJ,YAAa;QACxD,IAAI,CAAC5B,mBAAmBiC,GAAG,CAACjB,QAAQ,CAACc,UAAU;QAC/C,MAAMI,QAAoC,CAAC;QAC3C,IAAIJ,SAASK,YAAY,EAAED,MAAMC,YAAY,GAAGL,SAASK,YAAY;QACrE,IAAIL,SAASM,cAAc,EAAEF,MAAME,cAAc,GAAGN,SAASM,cAAc;QAC3E,MAAMC,KAAKC,mBAAmBR,SAASO,EAAE;QACzC,IAAIA,IAAIH,MAAMG,EAAE,GAAGA;QACnB,MAAME,WAAWC,iBAAiBV,SAASS,QAAQ;QACnD,IAAIA,UAAUL,MAAMK,QAAQ,GAAGA;QAC/B,IAAIR,OAAOU,IAAI,CAACP,OAAOQ,MAAM,EAAEb,SAAS,CAACb,IAAI,GAAGkB;IAClD;IACA,OAAOH,OAAOU,IAAI,CAACZ,WAAWa,MAAM,GAAGb,YAAYxB;AACrD;AAEA,4DAA4D,GAC5D,SAASiC,mBACPK,KAAc;IAEd,IAAI,CAACC,cAAcD,QAAQ,OAAOtC;IAClC,MAAMwC,OAAgD,CAAC;IACvD,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIhB,OAAOC,OAAO,CAACW,OAAQ;QACjD,IAAIC,cAAcG,QAAQF,IAAI,CAACC,KAAK,GAAGC;IACzC;IACA,OAAOhB,OAAOU,IAAI,CAACI,MAAMH,MAAM,GAAGG,OAAOxC;AAC3C;AAEA;;;;CAIC,GACD,SAASmC,iBACPD,QAAiB;IAEjB,IAAI,CAACS,MAAMC,OAAO,CAACV,WAAW,OAAOlC;IACrC,MAAMwC,OAAoC,EAAE;IAC5C,KAAK,MAAMK,WAAWX,SAAU;QAC9B,IAAI,CAACK,cAAcM,YAAY,CAACN,cAAcM,OAAO,CAAC,QAAQ,GAAG;QACjE,MAAMC,QAAQD,OAAO,CAAC,QAAQ;QAC9B,IACEnB,OAAOqB,MAAM,CAACD,OAAOE,IAAI,CACvB,CAACC,QACCA,UAAU,QACV,CAAC;gBAAC;gBAAU;gBAAU;aAAU,CAACC,QAAQ,CAAC,OAAOD,SAErD;YACA;QACF;QACA,MAAMpB,QAAmC;YACvCiB,OAAOA;QACT;QACA,IAAIP,cAAcM,OAAO,CAAC,QAAQ,GAAGhB,MAAMa,KAAK,GAAGG,OAAO,CAAC,QAAQ;QACnE,IAAIN,cAAcM,OAAO,CAAC,KAAK,GAAGhB,MAAMG,EAAE,GAAGa,OAAO,CAAC,KAAK;QAC1D,IAAIhB,MAAMa,KAAK,IAAIb,MAAMG,EAAE,EAAEQ,KAAKW,IAAI,CAACtB;IACzC;IACA,OAAOW,KAAKH,MAAM,GAAGG,OAAOxC;AAC9B;AAKA;;;;;;;CAOC,GACD,SAASoD,QAAQpB,EAA2B;IAC1C,OAAO,CAAC,EAAEqB,KAAK,EAAsB,GACnCA,CAAAA,yBAAAA,MAAOC,WAAW,IAAGD,MAAMC,WAAW,CAACtB,MAAMA;AACjD;AAEA;;;;CAIC,GACD,SAASuB,yBACPhC,UAAsD;IAEtD,MAAMiC,YAAqD,CAAC;IAC5D,KAAK,MAAM,CAAC7C,KAAKc,SAAS,IAAIC,OAAOC,OAAO,CAACJ,YAAa;YAIvCE,0BACAA;YAgBbA;QApBJ,MAAMI,QAAiC,CAAC;QACxC,IAAIJ,SAASK,YAAY,EAAED,KAAK,CAAC,eAAe,GAAGJ,SAASK,YAAY;QACxE,MAAMQ,QAAQ,IAAI1C,IAAI;eACjB8B,OAAOU,IAAI,EAACX,2BAAAA,SAASM,cAAc,YAAvBN,2BAA2B,CAAC;eACxCC,OAAOU,IAAI,EAACX,eAAAA,SAASO,EAAE,YAAXP,eAAe,CAAC;SAChC;QACD,IAAIa,MAAMmB,IAAI,EAAE;YACd,MAAM1B,iBAA0C,CAAC;YACjD,KAAK,MAAMU,QAAQH,MAAO;oBACRb,2BACLA;gBADX,MAAMiC,WAAUjC,4BAAAA,SAASM,cAAc,qBAAvBN,yBAAyB,CAACgB,KAAK;gBAC/C,MAAMT,MAAKP,gBAAAA,SAASO,EAAE,qBAAXP,aAAa,CAACgB,KAAK;gBAC9BV,cAAc,CAACU,KAAK,GAClBT,MAAM0B,YAAY1D,YACd;oBAAC0D;oBAASN,QAAQpB;iBAAI,GACtBA,KACEoB,QAAQpB,MACR0B;YACV;YACA7B,KAAK,CAAC,iBAAiB,GAAGE;QAC5B;QACA,KAAIN,qBAAAA,SAASS,QAAQ,qBAAjBT,mBAAmBY,MAAM,EAAE;YAC7BR,KAAK,CAAC,WAAW,GAAGJ,SAASS,QAAQ,CAACyB,GAAG,CAAC,CAAC,EAAEb,KAAK,EAAEJ,KAAK,EAAEV,EAAE,EAAE,GAAM,CAAA;oBACnEc;oBACAJ,OAAOV,KACH,CAAC4B,UAAgC;4BAC/BlB,gBAAAA,QAAS,CAAC;4BACVU,QAAQpB,IAAI4B;yBACb,GACDlB;gBACN,CAAA;QACF;QACAc,SAAS,CAAC7C,IAAI,GAAGkB;IACnB;IACA,OAAO2B;AACT;AAEA;;;CAGC,GACD,OAAO,SAASK,kBAAkBR,KAA4B;QAQ1CA;IAPlB,IAAI,CAACA,OAAO,OAAO,CAAC;IACpB,MAAM7B,YAAuB,aAAK6B;IAClC,MAAM9B,aAAaD,mBAAmB+B,MAAM9B,UAAU;IACtD,IAAIA,YAAYC,UAAUD,UAAU,GAAGA;SAClC,OAAOC,UAAUD,UAAU;IAChC,8DAA8D;IAC9D,8DAA8D;IAC9D,IAAIgB,eAAcc,gBAAAA,MAAMS,MAAM,qBAAZT,cAAcU,OAAO,GAAG;QACxCvC,UAAUsC,MAAM,GAAG;YAAEC,SAASV,MAAMS,MAAM,CAACC,OAAO;QAAC;IACrD,OAAO;QACL,OAAOvC,UAAUsC,MAAM;IACzB;IACA,2EAA2E;IAC3E,iEAAiE;IACjE,IAAIT,MAAMW,UAAU,KAAK,OAAO,OAAOxC,UAAUwC,UAAU;IAC3D,OAAOxC;AACT;AAEA,sEAAsE,GACtE,MAAMyC,oBAAoB;AAE1B;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASC,iBAAoBjB,KAAQ;IAC1C,IAAIN,MAAMC,OAAO,CAACK,QAAQ;QACxB,IAAIkB,QAAQ;QACZ,MAAMC,OAAOnB,MAAMU,GAAG,CAAC,CAAC9B;YACtB,MAAMwC,UAAUH,iBAAiBrC;YACjC,IAAIwC,YAAYxC,OAAOsC,QAAQ;YAC/B,OAAOE;QACT;QACA,OAAQF,QAAQC,OAAOnB;IACzB;IACA,IAAI,CAACV,cAAcU,QAAQ,OAAOA;IAElC,MAAMqB,SAASrB;IACf,MAAMb,OAAOV,OAAOU,IAAI,CAACkC;IACzB,MAAMC,eAAenC,KAAKoC,MAAM,CAAC,CAAC7D,MAAQ,CAAC4B,cAAc+B,MAAM,CAAC3D,IAAI;IACpE,MAAM8D,SAASrC,KAAKoC,MAAM,CAAC,CAAC7D,MAAQ4B,cAAc+B,MAAM,CAAC3D,IAAI;IAC7D,MAAM+D,aAAuB,EAAE;IAC/B,MAAMC,SAAgD,EAAE;IACxDF,OAAOG,OAAO,CAAC,CAACjE,KAAKkE;QACnB,MAAMC,QAAQb,kBAAkBc,IAAI,CAACpE;QACrC,IAAI,CAACmE,OAAO;QACZJ,WAAWvB,IAAI,CAAC0B;QAChBF,OAAOxB,IAAI,CAAC;YAAExC;YAAKqE,OAAOC,OAAOC,UAAU,CAACJ,KAAK,CAAC,EAAE;QAAE;IACxD;IACA,yEAAyE;IACzE,uDAAuD;IACvD,MAAMK,SAAS;WAAIR;KAAO,CAACS,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEL,KAAK,GAAGM,EAAEN,KAAK;IAC3DN,WAAWE,OAAO,CAAC,CAACnC,MAAM8C;QACxBd,MAAM,CAAChC,KAAK,GAAG0C,MAAM,CAACI,SAAS,CAAC5E,GAAG;IACrC;IAEA,MAAM6E,QAAQ;WAAIjB;WAAiBE;KAAO;IAC1C,IAAIN,QAAQqB,MAAMxC,IAAI,CAAC,CAACrC,KAAKkE,QAAUlE,QAAQyB,IAAI,CAACyC,MAAM;IAC1D,MAAMT,OAAgC,CAAC;IACvC,KAAK,MAAMzD,OAAO6E,MAAO;QACvB,MAAMC,QAAQvB,iBAAiBI,MAAM,CAAC3D,IAAI;QAC1C,IAAI8E,UAAUnB,MAAM,CAAC3D,IAAI,EAAEwD,QAAQ;QACnCC,IAAI,CAACzD,IAAI,GAAG8E;IACd;IACA,OAAQtB,QAAQC,OAAOnB;AACzB;AAEA;;;;;;CAMC,GACD,OAAO,SAASyC,wBACdrC,KAA4B,EAC5B/C,MAAuB;QAMnBkB,yBAcOA,kBAiBPA;IAnCJ,MAAMA,YAAYqC,kBAAkBR;IACpC,MAAMsC,UAAwB;QAC5BnF,SAASH,6BACPC,SACAkB,0BAAAA,UAAUoE,YAAY,qBAAtBpE,uBAAwB,CAAClB,OAAO;IAEpC;IAEA,MAAM,EAAEuF,UAAU,EAAE,GAAGrE;IACvB,IAAIqE,CAAAA,8BAAAA,WAAYC,UAAU,MAAID,8BAAAA,WAAY3D,QAAQ,GAAE;QAClD,4DAA4D;QAC5D,oEAAoE;QACpEyD,QAAQE,UAAU,GAAG,aACfA,WAAWC,UAAU,IAAI;YAAEA,YAAYD,WAAWC,UAAU;QAAC,GAC9DD,WAAW3D,QAAQ;IAE1B;IAEA,IAAI,SAAOV,mBAAAA,UAAUuE,KAAK,qBAAfvE,iBAAiBwE,YAAY,MAAK,UAAU;QACrDL,QAAQI,KAAK,GAAG;YAAEC,cAAcxE,UAAUuE,KAAK,CAACC,YAAY;QAAC;IAC/D;IACA,IAAI,OAAOxE,UAAUyE,OAAO,KAAK,UAAU;QACzCN,QAAQM,OAAO,GAAGzE,UAAUyE,OAAO;IACrC;IACA,IAAIzE,UAAUD,UAAU,EAAE;QACxB,sEAAsE;QACtE,kEAAkE;QAClEoE,QAAQpE,UAAU,GAAGgC,yBACnBW,iBAAiB1C,UAAUD,UAAU;IAEzC;IACA,oEAAoE;IACpE,yEAAyE;IACzE,2EAA2E;IAC3E,0DAA0D;IAC1D,KAAIC,oBAAAA,UAAUsC,MAAM,qBAAhBtC,kBAAkBuC,OAAO,EAAE;QAC7B4B,QAAQ7B,MAAM,GAAG;YAAEC,SAASvC,UAAUsC,MAAM,CAACC,OAAO;QAAC;IACvD;IAEA,wEAAwE;IACxE,wEAAwE;IACxE,sEAAsE;IACtE,gCAAgC;IAChC,OAAOG,iBAAiByB;AAC1B;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASO,kBACdC,IAAkB,EAClBC,SAAuB;QAmCrBD,kBACAC;IAlCF,MAAMC,SAAuB,aAAKF,MAASC;IAE3C,0EAA0E;IAC1E,0EAA0E;IAC1E,yEAAyE;IACzE,iEAAiE;IACjEC,OAAO7F,OAAO,GAAG,aAAK2F,KAAK3F,OAAO,EAAK4F,UAAU5F,OAAO;IACxD6F,OAAON,KAAK,GAAG,aAAKI,KAAKJ,KAAK,EAAKK,UAAUL,KAAK;IAClD,yEAAyE;IACzE,0BAA0B;IAC1BM,OAAOvC,MAAM,GAAG,aAAKqC,KAAKrC,MAAM,EAAKsC,UAAUtC,MAAM;IAErD,uEAAuE;IACvE,2EAA2E;IAC3E,2DAA2D;IAC3D,IACEvB,cAAc4D,KAAKN,UAAU,KAC7BtD,cAAc6D,UAAUP,UAAU,GAClC;QACAQ,OAAOR,UAAU,GAAGpG,6BAClB0G,KAAKN,UAAU,EACfO,UAAUP,UAAU;IAExB;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,mEAAmE;IACnE,yEAAyE;IACzE,uEAAuE;IACvE,uEAAuE;IACvE,yDAAyD;IACzDQ,OAAO9E,UAAU,GAAG9B,8BAClB0G,mBAAAA,KAAK5E,UAAU,YAAf4E,mBAAmB,CAAC,IACpBC,wBAAAA,UAAU7E,UAAU,YAApB6E,wBAAwB,CAAC;IAE3BE,uBAAuBD,OAAO9E,UAAU,EAAE4E,KAAK5E,UAAU,EAAE6E,UAAU7E,UAAU;IAE/E,OAAO8E;AACT;AAOA,0FAA0F,GAC1F,SAASE,UAAU7D,KAAc;IAC/B,OAAOC,MAAMC,OAAO,CAACF,SAASA,QAAQ;QAACA;KAAM;AAC/C;AAEA;;;;;;;;;;;;;;;CAeC,GACD,SAAS4D,uBACPD,MAAkC,EAClCF,IAAgC,EAChCC,SAAqC;IAErC,IAAI,CAACC,UAAU,CAACF,QAAQ,CAACC,WAAW;IACpC,MAAMI,SAASH;IACf,KAAK,MAAM,CAACI,MAAMhF,SAAS,IAAIC,OAAOC,OAAO,CAC3CyE,WACC;YAG0C3E;QAF3C,MAAMiF,OAAO,AAACP,IAAyC,CAACM,KAAK;QAC7D,IAAI,CAACC,QAAQ,CAACjF,UAAU;QACxB,KAAK,MAAM,CAACgB,MAAMC,MAAM,IAAIhB,OAAOC,OAAO,EAACF,2BAAAA,SAASM,cAAc,YAAvBN,2BAA2B,CAAC,GAAI;gBACvDiF;YAAlB,MAAMC,aAAYD,uBAAAA,KAAK3E,cAAc,qBAAnB2E,oBAAqB,CAACjE,KAAK;YAC7C,IAAIkE,cAAc3G,WAAW;YAC7B,IAAIuC,cAAcoE,cAAcpE,cAAcG,QAAQ;YACtD8D,MAAM,CAACC,KAAK,CAAC1E,cAAc,GAAG,aACzByE,MAAM,CAACC,KAAK,CAAC1E,cAAc;gBAC9B,CAACU,KAAK,EAAE;uBAAI8D,UAAUI;uBAAeJ,UAAU7D;iBAAO;;QAE1D;QACA,IAAIC,MAAMC,OAAO,CAAC8D,KAAKxE,QAAQ,KAAKS,MAAMC,OAAO,CAACnB,SAASS,QAAQ,GAAG;YACpEsE,MAAM,CAACC,KAAK,CAACvE,QAAQ,GAAG;mBAAIwE,KAAKxE,QAAQ;mBAAKT,SAASS,QAAQ;aAAC;QAClE;IACF;AACF;AAEA,mEAAmE,GACnE,SAASK,cAAcU,KAAc;IACnC,OACE,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACN,MAAMC,OAAO,CAACK;AAElE;AAEA,6HAA6H,GAC7H,OAAO,SAAS2D,aAAavD,KAA4B;IACvD,OAAO,CAAC,CAACA,SAAS3B,OAAOU,IAAI,CAACiB,OAAOhB,MAAM,GAAG;AAChD;AAEA;;;CAGC,GACD,OAAO,SAASwE,kBAAkBC,KAAuC;IACvE,MAAMC,WAAW,CAACD,gBAAAA,QAAS,EAAE,EAC1BtC,MAAM,CAAC,CAACwC;YAAyBA;eAAhBA,KAAKC,MAAM,IAAI,EAACD,eAAAA,KAAK1C,MAAM,YAAX0C,eAAe,cAAc;OAC9DrD,GAAG,CAAC,CAACqD;YAEYA;QADhB,MAAMC,SAASD,KAAKC,MAAM,CAACC,IAAI,GAAGC,OAAO,CAAC,QAAQ;QAClD,MAAMC,UAAUJ,EAAAA,gBAAAA,KAAKI,OAAO,qBAAZJ,cAAc3E,MAAM,IAChC,CAAC,MAAM,EAAE;eAAI2E,KAAKI,OAAO;SAAC,CAAChC,IAAI,CAAC,CAACC,GAAGC,IAAMD,IAAIC,GAAG+B,IAAI,CAAC,MAAM,GAC5D;QACJ,OAAO,CAAC,OAAO,EAAEJ,SAASG,SAAS;IACrC;IACF,IAAI,CAACL,SAAS1E,MAAM,EAAE,OAAOrC;IAC7B,OAAO,CAAC,kCAAkC,EAAE+G,SAASM,IAAI,CAAC,KAAK,aAAa,CAAC;AAC/E;AAEA,eAAe3B,wBAAuB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/host-theme.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeComponentVariant,\n HostThemeFont,\n HostThemePaletteColor,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport { objectDeepMergeReplaceArrays } from '@aglyn/shared-util-vendor'\nimport {\n darken,\n lighten,\n type PaletteOptions,\n type ThemeOptions,\n} from '../../vendor/mui'\n\n/**\n * Components a host theme may override. Persisted overrides are plain JSON;\n * anything outside this list is dropped by {@link sanitizeHostTheme} so a\n * tampered document can't restyle console-internal or portal-critical\n * components.\n *\n * The list is what a site actually renders — its elements, the forms, booking\n * and commerce surfaces, and the dialogs and menus they open — so a theme can\n * restyle a whole site rather than its buttons alone (AGL-3403). Layout\n * primitives (`MuiContainer`, `MuiGrid`, `MuiStack`, `MuiBox`) are absent on\n * purpose: they carry the page structure, and a theme that reflowed them\n * would break layouts the Besigner drew.\n */\nexport const HOST_THEME_COMPONENT_WHITELIST = [\n 'MuiAccordion',\n 'MuiAccordionDetails',\n 'MuiAccordionSummary',\n 'MuiAlert',\n 'MuiAlertTitle',\n 'MuiAppBar',\n 'MuiAvatar',\n 'MuiBadge',\n 'MuiBreadcrumbs',\n 'MuiButton',\n 'MuiButtonBase',\n 'MuiButtonGroup',\n 'MuiCard',\n 'MuiCardActions',\n 'MuiCardContent',\n 'MuiCardHeader',\n 'MuiCheckbox',\n 'MuiChip',\n 'MuiCircularProgress',\n 'MuiDialog',\n 'MuiDialogActions',\n 'MuiDialogContent',\n 'MuiDialogTitle',\n 'MuiDivider',\n 'MuiDrawer',\n 'MuiFab',\n 'MuiFilledInput',\n 'MuiFormControlLabel',\n 'MuiFormHelperText',\n 'MuiFormLabel',\n 'MuiIconButton',\n 'MuiInput',\n 'MuiInputBase',\n 'MuiInputLabel',\n 'MuiLinearProgress',\n 'MuiLink',\n 'MuiList',\n 'MuiListItem',\n 'MuiListItemButton',\n 'MuiMenu',\n 'MuiMenuItem',\n 'MuiOutlinedInput',\n 'MuiPagination',\n 'MuiPaginationItem',\n 'MuiPaper',\n 'MuiRadio',\n 'MuiRating',\n 'MuiSelect',\n 'MuiSlider',\n 'MuiSnackbarContent',\n 'MuiSwitch',\n 'MuiTab',\n 'MuiTable',\n 'MuiTableCell',\n 'MuiTableHead',\n 'MuiTableRow',\n 'MuiTabs',\n 'MuiTextField',\n 'MuiToggleButton',\n 'MuiToggleButtonGroup',\n 'MuiToolbar',\n 'MuiTooltip',\n 'MuiTypography',\n] as const\n\nexport type HostThemeComponentKey =\n (typeof HOST_THEME_COMPONENT_WHITELIST)[number]\n\nconst componentWhitelist: ReadonlySet<string> = new Set(\n HOST_THEME_COMPONENT_WHITELIST,\n)\n\nfunction pickPaletteColor(color: HostThemePaletteColor | undefined) {\n if (!color?.main) return undefined\n const picked: HostThemePaletteColor = { main: color.main }\n if (color.light) picked.light = color.light\n if (color.dark) picked.dark = color.dark\n if (color.contrastText) picked.contrastText = color.contrastText\n return picked\n}\n\nfunction schemeColorsToPaletteOptions(\n scheme: HostThemeScheme,\n colors: HostThemeSchemeColors | undefined,\n): PaletteOptions {\n const palette: PaletteOptions = { mode: scheme }\n if (!colors) return palette\n\n const colorKeys = [\n 'primary',\n 'secondary',\n 'tertiary',\n 'surface',\n 'error',\n 'warning',\n 'info',\n 'success',\n ] as const\n for (const key of colorKeys) {\n const color = pickPaletteColor(colors[key])\n if (color) (palette as Record<string, unknown>)[key] = color\n }\n\n if (colors.background?.default || colors.background?.paper) {\n palette.background = {\n ...(colors.background.default && { default: colors.background.default }),\n ...(colors.background.paper && { paper: colors.background.paper }),\n }\n }\n if (\n colors.text?.primary ||\n colors.text?.secondary ||\n colors.text?.disabled\n ) {\n palette.text = {\n ...(colors.text.primary && { primary: colors.text.primary }),\n ...(colors.text.secondary && { secondary: colors.text.secondary }),\n ...(colors.text.disabled && { disabled: colors.text.disabled }),\n }\n }\n // Tints are string leaves, not a PaletteColor, so they pass through the\n // same \"copy what was set\" path as `background`/`text` rather than\n // `pickPaletteColor` — which requires a `main` a tint does not have\n // (AGL-1244).\n if (colors.tint?.primary || colors.tint?.secondary || colors.tint?.tertiary) {\n palette.tint = {\n ...(colors.tint.primary && { primary: colors.tint.primary }),\n ...(colors.tint.secondary && { secondary: colors.tint.secondary }),\n ...(colors.tint.tertiary && { tertiary: colors.tint.tertiary }),\n }\n }\n if (colors.divider) palette.divider = colors.divider\n\n return palette\n}\n\nfunction sanitizeComponents(\n components: Record<string, HostThemeComponentOverride> | undefined,\n) {\n if (!components) return undefined\n const sanitized: Record<string, HostThemeComponentOverride> = {}\n for (const [key, override] of Object.entries(components)) {\n if (!componentWhitelist.has(key) || !override) continue\n const entry: HostThemeComponentOverride = {}\n if (override.defaultProps) entry.defaultProps = override.defaultProps\n if (override.styleOverrides) entry.styleOverrides = override.styleOverrides\n const sx = sanitizeSlotStyles(override.sx)\n if (sx) entry.sx = sx\n const variants = sanitizeVariants(override.variants)\n if (variants) entry.variants = variants\n if (Object.keys(entry).length) sanitized[key] = entry\n }\n return Object.keys(sanitized).length ? sanitized : undefined\n}\n\n/** `sx` slots: each a style object, anything else dropped. */\nfunction sanitizeSlotStyles(\n slots: unknown,\n): Record<string, Record<string, unknown>> | undefined {\n if (!isPlainObject(slots)) return undefined\n const kept: Record<string, Record<string, unknown>> = {}\n for (const [slot, style] of Object.entries(slots)) {\n if (isPlainObject(style)) kept[slot] = style\n }\n return Object.keys(kept).length ? kept : undefined\n}\n\n/**\n * Variants that can match and style something: a props object of scalars\n * and a style or an `sx`. A malformed entry is dropped rather than handed to\n * MUI, whose variant loop reads `props` and would throw on a missing one.\n */\nfunction sanitizeVariants(\n variants: unknown,\n): HostThemeComponentVariant[] | undefined {\n if (!Array.isArray(variants)) return undefined\n const kept: HostThemeComponentVariant[] = []\n for (const variant of variants) {\n if (!isPlainObject(variant) || !isPlainObject(variant['props'])) continue\n const props = variant['props'] as Record<string, unknown>\n if (\n Object.values(props).some(\n (value) =>\n value !== null &&\n !['string', 'number', 'boolean'].includes(typeof value),\n )\n ) {\n continue\n }\n const entry: HostThemeComponentVariant = {\n props: props as HostThemeComponentVariant['props'],\n }\n if (isPlainObject(variant['style'])) entry.style = variant['style']\n if (isPlainObject(variant['sx'])) entry.sx = variant['sx']\n if (entry.style || entry.sx) kept.push(entry)\n }\n return kept.length ? kept : undefined\n}\n\n/** The theme a style function is handed, as far as `sx` needs it. */\ntype SxTheme = { unstable_sx?: (style: unknown) => unknown }\n\n/**\n * An `sx` object as a style function of the theme it renders under.\n *\n * `components` are read against the BUILT theme of each scheme, so a palette\n * path resolves to that scheme's color: `borderColor: 'divider'` is the light\n * divider on light and the dark one on dark, which is the thing a literal\n * override cannot say.\n */\nfunction sxStyle(sx: Record<string, unknown>) {\n return ({ theme }: { theme: SxTheme }) =>\n theme?.unstable_sx ? theme.unstable_sx(sx) : sx\n}\n\n/**\n * The stored (JSON) components as MUI reads them: each `sx` slot joins its\n * literal `styleOverrides` slot, literal first, and each variant's `sx`\n * becomes a style function.\n */\nfunction componentsToThemeOptions(\n components: Record<string, HostThemeComponentOverride>,\n): NonNullable<ThemeOptions['components']> {\n const converted: Record<string, Record<string, unknown>> = {}\n for (const [key, override] of Object.entries(components)) {\n const entry: Record<string, unknown> = {}\n if (override.defaultProps) entry['defaultProps'] = override.defaultProps\n const slots = new Set([\n ...Object.keys(override.styleOverrides ?? {}),\n ...Object.keys(override.sx ?? {}),\n ])\n if (slots.size) {\n const styleOverrides: Record<string, unknown> = {}\n for (const slot of slots) {\n const literal = override.styleOverrides?.[slot]\n const sx = override.sx?.[slot]\n styleOverrides[slot] =\n sx && literal !== undefined\n ? [literal, sxStyle(sx)]\n : sx\n ? sxStyle(sx)\n : literal\n }\n entry['styleOverrides'] = styleOverrides\n }\n if (override.variants?.length) {\n entry['variants'] = override.variants.map(({ props, style, sx }) => ({\n props,\n style: sx\n ? (context: { theme: SxTheme }) => [\n style ?? {},\n sxStyle(sx)(context),\n ]\n : style,\n }))\n }\n converted[key] = entry\n }\n return converted as NonNullable<ThemeOptions['components']>\n}\n\n/**\n * Strips unknown component overrides and empty branches from a persisted\n * host theme. Returns a new object; the input is never mutated.\n */\nexport function sanitizeHostTheme(theme: HostTheme | undefined): HostTheme {\n if (!theme) return {}\n const sanitized: HostTheme = { ...theme }\n const components = sanitizeComponents(theme.components)\n if (components) sanitized.components = components\n else delete sanitized.components\n // `mixins.toolbar` must be a CSS object; a scalar would reach\n // `createTheme` and throw while building the Toolbar variant.\n if (isPlainObject(theme.mixins?.toolbar)) {\n sanitized.mixins = { toolbar: theme.mixins.toolbar }\n } else {\n delete sanitized.mixins\n }\n // Absent already means \"follows the visitor\", so only the opt-out is worth\n // persisting; anything else (a stale `'auto'`, junk) is dropped.\n if (theme.darkScheme !== 'off') delete sanitized.darkScheme\n return sanitized\n}\n\n/** An `@media` key that names a `min-width`, and the width it names. */\nconst MIN_WIDTH_AT_RULE = /^@media\\b[^{]*\\bmin-width\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*px/\n\n/**\n * Re-orders breakpoint at-rules ascending, so STORED key order cannot decide\n * which rule wins (AGL-3146).\n *\n * Two `min-width` rules of equal specificity both match a wide window, so the\n * later one wins — which makes key order a rendering decision. Firestore does\n * not preserve it: one stored toolbar mixin came back sm, landscape, base to\n * the server reader and landscape, base, sm to the browser, so aglyn.com's\n * nav drew 48px live and 72px on the besigner canvas from a single saved\n * document. The editor and the page disagreed about a value neither of them\n * had changed.\n *\n * Ascending is the order MUI writes its own breakpoint styles in and the\n * order the theme editor writes a toolbar mixin in: the wider query is the\n * more specific answer, so it belongs last.\n *\n * Only two things move. Plain declarations are hoisted ahead of the blocks,\n * keeping their order among themselves — which is where the style engine\n * emits them anyway, since a nested block becomes a rule of its own after the\n * base one, and where the shorthand/longhand hazard lives. Breakpoint rules\n * are sorted into the slots the blocks already occupy, so a nested selector\n * or a condition naming no width never changes position against one another.\n *\n * Returns its input by identity when no order changes, which is every theme\n * with at most one breakpoint rule per object.\n */\nexport function orderMediaWidths<T>(value: T): T {\n if (Array.isArray(value)) {\n let moved = false\n const next = value.map((entry) => {\n const ordered = orderMediaWidths(entry)\n if (ordered !== entry) moved = true\n return ordered\n })\n return (moved ? next : value) as T\n }\n if (!isPlainObject(value)) return value\n\n const source = value as Record<string, unknown>\n const keys = Object.keys(source)\n const declarations = keys.filter((key) => !isPlainObject(source[key]))\n const blocks = keys.filter((key) => isPlainObject(source[key]))\n const widthSlots: number[] = []\n const widths: Array<{ key: string; width: number }> = []\n blocks.forEach((key, index) => {\n const match = MIN_WIDTH_AT_RULE.exec(key)\n if (!match) return\n widthSlots.push(index)\n widths.push({ key, width: Number.parseFloat(match[1]) })\n })\n // A stable sort, so two rules at the same width keep the order they were\n // stored in — there is nothing to prefer between them.\n const sorted = [...widths].sort((a, b) => a.width - b.width)\n widthSlots.forEach((slot, position) => {\n blocks[slot] = sorted[position].key\n })\n\n const order = [...declarations, ...blocks]\n let moved = order.some((key, index) => key !== keys[index])\n const next: Record<string, unknown> = {}\n for (const key of order) {\n const child = orderMediaWidths(source[key])\n if (child !== source[key]) moved = true\n next[key] = child\n }\n return (moved ? next : value) as T\n}\n\n/**\n * A tint is a wash of its accent, so it is derived from `main` the way MUI\n * derives `light` and `dark`: with its own `lighten`/`darken`.\n */\nexport function accentTint(main: string, mode: PaletteOptions['mode']): string {\n return mode === 'dark' ? darken(main, 0.7) : lighten(main, 0.9)\n}\n\n/**\n * The tints a site renders: the base's, each replaced by the site's own where\n * it sets one, or washed from the site's accent where it sets the accent\n * without one, so a recolored brand never keeps the theme's old wash.\n */\nfunction mergeTints(\n base: PaletteOptions | undefined,\n overrides: PaletteOptions | undefined,\n): PaletteOptions['tint'] {\n if (!overrides?.tint && !overrides?.primary && !overrides?.secondary && !overrides?.tertiary) {\n return undefined\n }\n const tint: Record<string, string> = { ...base?.tint, ...overrides.tint }\n for (const key of ['primary', 'secondary', 'tertiary'] as const) {\n const main = (overrides[key] as { main?: unknown } | undefined)?.main\n if (overrides.tint?.[key] || typeof main !== 'string') continue\n try {\n tint[key] = accentTint(main, overrides.mode ?? base?.mode)\n } catch {\n // Not a color MUI can parse (a CSS variable): keep the inherited tint.\n }\n }\n return tint\n}\n\n/**\n * Converts a persisted {@link HostTheme} document into MUI `ThemeOptions`\n * for one color scheme. The result is meant to be passed through\n * `createResponsiveTheme` (or `createTheme`) by the consumer; shade and\n * contrast-text derivation for partial palettes is MUI's job, so only\n * explicitly set values are forwarded.\n */\nexport function hostThemeToThemeOptions(\n theme: HostTheme | undefined,\n scheme: HostThemeScheme,\n): ThemeOptions {\n const sanitized = sanitizeHostTheme(theme)\n const options: ThemeOptions = {\n palette: schemeColorsToPaletteOptions(\n scheme,\n sanitized.colorSchemes?.[scheme],\n ),\n }\n\n const { typography } = sanitized\n if (typography?.fontFamily || typography?.variants) {\n // HostThemeTypographyVariant is a sanitized subset of MUI's\n // TypographyStyleOptions; the missing index signature is by design.\n options.typography = {\n ...(typography.fontFamily && { fontFamily: typography.fontFamily }),\n ...typography.variants,\n } as ThemeOptions['typography']\n }\n\n if (typeof sanitized.shape?.borderRadius === 'number') {\n options.shape = { borderRadius: sanitized.shape.borderRadius }\n }\n if (typeof sanitized.spacing === 'number') {\n options.spacing = sanitized.spacing\n }\n if (sanitized.components) {\n // Ordered BEFORE conversion: an `sx` object ends up inside a closure,\n // where the pass over the finished options below cannot reach it.\n options.components = componentsToThemeOptions(\n orderMediaWidths(sanitized.components),\n )\n }\n // Toolbar height is only reachable here (AGL-1242). MUI derives the\n // Toolbar's `regular` variant style from `mixins.toolbar` and applies it\n // AFTER `components.MuiToolbar.styleOverrides`, so the slot override loses\n // every time — its nested media queries do not even emit.\n if (sanitized.mixins?.toolbar) {\n options.mixins = { toolbar: sanitized.mixins.toolbar }\n }\n\n // Every surface that renders a site's theme arrives here — the tenant's\n // provider, the besigner canvas, Preview, the theme editor — so this is\n // where a stored document stops being able to resolve differently for\n // different readers (AGL-3146).\n return orderMediaWidths(options)\n}\n\n/**\n * Layers a host's overrides ONTO a base set of theme options (AGL-1180).\n *\n * `hostThemeToThemeOptions` deliberately emits only what the host explicitly\n * set, so building a theme from it alone leaves every other slot to MUI's\n * stock palette. Consumers used to switch — console theme when the document\n * was empty, host document when it was not — which meant setting a single\n * value (the spec's own example is `{ spacing: 8 }`) silently repainted\n * secondary, tertiary, surface, info, success, warning, error, background\n * and paper in MUI blue/purple. Merging instead of switching keeps the brand\n * as the floor no matter how much the host customizes.\n *\n * `palette` merges one level deep so overriding `primary` cannot drop\n * `secondary`. Within a single colour the override replaces the whole record\n * — MUI derives shades and contrast text from `main`, which is exactly the\n * partial-palette behaviour the converter is written for.\n */\nexport function mergeThemeOptions(\n base: ThemeOptions,\n overrides: ThemeOptions,\n): ThemeOptions {\n const merged: ThemeOptions = { ...base, ...overrides }\n\n // Palette merges ONE level: overriding a colour replaces its whole record\n // so MUI re-derives light/dark/contrastText from the new `main`, which is\n // the partial-palette behaviour the converter is written for. Overriding\n // `primary` still must not disturb `secondary`, hence the level.\n merged.palette = { ...base.palette, ...overrides.palette }\n const tint = mergeTints(base.palette, overrides.palette)\n if (tint) merged.palette.tint = tint\n merged.shape = { ...base.shape, ...overrides.shape }\n // Same reasoning as `palette`: setting `toolbar` must not drop any other\n // mixin the base defines.\n merged.mixins = { ...base.mixins, ...overrides.mixins }\n\n // `typography` is an object in every base we ship, but MUI's type also\n // allows a function of the palette — merging into that would silently drop\n // the base, so prefer the override wholesale in that case.\n if (\n isPlainObject(base.typography) &&\n isPlainObject(overrides.typography)\n ) {\n merged.typography = objectDeepMergeReplaceArrays(\n base.typography,\n overrides.typography,\n ) as ThemeOptions['typography']\n }\n\n // Components merge DEEPLY. A host override names one component, and often\n // one property inside it — `MuiButton.defaultProps.color`. A shallow merge\n // would swap out the entire `MuiButton` entry and take the brand's\n // `styleOverrides` with it, and those styles are frequently FUNCTIONS of\n // the theme that JSON cannot express, so the editor could not put them\n // back even in principle. Deep-merging means you override the leaf you\n // named and inherit everything else, functions included.\n merged.components = objectDeepMergeReplaceArrays(\n base.components ?? {},\n overrides.components ?? {},\n ) as ThemeOptions['components']\n composeComponentStyles(merged.components, base.components, overrides.components)\n\n return merged\n}\n\ntype ComponentOptions = {\n styleOverrides?: Record<string, unknown>\n variants?: unknown[]\n}\n\n/** A slot's styles as a list MUI resolves in order (its `processStyle` flattens arrays). */\nfunction styleList(style: unknown): unknown[] {\n return Array.isArray(style) ? style : [style]\n}\n\n/**\n * Where a deep merge would REPLACE styling rather than add to it, composes\n * the two instead (AGL-3403).\n *\n * A deep merge merges two style OBJECTS key by key, which is right. But the\n * base styles several slots with a FUNCTION of the theme — `MuiButton.root`\n * carries the accent-text contrast fix — and merging an object onto a\n * function keeps only the object. So a theme that set one property on the\n * button root silently removed the fix, and nothing JSON can say could put it\n * back. MUI accepts a list per slot and applies it in order, so a function on\n * either side makes the slot `[base, override]`: the override still wins\n * every property it names, and everything else the base did still happens.\n *\n * `variants` concatenate for the same reason: the base's outlined\n * `IconButton` must not disappear because a theme styled its `small` size.\n */\nfunction composeComponentStyles(\n merged: ThemeOptions['components'],\n base: ThemeOptions['components'],\n overrides: ThemeOptions['components'],\n): void {\n if (!merged || !base || !overrides) return\n const target = merged as Record<string, ComponentOptions>\n for (const [name, override] of Object.entries(\n overrides as Record<string, ComponentOptions>,\n )) {\n const from = (base as Record<string, ComponentOptions>)[name]\n if (!from || !override) continue\n for (const [slot, style] of Object.entries(override.styleOverrides ?? {})) {\n const baseStyle = from.styleOverrides?.[slot]\n if (baseStyle === undefined) continue\n if (isPlainObject(baseStyle) && isPlainObject(style)) continue\n target[name].styleOverrides = {\n ...target[name].styleOverrides,\n [slot]: [...styleList(baseStyle), ...styleList(style)],\n }\n }\n if (Array.isArray(from.variants) && Array.isArray(override.variants)) {\n target[name].variants = [...from.variants, ...override.variants]\n }\n }\n}\n\n/** Plain data object — not an array, function, or class instance. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value)\n )\n}\n\n/** True when the document customizes anything, i.e. consumers should build a theme from it rather than using their default. */\nexport function hasHostTheme(theme: HostTheme | undefined): theme is HostTheme {\n return !!theme && Object.keys(theme).length > 0\n}\n\n/**\n * Builds a Google Fonts CSS2 stylesheet URL for the theme's loadable fonts.\n * Returns undefined when nothing needs loading (system/absent fonts).\n */\nexport function getGoogleFontsUrl(fonts: Array<HostThemeFont> | undefined) {\n const families = (fonts ?? [])\n .filter((font) => font.family && (font.source ?? 'google') === 'google')\n .map((font) => {\n const family = font.family.trim().replace(/\\s+/g, '+')\n const weights = font.weights?.length\n ? `:wght@${[...font.weights].sort((a, b) => a - b).join(';')}`\n : ''\n return `family=${family}${weights}`\n })\n if (!families.length) return undefined\n return `https://fonts.googleapis.com/css2?${families.join('&')}&display=swap`\n}\n\nexport default hostThemeToThemeOptions\n"],"names":["objectDeepMergeReplaceArrays","darken","lighten","HOST_THEME_COMPONENT_WHITELIST","componentWhitelist","Set","pickPaletteColor","color","main","undefined","picked","light","dark","contrastText","schemeColorsToPaletteOptions","scheme","colors","palette","mode","colorKeys","key","background","default","paper","text","primary","secondary","disabled","tint","tertiary","divider","sanitizeComponents","components","sanitized","override","Object","entries","has","entry","defaultProps","styleOverrides","sx","sanitizeSlotStyles","variants","sanitizeVariants","keys","length","slots","isPlainObject","kept","slot","style","Array","isArray","variant","props","values","some","value","includes","push","sxStyle","theme","unstable_sx","componentsToThemeOptions","converted","size","literal","map","context","sanitizeHostTheme","mixins","toolbar","darkScheme","MIN_WIDTH_AT_RULE","orderMediaWidths","moved","next","ordered","source","declarations","filter","blocks","widthSlots","widths","forEach","index","match","exec","width","Number","parseFloat","sorted","sort","a","b","position","order","child","accentTint","mergeTints","base","overrides","hostThemeToThemeOptions","options","colorSchemes","typography","fontFamily","shape","borderRadius","spacing","mergeThemeOptions","merged","composeComponentStyles","styleList","target","name","from","baseStyle","hasHostTheme","getGoogleFontsUrl","fonts","families","font","family","trim","replace","weights","join"],"mappings":"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"}
|
|
@@ -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;
|