@motion-proto/live-tokens 0.50.0 → 0.53.0

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 (91) hide show
  1. package/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +7 -1
  2. package/CHANGELOG.md +108 -0
  3. package/bin/adjust.mjs +16 -39
  4. package/bin/generate-theme.mjs +20 -6
  5. package/dist-plugin/adjust/index.cjs +781 -21
  6. package/dist-plugin/adjust/index.d.cts +2 -1
  7. package/dist-plugin/adjust/index.d.ts +2 -1
  8. package/dist-plugin/adjust/index.js +43 -21
  9. package/dist-plugin/{chunk-6OZFXIQI.js → chunk-232GZGQU.js} +207 -12
  10. package/dist-plugin/chunk-NE6N66EE.js +753 -0
  11. package/dist-plugin/{chunk-76TFDTJO.js → chunk-PB2JTK2H.js} +179 -128
  12. package/dist-plugin/{chunk-44RSTAII.js → chunk-TKZBVIW5.js} +33 -5
  13. package/dist-plugin/chunk-YR6GPXW2.js +129 -0
  14. package/dist-plugin/generateColorsAndType/index.cjs +135 -63
  15. package/dist-plugin/generateColorsAndType/index.d.cts +2 -1
  16. package/dist-plugin/generateColorsAndType/index.d.ts +2 -1
  17. package/dist-plugin/generateColorsAndType/index.js +28 -784
  18. package/dist-plugin/{themeTypes-DSwKq-bj.d.ts → index-DpTIRZ2H.d.cts} +67 -26
  19. package/dist-plugin/{themeTypes-DSwKq-bj.d.cts → index-DpTIRZ2H.d.ts} +67 -26
  20. package/dist-plugin/index.cjs +1254 -140
  21. package/dist-plugin/index.js +106 -82
  22. package/dist-plugin/migrateData/index.cjs +956 -14
  23. package/dist-plugin/migrateData/index.js +10 -3
  24. package/dist-plugin/tokensCssMigrations/index.cjs +71 -5
  25. package/dist-plugin/tokensCssMigrations/index.d.cts +20 -9
  26. package/dist-plugin/tokensCssMigrations/index.d.ts +20 -9
  27. package/dist-plugin/tokensCssMigrations/index.js +4 -1
  28. package/package.json +8 -4
  29. package/src/editor/component-editor/NotificationEditor.svelte +6 -0
  30. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +9 -23
  31. package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +8 -8
  32. package/src/editor/core/components/adjustAliases.ts +49 -22
  33. package/src/editor/core/components/aliasKinds.ts +7 -2
  34. package/src/editor/core/components/componentConfigService.ts +1 -1
  35. package/src/editor/core/palettes/oklch.ts +3 -137
  36. package/src/editor/core/palettes/paletteDerivation.ts +79 -37
  37. package/src/editor/core/store/editorStore.ts +7 -197
  38. package/src/editor/core/store/editorTypes.ts +9 -30
  39. package/src/editor/core/themes/generateColorsAndType.ts +18 -8
  40. package/src/editor/core/themes/lookSummary.ts +3 -25
  41. package/src/editor/core/themes/migrateComponentConfig.ts +205 -0
  42. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +1 -1
  43. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +1 -1
  44. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +1 -1
  45. package/src/editor/core/themes/parsers/gradient.ts +211 -0
  46. package/src/editor/core/themes/slices/gradients.ts +5 -58
  47. package/src/editor/core/themes/themeService.ts +22 -9
  48. package/src/editor/core/themes/themeTypes.ts +89 -27
  49. package/src/editor/docs/content/editing-tokens.md +6 -2
  50. package/src/editor/docs/content/themes-workflow.md +9 -11
  51. package/src/editor/docs/content/where-themes-live.md +2 -2
  52. package/src/editor/docs/content.generated.ts +3 -3
  53. package/src/editor/index.ts +1 -0
  54. package/src/editor/styles/ui-editor.css +5 -2
  55. package/src/editor/ui/BezierCurveEditor.svelte +90 -67
  56. package/src/editor/ui/PaletteEditor.svelte +180 -59
  57. package/src/editor/ui/ThemePanel.svelte +24 -15
  58. package/src/editor/ui/Toggle.svelte +36 -24
  59. package/src/editor/ui/UIPillButton.svelte +1 -1
  60. package/src/editor/ui/UIReveal.svelte +47 -0
  61. package/src/editor/ui/UISquareButton.svelte +1 -1
  62. package/src/editor/ui/colors/ColorStory.svelte +12 -8
  63. package/src/editor/ui/colors/ColorsTab.svelte +4 -5
  64. package/src/editor/ui/curveEngine.ts +101 -21
  65. package/src/editor/ui/palette/OverridesPanel.svelte +23 -8
  66. package/src/editor/ui/palette/PaletteBase.svelte +17 -78
  67. package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
  68. package/src/editor/ui/palette/ScaleCurveEditor.svelte +110 -20
  69. package/src/editor/ui/palette/curveSummary.ts +20 -0
  70. package/src/editor/ui/palette/dockMagnify.ts +5 -13
  71. package/src/editor/ui/palette/paletteMath.ts +7 -4
  72. package/src/live-tokens/data/colors-and-type/autumn.json +528 -528
  73. package/src/live-tokens/data/colors-and-type/default.json +247 -247
  74. package/src/live-tokens/data/colors-and-type/halloween.json +532 -532
  75. package/src/live-tokens/data/colors-and-type/midnight-study.json +538 -538
  76. package/src/live-tokens/data/colors-and-type/ocean.json +521 -521
  77. package/src/live-tokens/data/colors-and-type/royal-velvet.json +532 -532
  78. package/src/live-tokens/data/colors-and-type/spring-meadow.json +504 -504
  79. package/src/live-tokens/data/colors-and-type/sunset.json +531 -531
  80. package/src/live-tokens/data/themes/autumn.json +604 -568
  81. package/src/live-tokens/data/themes/halloween.json +584 -583
  82. package/src/live-tokens/data/themes/midnight-study.json +673 -605
  83. package/src/live-tokens/data/themes/ocean.json +597 -561
  84. package/src/live-tokens/data/themes/royal-velvet.json +574 -573
  85. package/src/live-tokens/data/themes/spring-meadow.json +590 -556
  86. package/src/live-tokens/data/themes/sunset.json +632 -571
  87. package/src/live-tokens/data/tokens.generated.css +282 -282
  88. package/src/system/components/FloatingTokenTags.css +0 -1
  89. package/src/system/internal/backgroundContrast.ts +7 -0
  90. package/src/system/internal/oklch.ts +189 -0
  91. package/src/system/styles/tokens.css +283 -283
@@ -1,101 +1,132 @@
1
+ import {
2
+ cssColorToOklch,
3
+ gamutClamp,
4
+ oklchToCssClamped
5
+ } from "./chunk-YR6GPXW2.js";
6
+
1
7
  // src/editor/core/storage/files/versionedFileResourceClient.ts
2
8
  function sanitizeFileName(name) {
3
9
  return name.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9\-_]/g, "").replace(/-+/g, "-").replace(/^[-_]+|-+$/g, "") || "unnamed";
4
10
  }
5
11
 
6
- // src/editor/core/palettes/oklch.ts
7
- function srgbToLinear(c) {
8
- return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
9
- }
10
- function linearToSrgb(c) {
11
- return c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
12
- }
13
- function hexToLinearRgb(hex) {
14
- const r = parseInt(hex.slice(1, 3), 16) / 255;
15
- const g = parseInt(hex.slice(3, 5), 16) / 255;
16
- const b = parseInt(hex.slice(5, 7), 16) / 255;
17
- return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
18
- }
19
- function linearRgbToHex(r, g, b) {
20
- const toHex = (c) => {
21
- const v = Math.round(Math.max(0, Math.min(1, linearToSrgb(c))) * 255);
22
- return v.toString(16).padStart(2, "0");
23
- };
24
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
25
- }
26
- function linearRgbToOklab(r, g, b) {
27
- const l_ = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
28
- const m_ = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
29
- const s_ = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
30
- const l = Math.cbrt(l_);
31
- const m = Math.cbrt(m_);
32
- const s = Math.cbrt(s_);
33
- return [
34
- 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
35
- 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
36
- 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
37
- ];
38
- }
39
- function oklabToLinearRgb(L, a, b) {
40
- const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
41
- const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
42
- const s_ = L - 0.0894841775 * a - 1.291485548 * b;
43
- const l = l_ * l_ * l_;
44
- const m = m_ * m_ * m_;
45
- const s = s_ * s_ * s_;
46
- return [
47
- 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
48
- -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
49
- -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
50
- ];
51
- }
52
- function oklabToOklch(L, a, b) {
53
- const c = Math.sqrt(a * a + b * b);
54
- let h = Math.atan2(b, a) * 180 / Math.PI;
55
- if (h < 0) h += 360;
56
- return { l: L, c, h };
57
- }
58
- function oklchToOklab(l, c, h) {
59
- const hRad = h * Math.PI / 180;
60
- return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
61
- }
62
- function hexToOklch(hex) {
63
- const [r, g, b] = hexToLinearRgb(hex);
64
- const [L, a, bVal] = linearRgbToOklab(r, g, b);
65
- return oklabToOklch(L, a, bVal);
66
- }
67
- function oklchToHex(l, c, h) {
68
- const [L, a, b] = oklchToOklab(l, c, h);
69
- const [r, g, bVal] = oklabToLinearRgb(L, a, b);
70
- return linearRgbToHex(r, g, bVal);
71
- }
72
- function isInGamut(r, g, b) {
73
- const eps = 1e-4;
74
- return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
75
- }
76
- function oklchToHexClamped(l, c, h) {
77
- const g = gamutClamp(l, c, h);
78
- return oklchToHex(g.l, g.c, g.h);
79
- }
80
- function gamutClamp(l, c, h) {
81
- if (l <= 0) return { l: 0, c: 0, h };
82
- if (l >= 1) return { l: 1, c: 0, h };
83
- const [L, a, b] = oklchToOklab(l, c, h);
84
- const [r, g, bVal] = oklabToLinearRgb(L, a, b);
85
- if (isInGamut(r, g, bVal)) return { l, c, h };
86
- let lo = 0;
87
- let hi = c;
88
- for (let i = 0; i < 20; i++) {
89
- const mid = (lo + hi) / 2;
90
- const [La, aa, ba] = oklchToOklab(l, mid, h);
91
- const [rr, gg, bb] = oklabToLinearRgb(La, aa, ba);
92
- if (isInGamut(rr, gg, bb)) {
93
- lo = mid;
94
- } else {
95
- hi = mid;
12
+ // src/editor/core/themes/parsers/colorOpacity.ts
13
+ var COLOR_OPACITY_RE = /^color-mix\(in srgb,\s*var\((--[a-z0-9-]+)\)\s+(\d+)%,\s*transparent\)$/i;
14
+ function parseColorOpacity(value) {
15
+ const m = value.trim().match(COLOR_OPACITY_RE);
16
+ if (!m) return null;
17
+ return { name: m[1], opacity: parseInt(m[2], 10) };
18
+ }
19
+ function formatColorOpacity(name, opacity) {
20
+ return `color-mix(in srgb, var(${name}) ${opacity}%, transparent)`;
21
+ }
22
+
23
+ // src/editor/core/themes/parsers/gradient.ts
24
+ var RADIAL_BASE_PX = 100;
25
+ function formatStopColor(s) {
26
+ const opacity = s.opacity ?? 100;
27
+ if (s.color.startsWith("--")) {
28
+ return opacity >= 100 ? `var(${s.color})` : formatColorOpacity(s.color, opacity);
29
+ }
30
+ return opacity >= 100 ? s.color : `color-mix(in srgb, ${s.color} ${opacity}%, transparent)`;
31
+ }
32
+ function formatGradientStops(stops) {
33
+ return stops.map((s) => `${formatStopColor(s)} ${s.position}%`).join(", ");
34
+ }
35
+ function formatRadialShape(v) {
36
+ const ax = v.aspectX ?? 1;
37
+ const ay = v.aspectY ?? 1;
38
+ if (ax === 1 && ay === 1) {
39
+ return v.radius && v.radius > 0 ? `circle ${v.radius}px` : "circle";
40
+ }
41
+ const base = v.radius && v.radius > 0 ? v.radius : RADIAL_BASE_PX;
42
+ return `ellipse ${(base * ax).toFixed(2)}px ${(base * ay).toFixed(2)}px`;
43
+ }
44
+ function formatGradientValue(v) {
45
+ if (v.type === "none") return "transparent";
46
+ if (v.type === "solid") {
47
+ const first = v.stops[0];
48
+ return first ? formatStopColor(first) : "transparent";
49
+ }
50
+ const stops = formatGradientStops(v.stops);
51
+ if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
52
+ return `radial-gradient(${formatRadialShape(v)} at ${v.centerX ?? 50}% 50%, ${stops})`;
53
+ }
54
+ var NUM = String.raw`-?\d+(?:\.\d+)?`;
55
+ var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${NUM})deg\s*,\s*(.+)\)$`, "i");
56
+ var RADIAL_RE = new RegExp(
57
+ String.raw`^radial-gradient\(\s*(circle|circle\s+${NUM}px|ellipse\s+${NUM}px\s+${NUM}px)\s+at\s+(${NUM})%\s+50%\s*,\s*(.+)\)$`,
58
+ "i"
59
+ );
60
+ var STOP_RE = new RegExp(String.raw`^(.*?)\s+(${NUM})%$`);
61
+ var CIRCLE_RADIUS_RE = new RegExp(String.raw`^circle\s+(${NUM})px$`, "i");
62
+ var ELLIPSE_RE = new RegExp(String.raw`^ellipse\s+(${NUM})px\s+(${NUM})px$`, "i");
63
+ var VAR_RE = /^var\((--[a-z0-9-]+)\)$/i;
64
+ function splitTopLevel(s) {
65
+ const parts = [];
66
+ let depth = 0;
67
+ let start = 0;
68
+ for (let i = 0; i < s.length; i++) {
69
+ const c = s[i];
70
+ if (c === "(") depth++;
71
+ else if (c === ")") depth--;
72
+ else if (c === "," && depth === 0) {
73
+ parts.push(s.slice(start, i));
74
+ start = i + 1;
96
75
  }
97
76
  }
98
- return { l, c: lo, h };
77
+ parts.push(s.slice(start));
78
+ return parts;
79
+ }
80
+ function parseStop(raw) {
81
+ const m = raw.trim().match(STOP_RE);
82
+ if (!m) return null;
83
+ const position = parseFloat(m[2]);
84
+ const color = m[1].trim();
85
+ const opacity = parseColorOpacity(color);
86
+ if (opacity) return { position, color: opacity.name, opacity: opacity.opacity };
87
+ const token = color.match(VAR_RE);
88
+ return { position, color: token ? token[1] : color };
89
+ }
90
+ function parseStops(raw) {
91
+ const stops = [];
92
+ for (const part of splitTopLevel(raw)) {
93
+ const stop = parseStop(part);
94
+ if (!stop) return null;
95
+ stops.push(stop);
96
+ }
97
+ return stops.length > 0 ? stops : null;
98
+ }
99
+ function parseRadialShape(raw) {
100
+ const shape = raw.trim();
101
+ if (/^circle$/i.test(shape)) return {};
102
+ const circle = shape.match(CIRCLE_RADIUS_RE);
103
+ if (circle) return { radius: parseFloat(circle[1]) };
104
+ const ellipse = shape.match(ELLIPSE_RE);
105
+ if (!ellipse) return null;
106
+ const ax = parseFloat(ellipse[1]) / RADIAL_BASE_PX;
107
+ const ay = parseFloat(ellipse[2]) / RADIAL_BASE_PX;
108
+ return { ...ax === 1 ? {} : { aspectX: ax }, ...ay === 1 ? {} : { aspectY: ay } };
109
+ }
110
+ function parseGradientValue(value) {
111
+ const css = value.trim();
112
+ const linear = css.match(LINEAR_RE);
113
+ if (linear) {
114
+ const stops2 = parseStops(linear[2]);
115
+ return stops2 && { type: "linear", angle: parseFloat(linear[1]), stops: stops2 };
116
+ }
117
+ const radial = css.match(RADIAL_RE);
118
+ if (!radial) return null;
119
+ const shape = parseRadialShape(radial[1]);
120
+ const stops = parseStops(radial[3]);
121
+ if (!shape || !stops) return null;
122
+ const centerX = parseFloat(radial[2]);
123
+ return {
124
+ type: "radial",
125
+ angle: 0,
126
+ ...shape,
127
+ ...centerX === 50 ? {} : { centerX },
128
+ stops
129
+ };
99
130
  }
100
131
 
101
132
  // src/editor/ui/curveEngine.ts
@@ -121,6 +152,15 @@ function tangentAnchor(x, y, prev, next) {
121
152
  const outDx = next ? (next.x - x) * HANDLE_SPAN : 0;
122
153
  return { x, y, inDx, inDy: m * inDx, outDx, outDy: m * outDx };
123
154
  }
155
+ function isCornerAnchor(a) {
156
+ return a.inDx === 0 && a.inDy === 0 && a.outDx === 0 && a.outDy === 0;
157
+ }
158
+ function resmoothAutoCurve(anchors) {
159
+ if (anchors.length < 2) return anchors;
160
+ return anchors.map(
161
+ (a, i) => isCornerAnchor(a) ? a : tangentAnchor(a.x, a.y, anchors[i - 1] ?? null, anchors[i + 1] ?? null)
162
+ );
163
+ }
124
164
  function evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, t) {
125
165
  const u = 1 - t, u2 = u * u, u3 = u2 * u;
126
166
  const t2 = t * t, t3 = t2 * t;
@@ -216,37 +256,39 @@ var SCALES = [
216
256
  ];
217
257
  var BW_GUARD_MIN_L = 0.17;
218
258
  var BW_GUARD_MAX_L = 0.93;
219
- var DEFAULT_PALETTE_LIGHTNESS = () => [makeAnchor(0, 95, 5), makeAnchor(100, 8, 5)];
220
- var DEFAULT_PALETTE_SATURATION = () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)];
259
+ var autoCurve = (...anchors) => resmoothAutoCurve(anchors);
260
+ var DEFAULT_PALETTE_LIGHTNESS = () => autoCurve(makeAnchor(0, 95), makeAnchor(100, 8));
261
+ var DEFAULT_PALETTE_SATURATION = () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100));
262
+ var wrapHue = (h) => (h % 360 + 360) % 360;
221
263
  function scaleCurveDefaults(scheme = "dark") {
222
264
  if (scheme === "light") {
223
265
  return {
224
266
  Surfaces: {
225
- lightness: () => [makeAnchor(0, 98, 5), makeAnchor(100, 82, 5)],
226
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
267
+ lightness: () => autoCurve(makeAnchor(0, 98), makeAnchor(100, 82)),
268
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
227
269
  },
228
270
  Borders: {
229
- lightness: () => [makeAnchor(0, 92, 5), makeAnchor(100, 45, 5)],
230
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
271
+ lightness: () => autoCurve(makeAnchor(0, 92), makeAnchor(100, 45)),
272
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
231
273
  },
232
274
  Text: {
233
- lightness: () => [makeAnchor(0, 30, 30), makeAnchor(100, 120, 30)],
234
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
275
+ lightness: () => autoCurve(makeAnchor(0, 30), makeAnchor(100, 120)),
276
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 15))
235
277
  }
236
278
  };
237
279
  }
238
280
  return {
239
281
  Surfaces: {
240
- lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
241
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
282
+ lightness: () => autoCurve(makeAnchor(0, 15), makeAnchor(100, 47)),
283
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
242
284
  },
243
285
  Borders: {
244
- lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
245
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
286
+ lightness: () => autoCurve(makeAnchor(0, 25), makeAnchor(100, 80)),
287
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
246
288
  },
247
289
  Text: {
248
- lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
249
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
290
+ lightness: () => autoCurve(makeAnchor(0, 120), makeAnchor(100, 55)),
291
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 15))
250
292
  }
251
293
  };
252
294
  }
@@ -265,14 +307,16 @@ function scaleStepToX(step, scale) {
265
307
  return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
266
308
  }
267
309
  function serializeDerivedValue(value) {
268
- return value.kind === "raw" ? value.css : oklchToHexClamped(value.l, value.c, value.h);
310
+ return value.kind === "raw" ? value.css : oklchToCssClamped(value.l, value.c, value.h);
269
311
  }
270
- function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset) {
271
- const { c: baseC, h } = base;
312
+ function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset, hueCurve) {
313
+ const { c: baseC } = base;
272
314
  const xPos = stepIndexToX(index);
273
315
  const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset.lightness ?? 0))) / 100;
274
316
  const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset.saturation ?? 0)) / 100));
275
317
  const targetC = baseC * satMul;
318
+ const hueDelta = (hueCurve ? sampleCurve(hueCurve, xPos) : 0) + (curveOffset.hue ?? 0);
319
+ const h = hueDelta === 0 ? base.h : wrapHue(base.h + hueDelta);
276
320
  return gamutClamp(targetL, targetC, h);
277
321
  }
278
322
  function nearestPaletteStep(lightnessCurve, baseL) {
@@ -368,10 +412,12 @@ function syncBaseAnchor(cfg) {
368
412
  const fresh = prev === void 0;
369
413
  let lCurve = cfg.lightnessCurve;
370
414
  let sCurve = cfg.saturationCurve;
415
+ let hCurve = cfg.hueCurve;
371
416
  if (prev) {
372
417
  const prevX = stepIndexToX(prev.step);
373
418
  lCurve = liftCurveAnchor(lCurve, prevX, prev.displacedL);
374
419
  sCurve = liftCurveAnchor(sCurve, prevX, prev.displacedS);
420
+ if (hCurve) hCurve = liftCurveAnchor(hCurve, prevX, prev.displacedH);
375
421
  }
376
422
  const step = nearestPaletteStep(lCurve, cfg.baseColor.l);
377
423
  const x = stepIndexToX(step);
@@ -379,12 +425,19 @@ function syncBaseAnchor(cfg) {
379
425
  const s = setCurveAnchor(sCurve, x, 100, fresh);
380
426
  cfg.lightnessCurve = l.curve;
381
427
  cfg.saturationCurve = s.curve;
428
+ const h = hCurve ? setCurveAnchor(hCurve, x, 0, fresh) : void 0;
429
+ if (h) cfg.hueCurve = h.curve;
382
430
  cfg.anchorPlacement = {
383
431
  step,
384
432
  displacedL: l.displacedY,
385
433
  displacedS: s.displacedY,
434
+ displacedH: h?.displacedY,
386
435
  priorLightnessEndpoints: prev?.priorLightnessEndpoints ?? l.priorEndpoints,
387
- priorSaturationEndpoints: prev?.priorSaturationEndpoints ?? s.priorEndpoints
436
+ priorSaturationEndpoints: prev?.priorSaturationEndpoints ?? s.priorEndpoints,
437
+ // No else-branch reading `prev?.priorHueEndpoints`: that could only fire
438
+ // if a placed hue curve later went absent, which RJC 7 (reset never
439
+ // deletes) never lets happen.
440
+ priorHueEndpoints: h ? prev?.priorHueEndpoints ?? h.priorEndpoints : void 0
388
441
  };
389
442
  }
390
443
  function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
@@ -393,8 +446,10 @@ function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
393
446
  const defs = defaultScaleCurves[scaleTitle];
394
447
  const lCurve = scaleCurves[scaleTitle]?.lightness ?? defs.lightness();
395
448
  const sCurve = scaleCurves[scaleTitle]?.saturation ?? defs.saturation();
449
+ const hCurve = scaleCurves[scaleTitle]?.hue;
396
450
  const lOff = curveOffset[`${scaleTitle}-lightness`] ?? 0;
397
451
  const sOff = curveOffset[`${scaleTitle}-saturation`] ?? 0;
452
+ const hOff = curveOffset[`${scaleTitle}-hue`] ?? 0;
398
453
  const { l: baseL, c: baseC, h: baseH } = base;
399
454
  let targetL;
400
455
  if (scale.isText) {
@@ -405,7 +460,9 @@ function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
405
460
  }
406
461
  const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
407
462
  const targetC = baseC * satMul;
408
- return gamutClamp(targetL, targetC, baseH);
463
+ const hueDelta = (hCurve ? sampleCurve(hCurve, xPos) : 0) + hOff;
464
+ const targetH = hueDelta === 0 ? baseH : wrapHue(baseH + hueDelta);
465
+ return gamutClamp(targetL, targetC, targetH);
409
466
  }
410
467
  function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
411
468
  if (cssNamespace === null) return null;
@@ -434,7 +491,7 @@ function derivePaletteValues(spec, config) {
434
491
  const saturationCurve = config.saturationCurve ?? DEFAULT_PALETTE_SATURATION();
435
492
  PALETTE_STEPS.forEach((ps, index) => {
436
493
  const k = paletteStepKey(ps.label);
437
- const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computePaletteOklch(index, baseColor, lightnessCurve, saturationCurve, curveOffset) };
494
+ const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computePaletteOklch(index, baseColor, lightnessCurve, saturationCurve, curveOffset, config.hueCurve) };
438
495
  out[`--color-${spec.cssNamespace}-${ps.label}`] = value;
439
496
  });
440
497
  for (const scale of SCALES) {
@@ -469,8 +526,8 @@ function derivePaletteValues(spec, config) {
469
526
  );
470
527
  const stopsCss = sortedStops.map((s) => {
471
528
  const stopValue = out[`--color-${spec.cssNamespace}-${s.paletteLabel}`];
472
- const hex = stopValue ? serializeDerivedValue(stopValue) : "#000000";
473
- return `${hex} ${s.position}%`;
529
+ const css = stopValue ? serializeDerivedValue(stopValue) : "oklch(0 0 0)";
530
+ return `${css} ${s.position}%`;
474
531
  }).join(", ");
475
532
  let gradient;
476
533
  switch (gradientStyle) {
@@ -489,13 +546,6 @@ function derivePaletteValues(spec, config) {
489
546
  }
490
547
  return out;
491
548
  }
492
- function derivePaletteVars(spec, config) {
493
- const out = {};
494
- for (const [k, v] of Object.entries(derivePaletteValues(spec, config))) {
495
- out[k] = serializeDerivedValue(v);
496
- }
497
- return out;
498
- }
499
549
  function palettesToValues(palettes) {
500
550
  const out = {};
501
551
  for (const spec of PALETTE_SPECS) {
@@ -510,7 +560,6 @@ function palettesToVars(palettes) {
510
560
  }
511
561
  return out;
512
562
  }
513
- var HEX_RE = /^#[0-9a-f]{6}$/i;
514
563
  function reconcilePalettesFromCssVars(palettes, cssVars) {
515
564
  const next = structuredClone(palettes);
516
565
  const consumed = /* @__PURE__ */ new Set();
@@ -519,16 +568,17 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
519
568
  const current = next[spec.label];
520
569
  if (current === void 0) continue;
521
570
  if (current._imported === true) {
522
- const anchorHex = cssVars[`--color-${spec.cssNamespace}-500`];
523
- if (anchorHex && HEX_RE.test(anchorHex.trim())) {
524
- next[spec.label] = { ...current, baseColor: hexToOklch(anchorHex.trim()), _imported: false };
571
+ const anchor = cssVars[`--color-${spec.cssNamespace}-500`];
572
+ const anchorOklch = anchor ? cssColorToOklch(anchor) : null;
573
+ if (anchorOklch) {
574
+ next[spec.label] = { ...current, baseColor: anchorOklch, _imported: false };
525
575
  syncBaseAnchor(next[spec.label]);
526
576
  snapped.add(spec.label);
527
577
  } else {
528
578
  next[spec.label] = { ...current, _imported: false };
529
579
  }
530
580
  }
531
- for (const k of Object.keys(derivePaletteVars(spec, next[spec.label]))) {
581
+ for (const k of Object.keys(derivePaletteValues(spec, next[spec.label]))) {
532
582
  consumed.add(k);
533
583
  }
534
584
  }
@@ -536,11 +586,12 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
536
586
  }
537
587
 
538
588
  export {
589
+ parseColorOpacity,
590
+ formatGradientValue,
591
+ parseGradientValue,
539
592
  sanitizeFileName,
540
- hexToOklch,
541
- oklchToHex,
542
- gamutClamp,
543
593
  makeAnchor,
594
+ resmoothAutoCurve,
544
595
  PALETTE_SPECS,
545
596
  PALETTE_STEPS,
546
597
  SCALES,
@@ -1,3 +1,8 @@
1
+ import {
2
+ hexToOklch,
3
+ oklchToCss
4
+ } from "./chunk-YR6GPXW2.js";
5
+
1
6
  // src/editor/core/themes/parsers/globalRootBlock.ts
2
7
  function extractGlobalRootBody(source) {
3
8
  const re = /:global\(:root\)\s*\{([^}]*)\}/g;
@@ -12,11 +17,17 @@ function extractGlobalRootBody(source) {
12
17
  // vite-plugin/tokensCssMigrations/cssTokenOps.ts
13
18
  var DECL_RE = /(^|[\s;{])(--[a-z0-9-]+)\s*:/gi;
14
19
  var REF_RE = /var\(\s*(--[a-z0-9-]+)/gi;
20
+ var DECL_VALUE_RE = /(^|[\s;{])(--[a-z0-9-]+)\s*:([^;}]*)/gi;
15
21
  function collectDefinedTokens(css) {
16
22
  const out = /* @__PURE__ */ new Set();
17
23
  for (const m of css.matchAll(DECL_RE)) out.add(m[2]);
18
24
  return out;
19
25
  }
26
+ function collectTokenValues(css) {
27
+ const out = /* @__PURE__ */ new Map();
28
+ for (const m of css.matchAll(DECL_VALUE_RE)) out.set(m[2], m[3].trim());
29
+ return out;
30
+ }
20
31
  function collectReferencedTokens(css) {
21
32
  const out = /* @__PURE__ */ new Set();
22
33
  for (const m of css.matchAll(REF_RE)) out.add(m[1]);
@@ -350,6 +361,20 @@ var tokensCssMigration_2026_07_20_semanticTextStyles = {
350
361
  }
351
362
  };
352
363
 
364
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-19-oklch-color-values.ts
365
+ var HEX_DECLARATION_RE = /(--[a-z0-9-]+:\s*)#([0-9a-f]{6})(?=\s*[;}])/gi;
366
+ var tokensCssMigration_2026_08_19_oklchColorValues = {
367
+ id: "2026-08-19-oklch-color-values",
368
+ kind: "breaking",
369
+ description: "Rewrite hex token values as oklch()",
370
+ apply(css) {
371
+ return css.replace(HEX_DECLARATION_RE, (_match, prefix, hex) => {
372
+ const { l, c, h } = hexToOklch(`#${hex}`);
373
+ return `${prefix}${oklchToCss(l, c, h)}`;
374
+ });
375
+ }
376
+ };
377
+
353
378
  // vite-plugin/tokensCssMigrations/index.ts
354
379
  var TOKENS_CSS_MIGRATIONS = [
355
380
  tokensCssMigration_2026_05_29_typographyScaleAdditions,
@@ -358,7 +383,8 @@ var TOKENS_CSS_MIGRATIONS = [
358
383
  tokensCssMigration_2026_06_04_removeDeadSizeIconScale,
359
384
  tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
360
385
  tokensCssMigration_2026_07_20_lineHeightRename,
361
- tokensCssMigration_2026_07_20_semanticTextStyles
386
+ tokensCssMigration_2026_07_20_semanticTextStyles,
387
+ tokensCssMigration_2026_08_19_oklchColorValues
362
388
  ];
363
389
  function runTokensCssMigrations(css) {
364
390
  return foldMigrations(css, () => true);
@@ -383,10 +409,11 @@ function findContractViolations(canonicalCss) {
383
409
  const violations = [];
384
410
  for (const m of TOKENS_CSS_MIGRATIONS) {
385
411
  if (m.kind !== "additive") continue;
386
- const before = collectDefinedTokens(canonicalCss);
387
- const after = collectDefinedTokens(m.apply(canonicalCss));
388
- const removed = [...before].filter((t) => !after.has(t)).sort();
389
- if (removed.length) violations.push({ id: m.id, removed });
412
+ const before = collectTokenValues(canonicalCss);
413
+ const after = collectTokenValues(m.apply(canonicalCss));
414
+ const removed = [...before.keys()].filter((t) => !after.has(t)).sort();
415
+ const changed = [...before.entries()].filter(([t, v]) => after.has(t) && after.get(t) !== v).map(([t]) => t).sort();
416
+ if (removed.length || changed.length) violations.push({ id: m.id, removed, changed });
390
417
  }
391
418
  return violations;
392
419
  }
@@ -446,6 +473,7 @@ function validateTokensCss(input) {
446
473
  export {
447
474
  extractGlobalRootBody,
448
475
  collectDefinedTokens,
476
+ collectTokenValues,
449
477
  collectReferencedTokens,
450
478
  ensureScale,
451
479
  renameToken,
@@ -0,0 +1,129 @@
1
+ // src/system/internal/oklch.ts
2
+ function srgbToLinear(c) {
3
+ return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
4
+ }
5
+ function linearToSrgb(c) {
6
+ return c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
7
+ }
8
+ function hexToLinearRgb(hex) {
9
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
10
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
11
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
12
+ return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
13
+ }
14
+ function linearRgbToHex(r, g, b) {
15
+ const toHex = (c) => {
16
+ const v = Math.round(Math.max(0, Math.min(1, linearToSrgb(c))) * 255);
17
+ return v.toString(16).padStart(2, "0");
18
+ };
19
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
20
+ }
21
+ function linearRgbToOklab(r, g, b) {
22
+ const l_ = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
23
+ const m_ = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
24
+ const s_ = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
25
+ const l = Math.cbrt(l_);
26
+ const m = Math.cbrt(m_);
27
+ const s = Math.cbrt(s_);
28
+ return [
29
+ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
30
+ 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
31
+ 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
32
+ ];
33
+ }
34
+ function oklabToLinearRgb(L, a, b) {
35
+ const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
36
+ const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
37
+ const s_ = L - 0.0894841775 * a - 1.291485548 * b;
38
+ const l = l_ * l_ * l_;
39
+ const m = m_ * m_ * m_;
40
+ const s = s_ * s_ * s_;
41
+ return [
42
+ 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
43
+ -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
44
+ -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
45
+ ];
46
+ }
47
+ function oklabToOklch(L, a, b) {
48
+ const c = Math.sqrt(a * a + b * b);
49
+ let h = Math.atan2(b, a) * 180 / Math.PI;
50
+ if (h < 0) h += 360;
51
+ return { l: L, c, h };
52
+ }
53
+ function oklchToOklab(l, c, h) {
54
+ const hRad = h * Math.PI / 180;
55
+ return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
56
+ }
57
+ function hexToOklch(hex) {
58
+ const [r, g, b] = hexToLinearRgb(hex);
59
+ const [L, a, bVal] = linearRgbToOklab(r, g, b);
60
+ return oklabToOklch(L, a, bVal);
61
+ }
62
+ function oklchToHex(l, c, h) {
63
+ const [L, a, b] = oklchToOklab(l, c, h);
64
+ const [r, g, bVal] = oklabToLinearRgb(L, a, b);
65
+ return linearRgbToHex(r, g, bVal);
66
+ }
67
+ function isInGamut(r, g, b) {
68
+ const eps = 1e-4;
69
+ return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
70
+ }
71
+ function oklchToHexClamped(l, c, h) {
72
+ const g = gamutClamp(l, c, h);
73
+ return oklchToHex(g.l, g.c, g.h);
74
+ }
75
+ function gamutClamp(l, c, h) {
76
+ if (l <= 0) return { l: 0, c: 0, h };
77
+ if (l >= 1) return { l: 1, c: 0, h };
78
+ const [L, a, b] = oklchToOklab(l, c, h);
79
+ const [r, g, bVal] = oklabToLinearRgb(L, a, b);
80
+ if (isInGamut(r, g, bVal)) return { l, c, h };
81
+ let lo = 0;
82
+ let hi = c;
83
+ for (let i = 0; i < 20; i++) {
84
+ const mid = (lo + hi) / 2;
85
+ const [La, aa, ba] = oklchToOklab(l, mid, h);
86
+ const [rr, gg, bb] = oklabToLinearRgb(La, aa, ba);
87
+ if (isInGamut(rr, gg, bb)) {
88
+ lo = mid;
89
+ } else {
90
+ hi = mid;
91
+ }
92
+ }
93
+ return { l, c: lo, h };
94
+ }
95
+ function trim(value, decimals) {
96
+ return Number(value.toFixed(decimals)).toString();
97
+ }
98
+ function oklchToCss(l, c, h) {
99
+ const cs = trim(c, 4);
100
+ return `oklch(${trim(l, 4)} ${cs} ${cs === "0" ? "0" : trim(h, 2)})`;
101
+ }
102
+ function oklchToCssClamped(l, c, h) {
103
+ const g = gamutClamp(l, c, h);
104
+ return oklchToCss(g.l, g.c, g.h);
105
+ }
106
+ var OKLCH_RE = /^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)(%?)\s+([\d.]+)(?:deg)?\s*\)$/i;
107
+ var HEX6_RE = /^#[0-9a-f]{6}$/i;
108
+ function parseOklchCss(value) {
109
+ const m = OKLCH_RE.exec(value.trim());
110
+ if (!m) return null;
111
+ const l = Number.parseFloat(m[1]) / (m[2] === "%" ? 100 : 1);
112
+ const c = Number.parseFloat(m[3]) * (m[4] === "%" ? 4e-3 : 1);
113
+ return { l, c, h: Number.parseFloat(m[5]) };
114
+ }
115
+ function cssColorToOklch(value) {
116
+ const raw = value.trim();
117
+ if (HEX6_RE.test(raw)) return hexToOklch(raw);
118
+ return parseOklchCss(raw);
119
+ }
120
+
121
+ export {
122
+ hexToOklch,
123
+ oklchToHex,
124
+ oklchToHexClamped,
125
+ gamutClamp,
126
+ oklchToCss,
127
+ oklchToCssClamped,
128
+ cssColorToOklch
129
+ };