@wildo-ai/presets-components-3d 1.1.6

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 (102) hide show
  1. package/LICENSE +34 -0
  2. package/dist/esm/frame/frame-3d-style.d.ts +156 -0
  3. package/dist/esm/frame/frame-3d-style.d.ts.map +1 -0
  4. package/dist/esm/frame/frame-3d-style.js +229 -0
  5. package/dist/esm/frame/frame-3d-style.js.map +1 -0
  6. package/dist/esm/frame/frame-appearance.d.ts +99 -0
  7. package/dist/esm/frame/frame-appearance.d.ts.map +1 -0
  8. package/dist/esm/frame/frame-appearance.js +207 -0
  9. package/dist/esm/frame/frame-appearance.js.map +1 -0
  10. package/dist/esm/frame/frame-canvas.d.ts +63 -0
  11. package/dist/esm/frame/frame-canvas.d.ts.map +1 -0
  12. package/dist/esm/frame/frame-canvas.js +134 -0
  13. package/dist/esm/frame/frame-canvas.js.map +1 -0
  14. package/dist/esm/frame/frame-canvas.lazy.d.ts +21 -0
  15. package/dist/esm/frame/frame-canvas.lazy.d.ts.map +1 -0
  16. package/dist/esm/frame/frame-canvas.lazy.js +31 -0
  17. package/dist/esm/frame/frame-canvas.lazy.js.map +1 -0
  18. package/dist/esm/frame/frame-color.d.ts +24 -0
  19. package/dist/esm/frame/frame-color.d.ts.map +1 -0
  20. package/dist/esm/frame/frame-color.js +52 -0
  21. package/dist/esm/frame/frame-color.js.map +1 -0
  22. package/dist/esm/frame/frame-driver.d.ts +60 -0
  23. package/dist/esm/frame/frame-driver.d.ts.map +1 -0
  24. package/dist/esm/frame/frame-driver.js +207 -0
  25. package/dist/esm/frame/frame-driver.js.map +1 -0
  26. package/dist/esm/frame/frame-renderer.d.ts +141 -0
  27. package/dist/esm/frame/frame-renderer.d.ts.map +1 -0
  28. package/dist/esm/frame/frame-renderer.js +635 -0
  29. package/dist/esm/frame/frame-renderer.js.map +1 -0
  30. package/dist/esm/frame/frame-shaders.d.ts +30 -0
  31. package/dist/esm/frame/frame-shaders.d.ts.map +1 -0
  32. package/dist/esm/frame/frame-shaders.js +97 -0
  33. package/dist/esm/frame/frame-shaders.js.map +1 -0
  34. package/dist/esm/frame/use-frame-appearance.d.ts +17 -0
  35. package/dist/esm/frame/use-frame-appearance.d.ts.map +1 -0
  36. package/dist/esm/frame/use-frame-appearance.js +38 -0
  37. package/dist/esm/frame/use-frame-appearance.js.map +1 -0
  38. package/dist/esm/index.d.ts +14 -0
  39. package/dist/esm/index.d.ts.map +1 -0
  40. package/dist/esm/index.js +14 -0
  41. package/dist/esm/index.js.map +1 -0
  42. package/dist/esm/layout/header-first-shell-container.d.ts +22 -0
  43. package/dist/esm/layout/header-first-shell-container.d.ts.map +1 -0
  44. package/dist/esm/layout/header-first-shell-container.js +31 -0
  45. package/dist/esm/layout/header-first-shell-container.js.map +1 -0
  46. package/dist/esm/layout/layout-3d-definition.d.ts +54 -0
  47. package/dist/esm/layout/layout-3d-definition.d.ts.map +1 -0
  48. package/dist/esm/layout/layout-3d-definition.js +106 -0
  49. package/dist/esm/layout/layout-3d-definition.js.map +1 -0
  50. package/dist/esm/layout/layout-3d-transition-choreography.d.ts +30 -0
  51. package/dist/esm/layout/layout-3d-transition-choreography.d.ts.map +1 -0
  52. package/dist/esm/layout/layout-3d-transition-choreography.js +43 -0
  53. package/dist/esm/layout/layout-3d-transition-choreography.js.map +1 -0
  54. package/dist/esm/layout/layout-3d-vocabulary.d.ts +118 -0
  55. package/dist/esm/layout/layout-3d-vocabulary.d.ts.map +1 -0
  56. package/dist/esm/layout/layout-3d-vocabulary.js +104 -0
  57. package/dist/esm/layout/layout-3d-vocabulary.js.map +1 -0
  58. package/dist/esm/layout/shell-layout-placement.d.ts +26 -0
  59. package/dist/esm/layout/shell-layout-placement.d.ts.map +1 -0
  60. package/dist/esm/layout/shell-layout-placement.js +18 -0
  61. package/dist/esm/layout/shell-layout-placement.js.map +1 -0
  62. package/dist/esm/layout-3d-main-container.d.ts +73 -0
  63. package/dist/esm/layout-3d-main-container.d.ts.map +1 -0
  64. package/dist/esm/layout-3d-main-container.js +124 -0
  65. package/dist/esm/layout-3d-main-container.js.map +1 -0
  66. package/dist/esm/register-3d-layout.d.ts +40 -0
  67. package/dist/esm/register-3d-layout.d.ts.map +1 -0
  68. package/dist/esm/register-3d-layout.js +72 -0
  69. package/dist/esm/register-3d-layout.js.map +1 -0
  70. package/dist/esm/shell-region-measurement.d.ts +75 -0
  71. package/dist/esm/shell-region-measurement.d.ts.map +1 -0
  72. package/dist/esm/shell-region-measurement.js +75 -0
  73. package/dist/esm/shell-region-measurement.js.map +1 -0
  74. package/dist/esm/spatial/spatial-camera.d.ts +106 -0
  75. package/dist/esm/spatial/spatial-camera.d.ts.map +1 -0
  76. package/dist/esm/spatial/spatial-camera.js +151 -0
  77. package/dist/esm/spatial/spatial-camera.js.map +1 -0
  78. package/dist/esm/spatial/spatial-environment.d.ts +6 -0
  79. package/dist/esm/spatial/spatial-environment.d.ts.map +1 -0
  80. package/dist/esm/spatial/spatial-environment.js +166 -0
  81. package/dist/esm/spatial/spatial-environment.js.map +1 -0
  82. package/dist/esm/spatial/spatial-environment.lazy.d.ts +10 -0
  83. package/dist/esm/spatial/spatial-environment.lazy.d.ts.map +1 -0
  84. package/dist/esm/spatial/spatial-environment.lazy.js +13 -0
  85. package/dist/esm/spatial/spatial-environment.lazy.js.map +1 -0
  86. package/dist/esm/spatial/spatial-shell-container.d.ts +21 -0
  87. package/dist/esm/spatial/spatial-shell-container.d.ts.map +1 -0
  88. package/dist/esm/spatial/spatial-shell-container.js +231 -0
  89. package/dist/esm/spatial/spatial-shell-container.js.map +1 -0
  90. package/dist/esm/spatial/spatial-stage-store.d.ts +27 -0
  91. package/dist/esm/spatial/spatial-stage-store.d.ts.map +1 -0
  92. package/dist/esm/spatial/spatial-stage-store.js +33 -0
  93. package/dist/esm/spatial/spatial-stage-store.js.map +1 -0
  94. package/dist/esm/tokens/design-token-resolution.d.ts +110 -0
  95. package/dist/esm/tokens/design-token-resolution.d.ts.map +1 -0
  96. package/dist/esm/tokens/design-token-resolution.js +410 -0
  97. package/dist/esm/tokens/design-token-resolution.js.map +1 -0
  98. package/dist/esm/tokens/use-design-token-resolution.d.ts +22 -0
  99. package/dist/esm/tokens/use-design-token-resolution.d.ts.map +1 -0
  100. package/dist/esm/tokens/use-design-token-resolution.js +49 -0
  101. package/dist/esm/tokens/use-design-token-resolution.js.map +1 -0
  102. package/package.json +61 -0
@@ -0,0 +1,410 @@
1
+ import { converter, parse } from 'culori';
2
+ import { DesignSystemToken } from '@wildo-ai/presets-components-models';
3
+ /**
4
+ * What a design-token value resolved to, in the units a WebGL frame uses (#1819).
5
+ *
6
+ * Token values are authored as CSS strings — `oklch(...)`, `color-mix(...)`, `150ms`, `cubic-bezier(...)`,
7
+ * `0.5rem` — because the DOM consumes them as CSS. The 3D frame draws with numbers, so each value is
8
+ * resolved once, per theme and per light/dark mode, into one of these kinds.
9
+ */
10
+ export var ResolvedDesignTokenKind;
11
+ (function (ResolvedDesignTokenKind) {
12
+ /** A colour, in linear-light sRGB (what three.js expects of a material colour) plus alpha. */
13
+ ResolvedDesignTokenKind["COLOR"] = "color";
14
+ /** A duration (`ms` / `s`), in seconds. */
15
+ ResolvedDesignTokenKind["DURATION"] = "duration";
16
+ /** An easing, as the four control points of a cubic Bézier. */
17
+ ResolvedDesignTokenKind["EASING"] = "easing";
18
+ /** A length (`px` / `rem`), in CSS pixels. */
19
+ ResolvedDesignTokenKind["SIZE"] = "size";
20
+ /** A plain number (an opacity). */
21
+ ResolvedDesignTokenKind["NUMBER"] = "number";
22
+ })(ResolvedDesignTokenKind || (ResolvedDesignTokenKind = {}));
23
+ /**
24
+ * Why a value was NOT resolved. A value the frame cannot use is reported, never guessed: a wrong number in
25
+ * a WebGL scene is invisible until someone notices the frame looks off.
26
+ */
27
+ export var UnresolvedDesignTokenReason;
28
+ (function (UnresolvedDesignTokenReason) {
29
+ /**
30
+ * The value depends on the viewport or the containing layout (`clamp()`, `calc()`, viewport units such as `vw`
31
+ * and `dvh`, container units such as `cqi`, `%`), so it has no single number.
32
+ */
33
+ UnresolvedDesignTokenReason["VIEWPORT_RELATIVE"] = "viewport-relative";
34
+ /**
35
+ * A reference (`var()`, `color-mix()`) that neither the token graph nor the browser probe resolved — an
36
+ * unknown custom property, a mixing space this resolver does not model, or a percentage CSS rejects.
37
+ */
38
+ UnresolvedDesignTokenReason["REFERENCE_UNRESOLVED"] = "reference-unresolved";
39
+ /** Not one of the forms the frame reads: a shadow list, a list of palette names, a stepped easing. */
40
+ UnresolvedDesignTokenReason["UNSUPPORTED_FORM"] = "unsupported-form";
41
+ })(UnresolvedDesignTokenReason || (UnresolvedDesignTokenReason = {}));
42
+ /** Where a resolved colour came from. The provenance travels through references and mixes. */
43
+ export var DesignTokenColorSource;
44
+ (function (DesignTokenColorSource) {
45
+ /** The token's own value was a colour literal. */
46
+ DesignTokenColorSource["VALUE"] = "value";
47
+ /** Resolved through other tokens (`var(--token)`, `color-mix()` of tokens) with no browser involved. */
48
+ DesignTokenColorSource["TOKEN_GRAPH"] = "token-graph";
49
+ /**
50
+ * At least one input came from the browser probe, which answers against the document's custom properties at
51
+ * the moment of the call — see `useDesignTokenResolution` for why such a result is recomputed after a commit.
52
+ */
53
+ DesignTokenColorSource["BROWSER_PROBE"] = "browser-probe";
54
+ })(DesignTokenColorSource || (DesignTokenColorSource = {}));
55
+ /** The hue interpolation methods of CSS Color 4 §13.5, as `color-mix()` spells them. */
56
+ var HueInterpolationMethod;
57
+ (function (HueInterpolationMethod) {
58
+ HueInterpolationMethod["SHORTER"] = "shorter";
59
+ HueInterpolationMethod["LONGER"] = "longer";
60
+ HueInterpolationMethod["INCREASING"] = "increasing";
61
+ HueInterpolationMethod["DECREASING"] = "decreasing";
62
+ })(HueInterpolationMethod || (HueInterpolationMethod = {}));
63
+ /**
64
+ * The CSS easing keywords, as cubic-Bézier control points. `linear` is the identity easing, which the
65
+ * Bézier (0, 0, 1, 1) reproduces exactly.
66
+ * spec-cite: CSS Easing Functions Level 1 §2.2 (the four keyword curves) and §2.1 (`linear` is the identity) — https://www.w3.org/TR/css-easing-1/
67
+ */
68
+ const EASING_KEYWORDS = {
69
+ linear: [0, 0, 1, 1],
70
+ ease: [0.25, 0.1, 0.25, 1],
71
+ 'ease-in': [0.42, 0, 1, 1],
72
+ 'ease-out': [0, 0, 0.58, 1],
73
+ 'ease-in-out': [0.42, 0, 0.58, 1],
74
+ };
75
+ /**
76
+ * The `color-mix()` interpolation spaces this resolver models: the culori mode, its channels, and which channel
77
+ * is a hue (polar spaces). Anything else is reported unresolved rather than approximated.
78
+ */
79
+ const COLOR_MIX_SPACES = {
80
+ oklab: { mode: 'oklab', channels: ['l', 'a', 'b'] },
81
+ lab: { mode: 'lab', channels: ['l', 'a', 'b'] },
82
+ srgb: { mode: 'rgb', channels: ['r', 'g', 'b'] },
83
+ 'srgb-linear': { mode: 'lrgb', channels: ['r', 'g', 'b'] },
84
+ oklch: { mode: 'oklch', channels: ['l', 'c', 'h'], hue: 'h' },
85
+ lch: { mode: 'lch', channels: ['l', 'c', 'h'], hue: 'h' },
86
+ };
87
+ /**
88
+ * Below this chroma, a hue produced by CONVERTING into a polar space is float residue of an achromatic colour —
89
+ * a powerless component, which CSS treats as missing and carries forward from the other colour.
90
+ */
91
+ const POWERLESS_CHROMA = 1e-4;
92
+ const NUMBER = /^-?(?:\d+\.?\d*|\.\d+)$/;
93
+ const DURATION = /^(-?(?:\d+\.?\d*|\.\d+))(ms|s)$/;
94
+ const SIZE = /^(-?(?:\d+\.?\d*|\.\d+))(px|rem)$/;
95
+ const CUBIC_BEZIER = /^cubic-bezier\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^,)]+)\)$/;
96
+ /** `var(--name)` or `var(--name, fallback)`. */
97
+ const VAR_REFERENCE = /^var\(\s*--([a-z0-9-]+)\s*(?:,\s*(.+))?\)$/i;
98
+ const COLOR_MIX = /^color-mix\(\s*in\s+([a-z0-9-]+)(?:\s+(shorter|longer|increasing|decreasing)\s+hue)?\s*,(.*)\)$/i;
99
+ const VIEWPORT_RELATIVE = /\b(?:clamp|calc|min|max)\(|\d(?:[dsl]?v(?:w|h|i|b|min|max)|cq(?:w|h|i|b|min|max)|%)(?![\w-])/;
100
+ /** A non-colour alias chain deeper than this is a cycle in the token graph (`a` → `b` → `a`), not a value. */
101
+ const MAX_REFERENCE_DEPTH = 12;
102
+ const toLinearRgb = converter('lrgb');
103
+ /**
104
+ * Resolve every token's value into the frame's units, or say why not.
105
+ *
106
+ * `values` is the whole token set for ONE theme and ONE mode — what `useUI().getTokenValue` returns — and
107
+ * must be the whole set, because a formula refers to other tokens: `border-strong` is
108
+ * `color-mix(in oklab, var(--foreground) 45%, var(--background))`.
109
+ *
110
+ * References to other tokens are resolved THROUGH THE TOKEN GRAPH rather than by asking the browser: the
111
+ * browser answers against whatever custom properties the document holds at the moment, which lags a light/dark
112
+ * switch, while the graph is the mode's own values by construction. The browser probe is the fallback for a
113
+ * reference the graph cannot express, and a result that used it says so (`BROWSER_PROBE`).
114
+ */
115
+ export function resolveDesignTokens(values, options = {}) {
116
+ const rootFontSizePx = options.rootFontSizePx ?? 16;
117
+ const colorCache = new Map();
118
+ const probeCache = new Map();
119
+ const resolving = new Set();
120
+ const probe = (value) => {
121
+ if (probeCache.has(value))
122
+ return probeCache.get(value) ?? null;
123
+ const computed = options.readComputedColor?.(value) ?? null;
124
+ const parsed = computed ? parse(computed) : undefined;
125
+ const result = parsed ? { color: parsed, source: DesignTokenColorSource.BROWSER_PROBE } : null;
126
+ probeCache.set(value, result);
127
+ return result;
128
+ };
129
+ const resolveColor = (raw) => {
130
+ const value = raw.trim();
131
+ if (colorCache.has(value))
132
+ return colorCache.get(value) ?? null;
133
+ // A value already being resolved refers to itself — typically `var(--x)` as the value of an unconfigured
134
+ // token `x` (`getTokenValue` returns exactly that). The graph cannot answer it; the browser may. This is the
135
+ // ONLY unbounded case: every other chain is finite, because the token set and each string are.
136
+ if (resolving.has(value))
137
+ return isColorReference(value) ? probe(value) : null;
138
+ resolving.add(value);
139
+ let result = null;
140
+ try {
141
+ const direct = COLOR_MIX.test(value) || VAR_REFERENCE.test(value) ? undefined : parse(value);
142
+ if (direct) {
143
+ result = { color: direct, source: DesignTokenColorSource.VALUE };
144
+ }
145
+ else {
146
+ const reference = VAR_REFERENCE.exec(value);
147
+ const mix = COLOR_MIX.exec(value);
148
+ if (reference) {
149
+ const name = reference[1];
150
+ if (isDesignSystemToken(name) && values[name] !== undefined) {
151
+ // A token the graph holds: its own value decides, including a probe of THAT value when it needs one.
152
+ // Asking the browser here as well would re-ask the same question, and for an alias to a non-colour
153
+ // token (a duration) it would ask about something that is not a colour at all.
154
+ const target = resolveColor(values[name]);
155
+ result = target ? { color: target.color, source: graphSource(target.source) } : null;
156
+ }
157
+ else {
158
+ // Not a token: only the document can say whether the property is defined, and CSS uses the fallback
159
+ // ONLY when it is not. So the whole expression goes to the browser, which applies that rule itself;
160
+ // resolving the fallback here would guess that the property is undefined.
161
+ result = probe(value);
162
+ }
163
+ }
164
+ else if (mix) {
165
+ result = mixColors(mix[1].toLowerCase(), mix[2]?.toLowerCase(), mix[3], resolveColor)
166
+ // A mix this resolver does not model (another space) may still be one the browser computes.
167
+ ?? probe(value);
168
+ }
169
+ }
170
+ }
171
+ finally {
172
+ resolving.delete(value);
173
+ }
174
+ colorCache.set(value, result);
175
+ return result;
176
+ };
177
+ const resolveOne = (token, raw, depth) => {
178
+ const value = raw.trim();
179
+ const resolved = (r) => ({ token, value, resolved: r });
180
+ const unresolved = (reason) => ({ token, value, unresolved: reason });
181
+ if (NUMBER.test(value))
182
+ return resolved({ kind: ResolvedDesignTokenKind.NUMBER, value: Number(value) });
183
+ const duration = DURATION.exec(value);
184
+ if (duration)
185
+ return resolved({ kind: ResolvedDesignTokenKind.DURATION, seconds: Number(duration[1]) / (duration[2] === 'ms' ? 1000 : 1) });
186
+ const size = SIZE.exec(value);
187
+ if (size)
188
+ return resolved({ kind: ResolvedDesignTokenKind.SIZE, px: Number(size[1]) * (size[2] === 'rem' ? rootFontSizePx : 1) });
189
+ if (Object.hasOwn(EASING_KEYWORDS, value))
190
+ return resolved({ kind: ResolvedDesignTokenKind.EASING, controlPoints: EASING_KEYWORDS[value] });
191
+ const bezier = CUBIC_BEZIER.exec(value);
192
+ if (bezier) {
193
+ const points = bezier.slice(1, 5).map(Number);
194
+ return points.every(Number.isFinite) ? resolved({ kind: ResolvedDesignTokenKind.EASING, controlPoints: points }) : unresolved(UnresolvedDesignTokenReason.UNSUPPORTED_FORM);
195
+ }
196
+ const color = resolveColor(value);
197
+ if (color) {
198
+ const linear = toLinearRgb(color.color);
199
+ return resolved({
200
+ kind: ResolvedDesignTokenKind.COLOR,
201
+ linearRgb: [Number(linear.r ?? 0), Number(linear.g ?? 0), Number(linear.b ?? 0)],
202
+ alpha: color.color.alpha ?? 1,
203
+ source: color.source,
204
+ });
205
+ }
206
+ // A reference to another token that is NOT a colour (a duration token aliasing another): the alias takes
207
+ // the target's resolution, whatever its kind.
208
+ const reference = VAR_REFERENCE.exec(value);
209
+ if (reference && isDesignSystemToken(reference[1]) && values[reference[1]] !== undefined && reference[1] !== token && depth < MAX_REFERENCE_DEPTH) {
210
+ const aliased = resolveOne(reference[1], values[reference[1]], depth + 1);
211
+ return 'resolved' in aliased ? resolved(aliased.resolved) : unresolved(aliased.unresolved);
212
+ }
213
+ if (isColorReference(value))
214
+ return unresolved(UnresolvedDesignTokenReason.REFERENCE_UNRESOLVED);
215
+ // Only a SINGLE value can be viewport-relative. A list (a shadow, `0 1px 2px rgb(0 0 0 / 5%)`) is unsupported
216
+ // whatever percentages its colours carry.
217
+ if (isSingleComponent(value) && VIEWPORT_RELATIVE.test(value))
218
+ return unresolved(UnresolvedDesignTokenReason.VIEWPORT_RELATIVE);
219
+ return unresolved(UnresolvedDesignTokenReason.UNSUPPORTED_FORM);
220
+ };
221
+ const resolutions = new Map();
222
+ for (const token of Object.values(DesignSystemToken)) {
223
+ const value = values[token];
224
+ if (value !== undefined)
225
+ resolutions.set(token, resolveOne(token, value, 0));
226
+ }
227
+ return resolutions;
228
+ }
229
+ function isColorReference(value) {
230
+ return /^var\(/i.test(value) || /^color-mix\(/i.test(value);
231
+ }
232
+ /** A reference resolved through the graph keeps BROWSER_PROBE if its target needed the browser. */
233
+ function graphSource(inner) {
234
+ return inner === DesignTokenColorSource.BROWSER_PROBE ? DesignTokenColorSource.BROWSER_PROBE : DesignTokenColorSource.TOKEN_GRAPH;
235
+ }
236
+ function isDesignSystemToken(name) {
237
+ return Object.values(DesignSystemToken).includes(name);
238
+ }
239
+ /**
240
+ * `color-mix(in <space> [<method> hue], <colour> [<p>%], <colour> [<p>%])`, computed.
241
+ *
242
+ * - Percentages (CSS Color 5 §3.2): each must lie in 0–100%; an omitted one makes the pair sum to 100%; any
243
+ * other total is normalised to 100%, a total under 100% becomes an alpha multiplier, and a total of 0% is
244
+ * rejected (CSS makes the whole function invalid).
245
+ * - A hue method (`in oklch longer hue`) is valid only for a polar space; on a rectangular one it is rejected.
246
+ * - Interpolation (CSS Color 4 §13): a missing component — including a hue made powerless by converting an
247
+ * achromatic colour into a polar space — is carried forward from the other colour; colours are premultiplied
248
+ * by alpha, EXCEPT the hue of a polar space; the hue follows the requested method.
249
+ * spec-cite: CSS Color Module Level 5 §3.2 (percentage normalization) and §3.3 (calculating the result) — https://www.w3.org/TR/css-color-5/
250
+ * spec-cite: CSS Color Module Level 4 §13.3 (missing components), §13.4 (premultiplied alpha, hue not premultiplied), §13.5.1–13.5.4 (hue methods) — https://www.w3.org/TR/css-color-4/
251
+ */
252
+ function mixColors(space, requestedHueMethod, argumentsText, resolveArgument) {
253
+ const model = COLOR_MIX_SPACES[space];
254
+ if (!model)
255
+ return null;
256
+ // A hue method names a hue to interpolate; a rectangular space has none, and CSS rejects the declaration.
257
+ if (requestedHueMethod !== undefined && !model.hue)
258
+ return null;
259
+ const hueMethod = requestedHueMethod ?? HueInterpolationMethod.SHORTER;
260
+ const parts = splitTopLevel(argumentsText);
261
+ if (parts.length !== 2)
262
+ return null;
263
+ const parsed = parts.map((part) => {
264
+ const match = /^(.*?)(?:\s+(-?\d+(?:\.\d+)?)%)?$/.exec(part.trim());
265
+ return match ? { colorText: match[1].trim(), percentage: match[2] === undefined ? undefined : Number(match[2]) } : null;
266
+ });
267
+ if (parsed.some((p) => p === null))
268
+ return null;
269
+ const [first, second] = parsed;
270
+ if ([first.percentage, second.percentage].some((p) => p !== undefined && (p < 0 || p > 100)))
271
+ return null;
272
+ let p1 = first.percentage;
273
+ let p2 = second.percentage;
274
+ if (p1 === undefined && p2 === undefined) {
275
+ p1 = 50;
276
+ p2 = 50;
277
+ }
278
+ else if (p1 === undefined) {
279
+ p1 = 100 - p2;
280
+ }
281
+ else if (p2 === undefined) {
282
+ p2 = 100 - p1;
283
+ }
284
+ const sum = p1 + p2;
285
+ if (sum === 0)
286
+ return null;
287
+ const alphaMultiplier = sum < 100 ? sum / 100 : 1;
288
+ const progress = p2 / sum;
289
+ const a = resolveArgument(first.colorText);
290
+ const b = resolveArgument(second.colorText);
291
+ if (!a || !b)
292
+ return null;
293
+ const mixed = interpolateCss(a.color, b.color, model, hueMethod, progress);
294
+ mixed.alpha = (mixed.alpha ?? 1) * alphaMultiplier;
295
+ const source = a.source === DesignTokenColorSource.BROWSER_PROBE || b.source === DesignTokenColorSource.BROWSER_PROBE
296
+ ? DesignTokenColorSource.BROWSER_PROBE
297
+ : DesignTokenColorSource.TOKEN_GRAPH;
298
+ return { color: mixed, source };
299
+ }
300
+ /** CSS Color 4 §13 interpolation of two colours in one space, at progress `t` (0 = first, 1 = second). */
301
+ function interpolateCss(first, second, model, hueMethod, t) {
302
+ const toSpace = converter(model.mode);
303
+ const read = (color) => {
304
+ const converted = toSpace(color);
305
+ const channels = {};
306
+ for (const channel of model.channels) {
307
+ const raw = converted[channel];
308
+ channels[channel] = typeof raw === 'number' && Number.isFinite(raw) ? raw : undefined;
309
+ }
310
+ // A hue made powerless by conversion (an achromatic colour entering a polar space) is missing.
311
+ if (model.hue && color.mode !== model.mode && (channels.c ?? 0) < POWERLESS_CHROMA)
312
+ channels[model.hue] = undefined;
313
+ // §13.5: both angles are brought into [0, 360) BEFORE the hue method adjusts them. `oklch(… 730)` is hue 10.
314
+ if (model.hue && channels[model.hue] !== undefined)
315
+ channels[model.hue] = ((channels[model.hue] % 360) + 360) % 360;
316
+ channels.alpha = typeof color.alpha === 'number' ? color.alpha : 1;
317
+ return channels;
318
+ };
319
+ const c1 = read(first);
320
+ const c2 = read(second);
321
+ // §13.3: a component missing in ONE colour takes the other's value; missing in both stays missing (0).
322
+ for (const channel of [...model.channels, 'alpha']) {
323
+ if (c1[channel] === undefined && c2[channel] !== undefined)
324
+ c1[channel] = c2[channel];
325
+ if (c2[channel] === undefined && c1[channel] !== undefined)
326
+ c2[channel] = c1[channel];
327
+ }
328
+ if (model.hue && c1[model.hue] !== undefined && c2[model.hue] !== undefined) {
329
+ let h1 = c1[model.hue];
330
+ let h2 = c2[model.hue];
331
+ const delta = h2 - h1;
332
+ // §13.5.1–13.5.4, verbatim adjustments.
333
+ if (hueMethod === HueInterpolationMethod.SHORTER) {
334
+ if (delta > 180)
335
+ h1 += 360;
336
+ else if (delta < -180)
337
+ h2 += 360;
338
+ }
339
+ if (hueMethod === HueInterpolationMethod.LONGER) {
340
+ if (delta > 0 && delta < 180)
341
+ h1 += 360;
342
+ else if (delta > -180 && delta <= 0)
343
+ h2 += 360;
344
+ }
345
+ if (hueMethod === HueInterpolationMethod.INCREASING) {
346
+ if (h2 < h1)
347
+ h2 += 360;
348
+ }
349
+ if (hueMethod === HueInterpolationMethod.DECREASING) {
350
+ if (h1 < h2)
351
+ h1 += 360;
352
+ }
353
+ c1[model.hue] = h1;
354
+ c2[model.hue] = h2;
355
+ }
356
+ const a1 = c1.alpha;
357
+ const a2 = c2.alpha;
358
+ const alpha = a1 + (a2 - a1) * t;
359
+ const result = { mode: model.mode, alpha };
360
+ for (const channel of model.channels) {
361
+ const v1 = c1[channel];
362
+ const v2 = c2[channel];
363
+ if (v1 === undefined || v2 === undefined) {
364
+ result[channel] = channel === model.hue ? undefined : 0;
365
+ continue;
366
+ }
367
+ if (channel === model.hue) {
368
+ // §13.4: the hue is NOT premultiplied.
369
+ result[channel] = (((v1 + (v2 - v1) * t) % 360) + 360) % 360;
370
+ continue;
371
+ }
372
+ const premultiplied = v1 * a1 + (v2 * a2 - v1 * a1) * t;
373
+ result[channel] = alpha === 0 ? premultiplied : premultiplied / alpha;
374
+ }
375
+ return result;
376
+ }
377
+ /** Split on commas that are not inside parentheses. */
378
+ function splitTopLevel(text) {
379
+ const parts = [];
380
+ let depth = 0;
381
+ let current = '';
382
+ for (const character of text) {
383
+ if (character === '(')
384
+ depth += 1;
385
+ if (character === ')')
386
+ depth -= 1;
387
+ if (character === ',' && depth === 0) {
388
+ parts.push(current);
389
+ current = '';
390
+ continue;
391
+ }
392
+ current += character;
393
+ }
394
+ parts.push(current);
395
+ return parts;
396
+ }
397
+ /** Whether a value is ONE component: no whitespace or comma outside parentheses. */
398
+ function isSingleComponent(text) {
399
+ let depth = 0;
400
+ for (const character of text.trim()) {
401
+ if (character === '(')
402
+ depth += 1;
403
+ if (character === ')')
404
+ depth -= 1;
405
+ if (depth === 0 && /[\s,]/.test(character))
406
+ return false;
407
+ }
408
+ return true;
409
+ }
410
+ //# sourceMappingURL=design-token-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-token-resolution.js","sourceRoot":"","sources":["../../../../src/tokens/design-token-resolution.ts"],"names":[],"mappings":"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","sourcesContent":["import { converter, parse, type CuloriColor } from 'culori';\n\nimport { DesignSystemToken } from '@wildo-ai/presets-components-models';\n\n/**\n * What a design-token value resolved to, in the units a WebGL frame uses (#1819).\n *\n * Token values are authored as CSS strings — `oklch(...)`, `color-mix(...)`, `150ms`, `cubic-bezier(...)`,\n * `0.5rem` — because the DOM consumes them as CSS. The 3D frame draws with numbers, so each value is\n * resolved once, per theme and per light/dark mode, into one of these kinds.\n */\nexport enum ResolvedDesignTokenKind {\n /** A colour, in linear-light sRGB (what three.js expects of a material colour) plus alpha. */\n COLOR = 'color',\n /** A duration (`ms` / `s`), in seconds. */\n DURATION = 'duration',\n /** An easing, as the four control points of a cubic Bézier. */\n EASING = 'easing',\n /** A length (`px` / `rem`), in CSS pixels. */\n SIZE = 'size',\n /** A plain number (an opacity). */\n NUMBER = 'number',\n}\n\n/**\n * Why a value was NOT resolved. A value the frame cannot use is reported, never guessed: a wrong number in\n * a WebGL scene is invisible until someone notices the frame looks off.\n */\nexport enum UnresolvedDesignTokenReason {\n /**\n * The value depends on the viewport or the containing layout (`clamp()`, `calc()`, viewport units such as `vw`\n * and `dvh`, container units such as `cqi`, `%`), so it has no single number.\n */\n VIEWPORT_RELATIVE = 'viewport-relative',\n /**\n * A reference (`var()`, `color-mix()`) that neither the token graph nor the browser probe resolved — an\n * unknown custom property, a mixing space this resolver does not model, or a percentage CSS rejects.\n */\n REFERENCE_UNRESOLVED = 'reference-unresolved',\n /** Not one of the forms the frame reads: a shadow list, a list of palette names, a stepped easing. */\n UNSUPPORTED_FORM = 'unsupported-form',\n}\n\n/** Where a resolved colour came from. The provenance travels through references and mixes. */\nexport enum DesignTokenColorSource {\n /** The token's own value was a colour literal. */\n VALUE = 'value',\n /** Resolved through other tokens (`var(--token)`, `color-mix()` of tokens) with no browser involved. */\n TOKEN_GRAPH = 'token-graph',\n /**\n * At least one input came from the browser probe, which answers against the document's custom properties at\n * the moment of the call — see `useDesignTokenResolution` for why such a result is recomputed after a commit.\n */\n BROWSER_PROBE = 'browser-probe',\n}\n\n/**\n * A token value in the frame's units. The kind decides the unit: linear-light sRGB plus alpha for a colour,\n * seconds for a duration, cubic-Bézier control points for an easing, CSS pixels for a size.\n */\nexport type ResolvedDesignTokenValue =\n | { kind: ResolvedDesignTokenKind.COLOR; linearRgb: readonly [number, number, number]; alpha: number; source: DesignTokenColorSource }\n | { kind: ResolvedDesignTokenKind.DURATION; seconds: number }\n | { kind: ResolvedDesignTokenKind.EASING; controlPoints: readonly [number, number, number, number] }\n | { kind: ResolvedDesignTokenKind.SIZE; px: number }\n | { kind: ResolvedDesignTokenKind.NUMBER; value: number };\n\n/**\n * One token's outcome: its authored CSS `value`, and either what it `resolved` to or why it is `unresolved`.\n * There is no third state, so a consumer must handle both, and an unresolved token is never read as a default.\n */\nexport type DesignTokenResolution =\n | { token: DesignSystemToken; value: string; resolved: ResolvedDesignTokenValue }\n | { token: DesignSystemToken; value: string; unresolved: UnresolvedDesignTokenReason };\n\nexport interface DesignTokenResolutionOptions {\n /** The root font size a `rem` is measured against. The framework's own is 16 px. */\n rootFontSizePx?: number;\n /**\n * The browser's computed form of a colour reference the token graph cannot express: a `var()` naming a custom\n * property that is not a token (the browser also applies its fallback, when the property is undefined), or a\n * `color-mix()` this resolver does not model. `readComputedCssColorViaDom` from `@wildo-ai/saas-frontend-lib`\n * in a browser. Omitted in a test, where there is no browser to ask; such a reference is then unresolved.\n */\n readComputedColor?: (value: string) => string | null;\n}\n\n/** The hue interpolation methods of CSS Color 4 §13.5, as `color-mix()` spells them. */\nenum HueInterpolationMethod {\n SHORTER = 'shorter',\n LONGER = 'longer',\n INCREASING = 'increasing',\n DECREASING = 'decreasing',\n}\n\n/**\n * The CSS easing keywords, as cubic-Bézier control points. `linear` is the identity easing, which the\n * Bézier (0, 0, 1, 1) reproduces exactly.\n * spec-cite: CSS Easing Functions Level 1 §2.2 (the four keyword curves) and §2.1 (`linear` is the identity) — https://www.w3.org/TR/css-easing-1/\n */\nconst EASING_KEYWORDS: Readonly<Record<string, readonly [number, number, number, number]>> = {\n linear: [0, 0, 1, 1],\n ease: [0.25, 0.1, 0.25, 1],\n 'ease-in': [0.42, 0, 1, 1],\n 'ease-out': [0, 0, 0.58, 1],\n 'ease-in-out': [0.42, 0, 0.58, 1],\n};\n\n/**\n * The `color-mix()` interpolation spaces this resolver models: the culori mode, its channels, and which channel\n * is a hue (polar spaces). Anything else is reported unresolved rather than approximated.\n */\nconst COLOR_MIX_SPACES: Readonly<Record<string, { mode: string; channels: readonly string[]; hue?: string }>> = {\n oklab: { mode: 'oklab', channels: ['l', 'a', 'b'] },\n lab: { mode: 'lab', channels: ['l', 'a', 'b'] },\n srgb: { mode: 'rgb', channels: ['r', 'g', 'b'] },\n 'srgb-linear': { mode: 'lrgb', channels: ['r', 'g', 'b'] },\n oklch: { mode: 'oklch', channels: ['l', 'c', 'h'], hue: 'h' },\n lch: { mode: 'lch', channels: ['l', 'c', 'h'], hue: 'h' },\n};\n\n/**\n * Below this chroma, a hue produced by CONVERTING into a polar space is float residue of an achromatic colour —\n * a powerless component, which CSS treats as missing and carries forward from the other colour.\n */\nconst POWERLESS_CHROMA = 1e-4;\n\nconst NUMBER = /^-?(?:\\d+\\.?\\d*|\\.\\d+)$/;\nconst DURATION = /^(-?(?:\\d+\\.?\\d*|\\.\\d+))(ms|s)$/;\nconst SIZE = /^(-?(?:\\d+\\.?\\d*|\\.\\d+))(px|rem)$/;\nconst CUBIC_BEZIER = /^cubic-bezier\\(\\s*([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^,)]+)\\)$/;\n/** `var(--name)` or `var(--name, fallback)`. */\nconst VAR_REFERENCE = /^var\\(\\s*--([a-z0-9-]+)\\s*(?:,\\s*(.+))?\\)$/i;\nconst COLOR_MIX = /^color-mix\\(\\s*in\\s+([a-z0-9-]+)(?:\\s+(shorter|longer|increasing|decreasing)\\s+hue)?\\s*,(.*)\\)$/i;\nconst VIEWPORT_RELATIVE = /\\b(?:clamp|calc|min|max)\\(|\\d(?:[dsl]?v(?:w|h|i|b|min|max)|cq(?:w|h|i|b|min|max)|%)(?![\\w-])/;\n/** A non-colour alias chain deeper than this is a cycle in the token graph (`a` → `b` → `a`), not a value. */\nconst MAX_REFERENCE_DEPTH = 12;\n\nconst toLinearRgb = converter('lrgb');\n\ntype ResolvedColor = { color: CuloriColor; source: DesignTokenColorSource };\n\n/**\n * Resolve every token's value into the frame's units, or say why not.\n *\n * `values` is the whole token set for ONE theme and ONE mode — what `useUI().getTokenValue` returns — and\n * must be the whole set, because a formula refers to other tokens: `border-strong` is\n * `color-mix(in oklab, var(--foreground) 45%, var(--background))`.\n *\n * References to other tokens are resolved THROUGH THE TOKEN GRAPH rather than by asking the browser: the\n * browser answers against whatever custom properties the document holds at the moment, which lags a light/dark\n * switch, while the graph is the mode's own values by construction. The browser probe is the fallback for a\n * reference the graph cannot express, and a result that used it says so (`BROWSER_PROBE`).\n */\nexport function resolveDesignTokens(\n values: Readonly<Partial<Record<DesignSystemToken, string>>>,\n options: DesignTokenResolutionOptions = {},\n): Map<DesignSystemToken, DesignTokenResolution> {\n const rootFontSizePx = options.rootFontSizePx ?? 16;\n const colorCache = new Map<string, ResolvedColor | null>();\n const probeCache = new Map<string, ResolvedColor | null>();\n const resolving = new Set<string>();\n\n const probe = (value: string): ResolvedColor | null => {\n if (probeCache.has(value)) return probeCache.get(value) ?? null;\n const computed = options.readComputedColor?.(value) ?? null;\n const parsed = computed ? parse(computed) : undefined;\n const result = parsed ? { color: parsed, source: DesignTokenColorSource.BROWSER_PROBE } : null;\n probeCache.set(value, result);\n return result;\n };\n\n const resolveColor = (raw: string): ResolvedColor | null => {\n const value = raw.trim();\n if (colorCache.has(value)) return colorCache.get(value) ?? null;\n // A value already being resolved refers to itself — typically `var(--x)` as the value of an unconfigured\n // token `x` (`getTokenValue` returns exactly that). The graph cannot answer it; the browser may. This is the\n // ONLY unbounded case: every other chain is finite, because the token set and each string are.\n if (resolving.has(value)) return isColorReference(value) ? probe(value) : null;\n resolving.add(value);\n let result: ResolvedColor | null = null;\n try {\n const direct = COLOR_MIX.test(value) || VAR_REFERENCE.test(value) ? undefined : parse(value);\n if (direct) {\n result = { color: direct, source: DesignTokenColorSource.VALUE };\n } else {\n const reference = VAR_REFERENCE.exec(value);\n const mix = COLOR_MIX.exec(value);\n if (reference) {\n const name = reference[1];\n if (isDesignSystemToken(name) && values[name] !== undefined) {\n // A token the graph holds: its own value decides, including a probe of THAT value when it needs one.\n // Asking the browser here as well would re-ask the same question, and for an alias to a non-colour\n // token (a duration) it would ask about something that is not a colour at all.\n const target = resolveColor(values[name] as string);\n result = target ? { color: target.color, source: graphSource(target.source) } : null;\n } else {\n // Not a token: only the document can say whether the property is defined, and CSS uses the fallback\n // ONLY when it is not. So the whole expression goes to the browser, which applies that rule itself;\n // resolving the fallback here would guess that the property is undefined.\n result = probe(value);\n }\n } else if (mix) {\n result = mixColors(mix[1].toLowerCase(), mix[2]?.toLowerCase() as HueInterpolationMethod | undefined, mix[3], resolveColor)\n // A mix this resolver does not model (another space) may still be one the browser computes.\n ?? probe(value);\n }\n }\n } finally {\n resolving.delete(value);\n }\n colorCache.set(value, result);\n return result;\n };\n\n const resolveOne = (token: DesignSystemToken, raw: string, depth: number): DesignTokenResolution => {\n const value = raw.trim();\n const resolved = (r: ResolvedDesignTokenValue): DesignTokenResolution => ({ token, value, resolved: r });\n const unresolved = (reason: UnresolvedDesignTokenReason): DesignTokenResolution => ({ token, value, unresolved: reason });\n\n if (NUMBER.test(value)) return resolved({ kind: ResolvedDesignTokenKind.NUMBER, value: Number(value) });\n const duration = DURATION.exec(value);\n if (duration) return resolved({ kind: ResolvedDesignTokenKind.DURATION, seconds: Number(duration[1]) / (duration[2] === 'ms' ? 1000 : 1) });\n const size = SIZE.exec(value);\n if (size) return resolved({ kind: ResolvedDesignTokenKind.SIZE, px: Number(size[1]) * (size[2] === 'rem' ? rootFontSizePx : 1) });\n if (Object.hasOwn(EASING_KEYWORDS, value)) return resolved({ kind: ResolvedDesignTokenKind.EASING, controlPoints: EASING_KEYWORDS[value] });\n const bezier = CUBIC_BEZIER.exec(value);\n if (bezier) {\n const points = bezier.slice(1, 5).map(Number) as [number, number, number, number];\n return points.every(Number.isFinite) ? resolved({ kind: ResolvedDesignTokenKind.EASING, controlPoints: points }) : unresolved(UnresolvedDesignTokenReason.UNSUPPORTED_FORM);\n }\n\n const color = resolveColor(value);\n if (color) {\n const linear = toLinearRgb(color.color);\n return resolved({\n kind: ResolvedDesignTokenKind.COLOR,\n linearRgb: [Number(linear.r ?? 0), Number(linear.g ?? 0), Number(linear.b ?? 0)],\n alpha: color.color.alpha ?? 1,\n source: color.source,\n });\n }\n // A reference to another token that is NOT a colour (a duration token aliasing another): the alias takes\n // the target's resolution, whatever its kind.\n const reference = VAR_REFERENCE.exec(value);\n if (reference && isDesignSystemToken(reference[1]) && values[reference[1]] !== undefined && reference[1] !== token && depth < MAX_REFERENCE_DEPTH) {\n const aliased = resolveOne(reference[1], values[reference[1]] as string, depth + 1);\n return 'resolved' in aliased ? resolved(aliased.resolved) : unresolved(aliased.unresolved);\n }\n if (isColorReference(value)) return unresolved(UnresolvedDesignTokenReason.REFERENCE_UNRESOLVED);\n // Only a SINGLE value can be viewport-relative. A list (a shadow, `0 1px 2px rgb(0 0 0 / 5%)`) is unsupported\n // whatever percentages its colours carry.\n if (isSingleComponent(value) && VIEWPORT_RELATIVE.test(value)) return unresolved(UnresolvedDesignTokenReason.VIEWPORT_RELATIVE);\n return unresolved(UnresolvedDesignTokenReason.UNSUPPORTED_FORM);\n };\n\n const resolutions = new Map<DesignSystemToken, DesignTokenResolution>();\n for (const token of Object.values(DesignSystemToken)) {\n const value = values[token];\n if (value !== undefined) resolutions.set(token, resolveOne(token, value, 0));\n }\n return resolutions;\n}\n\nfunction isColorReference(value: string): boolean {\n return /^var\\(/i.test(value) || /^color-mix\\(/i.test(value);\n}\n\n/** A reference resolved through the graph keeps BROWSER_PROBE if its target needed the browser. */\nfunction graphSource(inner: DesignTokenColorSource): DesignTokenColorSource {\n return inner === DesignTokenColorSource.BROWSER_PROBE ? DesignTokenColorSource.BROWSER_PROBE : DesignTokenColorSource.TOKEN_GRAPH;\n}\n\nfunction isDesignSystemToken(name: string): name is DesignSystemToken {\n return (Object.values(DesignSystemToken) as string[]).includes(name);\n}\n\n/**\n * `color-mix(in <space> [<method> hue], <colour> [<p>%], <colour> [<p>%])`, computed.\n *\n * - Percentages (CSS Color 5 §3.2): each must lie in 0–100%; an omitted one makes the pair sum to 100%; any\n * other total is normalised to 100%, a total under 100% becomes an alpha multiplier, and a total of 0% is\n * rejected (CSS makes the whole function invalid).\n * - A hue method (`in oklch longer hue`) is valid only for a polar space; on a rectangular one it is rejected.\n * - Interpolation (CSS Color 4 §13): a missing component — including a hue made powerless by converting an\n * achromatic colour into a polar space — is carried forward from the other colour; colours are premultiplied\n * by alpha, EXCEPT the hue of a polar space; the hue follows the requested method.\n * spec-cite: CSS Color Module Level 5 §3.2 (percentage normalization) and §3.3 (calculating the result) — https://www.w3.org/TR/css-color-5/\n * spec-cite: CSS Color Module Level 4 §13.3 (missing components), §13.4 (premultiplied alpha, hue not premultiplied), §13.5.1–13.5.4 (hue methods) — https://www.w3.org/TR/css-color-4/\n */\nfunction mixColors(\n space: string,\n requestedHueMethod: HueInterpolationMethod | undefined,\n argumentsText: string,\n resolveArgument: (value: string) => ResolvedColor | null,\n): ResolvedColor | null {\n const model = COLOR_MIX_SPACES[space];\n if (!model) return null;\n // A hue method names a hue to interpolate; a rectangular space has none, and CSS rejects the declaration.\n if (requestedHueMethod !== undefined && !model.hue) return null;\n const hueMethod = requestedHueMethod ?? HueInterpolationMethod.SHORTER;\n const parts = splitTopLevel(argumentsText);\n if (parts.length !== 2) return null;\n const parsed = parts.map((part) => {\n const match = /^(.*?)(?:\\s+(-?\\d+(?:\\.\\d+)?)%)?$/.exec(part.trim());\n return match ? { colorText: match[1].trim(), percentage: match[2] === undefined ? undefined : Number(match[2]) } : null;\n });\n if (parsed.some((p) => p === null)) return null;\n const [first, second] = parsed as [{ colorText: string; percentage?: number }, { colorText: string; percentage?: number }];\n if ([first.percentage, second.percentage].some((p) => p !== undefined && (p < 0 || p > 100))) return null;\n let p1 = first.percentage;\n let p2 = second.percentage;\n if (p1 === undefined && p2 === undefined) { p1 = 50; p2 = 50; } else if (p1 === undefined) { p1 = 100 - (p2 as number); } else if (p2 === undefined) { p2 = 100 - p1; }\n const sum = (p1 as number) + (p2 as number);\n if (sum === 0) return null;\n const alphaMultiplier = sum < 100 ? sum / 100 : 1;\n const progress = (p2 as number) / sum;\n\n const a = resolveArgument(first.colorText);\n const b = resolveArgument(second.colorText);\n if (!a || !b) return null;\n const mixed = interpolateCss(a.color, b.color, model, hueMethod, progress);\n mixed.alpha = (mixed.alpha ?? 1) * alphaMultiplier;\n const source = a.source === DesignTokenColorSource.BROWSER_PROBE || b.source === DesignTokenColorSource.BROWSER_PROBE\n ? DesignTokenColorSource.BROWSER_PROBE\n : DesignTokenColorSource.TOKEN_GRAPH;\n return { color: mixed, source };\n}\n\n/** CSS Color 4 §13 interpolation of two colours in one space, at progress `t` (0 = first, 1 = second). */\nfunction interpolateCss(\n first: CuloriColor,\n second: CuloriColor,\n model: { mode: string; channels: readonly string[]; hue?: string },\n hueMethod: HueInterpolationMethod,\n t: number,\n): CuloriColor {\n const toSpace = converter(model.mode);\n const read = (color: CuloriColor): Record<string, number | undefined> => {\n const converted = toSpace(color);\n const channels: Record<string, number | undefined> = {};\n for (const channel of model.channels) {\n const raw = converted[channel];\n channels[channel] = typeof raw === 'number' && Number.isFinite(raw) ? raw : undefined;\n }\n // A hue made powerless by conversion (an achromatic colour entering a polar space) is missing.\n if (model.hue && color.mode !== model.mode && (channels.c ?? 0) < POWERLESS_CHROMA) channels[model.hue] = undefined;\n // §13.5: both angles are brought into [0, 360) BEFORE the hue method adjusts them. `oklch(… 730)` is hue 10.\n if (model.hue && channels[model.hue] !== undefined) channels[model.hue] = (((channels[model.hue] as number) % 360) + 360) % 360;\n channels.alpha = typeof color.alpha === 'number' ? color.alpha : 1;\n return channels;\n };\n const c1 = read(first);\n const c2 = read(second);\n // §13.3: a component missing in ONE colour takes the other's value; missing in both stays missing (0).\n for (const channel of [...model.channels, 'alpha']) {\n if (c1[channel] === undefined && c2[channel] !== undefined) c1[channel] = c2[channel];\n if (c2[channel] === undefined && c1[channel] !== undefined) c2[channel] = c1[channel];\n }\n if (model.hue && c1[model.hue] !== undefined && c2[model.hue] !== undefined) {\n let h1 = c1[model.hue] as number;\n let h2 = c2[model.hue] as number;\n const delta = h2 - h1;\n // §13.5.1–13.5.4, verbatim adjustments.\n if (hueMethod === HueInterpolationMethod.SHORTER) { if (delta > 180) h1 += 360; else if (delta < -180) h2 += 360; }\n if (hueMethod === HueInterpolationMethod.LONGER) { if (delta > 0 && delta < 180) h1 += 360; else if (delta > -180 && delta <= 0) h2 += 360; }\n if (hueMethod === HueInterpolationMethod.INCREASING) { if (h2 < h1) h2 += 360; }\n if (hueMethod === HueInterpolationMethod.DECREASING) { if (h1 < h2) h1 += 360; }\n c1[model.hue] = h1;\n c2[model.hue] = h2;\n }\n const a1 = c1.alpha as number;\n const a2 = c2.alpha as number;\n const alpha = a1 + (a2 - a1) * t;\n const result: CuloriColor = { mode: model.mode, alpha };\n for (const channel of model.channels) {\n const v1 = c1[channel];\n const v2 = c2[channel];\n if (v1 === undefined || v2 === undefined) { result[channel] = channel === model.hue ? undefined : 0; continue; }\n if (channel === model.hue) {\n // §13.4: the hue is NOT premultiplied.\n result[channel] = (((v1 + (v2 - v1) * t) % 360) + 360) % 360;\n continue;\n }\n const premultiplied = v1 * a1 + (v2 * a2 - v1 * a1) * t;\n result[channel] = alpha === 0 ? premultiplied : premultiplied / alpha;\n }\n return result;\n}\n\n/** Split on commas that are not inside parentheses. */\nfunction splitTopLevel(text: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let current = '';\n for (const character of text) {\n if (character === '(') depth += 1;\n if (character === ')') depth -= 1;\n if (character === ',' && depth === 0) { parts.push(current); current = ''; continue; }\n current += character;\n }\n parts.push(current);\n return parts;\n}\n\n/** Whether a value is ONE component: no whitespace or comma outside parentheses. */\nfunction isSingleComponent(text: string): boolean {\n let depth = 0;\n for (const character of text.trim()) {\n if (character === '(') depth += 1;\n if (character === ')') depth -= 1;\n if (depth === 0 && /[\\s,]/.test(character)) return false;\n }\n return true;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { DesignSystemToken } from '@wildo-ai/presets-components-models';
2
+ import { type DesignTokenResolution } from './design-token-resolution';
3
+ /**
4
+ * The active theme's tokens, resolved into the frame's units for the CURRENT light/dark mode (#1819).
5
+ *
6
+ * Recomputed exactly when a resolved value can change: `getTokenValue` changes identity with the design-system
7
+ * configuration and with `isDark`, and the theme id covers a theme switch. So a formula is resolved once per
8
+ * theme and mode — never per frame, which is the frame renderer's hot path.
9
+ *
10
+ * The WHOLE token set is resolved even when a caller wants a few tokens, because a formula refers to other
11
+ * tokens (`border-strong` mixes `foreground` and `background`).
12
+ *
13
+ * **The browser probe answers against the DOCUMENT, which lags a render.** The theme provider writes the new
14
+ * mode's custom properties in an effect, i.e. after the render in which this memo runs, so the probe reads the
15
+ * PREVIOUS mode's properties — or none at all, for a custom token the new theme introduces. So whenever the
16
+ * probe was CONSULTED, whatever it answered, the hook resolves once more on the next animation frame after the
17
+ * commit. The provider is an ancestor, so its effects run after this component's in the same flush and have
18
+ * written the document before that frame. A theme whose every value resolves through the graph (the default
19
+ * theme does) never consults the probe and pays nothing.
20
+ */
21
+ export declare function useDesignTokenResolution(): ReadonlyMap<DesignSystemToken, DesignTokenResolution>;
22
+ //# sourceMappingURL=use-design-token-resolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-design-token-resolution.d.ts","sourceRoot":"","sources":["../../../../src/tokens/use-design-token-resolution.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAGxE,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAE5F;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,IAAI,WAAW,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CA0BhG"}
@@ -0,0 +1,49 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { DesignSystemToken } from '@wildo-ai/presets-components-models';
3
+ import { readComputedCssColorViaDom, useUI } from '@wildo-ai/saas-frontend-lib';
4
+ import { resolveDesignTokens } from './design-token-resolution.js';
5
+ /**
6
+ * The active theme's tokens, resolved into the frame's units for the CURRENT light/dark mode (#1819).
7
+ *
8
+ * Recomputed exactly when a resolved value can change: `getTokenValue` changes identity with the design-system
9
+ * configuration and with `isDark`, and the theme id covers a theme switch. So a formula is resolved once per
10
+ * theme and mode — never per frame, which is the frame renderer's hot path.
11
+ *
12
+ * The WHOLE token set is resolved even when a caller wants a few tokens, because a formula refers to other
13
+ * tokens (`border-strong` mixes `foreground` and `background`).
14
+ *
15
+ * **The browser probe answers against the DOCUMENT, which lags a render.** The theme provider writes the new
16
+ * mode's custom properties in an effect, i.e. after the render in which this memo runs, so the probe reads the
17
+ * PREVIOUS mode's properties — or none at all, for a custom token the new theme introduces. So whenever the
18
+ * probe was CONSULTED, whatever it answered, the hook resolves once more on the next animation frame after the
19
+ * commit. The provider is an ancestor, so its effects run after this component's in the same flush and have
20
+ * written the document before that frame. A theme whose every value resolves through the graph (the default
21
+ * theme does) never consults the probe and pays nothing.
22
+ */
23
+ export function useDesignTokenResolution() {
24
+ const { getTokenValue, isDark, designTheme } = useUI();
25
+ const [documentEpoch, setDocumentEpoch] = useState(0);
26
+ const { resolutions, consultedProbe } = useMemo(() => {
27
+ const values = {};
28
+ for (const token of Object.values(DesignSystemToken))
29
+ values[token] = getTokenValue(token);
30
+ let consulted = false;
31
+ const readComputedColor = (value) => {
32
+ consulted = true;
33
+ return readComputedCssColorViaDom(value);
34
+ };
35
+ return { resolutions: resolveDesignTokens(values, { readComputedColor }), consultedProbe: consulted };
36
+ // `isDark` and the theme id are listed although `getTokenValue` already changes with them: they are the
37
+ // inputs the result depends on, and naming them keeps that true if `getTokenValue` is ever memoised differently.
38
+ // `documentEpoch` is the post-commit re-read described above.
39
+ }, [getTokenValue, isDark, designTheme.id, documentEpoch]);
40
+ // Keyed on the inputs, NOT on `resolutions`: the re-read produces a new map, and keying on it would loop.
41
+ useEffect(() => {
42
+ if (!consultedProbe)
43
+ return undefined;
44
+ const frame = requestAnimationFrame(() => setDocumentEpoch((epoch) => epoch + 1));
45
+ return () => cancelAnimationFrame(frame);
46
+ }, [getTokenValue, isDark, designTheme.id]);
47
+ return resolutions;
48
+ }
49
+ //# sourceMappingURL=use-design-token-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-design-token-resolution.js","sourceRoot":"","sources":["../../../../src/tokens/use-design-token-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAEhF,OAAO,EAAE,mBAAmB,EAA8B,MAAM,2BAA2B,CAAC;AAE5F;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,wBAAwB;IACtC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,CAAC;IACvD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtD,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;QACnD,MAAM,MAAM,GAA+C,EAAE,CAAC;QAC9D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC;YAAE,MAAM,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3F,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAiB,EAAE;YACzD,SAAS,GAAG,IAAI,CAAC;YACjB,OAAO,0BAA0B,CAAC,KAAK,CAAC,CAAC;QAC3C,CAAC,CAAC;QACF,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC,MAAM,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;QACtG,wGAAwG;QACxG,iHAAiH;QACjH,8DAA8D;IAChE,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;IAE3D,0GAA0G;IAC1G,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,cAAc;YAAE,OAAO,SAAS,CAAC;QACtC,MAAM,KAAK,GAAG,qBAAqB,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QAClF,OAAO,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5C,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { useEffect, useMemo, useState } from 'react';\n\nimport { DesignSystemToken } from '@wildo-ai/presets-components-models';\nimport { readComputedCssColorViaDom, useUI } from '@wildo-ai/saas-frontend-lib';\n\nimport { resolveDesignTokens, type DesignTokenResolution } from './design-token-resolution';\n\n/**\n * The active theme's tokens, resolved into the frame's units for the CURRENT light/dark mode (#1819).\n *\n * Recomputed exactly when a resolved value can change: `getTokenValue` changes identity with the design-system\n * configuration and with `isDark`, and the theme id covers a theme switch. So a formula is resolved once per\n * theme and mode — never per frame, which is the frame renderer's hot path.\n *\n * The WHOLE token set is resolved even when a caller wants a few tokens, because a formula refers to other\n * tokens (`border-strong` mixes `foreground` and `background`).\n *\n * **The browser probe answers against the DOCUMENT, which lags a render.** The theme provider writes the new\n * mode's custom properties in an effect, i.e. after the render in which this memo runs, so the probe reads the\n * PREVIOUS mode's properties — or none at all, for a custom token the new theme introduces. So whenever the\n * probe was CONSULTED, whatever it answered, the hook resolves once more on the next animation frame after the\n * commit. The provider is an ancestor, so its effects run after this component's in the same flush and have\n * written the document before that frame. A theme whose every value resolves through the graph (the default\n * theme does) never consults the probe and pays nothing.\n */\nexport function useDesignTokenResolution(): ReadonlyMap<DesignSystemToken, DesignTokenResolution> {\n const { getTokenValue, isDark, designTheme } = useUI();\n const [documentEpoch, setDocumentEpoch] = useState(0);\n\n const { resolutions, consultedProbe } = useMemo(() => {\n const values: Partial<Record<DesignSystemToken, string>> = {};\n for (const token of Object.values(DesignSystemToken)) values[token] = getTokenValue(token);\n let consulted = false;\n const readComputedColor = (value: string): string | null => {\n consulted = true;\n return readComputedCssColorViaDom(value);\n };\n return { resolutions: resolveDesignTokens(values, { readComputedColor }), consultedProbe: consulted };\n // `isDark` and the theme id are listed although `getTokenValue` already changes with them: they are the\n // inputs the result depends on, and naming them keeps that true if `getTokenValue` is ever memoised differently.\n // `documentEpoch` is the post-commit re-read described above.\n }, [getTokenValue, isDark, designTheme.id, documentEpoch]);\n\n // Keyed on the inputs, NOT on `resolutions`: the re-read produces a new map, and keying on it would loop.\n useEffect(() => {\n if (!consultedProbe) return undefined;\n const frame = requestAnimationFrame(() => setDocumentEpoch((epoch) => epoch + 1));\n return () => cancelAnimationFrame(frame);\n }, [getTokenValue, isDark, designTheme.id]);\n\n return resolutions;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@wildo-ai/presets-components-3d",
3
+ "version": "1.1.6",
4
+ "license": "SEE LICENSE IN LICENSE",
5
+ "description": "Optional 3D layouts for the Wildo AI application shell: its regions, unchanged HTML, arranged by a 3D layout over a decorative WebGL frame",
6
+ "author": "Wildo AI",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/esm/index.d.ts",
11
+ "import": "./dist/esm/index.js",
12
+ "default": "./dist/esm/index.js"
13
+ }
14
+ },
15
+ "main": "./dist/esm/index.js",
16
+ "module": "./dist/esm/index.js",
17
+ "types": "./dist/esm/index.d.ts",
18
+ "files": [
19
+ "dist/"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.build.json && node ../../scripts/run-tsc-alias-safe.mjs --project tsconfig.build.json --dist dist/esm",
23
+ "dev": "node ../../scripts/run-tsc-watch-single-writer.mjs --dist dist/esm --exec tsc-watch -- -p tsconfig.build.json --preserveWatchOutput --onSuccess \"node ../../scripts/run-tsc-alias-safe.mjs --project tsconfig.build.json --dist dist/esm\"",
24
+ "rebuild": "pnpm clean && pnpm build",
25
+ "clean": "rimraf dist",
26
+ "watch": "node ../../scripts/run-tsc-watch-single-writer.mjs --dist dist/esm --exec tsc-watch -- -p tsconfig.build.json --preserveWatchOutput --onSuccess \"node ../../scripts/run-tsc-alias-safe.mjs --project tsconfig.build.json --dist dist/esm\"",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest"
30
+ },
31
+ "dependencies": {
32
+ "@wildo-ai/presets-components-models": "1.1.6",
33
+ "@wildo-ai/saas-frontend-lib": "1.1.6",
34
+ "culori": "^4.0.2"
35
+ },
36
+ "peerDependencies": {
37
+ "@react-three/fiber": "^9.8.1",
38
+ "react": "^19.0.0",
39
+ "three": "^0.186.1"
40
+ },
41
+ "devDependencies": {
42
+ "@react-three/fiber": "9.8.1",
43
+ "@testing-library/react": "^16.0.0",
44
+ "@types/react": "^19.0.0",
45
+ "@types/three": "0.186.0",
46
+ "@vitejs/plugin-react": "^4.5.1",
47
+ "jsdom": "^24.0.0",
48
+ "react": "^19.0.0",
49
+ "react-dom": "^19.0.0",
50
+ "rimraf": "^6.0.1",
51
+ "three": "0.186.1",
52
+ "tsc-watch": "7.2.1",
53
+ "typescript": "5.9.3",
54
+ "vitest": "^1.6.1"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/wildo-ai/wildo-ai.git",
59
+ "directory": "engine/presets-components-3d"
60
+ }
61
+ }