@motion-proto/live-tokens 0.49.1 → 0.52.1

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 (111) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/dist-plugin/adjust/index.d.cts +1 -1
  3. package/dist-plugin/adjust/index.d.ts +1 -1
  4. package/dist-plugin/{chunk-76TFDTJO.js → chunk-JIGIIE5P.js} +60 -132
  5. package/dist-plugin/{chunk-44RSTAII.js → chunk-TKZBVIW5.js} +33 -5
  6. package/dist-plugin/chunk-YR6GPXW2.js +129 -0
  7. package/dist-plugin/generateColorsAndType/index.cjs +100 -33
  8. package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
  9. package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
  10. package/dist-plugin/generateColorsAndType/index.js +35 -14
  11. package/dist-plugin/index.cjs +95 -55
  12. package/dist-plugin/index.js +5 -3
  13. package/dist-plugin/{themeTypes-DSwKq-bj.d.cts → themeTypes-Dky03HL9.d.cts} +18 -5
  14. package/dist-plugin/{themeTypes-DSwKq-bj.d.ts → themeTypes-Dky03HL9.d.ts} +18 -5
  15. package/dist-plugin/tokensCssMigrations/index.cjs +71 -5
  16. package/dist-plugin/tokensCssMigrations/index.d.cts +20 -9
  17. package/dist-plugin/tokensCssMigrations/index.d.ts +20 -9
  18. package/dist-plugin/tokensCssMigrations/index.js +4 -1
  19. package/package.json +8 -1
  20. package/src/editor/bootstrap.ts +2 -0
  21. package/src/editor/component-editor/ButtonEditor.svelte +1 -1
  22. package/src/editor/component-editor/CardEditor.svelte +5 -2
  23. package/src/editor/component-editor/IconButtonEditor.svelte +1 -1
  24. package/src/editor/component-editor/ImageEditor.svelte +1 -1
  25. package/src/editor/component-editor/NotificationEditor.svelte +6 -0
  26. package/src/editor/component-editor/SectionDividerEditor.svelte +4 -1
  27. package/src/editor/component-editor/SideNavigationEditor.svelte +6 -0
  28. package/src/editor/component-editor/TabBarEditor.svelte +1 -5
  29. package/src/editor/component-editor/scaffolding/ComponentEditorBase.svelte +8 -1
  30. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +10 -32
  31. package/src/editor/component-editor/scaffolding/StateBlock.svelte +2 -0
  32. package/src/editor/component-editor/scaffolding/TypeEditor.svelte +3 -1
  33. package/src/editor/component-editor/scaffolding/types.ts +3 -0
  34. package/src/editor/core/components/componentConfigService.ts +25 -0
  35. package/src/editor/core/cssVarSync.ts +65 -7
  36. package/src/editor/core/fonts/fontLoader.ts +12 -3
  37. package/src/editor/core/fonts/fontWeightAvailability.ts +97 -0
  38. package/src/editor/core/palettes/oklch.ts +3 -137
  39. package/src/editor/core/palettes/paletteDerivation.ts +79 -37
  40. package/src/editor/core/preview/lookPreview.ts +23 -18
  41. package/src/editor/core/store/editorRenderer.ts +22 -7
  42. package/src/editor/core/store/editorStore.ts +31 -0
  43. package/src/editor/core/store/gradientSource.ts +5 -0
  44. package/src/editor/core/themes/colorsAndTypeService.ts +2 -12
  45. package/src/editor/core/themes/generateColorsAndType.ts +18 -8
  46. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +4 -1
  47. package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +4 -1
  48. package/src/editor/core/themes/migrations/2026-05-25-cornerbadge-flatten-variants.ts +8 -0
  49. package/src/editor/core/themes/slices/fonts.ts +4 -5
  50. package/src/editor/core/themes/themeDocumentSync.ts +66 -0
  51. package/src/editor/core/themes/themeInit.ts +7 -10
  52. package/src/editor/core/themes/themeService.ts +10 -3
  53. package/src/editor/core/themes/themeTypes.ts +18 -6
  54. package/src/editor/docs/content/editing-tokens.md +6 -2
  55. package/src/editor/docs/content/themes-workflow.md +14 -11
  56. package/src/editor/docs/content/where-themes-live.md +2 -2
  57. package/src/editor/docs/content.generated.ts +3 -3
  58. package/src/editor/ui/BezierCurveEditor.svelte +90 -67
  59. package/src/editor/ui/FileLoadList.svelte +1 -0
  60. package/src/editor/ui/FontStackEditor.svelte +1 -2
  61. package/src/editor/ui/GradientEditor.svelte +5 -1
  62. package/src/editor/ui/PaletteEditor.svelte +150 -44
  63. package/src/editor/ui/ProjectFontsSection.svelte +0 -5
  64. package/src/editor/ui/ThemePanel.svelte +206 -42
  65. package/src/editor/ui/Toggle.svelte +14 -2
  66. package/src/editor/ui/UIDialog.svelte +7 -1
  67. package/src/editor/ui/UIFontFamilySelector.svelte +13 -13
  68. package/src/editor/ui/UIFontSizeSelector.svelte +5 -5
  69. package/src/editor/ui/UIFontWeightSelector.svelte +52 -2
  70. package/src/editor/ui/UIOptionItem.svelte +14 -1
  71. package/src/editor/ui/UIPaddingSelector.svelte +4 -3
  72. package/src/editor/ui/UIPaletteSelector.svelte +20 -13
  73. package/src/editor/ui/UIRelinkConfirmDialog.svelte +18 -3
  74. package/src/editor/ui/UITextTransformSelector.svelte +5 -5
  75. package/src/editor/ui/UITokenSelector.svelte +29 -7
  76. package/src/editor/ui/colors/ColorStory.svelte +12 -8
  77. package/src/editor/ui/curveEngine.ts +101 -21
  78. package/src/editor/ui/palette/OverridesPanel.svelte +19 -5
  79. package/src/editor/ui/palette/PaletteBase.svelte +17 -78
  80. package/src/editor/ui/palette/ScaleCurveEditor.svelte +109 -20
  81. package/src/editor/ui/palette/curveSummary.ts +20 -0
  82. package/src/editor/ui/palette/dockMagnify.ts +5 -13
  83. package/src/editor/ui/palette/paletteMath.ts +7 -4
  84. package/src/live-tokens/data/colors-and-type/autumn.json +528 -528
  85. package/src/live-tokens/data/colors-and-type/default.json +247 -247
  86. package/src/live-tokens/data/colors-and-type/halloween.json +532 -532
  87. package/src/live-tokens/data/colors-and-type/midnight-study.json +538 -538
  88. package/src/live-tokens/data/colors-and-type/ocean.json +521 -521
  89. package/src/live-tokens/data/colors-and-type/royal-velvet.json +532 -532
  90. package/src/live-tokens/data/colors-and-type/spring-meadow.json +504 -504
  91. package/src/live-tokens/data/colors-and-type/sunset.json +531 -531
  92. package/src/live-tokens/data/themes/autumn.json +527 -527
  93. package/src/live-tokens/data/themes/halloween.json +531 -531
  94. package/src/live-tokens/data/themes/midnight-study.json +537 -537
  95. package/src/live-tokens/data/themes/ocean.json +520 -520
  96. package/src/live-tokens/data/themes/royal-velvet.json +531 -531
  97. package/src/live-tokens/data/themes/spring-meadow.json +516 -514
  98. package/src/live-tokens/data/themes/sunset.json +531 -531
  99. package/src/live-tokens/data/tokens.generated.css +282 -282
  100. package/src/system/components/Button.svelte +2 -1
  101. package/src/system/components/FloatingTokenTags.css +2 -2
  102. package/src/system/components/FloatingTokenTags.svelte +11 -0
  103. package/src/system/components/IconButton.svelte +2 -1
  104. package/src/system/components/Image.svelte +9 -3
  105. package/src/system/components/ImageLightbox.svelte +2 -0
  106. package/src/system/components/SectionDivider.svelte +20 -2
  107. package/src/system/components/SideNavigation.svelte +15 -1
  108. package/src/system/internal/backgroundContrast.ts +7 -0
  109. package/src/system/internal/oklch.ts +189 -0
  110. package/src/system/styles/fonts.css +6 -6
  111. package/src/system/styles/tokens.css +283 -283
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.52.1 — Notification header actions are previewable
4
+
5
+ ### Fixed
6
+
7
+ - **The Notification editor can show a header action.** The four
8
+ `--notification-*-action-surface` tokens paint the backdrop behind a
9
+ header-slot button, and no preview ever rendered one, so nothing in the
10
+ editor could show what they do. A "Header button" checkbox in the preview
11
+ toolbar turns the slot on.
12
+
13
+ ## 0.52.0 — A third palette curve
14
+
15
+ ### Added
16
+
17
+ - **Palettes gain a third curve, for hue.** Alongside Saturation and
18
+ Lightness, a Hue curve offsets each step by up to ±45 degrees, so a
19
+ palette can drift warmer or cooler across its ramp without moving
20
+ contrast. OKLCH keeps hue rotation close to lightness-preserving, which
21
+ makes that possible. The curve is closed by default, and existing
22
+ themes are unaffected: an absent hue curve renders as flat zero, exactly
23
+ as before.
24
+
25
+ ## 0.51.0 — OKLCH end to end
26
+
27
+ ### Changed (breaking)
28
+
29
+ - **Color tokens are written as `oklch()`, not hex.** The editor already
30
+ stored, edited, and computed color in OKLCH; only the final CSS string was
31
+ hex, which quantized every value to 8 bits on the way out. Derived colors now
32
+ serialize as `oklch()` in the live `:root` and in `tokens.generated.css`, and
33
+ `tokens.css` ships its 283 primitives in the same form. Values are still
34
+ clamped into the sRGB gamut before serialization, so nothing changes color.
35
+ Hex remains the readout and text-field format in the editor, and is still
36
+ what you copy out.
37
+
38
+ This sets a browser floor of Chrome 111, Safari 15.4, and Firefox 113
39
+ (2023). Run `npx live-tokens migrate` to convert a vendored `tokens.css`; the
40
+ transform is idempotent and touches values only, never names. Because it
41
+ rewrites values rather than renaming them, it never auto-applies — the dev
42
+ plugin will not touch your file.
43
+
44
+ Any code of your own that parses these tokens as hex needs updating. Inside
45
+ the package, the generator's contrast gate, the Color Story readouts, and the
46
+ runtime background-contrast picker were the three such readers.
47
+
48
+ ## 0.50.0 — Live editing stays in sync
49
+
50
+ ### Added
51
+
52
+ - **Save and Adopt can capture every unsaved component.** When component
53
+ editors have pending changes, the Theme panel offers to save them together
54
+ before saving or adopting the theme, so the look on screen can be persisted
55
+ without visiting each component editor first.
56
+
57
+ - **Font-weight choices follow the selected family.** The editor detects the
58
+ weights available for project and Google fonts, keeps the current custom
59
+ value visible, and avoids offering weights the font cannot render.
60
+
61
+ ### Fixed
62
+
63
+ - **Theme changes paint as one transaction.** Loading, previewing, and applying
64
+ a theme now update colors, type, and components together instead of exposing
65
+ intermediate mixed states or flashing through the previous live look.
66
+
67
+ - **Editor controls react reliably to live token changes.** Reused selectors,
68
+ gradient controls, linked aliases, typography, spacing, and component state
69
+ now refresh when their bound variable or upstream token changes.
70
+
71
+ - **Component saves preserve migrated values.** Theme-level saves serialize
72
+ the current component state consistently, including corrected migration
73
+ handling for Section Divider and Corner Badge configurations.
74
+
3
75
  ## 0.49.1 — Automatic working-copy cleanup
4
76
 
5
77
  ### Fixed
@@ -1,4 +1,4 @@
1
- import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSwKq-bj.cjs';
1
+ import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-Dky03HL9.cjs';
2
2
  export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
3
3
 
4
4
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -1,4 +1,4 @@
1
- import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSwKq-bj.js';
1
+ import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-Dky03HL9.js';
2
2
  export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
3
3
 
4
4
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -1,103 +1,14 @@
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;
96
- }
97
- }
98
- return { l, c: lo, h };
99
- }
100
-
101
12
  // src/editor/ui/curveEngine.ts
102
13
  function makeAnchor(x, y, tangentLen = 15) {
103
14
  return { x, y, inDx: -tangentLen, inDy: 0, outDx: tangentLen, outDy: 0 };
@@ -121,6 +32,15 @@ function tangentAnchor(x, y, prev, next) {
121
32
  const outDx = next ? (next.x - x) * HANDLE_SPAN : 0;
122
33
  return { x, y, inDx, inDy: m * inDx, outDx, outDy: m * outDx };
123
34
  }
35
+ function isCornerAnchor(a) {
36
+ return a.inDx === 0 && a.inDy === 0 && a.outDx === 0 && a.outDy === 0;
37
+ }
38
+ function resmoothAutoCurve(anchors) {
39
+ if (anchors.length < 2) return anchors;
40
+ return anchors.map(
41
+ (a, i) => isCornerAnchor(a) ? a : tangentAnchor(a.x, a.y, anchors[i - 1] ?? null, anchors[i + 1] ?? null)
42
+ );
43
+ }
124
44
  function evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, t) {
125
45
  const u = 1 - t, u2 = u * u, u3 = u2 * u;
126
46
  const t2 = t * t, t3 = t2 * t;
@@ -216,37 +136,39 @@ var SCALES = [
216
136
  ];
217
137
  var BW_GUARD_MIN_L = 0.17;
218
138
  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)];
139
+ var autoCurve = (...anchors) => resmoothAutoCurve(anchors);
140
+ var DEFAULT_PALETTE_LIGHTNESS = () => autoCurve(makeAnchor(0, 95), makeAnchor(100, 8));
141
+ var DEFAULT_PALETTE_SATURATION = () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100));
142
+ var wrapHue = (h) => (h % 360 + 360) % 360;
221
143
  function scaleCurveDefaults(scheme = "dark") {
222
144
  if (scheme === "light") {
223
145
  return {
224
146
  Surfaces: {
225
- lightness: () => [makeAnchor(0, 98, 5), makeAnchor(100, 82, 5)],
226
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
147
+ lightness: () => autoCurve(makeAnchor(0, 98), makeAnchor(100, 82)),
148
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
227
149
  },
228
150
  Borders: {
229
- lightness: () => [makeAnchor(0, 92, 5), makeAnchor(100, 45, 5)],
230
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
151
+ lightness: () => autoCurve(makeAnchor(0, 92), makeAnchor(100, 45)),
152
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
231
153
  },
232
154
  Text: {
233
- lightness: () => [makeAnchor(0, 30, 30), makeAnchor(100, 120, 30)],
234
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
155
+ lightness: () => autoCurve(makeAnchor(0, 30), makeAnchor(100, 120)),
156
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 15))
235
157
  }
236
158
  };
237
159
  }
238
160
  return {
239
161
  Surfaces: {
240
- lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
241
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
162
+ lightness: () => autoCurve(makeAnchor(0, 15), makeAnchor(100, 47)),
163
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
242
164
  },
243
165
  Borders: {
244
- lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
245
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
166
+ lightness: () => autoCurve(makeAnchor(0, 25), makeAnchor(100, 80)),
167
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 100))
246
168
  },
247
169
  Text: {
248
- lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
249
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
170
+ lightness: () => autoCurve(makeAnchor(0, 120), makeAnchor(100, 55)),
171
+ saturation: () => autoCurve(makeAnchor(0, 100), makeAnchor(100, 15))
250
172
  }
251
173
  };
252
174
  }
@@ -265,14 +187,16 @@ function scaleStepToX(step, scale) {
265
187
  return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
266
188
  }
267
189
  function serializeDerivedValue(value) {
268
- return value.kind === "raw" ? value.css : oklchToHexClamped(value.l, value.c, value.h);
190
+ return value.kind === "raw" ? value.css : oklchToCssClamped(value.l, value.c, value.h);
269
191
  }
270
- function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset) {
271
- const { c: baseC, h } = base;
192
+ function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset, hueCurve) {
193
+ const { c: baseC } = base;
272
194
  const xPos = stepIndexToX(index);
273
195
  const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset.lightness ?? 0))) / 100;
274
196
  const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset.saturation ?? 0)) / 100));
275
197
  const targetC = baseC * satMul;
198
+ const hueDelta = (hueCurve ? sampleCurve(hueCurve, xPos) : 0) + (curveOffset.hue ?? 0);
199
+ const h = hueDelta === 0 ? base.h : wrapHue(base.h + hueDelta);
276
200
  return gamutClamp(targetL, targetC, h);
277
201
  }
278
202
  function nearestPaletteStep(lightnessCurve, baseL) {
@@ -368,10 +292,12 @@ function syncBaseAnchor(cfg) {
368
292
  const fresh = prev === void 0;
369
293
  let lCurve = cfg.lightnessCurve;
370
294
  let sCurve = cfg.saturationCurve;
295
+ let hCurve = cfg.hueCurve;
371
296
  if (prev) {
372
297
  const prevX = stepIndexToX(prev.step);
373
298
  lCurve = liftCurveAnchor(lCurve, prevX, prev.displacedL);
374
299
  sCurve = liftCurveAnchor(sCurve, prevX, prev.displacedS);
300
+ if (hCurve) hCurve = liftCurveAnchor(hCurve, prevX, prev.displacedH);
375
301
  }
376
302
  const step = nearestPaletteStep(lCurve, cfg.baseColor.l);
377
303
  const x = stepIndexToX(step);
@@ -379,12 +305,19 @@ function syncBaseAnchor(cfg) {
379
305
  const s = setCurveAnchor(sCurve, x, 100, fresh);
380
306
  cfg.lightnessCurve = l.curve;
381
307
  cfg.saturationCurve = s.curve;
308
+ const h = hCurve ? setCurveAnchor(hCurve, x, 0, fresh) : void 0;
309
+ if (h) cfg.hueCurve = h.curve;
382
310
  cfg.anchorPlacement = {
383
311
  step,
384
312
  displacedL: l.displacedY,
385
313
  displacedS: s.displacedY,
314
+ displacedH: h?.displacedY,
386
315
  priorLightnessEndpoints: prev?.priorLightnessEndpoints ?? l.priorEndpoints,
387
- priorSaturationEndpoints: prev?.priorSaturationEndpoints ?? s.priorEndpoints
316
+ priorSaturationEndpoints: prev?.priorSaturationEndpoints ?? s.priorEndpoints,
317
+ // No else-branch reading `prev?.priorHueEndpoints`: that could only fire
318
+ // if a placed hue curve later went absent, which RJC 7 (reset never
319
+ // deletes) never lets happen.
320
+ priorHueEndpoints: h ? prev?.priorHueEndpoints ?? h.priorEndpoints : void 0
388
321
  };
389
322
  }
390
323
  function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
@@ -393,8 +326,10 @@ function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
393
326
  const defs = defaultScaleCurves[scaleTitle];
394
327
  const lCurve = scaleCurves[scaleTitle]?.lightness ?? defs.lightness();
395
328
  const sCurve = scaleCurves[scaleTitle]?.saturation ?? defs.saturation();
329
+ const hCurve = scaleCurves[scaleTitle]?.hue;
396
330
  const lOff = curveOffset[`${scaleTitle}-lightness`] ?? 0;
397
331
  const sOff = curveOffset[`${scaleTitle}-saturation`] ?? 0;
332
+ const hOff = curveOffset[`${scaleTitle}-hue`] ?? 0;
398
333
  const { l: baseL, c: baseC, h: baseH } = base;
399
334
  let targetL;
400
335
  if (scale.isText) {
@@ -405,7 +340,9 @@ function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
405
340
  }
406
341
  const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
407
342
  const targetC = baseC * satMul;
408
- return gamutClamp(targetL, targetC, baseH);
343
+ const hueDelta = (hCurve ? sampleCurve(hCurve, xPos) : 0) + hOff;
344
+ const targetH = hueDelta === 0 ? baseH : wrapHue(baseH + hueDelta);
345
+ return gamutClamp(targetL, targetC, targetH);
409
346
  }
410
347
  function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
411
348
  if (cssNamespace === null) return null;
@@ -434,7 +371,7 @@ function derivePaletteValues(spec, config) {
434
371
  const saturationCurve = config.saturationCurve ?? DEFAULT_PALETTE_SATURATION();
435
372
  PALETTE_STEPS.forEach((ps, index) => {
436
373
  const k = paletteStepKey(ps.label);
437
- const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computePaletteOklch(index, baseColor, lightnessCurve, saturationCurve, curveOffset) };
374
+ const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computePaletteOklch(index, baseColor, lightnessCurve, saturationCurve, curveOffset, config.hueCurve) };
438
375
  out[`--color-${spec.cssNamespace}-${ps.label}`] = value;
439
376
  });
440
377
  for (const scale of SCALES) {
@@ -469,8 +406,8 @@ function derivePaletteValues(spec, config) {
469
406
  );
470
407
  const stopsCss = sortedStops.map((s) => {
471
408
  const stopValue = out[`--color-${spec.cssNamespace}-${s.paletteLabel}`];
472
- const hex = stopValue ? serializeDerivedValue(stopValue) : "#000000";
473
- return `${hex} ${s.position}%`;
409
+ const css = stopValue ? serializeDerivedValue(stopValue) : "oklch(0 0 0)";
410
+ return `${css} ${s.position}%`;
474
411
  }).join(", ");
475
412
  let gradient;
476
413
  switch (gradientStyle) {
@@ -489,13 +426,6 @@ function derivePaletteValues(spec, config) {
489
426
  }
490
427
  return out;
491
428
  }
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
429
  function palettesToValues(palettes) {
500
430
  const out = {};
501
431
  for (const spec of PALETTE_SPECS) {
@@ -510,7 +440,6 @@ function palettesToVars(palettes) {
510
440
  }
511
441
  return out;
512
442
  }
513
- var HEX_RE = /^#[0-9a-f]{6}$/i;
514
443
  function reconcilePalettesFromCssVars(palettes, cssVars) {
515
444
  const next = structuredClone(palettes);
516
445
  const consumed = /* @__PURE__ */ new Set();
@@ -519,16 +448,17 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
519
448
  const current = next[spec.label];
520
449
  if (current === void 0) continue;
521
450
  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 };
451
+ const anchor = cssVars[`--color-${spec.cssNamespace}-500`];
452
+ const anchorOklch = anchor ? cssColorToOklch(anchor) : null;
453
+ if (anchorOklch) {
454
+ next[spec.label] = { ...current, baseColor: anchorOklch, _imported: false };
525
455
  syncBaseAnchor(next[spec.label]);
526
456
  snapped.add(spec.label);
527
457
  } else {
528
458
  next[spec.label] = { ...current, _imported: false };
529
459
  }
530
460
  }
531
- for (const k of Object.keys(derivePaletteVars(spec, next[spec.label]))) {
461
+ for (const k of Object.keys(derivePaletteValues(spec, next[spec.label]))) {
532
462
  consumed.add(k);
533
463
  }
534
464
  }
@@ -537,10 +467,8 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
537
467
 
538
468
  export {
539
469
  sanitizeFileName,
540
- hexToOklch,
541
- oklchToHex,
542
- gamutClamp,
543
470
  makeAnchor,
471
+ resmoothAutoCurve,
544
472
  PALETTE_SPECS,
545
473
  PALETTE_STEPS,
546
474
  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
+ };