@aglyn/aglyn 1.0.0-beta.268 → 1.0.0-beta.270

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 (82) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/account-preferences.d.ts +248 -0
  3. package/src/lib/app-utils/account-preferences.js +434 -0
  4. package/src/lib/app-utils/account-preferences.js.map +1 -0
  5. package/src/lib/app-utils/country-options.d.ts +27 -0
  6. package/src/lib/app-utils/country-options.js +96 -0
  7. package/src/lib/app-utils/country-options.js.map +1 -0
  8. package/src/lib/app-utils/docs-help.generated.d.ts +6 -0
  9. package/src/lib/app-utils/docs-help.generated.js +13 -0
  10. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  11. package/src/lib/app-utils/docs-index.generated.js +17 -10
  12. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  13. package/src/lib/app-utils/element-motion.d.ts +14 -2
  14. package/src/lib/app-utils/element-motion.js +33 -13
  15. package/src/lib/app-utils/element-motion.js.map +1 -1
  16. package/src/lib/app-utils/mobile-push.d.ts +8 -0
  17. package/src/lib/app-utils/mobile-push.js +4 -1
  18. package/src/lib/app-utils/mobile-push.js.map +1 -1
  19. package/src/lib/app-utils/native-app-association.d.ts +3 -2
  20. package/src/lib/app-utils/native-app-association.js +4 -2
  21. package/src/lib/app-utils/native-app-association.js.map +1 -1
  22. package/src/lib/app-utils/native-nav.d.ts +265 -0
  23. package/src/lib/app-utils/native-nav.js +741 -0
  24. package/src/lib/app-utils/native-nav.js.map +1 -0
  25. package/src/lib/app-utils/native-themes.d.ts +249 -0
  26. package/src/lib/app-utils/native-themes.js +732 -0
  27. package/src/lib/app-utils/native-themes.js.map +1 -0
  28. package/src/lib/app-utils/notifications.d.ts +1 -1
  29. package/src/lib/app-utils/notifications.js +10 -0
  30. package/src/lib/app-utils/notifications.js.map +1 -1
  31. package/src/lib/app-utils/one-box-search.d.ts +108 -0
  32. package/src/lib/app-utils/one-box-search.js +167 -0
  33. package/src/lib/app-utils/one-box-search.js.map +1 -0
  34. package/src/lib/app-utils/org-permissions.js +10 -61
  35. package/src/lib/app-utils/org-permissions.js.map +1 -1
  36. package/src/lib/app-utils/org-role-grants.d.ts +95 -0
  37. package/src/lib/app-utils/org-role-grants.js +193 -0
  38. package/src/lib/app-utils/org-role-grants.js.map +1 -0
  39. package/src/lib/app-utils/plan-entitlements.d.ts +41 -1
  40. package/src/lib/app-utils/plan-entitlements.js +65 -3
  41. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  42. package/src/lib/app-utils/tenant-dns-targets.d.ts +62 -0
  43. package/src/lib/app-utils/tenant-dns-targets.js +80 -0
  44. package/src/lib/app-utils/tenant-dns-targets.js.map +1 -0
  45. package/src/lib/foundation/definitions/org-billing.types.d.ts +14 -0
  46. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  47. package/src/lib/foundation/definitions/platform.types.d.ts +9 -0
  48. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  49. package/src/lib/plugin-manager/feature-plugins.d.ts +38 -0
  50. package/src/lib/plugin-manager/feature-plugins.js +20 -0
  51. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  52. package/src/lib/plugin-manager/first-party-plugins.generated.js +22 -1
  53. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  54. package/src/lib/plugin-manager/operator-alerts.js +10 -1
  55. package/src/lib/plugin-manager/operator-alerts.js.map +1 -1
  56. package/src/lib/plugin-manager/plugin-config.js +10 -1
  57. package/src/lib/plugin-manager/plugin-config.js.map +1 -1
  58. package/src/lib/plugin-manager/plugin-consent-group-change.js +11 -2
  59. package/src/lib/plugin-manager/plugin-consent-group-change.js.map +1 -1
  60. package/src/lib/plugin-manager/plugin-console-crons.js +10 -1
  61. package/src/lib/plugin-manager/plugin-console-crons.js.map +1 -1
  62. package/src/lib/plugin-manager/plugin-domain-events.d.ts +4 -3
  63. package/src/lib/plugin-manager/plugin-domain-events.js.map +1 -1
  64. package/src/lib/plugin-manager/plugin-events.js +11 -2
  65. package/src/lib/plugin-manager/plugin-events.js.map +1 -1
  66. package/src/lib/plugin-manager/plugin-lead-conversion.js +10 -1
  67. package/src/lib/plugin-manager/plugin-lead-conversion.js.map +1 -1
  68. package/src/lib/plugin-manager/plugin-media-ingest.d.ts +25 -0
  69. package/src/lib/plugin-manager/plugin-media-ingest.js.map +1 -1
  70. package/src/lib/plugin-manager/plugin-membership-detach.js +10 -1
  71. package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
  72. package/src/lib/plugin-manager/plugin-person-matches.js +10 -1
  73. package/src/lib/plugin-manager/plugin-person-matches.js.map +1 -1
  74. package/src/lib/plugin-manager/plugin-search-sources.d.ts +85 -0
  75. package/src/lib/plugin-manager/plugin-search-sources.js +91 -0
  76. package/src/lib/plugin-manager/plugin-search-sources.js.map +1 -0
  77. package/src/lib/plugin-manager/plugin-services.js +6 -0
  78. package/src/lib/plugin-manager/plugin-services.js.map +1 -1
  79. package/src/lib/plugin-manager/plugin-usage-meters.js +10 -1
  80. package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -1
  81. package/src/lib/plugin-manager/usage-alert-contributors.js +10 -1
  82. package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
@@ -0,0 +1,732 @@
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
+ * THE NATIVE APPS' THEMES, AS DATA (AGL-3950).
18
+ *
19
+ * The values are read from the Native App Figma file's variables (Themes,
20
+ * page 2:8; collections Aglyn / Theme, Aglyn / Appearance, Aglyn / Accent,
21
+ * Aglyn / Brand and Aglyn / Density), which win wherever they define a value:
22
+ *
23
+ * - `THEME_PALETTES`: Aglyn is the look that ships, the console's resolved
24
+ * MUI scheme (see `AGLYN_LIGHT`); Quiet, Night and High contrast, light and
25
+ * dark, are exactly as Figma's Theme modes hold them. Ocean and Warm exist in Figma
26
+ * only as four-color previews (rail, surface, canvas, accent), so their
27
+ * full palettes are derived here by `deriveThemeFromPreview`, the rule
28
+ * below, and drawn back into Figma as swatch boards.
29
+ * - `THEME_ACCENTS`: the Accent modes Blue, Violet, Pink and Workspace
30
+ * brand, exactly. `deriveAccent` returns them for their ring colors and
31
+ * derives any other color (a picked accent, a workspace's brand) by rule.
32
+ * - `THEME_ACCENT_SWATCHES`: the six swatches the Theme settings sheet shows.
33
+ * - `THEME_DENSITIES`: Comfortable and Compact.
34
+ *
35
+ * Contrast is WCAG 2's relative luminance, as `accessible-shade.ts` (the
36
+ * console's theme) measures it; lightness steps are HSL lightness, as it
37
+ * walks them.
38
+ *
39
+ * NO IMPORTS, on purpose: the native apps read the values, the types and the
40
+ * answers of these functions through the generated contracts.
41
+ */ /** A theme the apps offer. */ import { _ as _extends } from "@swc/helpers/_/_extends";
42
+ // ── Color arithmetic ────────────────────────────────────────────────────────
43
+ const HEX = /^#([0-9a-f]{6})([0-9a-f]{2})?$/i;
44
+ /** `[r, g, b]` in 0–255, or null for anything that is not `#RRGGBB(AA)`. */ function channels(color) {
45
+ const match = HEX.exec(color != null ? color : '');
46
+ if (!match) return null;
47
+ const value = match[1];
48
+ return [
49
+ 0,
50
+ 2,
51
+ 4
52
+ ].map((index)=>parseInt(value.slice(index, index + 2), 16));
53
+ }
54
+ function hexOf(rgb) {
55
+ return `#${rgb.map((value)=>Math.round(Math.min(255, Math.max(0, value))).toString(16).padStart(2, '0')).join('')}`;
56
+ }
57
+ /** Whether a value is `#RRGGBB`. */ export function isThemeColor(value) {
58
+ return typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value);
59
+ }
60
+ function linear(channel) {
61
+ const value = channel / 255;
62
+ return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
63
+ }
64
+ function luminance(rgb) {
65
+ return 0.2126 * linear(rgb[0]) + 0.7152 * linear(rgb[1]) + 0.0722 * linear(rgb[2]);
66
+ }
67
+ /** WCAG 2 contrast between two `#RRGGBB` colors, as the console's `contrastRatio` measures it. */ export function themeContrast(a, b) {
68
+ const x = channels(a);
69
+ const y = channels(b);
70
+ if (!x || !y) return 1;
71
+ const [high, low] = [
72
+ luminance(x),
73
+ luminance(y)
74
+ ].sort((p, q)=>q - p);
75
+ return (high + 0.05) / (low + 0.05);
76
+ }
77
+ function toHsl([r, g, b]) {
78
+ const [red, green, blue] = [
79
+ r / 255,
80
+ g / 255,
81
+ b / 255
82
+ ];
83
+ const max = Math.max(red, green, blue);
84
+ const min = Math.min(red, green, blue);
85
+ const lightness = (max + min) / 2;
86
+ if (max === min) return [
87
+ 0,
88
+ 0,
89
+ lightness
90
+ ];
91
+ const delta = max - min;
92
+ const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);
93
+ let hue;
94
+ if (max === red) hue = (green - blue) / delta + (green < blue ? 6 : 0);
95
+ else if (max === green) hue = (blue - red) / delta + 2;
96
+ else hue = (red - green) / delta + 4;
97
+ return [
98
+ hue / 6,
99
+ saturation,
100
+ lightness
101
+ ];
102
+ }
103
+ function fromHsl(hue, saturation, lightness) {
104
+ const l = Math.min(1, Math.max(0, lightness));
105
+ if (saturation === 0) return hexOf([
106
+ l * 255,
107
+ l * 255,
108
+ l * 255
109
+ ]);
110
+ const q = l < 0.5 ? l * (1 + saturation) : l + saturation - l * saturation;
111
+ const p = 2 * l - q;
112
+ const channel = (t)=>{
113
+ let x = t;
114
+ if (x < 0) x += 1;
115
+ if (x > 1) x -= 1;
116
+ if (x < 1 / 6) return p + (q - p) * 6 * x;
117
+ if (x < 1 / 2) return q;
118
+ if (x < 2 / 3) return p + (q - p) * (2 / 3 - x) * 6;
119
+ return p;
120
+ };
121
+ return hexOf([
122
+ channel(hue + 1 / 3) * 255,
123
+ channel(hue) * 255,
124
+ channel(hue - 1 / 3) * 255
125
+ ]);
126
+ }
127
+ /** `color` with its HSL lightness moved by `delta` (−1 to 1). */ function shiftLightness(color, delta) {
128
+ const rgb = channels(color);
129
+ if (!rgb) return color;
130
+ const [h, s, l] = toHsl(rgb);
131
+ return fromHsl(h, s, l + delta);
132
+ }
133
+ /**
134
+ * `color` walked in 2% HSL-lightness steps (darker, or lighter) until it
135
+ * reaches `minContrast` against `against`; the pole when nothing reaches it.
136
+ */ function walkToContrast(color, against, minContrast, direction) {
137
+ let candidate = color;
138
+ for(let step = 0; step <= 50; step += 1){
139
+ candidate = shiftLightness(color, direction * 0.02 * step);
140
+ if (themeContrast(candidate, against) >= minContrast) return candidate;
141
+ }
142
+ return candidate;
143
+ }
144
+ /** `from` moved `amount` (0–1) of the way to `to`, per sRGB channel. */ export function themeLerp(from, to, amount) {
145
+ const a = channels(from);
146
+ const b = channels(to);
147
+ if (!a || !b) return from;
148
+ return hexOf(a.map((value, index)=>value + (b[index] - value) * amount));
149
+ }
150
+ /** `color` with an alpha byte: `#RRGGBBAA`. */ function withAlpha(color, alpha) {
151
+ const rgb = channels(color);
152
+ if (!rgb) return color;
153
+ return `${hexOf(rgb)}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`;
154
+ }
155
+ // ── Themes ──────────────────────────────────────────────────────────────────
156
+ /** Text that must read on every surface: WCAG AA. */ export const THEME_TEXT_CONTRAST = 4.5;
157
+ /** An icon, a border or an indicator: WCAG AA for non-text. */ export const THEME_NON_TEXT_CONTRAST = 3;
158
+ const DANGER_LIGHT = {
159
+ dangerFill: '#b3261e',
160
+ dangerFillHover: '#9c211a',
161
+ dangerFillPressed: '#861c16'
162
+ };
163
+ const DANGER_DARK = {
164
+ dangerFill: '#c93a31',
165
+ dangerFillHover: '#b3261e',
166
+ dangerFillPressed: '#9c211a'
167
+ };
168
+ /**
169
+ * Figma's Aglyn mode as it stood before that mode moved to the console's
170
+ * scheme (2026-10-19): the base Quiet's status colors and the Ocean and Warm
171
+ * derivations build on, as the native shells' themes already do. Kept by
172
+ * value, so moving the shipping Aglyn look moves nothing else.
173
+ */ const FIGMA_AGLYN_LIGHT = _extends({
174
+ canvas: '#f5f5f5',
175
+ surface: '#ffffff',
176
+ raised: '#ffffff',
177
+ sunken: '#edf0f4',
178
+ rail: '#e6eaf0',
179
+ hover: '#eef1f5',
180
+ titlebar: '#fafbfc',
181
+ borderSubtle: '#e3e7ec',
182
+ borderStrong: '#c8d0d9',
183
+ textPrimary: '#1f2329',
184
+ textSecondary: '#5e6773',
185
+ textTertiary: '#7a838e',
186
+ successBg: '#e5f3e6',
187
+ successFg: '#2e6f31',
188
+ warningBg: '#fff0da',
189
+ warningFg: '#85560f',
190
+ dangerBg: '#fce6e5',
191
+ dangerFg: '#a8231e',
192
+ infoBg: '#e3f0fb',
193
+ infoFg: '#10548f',
194
+ newBg: '#f5e4f9',
195
+ newFg: '#8e2aa1',
196
+ scrim: '#0f172152'
197
+ }, DANGER_LIGHT);
198
+ const FIGMA_AGLYN_DARK = _extends({
199
+ canvas: '#161c21',
200
+ surface: '#1e262e',
201
+ raised: '#27313b',
202
+ sunken: '#12181d',
203
+ rail: '#0f1418',
204
+ hover: '#2a343e',
205
+ titlebar: '#1a2128',
206
+ borderSubtle: '#2e3944',
207
+ borderStrong: '#45525f',
208
+ textPrimary: '#f3f5f7',
209
+ textSecondary: '#b3bcc6',
210
+ textTertiary: '#8a96a3',
211
+ successBg: '#1b3320',
212
+ successFg: '#82c785',
213
+ warningBg: '#3a2c14',
214
+ warningFg: '#ffc479',
215
+ dangerBg: '#3b1a1a',
216
+ dangerFg: '#ed7876',
217
+ infoBg: '#132c42',
218
+ infoFg: '#5da1dc',
219
+ newBg: '#3a1e44',
220
+ newFg: '#e979fc',
221
+ scrim: '#0000008c'
222
+ }, DANGER_DARK);
223
+ /**
224
+ * The palettes `deriveThemeFromPreview` starts from (the original Figma Aglyn
225
+ * mode), exported so each platform can port the derivation and replay its
226
+ * cases against the same base.
227
+ */ export const THEME_FIGMA_BASE = {
228
+ light: FIGMA_AGLYN_LIGHT,
229
+ dark: FIGMA_AGLYN_DARK
230
+ };
231
+ /**
232
+ * The Aglyn theme is the look that ships: the console's resolved MUI scheme
233
+ * (`getConsoleTheme`, the same scheme the native shared tokens are generated
234
+ * from), with translucent text, dividers and hover composited onto the paper
235
+ * they sit on, and the status colors as the console's MUI Alert draws them
236
+ * (standard: the intent's light shade lightened 90% for the ground and
237
+ * darkened 60% for the text in light, the reverse in dark). Where the console
238
+ * scheme has no such color (sunken, rail, titlebar, the strong border, the
239
+ * "new" status, the scrim) Figma's Aglyn mode supplies it, and so does the
240
+ * danger FILL: the console's `error.main` carries white text at 4.36:1, under
241
+ * the 4.5:1 a button label owes. `apps/console/specs/native-theme-aglyn.spec.ts`
242
+ * holds these to the console's scheme.
243
+ */ const AGLYN_LIGHT = _extends({}, FIGMA_AGLYN_LIGHT, {
244
+ canvas: '#f5f5f5',
245
+ surface: '#ffffff',
246
+ raised: '#ffffff',
247
+ hover: '#f5f5f5',
248
+ borderSubtle: '#e0e0e0',
249
+ textPrimary: '#212121',
250
+ textSecondary: '#666666',
251
+ textTertiary: '#9e9e9e',
252
+ successBg: '#f0f8f1',
253
+ successFg: '#2c4c2e',
254
+ warningBg: '#fff8ef',
255
+ warningFg: '#664a28',
256
+ dangerBg: '#fceeed',
257
+ dangerFg: '#5c2220',
258
+ infoBg: '#ecf4fb',
259
+ infoFg: '#1c3a56'
260
+ });
261
+ const AGLYN_DARK = _extends({}, FIGMA_AGLYN_DARK, {
262
+ canvas: '#161c21',
263
+ surface: '#2a3440',
264
+ raised: '#2a3440',
265
+ hover: '#3b444f',
266
+ borderSubtle: '#444c57',
267
+ textPrimary: '#ffffff',
268
+ textSecondary: '#bfc2c6',
269
+ textTertiary: '#959aa0',
270
+ successBg: '#0b130b',
271
+ successFg: '#c5e5c7',
272
+ warningBg: '#19120a',
273
+ warningFg: '#ffe3c1',
274
+ dangerBg: '#170c0b',
275
+ dangerFg: '#f7c9c8',
276
+ infoBg: '#091015',
277
+ infoFg: '#bfd9f1'
278
+ });
279
+ /** The Aglyn theme's fields the console's scheme supplies; the rest are Figma's Aglyn mode. */ export const THEME_CONSOLE_FIELDS = [
280
+ 'canvas',
281
+ 'surface',
282
+ 'raised',
283
+ 'hover',
284
+ 'borderSubtle',
285
+ 'textPrimary',
286
+ 'textSecondary',
287
+ 'textTertiary',
288
+ 'successBg',
289
+ 'successFg',
290
+ 'warningBg',
291
+ 'warningFg',
292
+ 'dangerBg',
293
+ 'dangerFg',
294
+ 'infoBg',
295
+ 'infoFg'
296
+ ];
297
+ const QUIET_LIGHT = _extends({}, FIGMA_AGLYN_LIGHT, {
298
+ canvas: '#f4f4f2',
299
+ surface: '#ffffff',
300
+ raised: '#ffffff',
301
+ sunken: '#ecece9',
302
+ rail: '#eaeae7',
303
+ hover: '#efefec',
304
+ titlebar: '#fafaf8',
305
+ borderSubtle: '#e2e2de',
306
+ borderStrong: '#cbcbc5',
307
+ textPrimary: '#222220',
308
+ textSecondary: '#5f5f5a',
309
+ textTertiary: '#7c7c76'
310
+ });
311
+ const QUIET_DARK = _extends({}, FIGMA_AGLYN_DARK, {
312
+ canvas: '#1a1a19',
313
+ surface: '#222221',
314
+ raised: '#2b2b29',
315
+ sunken: '#141413',
316
+ rail: '#121211',
317
+ hover: '#30302e',
318
+ titlebar: '#1e1e1d',
319
+ borderSubtle: '#353533',
320
+ borderStrong: '#4a4a47',
321
+ textPrimary: '#f2f2ef',
322
+ textSecondary: '#b5b5af',
323
+ textTertiary: '#8e8e88'
324
+ });
325
+ const NIGHT = _extends({
326
+ canvas: '#0b1120',
327
+ surface: '#121a2b',
328
+ raised: '#1a2337',
329
+ sunken: '#070c17',
330
+ rail: '#050913',
331
+ hover: '#1e2840',
332
+ titlebar: '#0e1525',
333
+ borderSubtle: '#222c42',
334
+ borderStrong: '#33405c',
335
+ textPrimary: '#eef2fa',
336
+ textSecondary: '#a9b4cb',
337
+ textTertiary: '#8290ab',
338
+ successBg: '#12301f',
339
+ successFg: '#7bcb8f',
340
+ warningBg: '#352914',
341
+ warningFg: '#f6be6e',
342
+ dangerBg: '#3a1820',
343
+ dangerFg: '#f08a8f',
344
+ infoBg: '#122a47',
345
+ infoFg: '#79b2ee',
346
+ newBg: '#36193f',
347
+ newFg: '#e68af6',
348
+ scrim: '#00000099'
349
+ }, DANGER_DARK);
350
+ const HIGH_CONTRAST_LIGHT = _extends({
351
+ canvas: '#ffffff',
352
+ surface: '#ffffff',
353
+ raised: '#ffffff',
354
+ sunken: '#f0f0f0',
355
+ rail: '#efefef',
356
+ hover: '#e4e4e4',
357
+ titlebar: '#ffffff',
358
+ borderSubtle: '#6b6b6b',
359
+ borderStrong: '#000000',
360
+ textPrimary: '#000000',
361
+ textSecondary: '#1f1f1f',
362
+ textTertiary: '#3d3d3d',
363
+ successBg: '#ddf3de',
364
+ successFg: '#0b4d10',
365
+ warningBg: '#ffe9c7',
366
+ warningFg: '#5c3a00',
367
+ dangerBg: '#fadbd9',
368
+ dangerFg: '#7a0f0b',
369
+ infoBg: '#ddebfa',
370
+ infoFg: '#0a3a66',
371
+ newBg: '#f1ddf6',
372
+ newFg: '#5e0f70',
373
+ scrim: '#00000080'
374
+ }, DANGER_LIGHT);
375
+ const HIGH_CONTRAST_DARK = _extends({
376
+ canvas: '#000000',
377
+ surface: '#000000',
378
+ raised: '#0a0a0a',
379
+ sunken: '#141414',
380
+ rail: '#0f0f0f',
381
+ hover: '#262626',
382
+ titlebar: '#000000',
383
+ borderSubtle: '#9a9a9a',
384
+ borderStrong: '#ffffff',
385
+ textPrimary: '#ffffff',
386
+ textSecondary: '#ededed',
387
+ textTertiary: '#c7c7c7',
388
+ successBg: '#0b2a0e',
389
+ successFg: '#9be7a0',
390
+ warningBg: '#2e2000',
391
+ warningFg: '#ffd08a',
392
+ dangerBg: '#330b0a',
393
+ dangerFg: '#ff9e9b',
394
+ infoBg: '#0a2340',
395
+ infoFg: '#9ccbff',
396
+ newBg: '#2e0b38',
397
+ newFg: '#f2a8ff',
398
+ scrim: '#000000b2'
399
+ }, DANGER_DARK);
400
+ /** Figma's previews of the two themes it does not hold in full. */ export const THEME_PREVIEWS = {
401
+ ocean: {
402
+ rail: '#0b3954',
403
+ surface: '#f2f8fb',
404
+ canvas: '#e3eef4',
405
+ accent: '#087e8b'
406
+ },
407
+ warm: {
408
+ rail: '#e9dccb',
409
+ surface: '#fffaf3',
410
+ canvas: '#f5ece0',
411
+ accent: '#c2571a'
412
+ }
413
+ };
414
+ /**
415
+ * The rail color a theme's DARK appearance is tinted toward. Ocean's rail is
416
+ * already dark, so it is its own seed; Warm's is a light sand, so its dark
417
+ * appearance leans to a dark sand instead.
418
+ */ export const THEME_DARK_RAIL_SEEDS = {
419
+ ocean: '#0b3954',
420
+ warm: '#7a5634'
421
+ };
422
+ /**
423
+ * A theme's full palette from Figma's four-color preview (AGL-3950), by the
424
+ * same rules for both:
425
+ *
426
+ * Light: the preview's canvas, surface and rail as given; raised = surface;
427
+ * sunken = canvas 25% toward the rail; hover = surface 10% toward the rail;
428
+ * titlebar = surface 35% toward the canvas; the borders are Figma's Aglyn
429
+ * mode's, 18% (subtle) and 22% (strong) toward the rail. Text, status, scrim
430
+ * and the danger fill are Figma's Aglyn mode's.
431
+ *
432
+ * Dark: Figma's Aglyn dark mode tinted toward the dark rail seed: canvas
433
+ * 22%, surface and raised 18%, rail 45%; everything else is that mode's.
434
+ *
435
+ * "Toward" is a straight per-channel sRGB blend, rounded to the byte.
436
+ */ export function deriveThemeFromPreview(preview, darkRailSeed) {
437
+ return {
438
+ light: _extends({}, FIGMA_AGLYN_LIGHT, {
439
+ canvas: preview.canvas,
440
+ surface: preview.surface,
441
+ raised: preview.surface,
442
+ rail: preview.rail,
443
+ sunken: themeLerp(preview.canvas, preview.rail, 0.25),
444
+ hover: themeLerp(preview.surface, preview.rail, 0.1),
445
+ titlebar: themeLerp(preview.surface, preview.canvas, 0.35),
446
+ borderSubtle: themeLerp(FIGMA_AGLYN_LIGHT.borderSubtle, preview.rail, 0.18),
447
+ borderStrong: themeLerp(FIGMA_AGLYN_LIGHT.borderStrong, preview.rail, 0.22)
448
+ }),
449
+ dark: _extends({}, FIGMA_AGLYN_DARK, {
450
+ canvas: themeLerp(FIGMA_AGLYN_DARK.canvas, darkRailSeed, 0.22),
451
+ surface: themeLerp(FIGMA_AGLYN_DARK.surface, darkRailSeed, 0.18),
452
+ raised: themeLerp(FIGMA_AGLYN_DARK.raised, darkRailSeed, 0.18),
453
+ rail: themeLerp(FIGMA_AGLYN_DARK.rail, darkRailSeed, 0.45)
454
+ })
455
+ };
456
+ }
457
+ const OCEAN = deriveThemeFromPreview(THEME_PREVIEWS.ocean, THEME_DARK_RAIL_SEEDS.ocean);
458
+ const WARM = deriveThemeFromPreview(THEME_PREVIEWS.warm, THEME_DARK_RAIL_SEEDS.warm);
459
+ /** The Aglyn blue every Figma theme's "Accent: Theme" resolves to. */ const AGLYN_ACCENT = '#00b0ff';
460
+ /** Every theme, in the order the Theme settings sheet lists them. */ export const THEME_PALETTES = [
461
+ {
462
+ id: 'aglyn',
463
+ label: '',
464
+ brandLabel: true,
465
+ light: AGLYN_LIGHT,
466
+ dark: AGLYN_DARK,
467
+ accent: AGLYN_ACCENT,
468
+ source: 'console'
469
+ },
470
+ {
471
+ id: 'quiet',
472
+ label: 'Quiet',
473
+ light: QUIET_LIGHT,
474
+ dark: QUIET_DARK,
475
+ accent: AGLYN_ACCENT,
476
+ source: 'figma'
477
+ },
478
+ {
479
+ id: 'night',
480
+ label: 'Night',
481
+ light: NIGHT,
482
+ dark: NIGHT,
483
+ accent: AGLYN_ACCENT,
484
+ alwaysDark: true,
485
+ source: 'figma'
486
+ },
487
+ {
488
+ id: 'ocean',
489
+ label: 'Ocean',
490
+ light: OCEAN.light,
491
+ dark: OCEAN.dark,
492
+ accent: THEME_PREVIEWS.ocean.accent,
493
+ source: 'derived'
494
+ },
495
+ {
496
+ id: 'warm',
497
+ label: 'Warm',
498
+ light: WARM.light,
499
+ dark: WARM.dark,
500
+ accent: THEME_PREVIEWS.warm.accent,
501
+ source: 'derived'
502
+ },
503
+ {
504
+ id: 'highContrast',
505
+ label: 'High contrast',
506
+ light: HIGH_CONTRAST_LIGHT,
507
+ dark: HIGH_CONTRAST_DARK,
508
+ accent: AGLYN_ACCENT,
509
+ source: 'figma'
510
+ }
511
+ ];
512
+ // ── Accents ─────────────────────────────────────────────────────────────────
513
+ /** The surfaces an accent's text is held to. */ const LIGHT_PAGE = AGLYN_LIGHT.surface;
514
+ const DARK_PAGE = AGLYN_DARK.surface;
515
+ /** Text and icons on a fill that keeps a light brand color. */ const DARK_ON_LIGHTNESS = 0.08;
516
+ function accentText(raw) {
517
+ return {
518
+ textLight: walkToContrast(raw, LIGHT_PAGE, THEME_TEXT_CONTRAST, -1),
519
+ textDark: walkToContrast(raw, DARK_PAGE, THEME_TEXT_CONTRAST, 1)
520
+ };
521
+ }
522
+ /** Figma's Accent modes, exactly as the Accent collection holds them. */ const FIGMA_ACCENTS = [
523
+ {
524
+ id: 'blue',
525
+ label: 'Blue',
526
+ accent: {
527
+ fill: '#0073ae',
528
+ icon: '#0091d5',
529
+ soft: '#00b0ff29',
530
+ ring: '#00b0ff',
531
+ on: '#ffffff',
532
+ hover: '#00669b',
533
+ pressed: '#005884'
534
+ }
535
+ },
536
+ {
537
+ id: 'violet',
538
+ label: 'Violet',
539
+ accent: {
540
+ fill: '#5b3fd6',
541
+ icon: '#8c72f5',
542
+ soft: '#7a5cf02e',
543
+ ring: '#7a5cf0',
544
+ on: '#ffffff',
545
+ hover: '#4e34c2',
546
+ pressed: '#4129aa'
547
+ }
548
+ },
549
+ {
550
+ id: 'pink',
551
+ label: 'Pink',
552
+ accent: {
553
+ fill: '#e040fb',
554
+ icon: '#c93ae3',
555
+ soft: '#e040fb29',
556
+ ring: '#e040fb',
557
+ on: '#1e0424',
558
+ hover: '#cc2ee6',
559
+ pressed: '#b523cf'
560
+ }
561
+ },
562
+ {
563
+ id: 'workspaceBrand',
564
+ label: 'Workspace brand',
565
+ accent: {
566
+ fill: '#2e7d5b',
567
+ icon: '#2e8f66',
568
+ soft: '#2e7d5b2e',
569
+ ring: '#3fa77a',
570
+ on: '#ffffff',
571
+ hover: '#276c4e',
572
+ pressed: '#205a41'
573
+ }
574
+ }
575
+ ];
576
+ /** The named accents, Figma's own values with their text shades. */ export const THEME_ACCENTS = FIGMA_ACCENTS.map(({ id, label, accent })=>({
577
+ id,
578
+ label,
579
+ accent: _extends({}, accent, accentText(accent.ring))
580
+ }));
581
+ /** The accent picker's swatches, in Figma's order. */ export const THEME_ACCENT_SWATCHES = [
582
+ {
583
+ id: 'aglynBlue',
584
+ label: 'Brand blue',
585
+ color: '#0073ae'
586
+ },
587
+ {
588
+ id: 'sky',
589
+ label: 'Sky',
590
+ color: '#00b0ff'
591
+ },
592
+ {
593
+ id: 'violet',
594
+ label: 'Violet',
595
+ color: '#7a5cf0'
596
+ },
597
+ {
598
+ id: 'pink',
599
+ label: 'Pink',
600
+ color: '#e040fb'
601
+ },
602
+ {
603
+ id: 'green',
604
+ label: 'Green',
605
+ color: '#2e7d5b'
606
+ },
607
+ {
608
+ id: 'warm',
609
+ label: 'Warm',
610
+ color: '#c2571a'
611
+ }
612
+ ];
613
+ /**
614
+ * Every control's shade of one accent color (AGL-3950).
615
+ *
616
+ * A color one of Figma's Accent modes is drawn from (its ring) answers with
617
+ * that mode exactly: Figma wins where it defines a value. Any other color is
618
+ * derived:
619
+ *
620
+ * 1. White text on the color already reads (4.5:1): the fill is the color,
621
+ * `on` is white.
622
+ * 2. The color carries white text only at the large-text bar (3:1 up to
623
+ * 4.5:1) and near-black text reads on it at 4.5:1: the fill KEEPS the
624
+ * brand color and `on` flips to near-black, the color's own hue at 8%
625
+ * lightness. Darkening such a color far enough for white text moves it
626
+ * visibly off the brand; this is Figma's Pink (#E040FB, 3.34:1). The 3:1
627
+ * threshold is the one that sends Pink here and Blue (2.43:1) and the
628
+ * workspace green (2.99:1) to rule 3, as Figma draws them.
629
+ * 3. Otherwise the fill is the color darkened in 2% HSL-lightness steps until
630
+ * white reaches 4.5:1, and `on` is white.
631
+ *
632
+ * hover and pressed are the fill 4% and 8% darker (HSL lightness), soft is
633
+ * the color at 16% alpha, ring is the color, icon is the color darkened in 2%
634
+ * steps until it reaches 3:1 on the light surface, and the two text shades
635
+ * walk it (darker on light, lighter on dark) until 4.5:1 on that surface.
636
+ * Null for anything that is not `#RRGGBB`.
637
+ */ export function deriveAccent(color) {
638
+ if (!isThemeColor(color)) return null;
639
+ const raw = color.toLowerCase();
640
+ const named = THEME_ACCENTS.find((entry)=>entry.accent.ring === raw);
641
+ if (named) return _extends({}, named.accent);
642
+ const white = '#ffffff';
643
+ const onWhite = themeContrast(raw, white);
644
+ let fill = raw;
645
+ let on = white;
646
+ if (onWhite < THEME_TEXT_CONTRAST) {
647
+ const rgb = channels(raw);
648
+ const [h, s] = toHsl(rgb);
649
+ const nearBlack = fromHsl(h, Math.min(s, 0.8), DARK_ON_LIGHTNESS);
650
+ if (onWhite >= THEME_NON_TEXT_CONTRAST && themeContrast(raw, nearBlack) >= THEME_TEXT_CONTRAST) {
651
+ on = nearBlack;
652
+ } else {
653
+ fill = walkToContrast(raw, white, THEME_TEXT_CONTRAST, -1);
654
+ }
655
+ }
656
+ return _extends({
657
+ fill,
658
+ icon: walkToContrast(raw, LIGHT_PAGE, THEME_NON_TEXT_CONTRAST, -1),
659
+ soft: withAlpha(raw, 0.16),
660
+ ring: raw,
661
+ on,
662
+ hover: shiftLightness(fill, -0.04),
663
+ pressed: shiftLightness(fill, -0.08)
664
+ }, accentText(raw));
665
+ }
666
+ // ── Density ─────────────────────────────────────────────────────────────────
667
+ /** Figma's Density modes. */ export const THEME_DENSITIES = [
668
+ {
669
+ id: 'comfortable',
670
+ label: 'Comfortable',
671
+ space2: 2,
672
+ space4: 4,
673
+ space6: 6,
674
+ space8: 8,
675
+ space10: 10,
676
+ space12: 12,
677
+ space16: 16,
678
+ space20: 20,
679
+ space24: 24,
680
+ space32: 32,
681
+ space40: 40,
682
+ space48: 48,
683
+ space64: 64,
684
+ radiusXs: 4,
685
+ radiusSm: 6,
686
+ radiusMd: 8,
687
+ radiusLg: 12,
688
+ radiusXl: 16,
689
+ radius2xl: 24,
690
+ radiusFull: 999,
691
+ rowHeight: 32,
692
+ controlHeight: 32,
693
+ listRowHeight: 60,
694
+ phoneRowHeight: 52,
695
+ titlebarHeight: 52
696
+ },
697
+ {
698
+ id: 'compact',
699
+ label: 'Compact',
700
+ space2: 2,
701
+ space4: 4,
702
+ space6: 4,
703
+ space8: 6,
704
+ space10: 8,
705
+ space12: 8,
706
+ space16: 12,
707
+ space20: 16,
708
+ space24: 20,
709
+ space32: 24,
710
+ space40: 32,
711
+ space48: 40,
712
+ space64: 48,
713
+ radiusXs: 4,
714
+ radiusSm: 6,
715
+ radiusMd: 8,
716
+ radiusLg: 12,
717
+ radiusXl: 16,
718
+ radius2xl: 24,
719
+ radiusFull: 999,
720
+ rowHeight: 28,
721
+ controlHeight: 28,
722
+ listRowHeight: 48,
723
+ phoneRowHeight: 44,
724
+ titlebarHeight: 44
725
+ }
726
+ ];
727
+ /** A theme by id, Aglyn for anything unknown. */ export function themePalette(id) {
728
+ var _THEME_PALETTES_find;
729
+ return (_THEME_PALETTES_find = THEME_PALETTES.find((entry)=>entry.id === id)) != null ? _THEME_PALETTES_find : THEME_PALETTES[0];
730
+ }
731
+
732
+ //# sourceMappingURL=native-themes.js.map