@aglyn/shared-ui-theme 1.0.0-beta.143

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.
Files changed (78) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/package.json +51 -0
  4. package/src/index.d.ts +34 -0
  5. package/src/index.js +50 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/host-theme-provider.d.ts +84 -0
  8. package/src/lib/components/host-theme-provider.js +146 -0
  9. package/src/lib/components/host-theme-provider.js.map +1 -0
  10. package/src/lib/components/theme-css-var-provider.d.ts +44 -0
  11. package/src/lib/components/theme-css-var-provider.js +79 -0
  12. package/src/lib/components/theme-css-var-provider.js.map +1 -0
  13. package/src/lib/console.theme.d.ts +34 -0
  14. package/src/lib/console.theme.js +598 -0
  15. package/src/lib/console.theme.js.map +1 -0
  16. package/src/lib/constants.d.ts +72 -0
  17. package/src/lib/constants.js +85 -0
  18. package/src/lib/constants.js.map +1 -0
  19. package/src/lib/hocs/create-with-emotion-client-cache.d.ts +30 -0
  20. package/src/lib/hocs/create-with-emotion-client-cache.js +53 -0
  21. package/src/lib/hocs/create-with-emotion-client-cache.js.map +1 -0
  22. package/src/lib/hocs/create-with-theme-provider.d.ts +86 -0
  23. package/src/lib/hocs/create-with-theme-provider.js +231 -0
  24. package/src/lib/hocs/create-with-theme-provider.js.map +1 -0
  25. package/src/lib/tenant.theme.d.ts +58 -0
  26. package/src/lib/tenant.theme.js +282 -0
  27. package/src/lib/tenant.theme.js.map +1 -0
  28. package/src/lib/theme.types.d.ts +26 -0
  29. package/src/lib/theme.types.js +18 -0
  30. package/src/lib/theme.types.js.map +1 -0
  31. package/src/lib/util/accent-text.d.ts +187 -0
  32. package/src/lib/util/accent-text.js +277 -0
  33. package/src/lib/util/accent-text.js.map +1 -0
  34. package/src/lib/util/accessible-shade.d.ts +61 -0
  35. package/src/lib/util/accessible-shade.js +117 -0
  36. package/src/lib/util/accessible-shade.js.map +1 -0
  37. package/src/lib/util/color-scheme-hint.d.ts +68 -0
  38. package/src/lib/util/color-scheme-hint.js +69 -0
  39. package/src/lib/util/color-scheme-hint.js.map +1 -0
  40. package/src/lib/util/create-responsive-theme.d.ts +71 -0
  41. package/src/lib/util/create-responsive-theme.js +287 -0
  42. package/src/lib/util/create-responsive-theme.js.map +1 -0
  43. package/src/lib/util/emotion-cache.d.ts +81 -0
  44. package/src/lib/util/emotion-cache.js +79 -0
  45. package/src/lib/util/emotion-cache.js.map +1 -0
  46. package/src/lib/util/generate-component-class-keys.d.ts +41 -0
  47. package/src/lib/util/generate-component-class-keys.js +53 -0
  48. package/src/lib/util/generate-component-class-keys.js.map +1 -0
  49. package/src/lib/util/host-theme.d.ts +92 -0
  50. package/src/lib/util/host-theme.js +340 -0
  51. package/src/lib/util/host-theme.js.map +1 -0
  52. package/src/lib/util/layered-emotion-cache.d.ts +78 -0
  53. package/src/lib/util/layered-emotion-cache.js +90 -0
  54. package/src/lib/util/layered-emotion-cache.js.map +1 -0
  55. package/src/lib/util/merge-sx-props.d.ts +22 -0
  56. package/src/lib/util/merge-sx-props.js +27 -0
  57. package/src/lib/util/merge-sx-props.js.map +1 -0
  58. package/src/lib/util/scheme-route-segment.d.ts +95 -0
  59. package/src/lib/util/scheme-route-segment.js +101 -0
  60. package/src/lib/util/scheme-route-segment.js.map +1 -0
  61. package/src/lib/util/theme-editor-defaults.d.ts +41 -0
  62. package/src/lib/util/theme-editor-defaults.js +49 -0
  63. package/src/lib/util/theme-editor-defaults.js.map +1 -0
  64. package/src/lib/util/theme-editor-fields.d.ts +315 -0
  65. package/src/lib/util/theme-editor-fields.js +714 -0
  66. package/src/lib/util/theme-editor-fields.js.map +1 -0
  67. package/src/lib/util/theme-mode-cookie.d.ts +48 -0
  68. package/src/lib/util/theme-mode-cookie.js +48 -0
  69. package/src/lib/util/theme-mode-cookie.js.map +1 -0
  70. package/src/vendor/emotion.d.ts +18 -0
  71. package/src/vendor/emotion.js +22 -0
  72. package/src/vendor/emotion.js.map +1 -0
  73. package/src/vendor/jss.d.ts +18 -0
  74. package/src/vendor/jss.js +20 -0
  75. package/src/vendor/jss.js.map +1 -0
  76. package/src/vendor/mui.d.ts +282 -0
  77. package/src/vendor/mui.js +20 -0
  78. package/src/vendor/mui.js.map +1 -0
@@ -0,0 +1,231 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * @license
5
+ * Copyright 2023 Aglyn LLC
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */ import { _isArr, _isNull } from "@aglyn/shared-util-tools";
19
+ import { getDisplayName, noop } from "@aglyn/shared-util-tools";
20
+ import { hoistNonReactStatics } from "@aglyn/shared-util-vendor";
21
+ import { CssBaseline, useMediaQuery } from "@mui/material";
22
+ import Cookies from "js-cookie";
23
+ import { createContext, forwardRef, useCallback, useContext, useMemo, useState } from "react";
24
+ import { useIsomorphicLayoutEffect } from "react-use";
25
+ import { createTheme, ThemeProvider } from "../../vendor/mui.js";
26
+ import { parseThemeModeCookie, THEME_MODE_COOKIE } from "../util/theme-mode-cookie.js";
27
+ export const THEME_DISPLAY_NAME = {
28
+ light: 'Light',
29
+ dark: 'Dark',
30
+ system: 'Device default'
31
+ };
32
+ export const getThemeModeDisplayName = (theme)=>{
33
+ return THEME_DISPLAY_NAME[theme] || THEME_DISPLAY_NAME.system;
34
+ };
35
+ /**
36
+ * The cookie the visitor's choice is persisted in, spelled out as a type so
37
+ * the name has one definition across the browser reader here and the
38
+ * server-side one in `util/theme-mode-cookie`: the annotation fails to compile
39
+ * if the two ever name different cookies.
40
+ */ export const COOKIE_THEME_KEY = THEME_MODE_COOKIE;
41
+ export const ThemeContextDispatch = /*#__PURE__*/ createContext([
42
+ [
43
+ 'system',
44
+ 'system'
45
+ ],
46
+ noop,
47
+ 'system'
48
+ ]);
49
+ export function useThemeMode() {
50
+ return useContext(ThemeContextDispatch);
51
+ }
52
+ /**
53
+ * The stored choice as this browser reads it.
54
+ *
55
+ * `js-cookie` reads `document.cookie`, so on a server render there is nothing
56
+ * to read and every visitor looks like one who has chosen nothing. That is why
57
+ * a server-rendered surface passes the mode it resolved from the request's
58
+ * cookies into {@link useCookieThemeMode} instead of relying on this.
59
+ */ function getCookieThemeMode() {
60
+ return parseThemeModeCookie(Cookies.get(COOKIE_THEME_KEY));
61
+ }
62
+ /**
63
+ * The visitor's stored light/dark choice, and a setter that persists it.
64
+ *
65
+ * `initialMode` is the mode the surface's server render resolved from the
66
+ * request's cookies — `null` when the request carried no choice. Supplying it
67
+ * is what makes the FIRST render right: the state starts at the visitor's
68
+ * choice on the server and at the identical value through hydration, rather
69
+ * than starting empty and being corrected by the effect below once React has
70
+ * hydrated the whole page. Omit it on a surface that only ever renders in a
71
+ * browser, and the cookie is read directly.
72
+ */ export function useCookieThemeMode(initialMode) {
73
+ const [mode, setMode] = useState(()=>initialMode === undefined ? getCookieThemeMode() : initialMode);
74
+ /**
75
+ * Keeps the state in step with the cookie on each paint — another tab's
76
+ * switcher, or a seed that went stale between the server render and
77
+ * hydration. Browser-only: effects do not run during a server render, so
78
+ * this never overwrites the seed with the server's empty read.
79
+ */ useIsomorphicLayoutEffect(()=>{
80
+ const cookieMode = getCookieThemeMode();
81
+ setMode((prev)=>prev !== cookieMode ? cookieMode : prev);
82
+ });
83
+ const setCookieThemeMode = useCallback((newMode)=>{
84
+ Cookies.set(COOKIE_THEME_KEY, newMode, {
85
+ expires: 365
86
+ });
87
+ setMode((prev)=>prev !== newMode ? newMode : prev);
88
+ }, []);
89
+ return useMemo(()=>[
90
+ mode,
91
+ setCookieThemeMode
92
+ ], [
93
+ mode,
94
+ setCookieThemeMode
95
+ ]);
96
+ }
97
+ /**
98
+ * The resolved theme mode, in two layers with the visitor's own choice on top.
99
+ *
100
+ * An EXPLICIT choice comes from the cookie, which a server render can read
101
+ * from the request — pass it as `initialMode` and the surface paints that
102
+ * scheme in its first byte. It outranks the device: a visitor who asked for
103
+ * light gets light on a dark laptop, and the ordering below is the only thing
104
+ * that decides it, so the two layers are kept as separate inputs rather than
105
+ * collapsed into one seed by the caller.
106
+ *
107
+ * DEVICE DEFAULT is the other layer. `prefers-color-scheme` is a media
108
+ * feature, so a server has nothing to evaluate it against and `useMediaQuery`
109
+ * answers light for every server render. `initialDeviceMode` supplies that
110
+ * answer from the request instead — the `Sec-CH-Prefers-Color-Scheme` client
111
+ * hint, where the browser sends one.
112
+ *
113
+ * It is fed in as `defaultMatches` rather than substituted for the media query
114
+ * because that is the value React reads for BOTH the server render and the
115
+ * hydration render: `useMediaQuery` publishes `defaultMatches` as its server
116
+ * snapshot and only switches to the live `matchMedia` result once hydration is
117
+ * complete. Seeding any earlier layer instead leaves the media query answering
118
+ * light for the hydration commit, and the tree repaints light before the real
119
+ * snapshot arrives — the flash this exists to remove, moved one frame later.
120
+ */ export function useThemeModeState(initialMode, initialDeviceMode) {
121
+ const prefersDark = useMediaQuery('(prefers-color-scheme: dark)', {
122
+ defaultMatches: initialDeviceMode === 'dark'
123
+ });
124
+ const [cookieMode, setCookieMode] = useCookieThemeMode(initialMode);
125
+ const systemMode = useMemo(()=>{
126
+ return prefersDark ? 'dark' : 'light';
127
+ }, [
128
+ prefersDark
129
+ ]);
130
+ const [type, mode] = useMemo(()=>{
131
+ if (cookieMode && cookieMode !== 'system') {
132
+ return [
133
+ 'user',
134
+ cookieMode
135
+ ];
136
+ }
137
+ return [
138
+ 'system',
139
+ systemMode
140
+ ];
141
+ }, [
142
+ cookieMode,
143
+ systemMode
144
+ ]);
145
+ const toggleThemeMode = useCallback((event, to)=>{
146
+ let newMode;
147
+ switch(true){
148
+ case _isNull(to):
149
+ case to === 'system':
150
+ newMode = 'system';
151
+ break;
152
+ case to === 'dark':
153
+ case to === 'light':
154
+ newMode = to;
155
+ break;
156
+ case _isNull(cookieMode):
157
+ newMode = 'light';
158
+ break;
159
+ case cookieMode === 'light':
160
+ newMode = 'dark';
161
+ break;
162
+ case cookieMode === 'dark':
163
+ default:
164
+ newMode = 'system';
165
+ break;
166
+ }
167
+ setCookieMode(newMode);
168
+ }, [
169
+ cookieMode,
170
+ setCookieMode
171
+ ]);
172
+ return useMemo(()=>[
173
+ [
174
+ type,
175
+ mode
176
+ ],
177
+ toggleThemeMode,
178
+ cookieMode
179
+ ], [
180
+ type,
181
+ mode,
182
+ toggleThemeMode,
183
+ cookieMode
184
+ ]);
185
+ }
186
+ export function createWithThemeProvider(options) {
187
+ const { theme, disableCssBaseline } = options;
188
+ const [lightTheme, darkTheme] = !_isArr(theme) ? [
189
+ theme,
190
+ createTheme(_extends({}, theme, {
191
+ palette: _extends({}, theme == null ? void 0 : theme.palette, {
192
+ mode: 'dark'
193
+ })
194
+ }))
195
+ ] : theme;
196
+ return function withThemeProvider(WrappedComponent) {
197
+ const WithThemeProvider = /*#__PURE__*/ forwardRef((props, ref)=>{
198
+ const {} = props, rest = _extends({}, props);
199
+ const ThemeModeState = useThemeModeState();
200
+ const activeTheme = useMemo(()=>{
201
+ const [[, themeMode]] = ThemeModeState;
202
+ return themeMode === 'dark' ? darkTheme : lightTheme;
203
+ }, [
204
+ ThemeModeState
205
+ ]);
206
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
207
+ const WrappedAny = WrappedComponent;
208
+ return /*#__PURE__*/ _jsx(ThemeContextDispatch.Provider, {
209
+ value: ThemeModeState,
210
+ children: /*#__PURE__*/ _jsx(ThemeProvider, {
211
+ theme: activeTheme,
212
+ children: disableCssBaseline ? /*#__PURE__*/ _jsx(WrappedAny, _extends({
213
+ ref: ref
214
+ }, rest)) : /*#__PURE__*/ _jsx(CssBaseline, {
215
+ enableColorScheme: true,
216
+ children: /*#__PURE__*/ _jsx(WrappedAny, _extends({
217
+ ref: ref
218
+ }, rest))
219
+ })
220
+ })
221
+ });
222
+ });
223
+ const displayName = getDisplayName(WrappedComponent);
224
+ WithThemeProvider.displayName = `WithThemeProvider(${displayName})`;
225
+ hoistNonReactStatics(WithThemeProvider, WrappedComponent);
226
+ return WithThemeProvider;
227
+ };
228
+ }
229
+ export default createWithThemeProvider;
230
+
231
+ //# sourceMappingURL=create-with-theme-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/hocs/create-with-theme-provider.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2023 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 { _isArr, _isNull } from '@aglyn/shared-util-tools'\nimport { getDisplayName, noop } from '@aglyn/shared-util-tools'\nimport { hoistNonReactStatics } from '@aglyn/shared-util-vendor'\nimport { CssBaseline, useMediaQuery } from '@mui/material'\nimport Cookies from 'js-cookie'\nimport {\n type ComponentType,\n createContext,\n forwardRef,\n type SyntheticEvent,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react'\nimport { useIsomorphicLayoutEffect } from 'react-use'\nimport { createTheme, type Theme, ThemeProvider } from '../../vendor/mui'\nimport {\n parseThemeModeCookie,\n THEME_MODE_COOKIE,\n} from '../util/theme-mode-cookie'\n\nexport type ThemeMode = 'light' | 'dark' | 'system' | null\nexport type ThemeModeType = 'user' | 'system'\nexport type ThemeModeResult = [ThemeModeType, ThemeMode]\nexport type UseThemeMode = [\n mode: ThemeModeResult,\n toggleThemeMode: (event: SyntheticEvent<any>, to?: ThemeMode) => void,\n cookieMode: ThemeMode,\n /**\n * False when the surface renders light whatever the visitor asks for — a\n * site whose theme has no authored dark scheme (AGL-1292). Absent means\n * the surface can go dark; the console's own provider never sets it.\n */\n canGoDark?: boolean,\n]\n\nexport const THEME_DISPLAY_NAME: Record<ThemeMode, string> = {\n light: 'Light',\n dark: 'Dark',\n system: 'Device default',\n}\n\nexport const getThemeModeDisplayName = (theme: ThemeMode) => {\n return THEME_DISPLAY_NAME[theme] || THEME_DISPLAY_NAME.system\n}\n\n/**\n * The cookie the visitor's choice is persisted in, spelled out as a type so\n * the name has one definition across the browser reader here and the\n * server-side one in `util/theme-mode-cookie`: the annotation fails to compile\n * if the two ever name different cookies.\n */\nexport const COOKIE_THEME_KEY: 'theme-color-mode' = THEME_MODE_COOKIE\nexport const ThemeContextDispatch = createContext<UseThemeMode>([\n ['system', 'system'],\n noop,\n 'system',\n])\n\nexport function useThemeMode() {\n return useContext(ThemeContextDispatch)\n}\n\n/**\n * The stored choice as this browser reads it.\n *\n * `js-cookie` reads `document.cookie`, so on a server render there is nothing\n * to read and every visitor looks like one who has chosen nothing. That is why\n * a server-rendered surface passes the mode it resolved from the request's\n * cookies into {@link useCookieThemeMode} instead of relying on this.\n */\nfunction getCookieThemeMode(): ThemeMode {\n return parseThemeModeCookie(Cookies.get(COOKIE_THEME_KEY))\n}\n\n/**\n * The visitor's stored light/dark choice, and a setter that persists it.\n *\n * `initialMode` is the mode the surface's server render resolved from the\n * request's cookies — `null` when the request carried no choice. Supplying it\n * is what makes the FIRST render right: the state starts at the visitor's\n * choice on the server and at the identical value through hydration, rather\n * than starting empty and being corrected by the effect below once React has\n * hydrated the whole page. Omit it on a surface that only ever renders in a\n * browser, and the cookie is read directly.\n */\nexport function useCookieThemeMode(\n initialMode?: ThemeMode,\n): [ThemeMode, (mode: ThemeMode) => void] {\n const [mode, setMode] = useState<ThemeMode>(() =>\n initialMode === undefined ? getCookieThemeMode() : initialMode,\n )\n\n /**\n * Keeps the state in step with the cookie on each paint — another tab's\n * switcher, or a seed that went stale between the server render and\n * hydration. Browser-only: effects do not run during a server render, so\n * this never overwrites the seed with the server's empty read.\n */\n useIsomorphicLayoutEffect(() => {\n const cookieMode = getCookieThemeMode()\n setMode((prev) => (prev !== cookieMode ? cookieMode : prev))\n })\n\n const setCookieThemeMode = useCallback((newMode: ThemeMode) => {\n Cookies.set(COOKIE_THEME_KEY, newMode, { expires: 365 })\n setMode((prev) => (prev !== newMode ? newMode : prev))\n }, [])\n\n return useMemo(() => [mode, setCookieThemeMode], [mode, setCookieThemeMode])\n}\n\n/**\n * The resolved theme mode, in two layers with the visitor's own choice on top.\n *\n * An EXPLICIT choice comes from the cookie, which a server render can read\n * from the request — pass it as `initialMode` and the surface paints that\n * scheme in its first byte. It outranks the device: a visitor who asked for\n * light gets light on a dark laptop, and the ordering below is the only thing\n * that decides it, so the two layers are kept as separate inputs rather than\n * collapsed into one seed by the caller.\n *\n * DEVICE DEFAULT is the other layer. `prefers-color-scheme` is a media\n * feature, so a server has nothing to evaluate it against and `useMediaQuery`\n * answers light for every server render. `initialDeviceMode` supplies that\n * answer from the request instead — the `Sec-CH-Prefers-Color-Scheme` client\n * hint, where the browser sends one.\n *\n * It is fed in as `defaultMatches` rather than substituted for the media query\n * because that is the value React reads for BOTH the server render and the\n * hydration render: `useMediaQuery` publishes `defaultMatches` as its server\n * snapshot and only switches to the live `matchMedia` result once hydration is\n * complete. Seeding any earlier layer instead leaves the media query answering\n * light for the hydration commit, and the tree repaints light before the real\n * snapshot arrives — the flash this exists to remove, moved one frame later.\n */\nexport function useThemeModeState(\n initialMode?: ThemeMode,\n initialDeviceMode?: ThemeMode,\n): UseThemeMode {\n const prefersDark = useMediaQuery('(prefers-color-scheme: dark)', {\n defaultMatches: initialDeviceMode === 'dark',\n })\n const [cookieMode, setCookieMode] = useCookieThemeMode(initialMode)\n\n const systemMode = useMemo<ThemeMode>(() => {\n return prefersDark ? 'dark' : 'light'\n }, [prefersDark])\n\n const [type, mode] = useMemo<ThemeModeResult>(() => {\n if (cookieMode && cookieMode !== 'system') {\n return ['user', cookieMode]\n }\n return ['system', systemMode]\n }, [cookieMode, systemMode])\n\n const toggleThemeMode = useCallback(\n (event: SyntheticEvent<any>, to?: ThemeMode) => {\n let newMode: ThemeMode\n\n switch (true) {\n case _isNull(to):\n case to === 'system':\n newMode = 'system'\n break\n case to === 'dark':\n case to === 'light':\n newMode = to\n break\n case _isNull(cookieMode):\n newMode = 'light'\n break\n case cookieMode === 'light':\n newMode = 'dark'\n break\n case cookieMode === 'dark':\n default:\n newMode = 'system'\n break\n }\n\n setCookieMode(newMode)\n },\n [cookieMode, setCookieMode],\n )\n\n return useMemo(\n () => [[type, mode], toggleThemeMode, cookieMode],\n [type, mode, toggleThemeMode, cookieMode],\n )\n}\n\nexport type WithThemeProviderOptions = {\n theme: Theme | [lightTheme: Theme, darkTheme: Theme]\n disableCssBaseline?: boolean\n}\n\nexport function createWithThemeProvider(options: WithThemeProviderOptions) {\n const { theme, disableCssBaseline } = options\n const [lightTheme, darkTheme] = !_isArr(theme)\n ? [\n theme,\n createTheme({ ...theme, palette: { ...theme?.palette, mode: 'dark' } }),\n ]\n : theme\n\n return function withThemeProvider<P>(WrappedComponent: JSX.ComponentType<P>) {\n const WithThemeProvider = forwardRef<any, P>((props, ref) => {\n const { ...rest } = props\n const ThemeModeState = useThemeModeState()\n const activeTheme = useMemo<Theme>(() => {\n const [[, themeMode]] = ThemeModeState\n return themeMode === 'dark' ? darkTheme : lightTheme\n }, [ThemeModeState])\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const WrappedAny = WrappedComponent as ComponentType<any>\n return (\n <ThemeContextDispatch.Provider value={ThemeModeState}>\n <ThemeProvider theme={activeTheme}>\n {disableCssBaseline ? (\n <WrappedAny ref={ref} {...rest} />\n ) : (\n <CssBaseline enableColorScheme>\n <WrappedAny ref={ref} {...rest} />\n </CssBaseline>\n )}\n </ThemeProvider>\n </ThemeContextDispatch.Provider>\n )\n })\n const displayName = getDisplayName(WrappedComponent)\n WithThemeProvider.displayName = `WithThemeProvider(${displayName})`\n hoistNonReactStatics(WithThemeProvider, WrappedComponent)\n\n return WithThemeProvider\n }\n}\nexport default createWithThemeProvider\n"],"names":["_isArr","_isNull","getDisplayName","noop","hoistNonReactStatics","CssBaseline","useMediaQuery","Cookies","createContext","forwardRef","useCallback","useContext","useMemo","useState","useIsomorphicLayoutEffect","createTheme","ThemeProvider","parseThemeModeCookie","THEME_MODE_COOKIE","THEME_DISPLAY_NAME","light","dark","system","getThemeModeDisplayName","theme","COOKIE_THEME_KEY","ThemeContextDispatch","useThemeMode","getCookieThemeMode","get","useCookieThemeMode","initialMode","mode","setMode","undefined","cookieMode","prev","setCookieThemeMode","newMode","set","expires","useThemeModeState","initialDeviceMode","prefersDark","defaultMatches","setCookieMode","systemMode","type","toggleThemeMode","event","to","createWithThemeProvider","options","disableCssBaseline","lightTheme","darkTheme","palette","withThemeProvider","WrappedComponent","WithThemeProvider","props","ref","rest","ThemeModeState","activeTheme","themeMode","WrappedAny","Provider","value","enableColorScheme","displayName"],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { Theme, ThemeOptions } from '../vendor/mui';
18
+ export declare const tenantOptions: ThemeOptions;
19
+ export declare const tenantOptionsDark: ThemeOptions;
20
+ export declare const tenantThemeLight: Theme;
21
+ export declare const tenantThemeDark: Theme;
22
+ export declare const PLATFORM_BRAND_HOSTS: ReadonlySet<string>;
23
+ export declare function wearsPlatformBrand(host: string | undefined): boolean;
24
+ /**
25
+ * The site key {@link wearsPlatformBrand} is asked about, read off a host
26
+ * document (AGL-3068).
27
+ *
28
+ * The tenant is handed its `[host]` route param, which is the attached domain
29
+ * under the middleware's `cname--` sentinel or a bare subdomain label. A
30
+ * console surface has the document instead, where those are two fields: the
31
+ * attached domain is what a platform-brand host is listed under, and the
32
+ * subdomain is what every other site is reachable by. Reading them in that
33
+ * order is what makes the editor ask the same question the published page
34
+ * answers.
35
+ */
36
+ export declare function hostBrandKey(host: {
37
+ cname?: string;
38
+ subdomain?: string;
39
+ } | null | undefined): string | undefined;
40
+ /**
41
+ * The options a site's own theme is layered onto: the platform's brand for
42
+ * the operator's own hosts, the neutral tenant default for everyone else
43
+ * (AGL-3068).
44
+ *
45
+ * One decision, because it used to be four. The tenant made it per host,
46
+ * while the besigner canvas, the theme editor's preview and the "Default ·
47
+ * #…" the editor prints beside every unset slot all built on the brand base
48
+ * unconditionally — so a customer site was authored in Aglyn's cyan and
49
+ * published in MUI's blue, and an author correcting contrast was correcting a
50
+ * page that does not exist.
51
+ *
52
+ * An unknown host is NOT a platform host, exactly as `wearsPlatformBrand`
53
+ * already reads it: the operator's hosts are the named exception, so a
54
+ * surface that cannot name the site resolves what a customer site resolves.
55
+ */
56
+ export declare function siteBaseOptions(host: string | undefined, scheme: 'light' | 'dark'): ThemeOptions;
57
+ /** The built theme a site with no customization of its own renders under. */
58
+ export declare function siteFallbackTheme(host: string | undefined, scheme: 'light' | 'dark'): Theme;
@@ -0,0 +1,282 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ var // Dot notation, not brackets: Next substitutes `process.env.NAME`
18
+ // TEXTUALLY, and never the bracket form, so a bracket read is `undefined`
19
+ // in any browser or edge bundle and silently falls back to the default
20
+ // below — which on a self-host install would hand the operator Aglyn's
21
+ // hosts (AGL-2037).
22
+ _process_env_NEXT_PUBLIC_PLATFORM_BRAND_HOSTS;
23
+ import { consoleOptions, consoleOptionsDark, consoleThemeDark, consoleThemeLight } from "./console.theme.js";
24
+ import createResponsiveTheme from "./util/create-responsive-theme.js";
25
+ /**
26
+ * The default palette a tenant site wears when it has authored none of its
27
+ * own.
28
+ *
29
+ * MUI's stock accents, extended with every slot this platform adds on top of
30
+ * a MUI palette — `tertiary`, `surface`, `tint`, `inputOutline` and the three
31
+ * `svg*` records — so a site that never opens the theme editor still resolves
32
+ * every token a component can ask for.
33
+ *
34
+ * Stock rather than the Aglyn brand because the brand palette carries a
35
+ * signed-off sub-AA pairing (white on `#00b0ff`, 2.43:1): a considered
36
+ * decision about OUR brand, and not one to hand a customer who never made it.
37
+ * Every accent here clears the 4.5:1 AA text bar as text and carries its own
38
+ * `contrastText` at AA on the fill, in both schemes.
39
+ *
40
+ * Non-palette options come from `consoleOptions`, so component behaviour,
41
+ * type ramp, spacing and shadows are identical platform-wide — the palette is
42
+ * the only thing a tenant default changes.
43
+ */ /**
44
+ * The one grey ramp, read from the brand palette rather than copied. A
45
+ * neutral scale is not a brand decision, and a second copy is a second thing
46
+ * to drift.
47
+ */ const grey = consoleOptions.palette.grey;
48
+ const tenantColorScheme = {
49
+ light: {
50
+ primary: {
51
+ main: '#1976d2',
52
+ // The accent-as-text shade (`ACCENT_TEXT_SHADE`): 7.50:1 on the page,
53
+ // 7.82:1 on paper. Authored for the same reason as the console's — the
54
+ // slot every link resolves to belongs in the palette, not in a walk.
55
+ dark: '#125393',
56
+ contrastText: '#FFFFFF'
57
+ },
58
+ secondary: {
59
+ main: '#9c27b0',
60
+ dark: '#6d1b7b',
61
+ contrastText: '#FFFFFF'
62
+ },
63
+ // A neutral slate for the third accent, matching the role `tertiary`
64
+ // plays in the console palette.
65
+ tertiary: {
66
+ main: '#4a5568',
67
+ dark: '#343b49',
68
+ contrastText: '#FFFFFF'
69
+ },
70
+ surface: {
71
+ main: '#f1f3f5',
72
+ contrastText: '#000000DE'
73
+ },
74
+ tint: {
75
+ primary: '#e8f1fb',
76
+ secondary: '#f5e9f7',
77
+ tertiary: '#eef0f3'
78
+ },
79
+ inputOutline: 'rgba(0, 0, 0, 0.23)',
80
+ background: {
81
+ default: '#fafafa',
82
+ paper: '#ffffff'
83
+ },
84
+ info: {
85
+ main: '#0288d1',
86
+ dark: '#015f92',
87
+ contrastText: '#000000DE'
88
+ },
89
+ error: {
90
+ main: '#d32f2f',
91
+ dark: '#942121',
92
+ contrastText: '#FFFFFF'
93
+ },
94
+ success: {
95
+ main: '#2e7d32',
96
+ dark: '#205823',
97
+ contrastText: '#FFFFFF'
98
+ },
99
+ warning: {
100
+ main: '#ed6c02',
101
+ dark: '#a64c01',
102
+ contrastText: '#000000DE'
103
+ },
104
+ grey,
105
+ svgBackground: {
106
+ main: '#FAFAFA',
107
+ hover: '#FAFAFA',
108
+ active: '#FAFAFA',
109
+ focus: '#FAFAFA'
110
+ },
111
+ svgFilled: {
112
+ main: '#9E9E9E',
113
+ hover: '#1976d2',
114
+ active: '#1976d2',
115
+ focus: '#1976d2'
116
+ },
117
+ svgStroke: {
118
+ main: '#FFFFFF',
119
+ hover: '#FFFFFF',
120
+ active: '#FFFFFF',
121
+ focus: '#FFFFFF'
122
+ }
123
+ },
124
+ dark: {
125
+ primary: {
126
+ main: '#1976d2',
127
+ // LIGHTER than `main`, as every accent-as-text shade must be on a dark
128
+ // ground: 6.70:1 on the page, 5.96:1 on paper. MUI derives `dark` by
129
+ // darkening in both schemes, which points the wrong way here.
130
+ dark: '#5e9fe0',
131
+ contrastText: '#FFFFFF'
132
+ },
133
+ secondary: {
134
+ main: '#9c27b0',
135
+ dark: '#ba68c8',
136
+ contrastText: '#FFFFFF'
137
+ },
138
+ // Lifted from the light scheme's slate: `#4a5568` is 1.9:1 against this
139
+ // page and would be a third accent nobody can read.
140
+ tertiary: {
141
+ main: '#94a3b8',
142
+ dark: '#b4bfcd',
143
+ contrastText: '#000000DE'
144
+ },
145
+ surface: {
146
+ main: '#242c35',
147
+ contrastText: '#FFFFFF'
148
+ },
149
+ // Tile fills, held around 1.5:1 from the page so a tinted tile has a
150
+ // visible edge without a border.
151
+ tint: {
152
+ primary: '#1e3a5c',
153
+ secondary: '#472653',
154
+ tertiary: '#2e3742'
155
+ },
156
+ inputOutline: 'rgba(255, 255, 255, 0.23)',
157
+ background: {
158
+ default: '#121212',
159
+ paper: '#1e1e1e'
160
+ },
161
+ info: {
162
+ main: '#0288d1',
163
+ dark: '#4eacdf',
164
+ contrastText: '#000000DE'
165
+ },
166
+ error: {
167
+ main: '#d32f2f',
168
+ dark: '#e06d6d',
169
+ contrastText: '#FFFFFF'
170
+ },
171
+ success: {
172
+ main: '#2e7d32',
173
+ dark: '#6da470',
174
+ contrastText: '#FFFFFF'
175
+ },
176
+ warning: {
177
+ main: '#ed6c02',
178
+ dark: '#f2984e',
179
+ contrastText: '#000000DE'
180
+ },
181
+ grey,
182
+ svgBackground: {
183
+ main: '#242c35',
184
+ hover: '#242c35',
185
+ active: '#242c35',
186
+ focus: '#242c35'
187
+ },
188
+ svgFilled: {
189
+ main: '#9E9E9E',
190
+ hover: '#5e9fe0',
191
+ active: '#5e9fe0',
192
+ focus: '#5e9fe0'
193
+ },
194
+ svgStroke: {
195
+ main: '#FFFFFF',
196
+ hover: '#FFFFFF',
197
+ active: '#FFFFFF',
198
+ focus: '#FFFFFF'
199
+ }
200
+ }
201
+ };
202
+ export const tenantOptions = _extends({}, consoleOptions, {
203
+ palette: _extends({
204
+ mode: 'light'
205
+ }, tenantColorScheme.light)
206
+ });
207
+ export const tenantOptionsDark = _extends({}, consoleOptionsDark, {
208
+ palette: _extends({
209
+ mode: 'dark'
210
+ }, tenantColorScheme.dark)
211
+ });
212
+ export const tenantThemeLight = createResponsiveTheme({
213
+ themeOptions: _extends({}, tenantOptions)
214
+ });
215
+ export const tenantThemeDark = createResponsiveTheme({
216
+ themeOptions: _extends({}, tenantOptionsDark)
217
+ });
218
+ /**
219
+ * Hosts whose brand IS the operator's own brand, and which therefore keep
220
+ * `consoleOptions` rather than the tenant default.
221
+ *
222
+ * Comma-separated in `NEXT_PUBLIC_PLATFORM_BRAND_HOSTS`, so a self-host
223
+ * operator points it at their own marketing domain and their customers still
224
+ * resolve the neutral tenant palette. The literal is the `??` default and
225
+ * nothing else reads it: the platform's own deployment needs no variable to
226
+ * keep its brand. Setting the variable to an empty string puts every host,
227
+ * including the operator's own, on the tenant default.
228
+ *
229
+ * Matched on the registrable domain, after stripping the `cname--` prefix the
230
+ * tenant middleware puts on a CUSTOM DOMAIN before it becomes the `[host]`
231
+ * route segment: the param this is handed reads `cname--example.com`, never
232
+ * the bare apex. A platform subdomain resolves to a bare label instead
233
+ * (`acme` for `acme.aglyn.app`), which correctly matches nothing — a customer
234
+ * on a platform subdomain is still a tenant.
235
+ */ const CNAME_PREFIX = 'cname--';
236
+ export const PLATFORM_BRAND_HOSTS = new Set(((_process_env_NEXT_PUBLIC_PLATFORM_BRAND_HOSTS = process.env.NEXT_PUBLIC_PLATFORM_BRAND_HOSTS) != null ? _process_env_NEXT_PUBLIC_PLATFORM_BRAND_HOSTS : 'aglyn.com,aglyn.io').split(',').map((host)=>host.trim().toLowerCase()).filter(Boolean));
237
+ export function wearsPlatformBrand(host) {
238
+ if (!host) return false;
239
+ const normalized = host.trim().toLowerCase();
240
+ return PLATFORM_BRAND_HOSTS.has(normalized.startsWith(CNAME_PREFIX) ? normalized.slice(CNAME_PREFIX.length) : normalized);
241
+ }
242
+ /**
243
+ * The site key {@link wearsPlatformBrand} is asked about, read off a host
244
+ * document (AGL-3068).
245
+ *
246
+ * The tenant is handed its `[host]` route param, which is the attached domain
247
+ * under the middleware's `cname--` sentinel or a bare subdomain label. A
248
+ * console surface has the document instead, where those are two fields: the
249
+ * attached domain is what a platform-brand host is listed under, and the
250
+ * subdomain is what every other site is reachable by. Reading them in that
251
+ * order is what makes the editor ask the same question the published page
252
+ * answers.
253
+ */ export function hostBrandKey(host) {
254
+ return (host == null ? void 0 : host.cname) || (host == null ? void 0 : host.subdomain) || undefined;
255
+ }
256
+ /**
257
+ * The options a site's own theme is layered onto: the platform's brand for
258
+ * the operator's own hosts, the neutral tenant default for everyone else
259
+ * (AGL-3068).
260
+ *
261
+ * One decision, because it used to be four. The tenant made it per host,
262
+ * while the besigner canvas, the theme editor's preview and the "Default ·
263
+ * #…" the editor prints beside every unset slot all built on the brand base
264
+ * unconditionally — so a customer site was authored in Aglyn's cyan and
265
+ * published in MUI's blue, and an author correcting contrast was correcting a
266
+ * page that does not exist.
267
+ *
268
+ * An unknown host is NOT a platform host, exactly as `wearsPlatformBrand`
269
+ * already reads it: the operator's hosts are the named exception, so a
270
+ * surface that cannot name the site resolves what a customer site resolves.
271
+ */ export function siteBaseOptions(host, scheme) {
272
+ const brand = wearsPlatformBrand(host);
273
+ if (scheme === 'dark') return brand ? consoleOptionsDark : tenantOptionsDark;
274
+ return brand ? consoleOptions : tenantOptions;
275
+ }
276
+ /** The built theme a site with no customization of its own renders under. */ export function siteFallbackTheme(host, scheme) {
277
+ const brand = wearsPlatformBrand(host);
278
+ if (scheme === 'dark') return brand ? consoleThemeDark : tenantThemeDark;
279
+ return brand ? consoleThemeLight : tenantThemeLight;
280
+ }
281
+
282
+ //# sourceMappingURL=tenant.theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/shared/ui/theme/src/lib/tenant.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 */\nimport type { PaletteOptions, Theme, ThemeOptions } from '../vendor/mui'\nimport {\n consoleOptions,\n consoleOptionsDark,\n consoleThemeDark,\n consoleThemeLight,\n} from './console.theme'\nimport createResponsiveTheme from './util/create-responsive-theme'\n\n/**\n * The default palette a tenant site wears when it has authored none of its\n * own.\n *\n * MUI's stock accents, extended with every slot this platform adds on top of\n * a MUI palette — `tertiary`, `surface`, `tint`, `inputOutline` and the three\n * `svg*` records — so a site that never opens the theme editor still resolves\n * every token a component can ask for.\n *\n * Stock rather than the Aglyn brand because the brand palette carries a\n * signed-off sub-AA pairing (white on `#00b0ff`, 2.43:1): a considered\n * decision about OUR brand, and not one to hand a customer who never made it.\n * Every accent here clears the 4.5:1 AA text bar as text and carries its own\n * `contrastText` at AA on the fill, in both schemes.\n *\n * Non-palette options come from `consoleOptions`, so component behaviour,\n * type ramp, spacing and shadows are identical platform-wide — the palette is\n * the only thing a tenant default changes.\n */\n/**\n * The one grey ramp, read from the brand palette rather than copied. A\n * neutral scale is not a brand decision, and a second copy is a second thing\n * to drift.\n */\nconst grey = (consoleOptions.palette as { grey: PaletteOptions['grey'] }).grey\n\nconst tenantColorScheme = {\n light: {\n primary: {\n main: '#1976d2',\n // The accent-as-text shade (`ACCENT_TEXT_SHADE`): 7.50:1 on the page,\n // 7.82:1 on paper. Authored for the same reason as the console's — the\n // slot every link resolves to belongs in the palette, not in a walk.\n dark: '#125393',\n contrastText: '#FFFFFF',\n },\n secondary: {\n main: '#9c27b0',\n dark: '#6d1b7b',\n contrastText: '#FFFFFF',\n },\n // A neutral slate for the third accent, matching the role `tertiary`\n // plays in the console palette.\n tertiary: {\n main: '#4a5568',\n dark: '#343b49',\n contrastText: '#FFFFFF',\n },\n surface: {\n main: '#f1f3f5',\n contrastText: '#000000DE',\n },\n tint: {\n primary: '#e8f1fb',\n secondary: '#f5e9f7',\n tertiary: '#eef0f3',\n },\n inputOutline: 'rgba(0, 0, 0, 0.23)',\n background: {\n default: '#fafafa',\n paper: '#ffffff',\n },\n info: {\n main: '#0288d1',\n dark: '#015f92',\n contrastText: '#000000DE',\n },\n error: {\n main: '#d32f2f',\n dark: '#942121',\n contrastText: '#FFFFFF',\n },\n success: {\n main: '#2e7d32',\n dark: '#205823',\n contrastText: '#FFFFFF',\n },\n warning: {\n main: '#ed6c02',\n dark: '#a64c01',\n contrastText: '#000000DE',\n },\n grey,\n svgBackground: {\n main: '#FAFAFA',\n hover: '#FAFAFA',\n active: '#FAFAFA',\n focus: '#FAFAFA',\n },\n svgFilled: {\n main: '#9E9E9E',\n hover: '#1976d2',\n active: '#1976d2',\n focus: '#1976d2',\n },\n svgStroke: {\n main: '#FFFFFF',\n hover: '#FFFFFF',\n active: '#FFFFFF',\n focus: '#FFFFFF',\n },\n },\n dark: {\n primary: {\n main: '#1976d2',\n // LIGHTER than `main`, as every accent-as-text shade must be on a dark\n // ground: 6.70:1 on the page, 5.96:1 on paper. MUI derives `dark` by\n // darkening in both schemes, which points the wrong way here.\n dark: '#5e9fe0',\n contrastText: '#FFFFFF',\n },\n secondary: {\n main: '#9c27b0',\n dark: '#ba68c8',\n contrastText: '#FFFFFF',\n },\n // Lifted from the light scheme's slate: `#4a5568` is 1.9:1 against this\n // page and would be a third accent nobody can read.\n tertiary: {\n main: '#94a3b8',\n dark: '#b4bfcd',\n contrastText: '#000000DE',\n },\n surface: {\n main: '#242c35',\n contrastText: '#FFFFFF',\n },\n // Tile fills, held around 1.5:1 from the page so a tinted tile has a\n // visible edge without a border.\n tint: {\n primary: '#1e3a5c',\n secondary: '#472653',\n tertiary: '#2e3742',\n },\n inputOutline: 'rgba(255, 255, 255, 0.23)',\n background: {\n default: '#121212',\n paper: '#1e1e1e',\n },\n info: {\n main: '#0288d1',\n dark: '#4eacdf',\n contrastText: '#000000DE',\n },\n error: {\n main: '#d32f2f',\n dark: '#e06d6d',\n contrastText: '#FFFFFF',\n },\n success: {\n main: '#2e7d32',\n dark: '#6da470',\n contrastText: '#FFFFFF',\n },\n warning: {\n main: '#ed6c02',\n dark: '#f2984e',\n contrastText: '#000000DE',\n },\n grey,\n svgBackground: {\n main: '#242c35',\n hover: '#242c35',\n active: '#242c35',\n focus: '#242c35',\n },\n svgFilled: {\n main: '#9E9E9E',\n hover: '#5e9fe0',\n active: '#5e9fe0',\n focus: '#5e9fe0',\n },\n svgStroke: {\n main: '#FFFFFF',\n hover: '#FFFFFF',\n active: '#FFFFFF',\n focus: '#FFFFFF',\n },\n },\n}\n\nexport const tenantOptions: ThemeOptions = {\n ...consoleOptions,\n palette: {\n mode: 'light',\n ...tenantColorScheme.light,\n },\n}\nexport const tenantOptionsDark: ThemeOptions = {\n ...consoleOptionsDark,\n palette: {\n mode: 'dark',\n ...tenantColorScheme.dark,\n },\n}\n\nexport const tenantThemeLight: Theme = createResponsiveTheme({\n themeOptions: { ...tenantOptions },\n})\nexport const tenantThemeDark: Theme = createResponsiveTheme({\n themeOptions: { ...tenantOptionsDark },\n})\n\n/**\n * Hosts whose brand IS the operator's own brand, and which therefore keep\n * `consoleOptions` rather than the tenant default.\n *\n * Comma-separated in `NEXT_PUBLIC_PLATFORM_BRAND_HOSTS`, so a self-host\n * operator points it at their own marketing domain and their customers still\n * resolve the neutral tenant palette. The literal is the `??` default and\n * nothing else reads it: the platform's own deployment needs no variable to\n * keep its brand. Setting the variable to an empty string puts every host,\n * including the operator's own, on the tenant default.\n *\n * Matched on the registrable domain, after stripping the `cname--` prefix the\n * tenant middleware puts on a CUSTOM DOMAIN before it becomes the `[host]`\n * route segment: the param this is handed reads `cname--example.com`, never\n * the bare apex. A platform subdomain resolves to a bare label instead\n * (`acme` for `acme.aglyn.app`), which correctly matches nothing — a customer\n * on a platform subdomain is still a tenant.\n */\nconst CNAME_PREFIX = 'cname--'\n\nexport const PLATFORM_BRAND_HOSTS: ReadonlySet<string> = new Set(\n // Dot notation, not brackets: Next substitutes `process.env.NAME`\n // TEXTUALLY, and never the bracket form, so a bracket read is `undefined`\n // in any browser or edge bundle and silently falls back to the default\n // below — which on a self-host install would hand the operator Aglyn's\n // hosts (AGL-2037).\n (process.env.NEXT_PUBLIC_PLATFORM_BRAND_HOSTS ?? 'aglyn.com,aglyn.io')\n .split(',')\n .map((host) => host.trim().toLowerCase())\n .filter(Boolean),\n)\n\nexport function wearsPlatformBrand(host: string | undefined): boolean {\n if (!host) return false\n const normalized = host.trim().toLowerCase()\n return PLATFORM_BRAND_HOSTS.has(\n normalized.startsWith(CNAME_PREFIX)\n ? normalized.slice(CNAME_PREFIX.length)\n : normalized,\n )\n}\n\n/**\n * The site key {@link wearsPlatformBrand} is asked about, read off a host\n * document (AGL-3068).\n *\n * The tenant is handed its `[host]` route param, which is the attached domain\n * under the middleware's `cname--` sentinel or a bare subdomain label. A\n * console surface has the document instead, where those are two fields: the\n * attached domain is what a platform-brand host is listed under, and the\n * subdomain is what every other site is reachable by. Reading them in that\n * order is what makes the editor ask the same question the published page\n * answers.\n */\nexport function hostBrandKey(\n host: { cname?: string; subdomain?: string } | null | undefined,\n): string | undefined {\n return host?.cname || host?.subdomain || undefined\n}\n\n/**\n * The options a site's own theme is layered onto: the platform's brand for\n * the operator's own hosts, the neutral tenant default for everyone else\n * (AGL-3068).\n *\n * One decision, because it used to be four. The tenant made it per host,\n * while the besigner canvas, the theme editor's preview and the \"Default ·\n * #…\" the editor prints beside every unset slot all built on the brand base\n * unconditionally — so a customer site was authored in Aglyn's cyan and\n * published in MUI's blue, and an author correcting contrast was correcting a\n * page that does not exist.\n *\n * An unknown host is NOT a platform host, exactly as `wearsPlatformBrand`\n * already reads it: the operator's hosts are the named exception, so a\n * surface that cannot name the site resolves what a customer site resolves.\n */\nexport function siteBaseOptions(\n host: string | undefined,\n scheme: 'light' | 'dark',\n): ThemeOptions {\n const brand = wearsPlatformBrand(host)\n if (scheme === 'dark') return brand ? consoleOptionsDark : tenantOptionsDark\n return brand ? consoleOptions : tenantOptions\n}\n\n/** The built theme a site with no customization of its own renders under. */\nexport function siteFallbackTheme(\n host: string | undefined,\n scheme: 'light' | 'dark',\n): Theme {\n const brand = wearsPlatformBrand(host)\n if (scheme === 'dark') return brand ? consoleThemeDark : tenantThemeDark\n return brand ? consoleThemeLight : tenantThemeLight\n}\n"],"names":["process","consoleOptions","consoleOptionsDark","consoleThemeDark","consoleThemeLight","createResponsiveTheme","grey","palette","tenantColorScheme","light","primary","main","dark","contrastText","secondary","tertiary","surface","tint","inputOutline","background","default","paper","info","error","success","warning","svgBackground","hover","active","focus","svgFilled","svgStroke","tenantOptions","mode","tenantOptionsDark","tenantThemeLight","themeOptions","tenantThemeDark","CNAME_PREFIX","PLATFORM_BRAND_HOSTS","Set","env","NEXT_PUBLIC_PLATFORM_BRAND_HOSTS","split","map","host","trim","toLowerCase","filter","Boolean","wearsPlatformBrand","normalized","has","startsWith","slice","length","hostBrandKey","cname","subdomain","undefined","siteBaseOptions","scheme","brand","siteFallbackTheme"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;IA0OC,kEAAkE;AAClE,0EAA0E;AAC1E,uEAAuE;AACvE,uEAAuE;AACvE,oBAAoB;AACnBA;AA7OH,SACEC,cAAc,EACdC,kBAAkB,EAClBC,gBAAgB,EAChBC,iBAAiB,QACZ,qBAAiB;AACxB,OAAOC,2BAA2B,oCAAgC;AAElE;;;;;;;;;;;;;;;;;;CAkBC,GACD;;;;CAIC,GACD,MAAMC,OAAO,AAACL,eAAeM,OAAO,CAAsCD,IAAI;AAE9E,MAAME,oBAAoB;IACxBC,OAAO;QACLC,SAAS;YACPC,MAAM;YACN,sEAAsE;YACtE,uEAAuE;YACvE,qEAAqE;YACrEC,MAAM;YACNC,cAAc;QAChB;QACAC,WAAW;YACTH,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACA,qEAAqE;QACrE,gCAAgC;QAChCE,UAAU;YACRJ,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAG,SAAS;YACPL,MAAM;YACNE,cAAc;QAChB;QACAI,MAAM;YACJP,SAAS;YACTI,WAAW;YACXC,UAAU;QACZ;QACAG,cAAc;QACdC,YAAY;YACVC,SAAS;YACTC,OAAO;QACT;QACAC,MAAM;YACJX,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAU,OAAO;YACLZ,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAW,SAAS;YACPb,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAY,SAAS;YACPd,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAP;QACAoB,eAAe;YACbf,MAAM;YACNgB,OAAO;YACPC,QAAQ;YACRC,OAAO;QACT;QACAC,WAAW;YACTnB,MAAM;YACNgB,OAAO;YACPC,QAAQ;YACRC,OAAO;QACT;QACAE,WAAW;YACTpB,MAAM;YACNgB,OAAO;YACPC,QAAQ;YACRC,OAAO;QACT;IACF;IACAjB,MAAM;QACJF,SAAS;YACPC,MAAM;YACN,uEAAuE;YACvE,qEAAqE;YACrE,8DAA8D;YAC9DC,MAAM;YACNC,cAAc;QAChB;QACAC,WAAW;YACTH,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACA,wEAAwE;QACxE,oDAAoD;QACpDE,UAAU;YACRJ,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAG,SAAS;YACPL,MAAM;YACNE,cAAc;QAChB;QACA,qEAAqE;QACrE,iCAAiC;QACjCI,MAAM;YACJP,SAAS;YACTI,WAAW;YACXC,UAAU;QACZ;QACAG,cAAc;QACdC,YAAY;YACVC,SAAS;YACTC,OAAO;QACT;QACAC,MAAM;YACJX,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAU,OAAO;YACLZ,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAW,SAAS;YACPb,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAY,SAAS;YACPd,MAAM;YACNC,MAAM;YACNC,cAAc;QAChB;QACAP;QACAoB,eAAe;YACbf,MAAM;YACNgB,OAAO;YACPC,QAAQ;YACRC,OAAO;QACT;QACAC,WAAW;YACTnB,MAAM;YACNgB,OAAO;YACPC,QAAQ;YACRC,OAAO;QACT;QACAE,WAAW;YACTpB,MAAM;YACNgB,OAAO;YACPC,QAAQ;YACRC,OAAO;QACT;IACF;AACF;AAEA,OAAO,MAAMG,gBAA8B,aACtC/B;IACHM,SAAS;QACP0B,MAAM;OACHzB,kBAAkBC,KAAK;GAE7B;AACD,OAAO,MAAMyB,oBAAkC,aAC1ChC;IACHK,SAAS;QACP0B,MAAM;OACHzB,kBAAkBI,IAAI;GAE5B;AAED,OAAO,MAAMuB,mBAA0B9B,sBAAsB;IAC3D+B,cAAc,aAAKJ;AACrB,GAAE;AACF,OAAO,MAAMK,kBAAyBhC,sBAAsB;IAC1D+B,cAAc,aAAKF;AACrB,GAAE;AAEF;;;;;;;;;;;;;;;;;CAiBC,GACD,MAAMI,eAAe;AAErB,OAAO,MAAMC,uBAA4C,IAAIC,IAM3D,EAACxC,gDAAAA,QAAQyC,GAAG,CAACC,gCAAgC,YAA5C1C,gDAAgD,sBAC9C2C,KAAK,CAAC,KACNC,GAAG,CAAC,CAACC,OAASA,KAAKC,IAAI,GAAGC,WAAW,IACrCC,MAAM,CAACC,UACX;AAED,OAAO,SAASC,mBAAmBL,IAAwB;IACzD,IAAI,CAACA,MAAM,OAAO;IAClB,MAAMM,aAAaN,KAAKC,IAAI,GAAGC,WAAW;IAC1C,OAAOR,qBAAqBa,GAAG,CAC7BD,WAAWE,UAAU,CAACf,gBAClBa,WAAWG,KAAK,CAAChB,aAAaiB,MAAM,IACpCJ;AAER;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASK,aACdX,IAA+D;IAE/D,OAAOA,CAAAA,wBAAAA,KAAMY,KAAK,MAAIZ,wBAAAA,KAAMa,SAAS,KAAIC;AAC3C;AAEA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,gBACdf,IAAwB,EACxBgB,MAAwB;IAExB,MAAMC,QAAQZ,mBAAmBL;IACjC,IAAIgB,WAAW,QAAQ,OAAOC,QAAQ5D,qBAAqBgC;IAC3D,OAAO4B,QAAQ7D,iBAAiB+B;AAClC;AAEA,2EAA2E,GAC3E,OAAO,SAAS+B,kBACdlB,IAAwB,EACxBgB,MAAwB;IAExB,MAAMC,QAAQZ,mBAAmBL;IACjC,IAAIgB,WAAW,QAAQ,OAAOC,QAAQ3D,mBAAmBkC;IACzD,OAAOyB,QAAQ1D,oBAAoB+B;AACrC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ export interface IActionStates {
18
+ main: string;
19
+ hover: string;
20
+ active: string;
21
+ focus: string;
22
+ }
23
+ export interface ColorPropOverrides {
24
+ tertiary: true;
25
+ surface: true;
26
+ }