@motion-proto/live-tokens 0.52.1 → 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 (60) hide show
  1. package/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +7 -1
  2. package/CHANGELOG.md +63 -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-JIGIIE5P.js → chunk-PB2JTK2H.js} +123 -0
  12. package/dist-plugin/generateColorsAndType/index.cjs +48 -32
  13. package/dist-plugin/generateColorsAndType/index.d.cts +2 -1
  14. package/dist-plugin/generateColorsAndType/index.d.ts +2 -1
  15. package/dist-plugin/generateColorsAndType/index.js +7 -773
  16. package/dist-plugin/{themeTypes-Dky03HL9.d.cts → index-DpTIRZ2H.d.cts} +49 -21
  17. package/dist-plugin/{themeTypes-Dky03HL9.d.ts → index-DpTIRZ2H.d.ts} +49 -21
  18. package/dist-plugin/index.cjs +1159 -85
  19. package/dist-plugin/index.js +102 -80
  20. package/dist-plugin/migrateData/index.cjs +956 -14
  21. package/dist-plugin/migrateData/index.js +10 -3
  22. package/package.json +6 -4
  23. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +9 -23
  24. package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +8 -8
  25. package/src/editor/core/components/adjustAliases.ts +49 -22
  26. package/src/editor/core/components/aliasKinds.ts +7 -2
  27. package/src/editor/core/components/componentConfigService.ts +1 -1
  28. package/src/editor/core/store/editorStore.ts +7 -197
  29. package/src/editor/core/store/editorTypes.ts +9 -30
  30. package/src/editor/core/themes/lookSummary.ts +3 -25
  31. package/src/editor/core/themes/migrateComponentConfig.ts +205 -0
  32. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +1 -1
  33. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +1 -1
  34. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +1 -1
  35. package/src/editor/core/themes/parsers/gradient.ts +211 -0
  36. package/src/editor/core/themes/slices/gradients.ts +5 -58
  37. package/src/editor/core/themes/themeService.ts +22 -9
  38. package/src/editor/core/themes/themeTypes.ts +71 -21
  39. package/src/editor/docs/content/themes-workflow.md +9 -11
  40. package/src/editor/docs/content/where-themes-live.md +2 -2
  41. package/src/editor/docs/content.generated.ts +2 -2
  42. package/src/editor/index.ts +1 -0
  43. package/src/editor/styles/ui-editor.css +5 -2
  44. package/src/editor/ui/PaletteEditor.svelte +30 -15
  45. package/src/editor/ui/ThemePanel.svelte +24 -15
  46. package/src/editor/ui/Toggle.svelte +22 -22
  47. package/src/editor/ui/UIPillButton.svelte +1 -1
  48. package/src/editor/ui/UIReveal.svelte +47 -0
  49. package/src/editor/ui/UISquareButton.svelte +1 -1
  50. package/src/editor/ui/colors/ColorsTab.svelte +4 -5
  51. package/src/editor/ui/palette/OverridesPanel.svelte +4 -3
  52. package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
  53. package/src/editor/ui/palette/ScaleCurveEditor.svelte +3 -2
  54. package/src/live-tokens/data/themes/autumn.json +77 -41
  55. package/src/live-tokens/data/themes/halloween.json +53 -52
  56. package/src/live-tokens/data/themes/midnight-study.json +136 -68
  57. package/src/live-tokens/data/themes/ocean.json +77 -41
  58. package/src/live-tokens/data/themes/royal-velvet.json +43 -42
  59. package/src/live-tokens/data/themes/spring-meadow.json +86 -52
  60. package/src/live-tokens/data/themes/sunset.json +101 -40
@@ -59,6 +59,118 @@ function parseColorOpacity(value) {
59
59
  if (!m) return null;
60
60
  return { name: m[1], opacity: parseInt(m[2], 10) };
61
61
  }
62
+ function formatColorOpacity(name, opacity) {
63
+ return `color-mix(in srgb, var(${name}) ${opacity}%, transparent)`;
64
+ }
65
+
66
+ // src/editor/core/themes/parsers/gradient.ts
67
+ var RADIAL_BASE_PX = 100;
68
+ function formatStopColor(s) {
69
+ const opacity = s.opacity ?? 100;
70
+ if (s.color.startsWith("--")) {
71
+ return opacity >= 100 ? `var(${s.color})` : formatColorOpacity(s.color, opacity);
72
+ }
73
+ return opacity >= 100 ? s.color : `color-mix(in srgb, ${s.color} ${opacity}%, transparent)`;
74
+ }
75
+ function formatGradientStops(stops) {
76
+ return stops.map((s) => `${formatStopColor(s)} ${s.position}%`).join(", ");
77
+ }
78
+ function formatRadialShape(v) {
79
+ const ax = v.aspectX ?? 1;
80
+ const ay = v.aspectY ?? 1;
81
+ if (ax === 1 && ay === 1) {
82
+ return v.radius && v.radius > 0 ? `circle ${v.radius}px` : "circle";
83
+ }
84
+ const base = v.radius && v.radius > 0 ? v.radius : RADIAL_BASE_PX;
85
+ return `ellipse ${(base * ax).toFixed(2)}px ${(base * ay).toFixed(2)}px`;
86
+ }
87
+ function formatGradientValue(v) {
88
+ if (v.type === "none") return "transparent";
89
+ if (v.type === "solid") {
90
+ const first = v.stops[0];
91
+ return first ? formatStopColor(first) : "transparent";
92
+ }
93
+ const stops = formatGradientStops(v.stops);
94
+ if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
95
+ return `radial-gradient(${formatRadialShape(v)} at ${v.centerX ?? 50}% 50%, ${stops})`;
96
+ }
97
+ var NUM = String.raw`-?\d+(?:\.\d+)?`;
98
+ var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${NUM})deg\s*,\s*(.+)\)$`, "i");
99
+ var RADIAL_RE = new RegExp(
100
+ 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*(.+)\)$`,
101
+ "i"
102
+ );
103
+ var STOP_RE = new RegExp(String.raw`^(.*?)\s+(${NUM})%$`);
104
+ var CIRCLE_RADIUS_RE = new RegExp(String.raw`^circle\s+(${NUM})px$`, "i");
105
+ var ELLIPSE_RE = new RegExp(String.raw`^ellipse\s+(${NUM})px\s+(${NUM})px$`, "i");
106
+ var VAR_RE = /^var\((--[a-z0-9-]+)\)$/i;
107
+ function splitTopLevel(s) {
108
+ const parts = [];
109
+ let depth = 0;
110
+ let start = 0;
111
+ for (let i = 0; i < s.length; i++) {
112
+ const c = s[i];
113
+ if (c === "(") depth++;
114
+ else if (c === ")") depth--;
115
+ else if (c === "," && depth === 0) {
116
+ parts.push(s.slice(start, i));
117
+ start = i + 1;
118
+ }
119
+ }
120
+ parts.push(s.slice(start));
121
+ return parts;
122
+ }
123
+ function parseStop(raw) {
124
+ const m = raw.trim().match(STOP_RE);
125
+ if (!m) return null;
126
+ const position = parseFloat(m[2]);
127
+ const color = m[1].trim();
128
+ const opacity = parseColorOpacity(color);
129
+ if (opacity) return { position, color: opacity.name, opacity: opacity.opacity };
130
+ const token = color.match(VAR_RE);
131
+ return { position, color: token ? token[1] : color };
132
+ }
133
+ function parseStops(raw) {
134
+ const stops = [];
135
+ for (const part of splitTopLevel(raw)) {
136
+ const stop = parseStop(part);
137
+ if (!stop) return null;
138
+ stops.push(stop);
139
+ }
140
+ return stops.length > 0 ? stops : null;
141
+ }
142
+ function parseRadialShape(raw) {
143
+ const shape = raw.trim();
144
+ if (/^circle$/i.test(shape)) return {};
145
+ const circle = shape.match(CIRCLE_RADIUS_RE);
146
+ if (circle) return { radius: parseFloat(circle[1]) };
147
+ const ellipse = shape.match(ELLIPSE_RE);
148
+ if (!ellipse) return null;
149
+ const ax = parseFloat(ellipse[1]) / RADIAL_BASE_PX;
150
+ const ay = parseFloat(ellipse[2]) / RADIAL_BASE_PX;
151
+ return { ...ax === 1 ? {} : { aspectX: ax }, ...ay === 1 ? {} : { aspectY: ay } };
152
+ }
153
+ function parseGradientValue(value) {
154
+ const css = value.trim();
155
+ const linear = css.match(LINEAR_RE);
156
+ if (linear) {
157
+ const stops2 = parseStops(linear[2]);
158
+ return stops2 && { type: "linear", angle: parseFloat(linear[1]), stops: stops2 };
159
+ }
160
+ const radial = css.match(RADIAL_RE);
161
+ if (!radial) return null;
162
+ const shape = parseRadialShape(radial[1]);
163
+ const stops = parseStops(radial[3]);
164
+ if (!shape || !stops) return null;
165
+ const centerX = parseFloat(radial[2]);
166
+ return {
167
+ type: "radial",
168
+ angle: 0,
169
+ ...shape,
170
+ ...centerX === 50 ? {} : { centerX },
171
+ stops
172
+ };
173
+ }
62
174
 
63
175
  // src/editor/core/storage/files/versionedFileResourceClient.ts
64
176
  function sanitizeFileName(name) {
@@ -642,6 +754,754 @@ function migratePaletteColorsToOklch(editorConfigs) {
642
754
  return out;
643
755
  }
644
756
 
757
+ // src/editor/core/themes/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts
758
+ var LEGACY_KEY_RENAMES = {
759
+ "--empty": "--page-bg",
760
+ "--empty-attachment": "--page-bg-attachment",
761
+ // Orphan exports dropped from tokens.css; no new home — silently discard.
762
+ "--border-neutral": null,
763
+ // Shadow cleanup: these five tokens were removed (low/zero consumers,
764
+ // aesthetic drift). Dialog migrated onto --shadow-2xl; focus rings moved
765
+ // to their own --ring-focus-* namespace, which themes don't carry by
766
+ // default — tokens.css provides the canonical values.
767
+ "--shadow-app": null,
768
+ "--shadow-card": null,
769
+ "--shadow-overlay": null,
770
+ "--shadow-focus": null,
771
+ "--shadow-glow-green": null
772
+ };
773
+ var BG_TO_CANVAS_PREFIXES = ["--color-bg-", "--surface-bg", "--border-bg", "--text-bg"];
774
+ function migrateBgToCanvas(rawVars) {
775
+ for (const oldKey of Object.keys(rawVars)) {
776
+ for (const prefix of BG_TO_CANVAS_PREFIXES) {
777
+ if (!oldKey.startsWith(prefix)) continue;
778
+ const suffix = oldKey.slice(prefix.length);
779
+ if (suffix && !suffix.startsWith("-")) continue;
780
+ const newKey = prefix.replace("-bg", "-canvas") + suffix;
781
+ if (newKey === oldKey) continue;
782
+ const value = rawVars[oldKey];
783
+ delete rawVars[oldKey];
784
+ if (!(newKey in rawVars)) rawVars[newKey] = value;
785
+ break;
786
+ }
787
+ }
788
+ }
789
+ function migrateLegacyKeys(rawVars) {
790
+ for (const [oldKey, newKey] of Object.entries(LEGACY_KEY_RENAMES)) {
791
+ if (!(oldKey in rawVars)) continue;
792
+ const value = rawVars[oldKey];
793
+ delete rawVars[oldKey];
794
+ if (newKey && !(newKey in rawVars)) rawVars[newKey] = value;
795
+ }
796
+ migrateBgToCanvas(rawVars);
797
+ }
798
+ var colorsAndTypeMigration_2026_04_24_legacyKeysAndBgToCanvas = {
799
+ id: "2026-04-24-legacy-keys-and-bg-to-canvas",
800
+ fromVersion: 0,
801
+ toVersion: 1,
802
+ appliesTo: "colors-and-type",
803
+ apply(rawVars) {
804
+ const out = { ...rawVars };
805
+ migrateLegacyKeys(out);
806
+ return out;
807
+ }
808
+ };
809
+
810
+ // src/editor/core/themes/migrations/2026-04-24-component-prefix-and-suffix-renames.ts
811
+ var COMPONENT_PREFIX_RENAMES = {
812
+ "--segment-": "--segmentedcontrol-",
813
+ "--collapsible-": "--collapsiblesection-",
814
+ "--progress-": "--progressbar-",
815
+ "--section-divider-": "--sectiondivider-",
816
+ "--radio-": "--radiobutton-",
817
+ "--inline-edit-": "--inlineeditactions-"
818
+ };
819
+ var COMPONENT_SUFFIX_RENAMES = {
820
+ progressbar: [["-track-bg", "-track-surface"]],
821
+ inlineeditactions: [
822
+ ["-bg-hover", "-hover-surface"],
823
+ ["-bg", "-surface"]
824
+ ]
825
+ };
826
+ var componentMigration_2026_04_24_prefixAndSuffixRenames = {
827
+ id: "2026-04-24-component-prefix-and-suffix-renames",
828
+ fromVersion: 0,
829
+ toVersion: 1,
830
+ appliesTo: "component-config",
831
+ apply(rawVars, meta) {
832
+ const component = meta.component ?? "";
833
+ const out = {};
834
+ const suffixRules = COMPONENT_SUFFIX_RENAMES[component] ?? [];
835
+ for (const [oldKey, value] of Object.entries(rawVars)) {
836
+ let key = oldKey;
837
+ if (key === "--detail-nav-bg") key = "--detailnav-surface";
838
+ for (const [oldPrefix, newPrefix] of Object.entries(COMPONENT_PREFIX_RENAMES)) {
839
+ if (key.startsWith(oldPrefix)) {
840
+ key = newPrefix + key.slice(oldPrefix.length);
841
+ break;
842
+ }
843
+ }
844
+ for (const [oldSuffix, newSuffix] of suffixRules) {
845
+ if (key.endsWith(oldSuffix)) {
846
+ key = key.slice(0, -oldSuffix.length) + newSuffix;
847
+ break;
848
+ }
849
+ }
850
+ if (!(key in out)) out[key] = value;
851
+ }
852
+ return out;
853
+ }
854
+ };
855
+
856
+ // src/editor/core/themes/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts
857
+ var OPTION_DISABLED_PREFIX = "--segmentedcontrol-option-disabled-";
858
+ var DISABLED_PREFIX = "--segmentedcontrol-disabled-";
859
+ var SELECTED_DISABLED_PREFIX = "--segmentedcontrol-selected-disabled-";
860
+ var SELECTED_HOVER_PREFIX = "--segmentedcontrol-selected-hover-";
861
+ var componentMigration_2026_04_27_segmentedcontrolDisabledFlatten = {
862
+ id: "2026-04-27-segmentedcontrol-disabled-flatten",
863
+ fromVersion: 1,
864
+ toVersion: 2,
865
+ appliesTo: "component-config",
866
+ apply(rawVars, meta) {
867
+ if (meta.component !== "segmentedcontrol") return { ...rawVars };
868
+ const out = {};
869
+ for (const [oldKey, value] of Object.entries(rawVars)) {
870
+ let key = oldKey;
871
+ if (key.startsWith(SELECTED_DISABLED_PREFIX)) continue;
872
+ if (key.startsWith(SELECTED_HOVER_PREFIX)) continue;
873
+ if (key.startsWith(OPTION_DISABLED_PREFIX)) {
874
+ key = DISABLED_PREFIX + key.slice(OPTION_DISABLED_PREFIX.length);
875
+ }
876
+ if (!(key in out)) out[key] = value;
877
+ }
878
+ return out;
879
+ }
880
+ };
881
+
882
+ // src/editor/core/themes/migrations/2026-05-08-collapsiblesection-variant-namespace.ts
883
+ var PREFIX = "--collapsiblesection-";
884
+ var STATE_NAMES = /* @__PURE__ */ new Set(["default", "hover", "active", "expanded"]);
885
+ var componentMigration_2026_05_08_collapsiblesectionVariantNamespace = {
886
+ id: "2026-05-08-collapsiblesection-variant-namespace",
887
+ fromVersion: 2,
888
+ toVersion: 3,
889
+ appliesTo: "component-config",
890
+ apply(rawVars, meta) {
891
+ if (meta.component !== "collapsiblesection") return { ...rawVars };
892
+ const out = {};
893
+ for (const [oldKey, value] of Object.entries(rawVars)) {
894
+ let key = oldKey;
895
+ if (key.startsWith(PREFIX)) {
896
+ const tail = key.slice(PREFIX.length);
897
+ const firstSegment = tail.split("-", 1)[0];
898
+ if (STATE_NAMES.has(firstSegment)) {
899
+ key = `${PREFIX}container-${tail}`;
900
+ }
901
+ }
902
+ if (!(key in out)) out[key] = value;
903
+ }
904
+ return out;
905
+ }
906
+ };
907
+
908
+ // src/editor/core/themes/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts
909
+ var PFX = "--collapsiblesection-";
910
+ var componentMigration_2026_05_08_collapsiblesectionFrameAndCleanup = {
911
+ id: "2026-05-08-collapsiblesection-frame-and-cleanup",
912
+ fromVersion: 3,
913
+ toVersion: 4,
914
+ appliesTo: "component-config",
915
+ apply(rawVars, meta) {
916
+ if (meta.component !== "collapsiblesection") return { ...rawVars };
917
+ const out = {};
918
+ for (const prop of ["surface", "border", "border-width", "radius"]) {
919
+ const src = `${PFX}container-default-${prop}`;
920
+ if (src in rawVars) {
921
+ out[`${PFX}container-frame-${prop}`] = rawVars[src];
922
+ }
923
+ }
924
+ for (const [key, value] of Object.entries(rawVars)) {
925
+ if (/^--collapsiblesection-chromeless-(default|hover|active)-(border|border-width|radius)$/.test(key)) continue;
926
+ if (/^--collapsiblesection-container-(hover|active)-(border|border-width|radius)$/.test(key)) continue;
927
+ if (/^--collapsiblesection-container-default-(border|border-width|radius)$/.test(key)) continue;
928
+ if (/^--collapsiblesection-divider-(default|hover|active)-radius$/.test(key)) continue;
929
+ if (/^--collapsiblesection-(chromeless|divider|container)-expanded-(border|border-width|radius)$/.test(key)) continue;
930
+ if (/^--collapsiblesection-(chromeless|divider)-expanded-surface$/.test(key)) continue;
931
+ if (!(key in out)) out[key] = value;
932
+ }
933
+ return out;
934
+ }
935
+ };
936
+
937
+ // src/editor/core/themes/migrations/2026-05-10-sectiondivider-gradient-stops.ts
938
+ var PFX2 = "--sectiondivider-";
939
+ var VARIANTS = ["canvas", "neutral", "alternate", "primary", "accent", "special"];
940
+ var OLD_STOP_RE = /^--sectiondivider-(canvas|neutral|alternate|primary|accent|special)-gradient-stop-\d+$/;
941
+ var componentMigration_2026_05_10_sectiondividerGradientStops = {
942
+ id: "2026-05-10-sectiondivider-gradient-stops",
943
+ fromVersion: 4,
944
+ toVersion: 5,
945
+ appliesTo: "component-config",
946
+ apply(rawVars, meta) {
947
+ if (meta.component !== "sectiondivider") return { ...rawVars };
948
+ const out = {};
949
+ for (const [key, value] of Object.entries(rawVars)) {
950
+ if (OLD_STOP_RE.test(key)) continue;
951
+ out[key] = value;
952
+ }
953
+ const setIfMissing = (key, fallback) => {
954
+ if (!(key in out)) out[key] = fallback;
955
+ };
956
+ for (const v of VARIANTS) {
957
+ const old1 = rawVars[`${PFX2}${v}-gradient-stop-1`];
958
+ const old2 = rawVars[`${PFX2}${v}-gradient-stop-2`];
959
+ const old4 = rawVars[`${PFX2}${v}-gradient-stop-4`];
960
+ setIfMissing(`${PFX2}${v}-gradient-angle`, "--gradient-angle-diagonal");
961
+ setIfMissing(`${PFX2}${v}-gradient-stop-1-color`, old1 ?? `--surface-${v}-highest`);
962
+ setIfMissing(`${PFX2}${v}-gradient-stop-1-position`, "--gradient-stop-start");
963
+ setIfMissing(`${PFX2}${v}-gradient-stop-2-color`, old2 ?? `--surface-${v}-higher`);
964
+ setIfMissing(`${PFX2}${v}-gradient-stop-2-position`, "--gradient-stop-mid");
965
+ setIfMissing(`${PFX2}${v}-gradient-stop-3-color`, old4 ?? `--surface-${v}`);
966
+ setIfMissing(`${PFX2}${v}-gradient-stop-3-position`, "--gradient-stop-end");
967
+ }
968
+ return out;
969
+ }
970
+ };
971
+
972
+ // src/editor/core/themes/migrations/2026-05-13-primary-to-brand.ts
973
+ var PALETTE_STEPS2 = ["100", "200", "300", "400", "500", "600", "700", "800", "850", "900", "950"];
974
+ var SURFACE_SUFFIXES = ["lowest", "lower", "low", "high", "higher", "highest"];
975
+ var BORDER_SUFFIXES = ["faint", "subtle", "medium", "strong"];
976
+ var TEXT_SUFFIXES = ["secondary", "tertiary", "muted", "disabled"];
977
+ var RENAME_MAP = (() => {
978
+ const m = {};
979
+ for (const step of PALETTE_STEPS2) m[`--color-primary-${step}`] = `--color-brand-${step}`;
980
+ m["--surface-primary"] = "--surface-brand";
981
+ for (const suf of SURFACE_SUFFIXES) m[`--surface-primary-${suf}`] = `--surface-brand-${suf}`;
982
+ m["--border-primary"] = "--border-brand";
983
+ for (const suf of BORDER_SUFFIXES) m[`--border-primary-${suf}`] = `--border-brand-${suf}`;
984
+ m["--text-primary-color"] = "--text-brand";
985
+ for (const suf of TEXT_SUFFIXES) m[`--text-primary-${suf}`] = `--text-brand-${suf}`;
986
+ return m;
987
+ })();
988
+ function renameToken(name) {
989
+ return RENAME_MAP[name] ?? name;
990
+ }
991
+ function renameKeys(rawVars) {
992
+ const out = {};
993
+ for (const [k, v] of Object.entries(rawVars)) {
994
+ out[renameToken(k)] = v;
995
+ }
996
+ return out;
997
+ }
998
+ function renameKeysAndValues(rawVars) {
999
+ const out = {};
1000
+ for (const [k, v] of Object.entries(rawVars)) {
1001
+ out[renameToken(k)] = renameToken(v);
1002
+ }
1003
+ return out;
1004
+ }
1005
+ var colorsAndTypeMigration_2026_05_13_primaryToBrand = {
1006
+ id: "2026-05-13-primary-to-brand-theme",
1007
+ fromVersion: 1,
1008
+ toVersion: 2,
1009
+ appliesTo: "colors-and-type",
1010
+ apply: renameKeys
1011
+ };
1012
+ var componentMigration_2026_05_13_primaryToBrand = {
1013
+ id: "2026-05-13-primary-to-brand-component",
1014
+ fromVersion: 5,
1015
+ toVersion: 6,
1016
+ appliesTo: "component-config",
1017
+ apply: renameKeysAndValues
1018
+ };
1019
+
1020
+ // src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts
1021
+ function dropFrameSurface(rawVars, meta) {
1022
+ if (meta.component !== "collapsiblesection") return rawVars;
1023
+ const { "--collapsiblesection-container-frame-surface": _drop, ...rest } = rawVars;
1024
+ return rest;
1025
+ }
1026
+ var componentMigration_2026_05_19_collapsiblesectionDropFrameSurface = {
1027
+ id: "2026-05-19-collapsiblesection-drop-frame-surface",
1028
+ fromVersion: 6,
1029
+ toVersion: 7,
1030
+ appliesTo: "component-config",
1031
+ apply: dropFrameSurface
1032
+ };
1033
+
1034
+ // src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts
1035
+ var FLAT_RE = /^--sectiondivider-(canvas|neutral|alternate|primary|accent|special)-gradient-(angle|stop-[123]-(color|position))$/;
1036
+ var componentMigration_2026_05_19_sectiondividerRichGradient = {
1037
+ id: "2026-05-19-sectiondivider-rich-gradient",
1038
+ fromVersion: 7,
1039
+ toVersion: 8,
1040
+ appliesTo: "component-config",
1041
+ apply(rawVars, meta) {
1042
+ if (meta.component !== "sectiondivider") return { ...rawVars };
1043
+ const out = {};
1044
+ for (const [key, value] of Object.entries(rawVars)) {
1045
+ if (FLAT_RE.test(key)) continue;
1046
+ out[key] = value;
1047
+ }
1048
+ return out;
1049
+ }
1050
+ };
1051
+
1052
+ // src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts
1053
+ var VARIANTS2 = ["lg", "md", "sm"];
1054
+ var SUFFIX_RENAMES = [
1055
+ [/-title-border-width$/, "-title-outline-width"],
1056
+ [/-title-stroke-color$/, "-title-outline-color"],
1057
+ [
1058
+ /^--sectiondivider-(canvas|neutral|alternate|primary|accent|special)-padding$/,
1059
+ "--sectiondivider-$1-spacing"
1060
+ ]
1061
+ ];
1062
+ function renameKeySuffix(key) {
1063
+ for (const [re, repl] of SUFFIX_RENAMES) {
1064
+ if (re.test(key)) return key.replace(re, repl);
1065
+ }
1066
+ return key;
1067
+ }
1068
+ function splitFamilyKey(key) {
1069
+ const m = key.match(/^--sectiondivider-(canvas|neutral|alternate|primary|accent|special)-(.+)$/);
1070
+ if (!m) return null;
1071
+ return { family: m[1], suffix: m[2] };
1072
+ }
1073
+ var componentMigration_2026_05_20_sectiondividerSlimVariants = {
1074
+ id: "2026-05-20-sectiondivider-slim-variants",
1075
+ fromVersion: 8,
1076
+ toVersion: 9,
1077
+ appliesTo: "component-config",
1078
+ apply(rawVars, meta) {
1079
+ if (meta.component !== "sectiondivider") return { ...rawVars };
1080
+ const out = {};
1081
+ const canvasValues = {};
1082
+ for (const [rawKey, value] of Object.entries(rawVars)) {
1083
+ const key = renameKeySuffix(rawKey);
1084
+ const parts = splitFamilyKey(key);
1085
+ if (!parts) {
1086
+ out[key] = value;
1087
+ continue;
1088
+ }
1089
+ if (parts.family !== "canvas") continue;
1090
+ canvasValues[parts.suffix] = value;
1091
+ }
1092
+ for (const suffix of Object.keys(canvasValues)) {
1093
+ for (const v of VARIANTS2) {
1094
+ out[`--sectiondivider-${v}-${suffix}`] = canvasValues[suffix];
1095
+ }
1096
+ }
1097
+ return out;
1098
+ }
1099
+ };
1100
+
1101
+ // src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts
1102
+ var componentMigration_2026_05_21_sectiondividerSpacingToPadding = {
1103
+ id: "2026-05-21-sectiondivider-spacing-to-padding",
1104
+ fromVersion: 9,
1105
+ toVersion: 10,
1106
+ appliesTo: "component-config",
1107
+ apply(rawVars, meta) {
1108
+ if (meta.component !== "sectiondivider") return { ...rawVars };
1109
+ const out = {};
1110
+ for (const [key, value] of Object.entries(rawVars)) {
1111
+ const renamed = key.replace(
1112
+ /^--sectiondivider-(lg|md|sm)-spacing$/,
1113
+ "--sectiondivider-$1-padding"
1114
+ );
1115
+ out[renamed] = value;
1116
+ }
1117
+ return out;
1118
+ }
1119
+ };
1120
+
1121
+ // src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts
1122
+ var componentMigration_2026_05_22_sectiondividerIntrinsicsToCss = {
1123
+ id: "2026-05-22-sectiondivider-intrinsics-to-css",
1124
+ fromVersion: 10,
1125
+ toVersion: 11,
1126
+ appliesTo: "component-config",
1127
+ apply(rawVars, meta) {
1128
+ if (meta.component !== "sectiondivider") return { ...rawVars };
1129
+ const VARIANTS4 = ["lg", "md", "sm"];
1130
+ const prefix = "--sectiondivider-";
1131
+ const dropped = /* @__PURE__ */ new Set();
1132
+ for (const v of VARIANTS4) {
1133
+ dropped.add(`${prefix}${v}-show-eyebrow`);
1134
+ dropped.add(`${prefix}${v}-show-description`);
1135
+ dropped.add(`${prefix}${v}-show-hairline`);
1136
+ dropped.add(`${prefix}${v}-hairline`);
1137
+ dropped.add(`${prefix}${v}-eyebrow-uppercase`);
1138
+ }
1139
+ const out = {};
1140
+ for (const [key, value] of Object.entries(rawVars)) {
1141
+ if (dropped.has(key)) continue;
1142
+ out[key] = value;
1143
+ }
1144
+ for (const v of VARIANTS4) {
1145
+ const showEyebrow = rawVars[`${prefix}${v}-show-eyebrow`];
1146
+ if (showEyebrow !== void 0) {
1147
+ out[`${prefix}${v}-eyebrow-display`] = showEyebrow === "1" ? "block" : "none";
1148
+ }
1149
+ const showDesc = rawVars[`${prefix}${v}-show-description`];
1150
+ if (showDesc !== void 0) {
1151
+ out[`${prefix}${v}-description-display`] = showDesc === "1" ? "flex" : "none";
1152
+ }
1153
+ const showHair = rawVars[`${prefix}${v}-show-hairline`];
1154
+ const oldHair = rawVars[`${prefix}${v}-hairline`];
1155
+ const oldHairIsPos = typeof oldHair === "string" && oldHair !== "" && oldHair !== "off";
1156
+ let newHair;
1157
+ if (showHair === "") newHair = "none";
1158
+ else if (showHair === "1") newHair = oldHairIsPos ? oldHair : "above-label";
1159
+ else if (oldHair !== void 0) newHair = oldHairIsPos ? oldHair : "none";
1160
+ if (newHair !== void 0) {
1161
+ out[`${prefix}${v}-hairline`] = newHair;
1162
+ }
1163
+ const upper = rawVars[`${prefix}${v}-eyebrow-uppercase`];
1164
+ if (upper !== void 0) {
1165
+ out[`${prefix}${v}-eyebrow-text-transform`] = upper === "1" ? "uppercase" : "none";
1166
+ }
1167
+ }
1168
+ return out;
1169
+ }
1170
+ };
1171
+
1172
+ // src/editor/core/themes/migrations/2026-05-24-segmentedcontrol-divider-inset.ts
1173
+ var componentMigration_2026_05_24_segmentedcontrolDividerInset = {
1174
+ id: "2026-05-24-segmentedcontrol-divider-inset",
1175
+ fromVersion: 11,
1176
+ toVersion: 12,
1177
+ appliesTo: "component-config",
1178
+ apply(rawVars, meta) {
1179
+ if (meta.component !== "segmentedcontrol") return { ...rawVars };
1180
+ const out = {};
1181
+ for (const [key, value] of Object.entries(rawVars)) {
1182
+ if (key === "--segmentedcontrol-divider-height") continue;
1183
+ out[key] = value;
1184
+ }
1185
+ return out;
1186
+ }
1187
+ };
1188
+
1189
+ // src/editor/core/themes/migrations/2026-05-24-promote-state-shared-tokens.ts
1190
+ var BUTTON_VARIANTS = ["primary", "secondary", "outline", "success", "danger", "warning"];
1191
+ var BUTTON_STATES = ["hover", "disabled"];
1192
+ var BUTTON_PROPS = ["padding", "radius", "border-width"];
1193
+ var buttonDeadKeys = new Set(
1194
+ BUTTON_VARIANTS.flatMap(
1195
+ (v) => BUTTON_STATES.flatMap(
1196
+ (s) => BUTTON_PROPS.map((p) => `--button-${v}-${s}-${p}`)
1197
+ )
1198
+ )
1199
+ );
1200
+ var segmentedcontrolDeadKeys = /* @__PURE__ */ new Set([
1201
+ "--segmentedcontrol-selected-icon-size",
1202
+ "--segmentedcontrol-option-hover-icon-size",
1203
+ "--segmentedcontrol-disabled-icon-size"
1204
+ ]);
1205
+ var componentMigration_2026_05_24_promoteStateSharedTokens = {
1206
+ id: "2026-05-24-promote-state-shared-tokens",
1207
+ fromVersion: 12,
1208
+ toVersion: 13,
1209
+ appliesTo: "component-config",
1210
+ apply(rawVars, meta) {
1211
+ const dead = meta.component === "button" ? buttonDeadKeys : meta.component === "segmentedcontrol" ? segmentedcontrolDeadKeys : null;
1212
+ if (!dead) return { ...rawVars };
1213
+ const out = {};
1214
+ for (const [key, value] of Object.entries(rawVars)) {
1215
+ if (dead.has(key)) continue;
1216
+ out[key] = value;
1217
+ }
1218
+ return out;
1219
+ }
1220
+ };
1221
+
1222
+ // src/editor/core/themes/migrations/2026-05-24-progressbar-collapse-variants.ts
1223
+ var PRIMARY_PREFIX = "--progressbar-primary-";
1224
+ var NEW_PREFIX = "--progressbar-";
1225
+ var DROP_PREFIXES = [
1226
+ "--progressbar-success-",
1227
+ "--progressbar-warning-",
1228
+ "--progressbar-danger-",
1229
+ "--progressbar-info-"
1230
+ ];
1231
+ var componentMigration_2026_05_24_progressbarCollapseVariants = {
1232
+ id: "2026-05-24-progressbar-collapse-variants",
1233
+ fromVersion: 13,
1234
+ toVersion: 14,
1235
+ appliesTo: "component-config",
1236
+ apply(rawVars, meta) {
1237
+ if (meta.component !== "progressbar") return { ...rawVars };
1238
+ const out = {};
1239
+ for (const [oldKey, value] of Object.entries(rawVars)) {
1240
+ if (DROP_PREFIXES.some((p) => oldKey.startsWith(p))) continue;
1241
+ const key = oldKey.startsWith(PRIMARY_PREFIX) ? NEW_PREFIX + oldKey.slice(PRIMARY_PREFIX.length) : oldKey;
1242
+ if (!(key in out)) out[key] = value;
1243
+ }
1244
+ return out;
1245
+ }
1246
+ };
1247
+
1248
+ // src/editor/core/themes/migrations/2026-05-24-collapsiblesection-drop-active-state.ts
1249
+ function dropActiveState(rawVars, meta) {
1250
+ if (meta.component !== "collapsiblesection") return rawVars;
1251
+ const re = /^--collapsiblesection-(chromeless|divider|container)-active-/;
1252
+ const out = {};
1253
+ for (const [key, value] of Object.entries(rawVars)) {
1254
+ if (re.test(key)) continue;
1255
+ out[key] = value;
1256
+ }
1257
+ return out;
1258
+ }
1259
+ var componentMigration_2026_05_24_collapsiblesectionDropActiveState = {
1260
+ id: "2026-05-24-collapsiblesection-drop-active-state",
1261
+ fromVersion: 14,
1262
+ toVersion: 15,
1263
+ appliesTo: "component-config",
1264
+ apply: dropActiveState
1265
+ };
1266
+
1267
+ // src/editor/core/themes/migrations/2026-05-25-cornerbadge-flatten-variants.ts
1268
+ var VARIANTS3 = ["primary", "accent", "neutral", "alternate", "canvas", "special", "success", "warning", "danger", "info"];
1269
+ var RE = new RegExp(`^--corner-badge-(${VARIANTS3.join("|")})-(.+)$`);
1270
+ var CURRENT_COLOR_PROPS = /* @__PURE__ */ new Set(["surface", "border", "text"]);
1271
+ function flattenVariants(rawVars, meta) {
1272
+ if (meta.component !== "cornerbadge") return rawVars;
1273
+ const out = {};
1274
+ const collected = /* @__PURE__ */ new Map();
1275
+ for (const [key, value] of Object.entries(rawVars)) {
1276
+ const m = key.match(RE);
1277
+ if (m) {
1278
+ const prop = m[2];
1279
+ if (CURRENT_COLOR_PROPS.has(prop)) {
1280
+ out[key] = value;
1281
+ continue;
1282
+ }
1283
+ if (!collected.has(prop)) collected.set(prop, value);
1284
+ continue;
1285
+ }
1286
+ out[key] = value;
1287
+ }
1288
+ for (const [prop, value] of collected) {
1289
+ const flatKey = `--corner-badge-${prop}`;
1290
+ if (!(flatKey in out)) out[flatKey] = value;
1291
+ }
1292
+ return out;
1293
+ }
1294
+ var componentMigration_2026_05_25_cornerbadgeFlattenVariants = {
1295
+ id: "2026-05-25-cornerbadge-flatten-variants",
1296
+ fromVersion: 15,
1297
+ toVersion: 16,
1298
+ appliesTo: "component-config",
1299
+ apply: flattenVariants
1300
+ };
1301
+
1302
+ // src/editor/core/themes/migrations/2026-05-26-drop-overlay-extra-stops.ts
1303
+ var DROPPED_TO_KEPT = {
1304
+ "--overlay-lowest": "--overlay-low",
1305
+ "--overlay-lower": "--overlay-low",
1306
+ "--overlay-higher": "--overlay-high",
1307
+ "--overlay-highest": "--overlay-high"
1308
+ };
1309
+ var colorsAndTypeMigration_2026_05_26_dropOverlayExtraStops = {
1310
+ id: "2026-05-26-drop-overlay-extra-stops-theme",
1311
+ fromVersion: 2,
1312
+ toVersion: 3,
1313
+ appliesTo: "colors-and-type",
1314
+ apply(rawVars) {
1315
+ const out = {};
1316
+ for (const [key, value] of Object.entries(rawVars)) {
1317
+ if (key in DROPPED_TO_KEPT) continue;
1318
+ out[key] = value;
1319
+ }
1320
+ return out;
1321
+ }
1322
+ };
1323
+ var componentMigration_2026_05_26_dropOverlayExtraStops = {
1324
+ id: "2026-05-26-drop-overlay-extra-stops-component",
1325
+ fromVersion: 16,
1326
+ toVersion: 17,
1327
+ appliesTo: "component-config",
1328
+ apply(rawVars) {
1329
+ const out = {};
1330
+ for (const [key, value] of Object.entries(rawVars)) {
1331
+ out[key] = DROPPED_TO_KEPT[value] ?? value;
1332
+ }
1333
+ return out;
1334
+ }
1335
+ };
1336
+
1337
+ // src/editor/core/themes/migrations/2026-05-29-tabbar-indicator-thickness-to-per-state-width.ts
1338
+ var TABBAR_STATES = ["default", "hover", "active", "disabled"];
1339
+ var OLD_KEY = "--tabbar-bar-indicator-thickness";
1340
+ var FALLBACK = "--border-width-2";
1341
+ var componentMigration_2026_05_29_tabbarIndicatorThicknessToPerStateWidth = {
1342
+ id: "2026-05-29-tabbar-indicator-thickness-to-per-state-width",
1343
+ fromVersion: 17,
1344
+ toVersion: 18,
1345
+ appliesTo: "component-config",
1346
+ apply(rawVars, meta) {
1347
+ if (meta.component !== "tabbar") return { ...rawVars };
1348
+ const seed = rawVars[OLD_KEY] ?? FALLBACK;
1349
+ const out = {};
1350
+ for (const [key, value] of Object.entries(rawVars)) {
1351
+ if (key === OLD_KEY) continue;
1352
+ out[key] = value;
1353
+ }
1354
+ for (const state of TABBAR_STATES) {
1355
+ const newKey = `--tabbar-${state}-indicator-border-width`;
1356
+ if (!(newKey in out)) out[newKey] = seed;
1357
+ }
1358
+ return out;
1359
+ }
1360
+ };
1361
+
1362
+ // src/editor/core/themes/migrations/2026-05-29-segmentedcontrol-small-divider-rename.ts
1363
+ var RENAMES = {
1364
+ "--segmentedcontrol-divider-small-thickness": "--segmentedcontrol-small-divider-thickness",
1365
+ "--segmentedcontrol-divider-small-inset": "--segmentedcontrol-small-divider-inset"
1366
+ };
1367
+ var componentMigration_2026_05_29_segmentedcontrolSmallDividerRename = {
1368
+ id: "2026-05-29-segmentedcontrol-small-divider-rename",
1369
+ fromVersion: 18,
1370
+ toVersion: 19,
1371
+ appliesTo: "component-config",
1372
+ apply(rawVars, meta) {
1373
+ if (meta.component !== "segmentedcontrol") return { ...rawVars };
1374
+ const out = {};
1375
+ for (const [key, value] of Object.entries(rawVars)) {
1376
+ out[RENAMES[key] ?? key] = value;
1377
+ }
1378
+ return out;
1379
+ }
1380
+ };
1381
+
1382
+ // src/editor/core/themes/migrations/2026-05-29-toggle-derive-track-from-thumb.ts
1383
+ var componentMigration_2026_05_29_toggleDeriveTrackFromThumb = {
1384
+ id: "2026-05-29-toggle-derive-track-from-thumb",
1385
+ fromVersion: 19,
1386
+ toVersion: 20,
1387
+ appliesTo: "component-config",
1388
+ apply(rawVars, meta) {
1389
+ if (meta.component !== "toggle") return { ...rawVars };
1390
+ const out = {};
1391
+ for (const [key, value] of Object.entries(rawVars)) {
1392
+ if (key === "--toggle-track-width" || key === "--toggle-track-thickness") continue;
1393
+ out[key] = value;
1394
+ }
1395
+ if (!("--toggle-track-padding" in out)) {
1396
+ out["--toggle-track-padding"] = "--space-2";
1397
+ }
1398
+ return out;
1399
+ }
1400
+ };
1401
+
1402
+ // src/editor/core/themes/migrations/2026-08-13-drop-legacy-shape-space-keys.ts
1403
+ var DROPPED = /* @__PURE__ */ new Set([
1404
+ "--badge-trait-radius",
1405
+ "--badge-trait-padding",
1406
+ "--sectiondivider-padding",
1407
+ "--dialog-primary-default-radius",
1408
+ "--dialog-primary-default-padding",
1409
+ "--dialog-primary-hover-radius",
1410
+ "--dialog-primary-hover-padding",
1411
+ "--dialog-secondary-default-radius",
1412
+ "--dialog-secondary-default-padding",
1413
+ "--dialog-secondary-hover-radius",
1414
+ "--dialog-secondary-hover-padding"
1415
+ ]);
1416
+ var colorsAndTypeMigration_2026_08_13_dropLegacyShapeSpaceKeys = {
1417
+ id: "2026-08-13-drop-legacy-shape-space-keys",
1418
+ fromVersion: 3,
1419
+ toVersion: 4,
1420
+ appliesTo: "colors-and-type",
1421
+ apply(rawVars) {
1422
+ const out = {};
1423
+ for (const [key, value] of Object.entries(rawVars)) {
1424
+ if (DROPPED.has(key)) continue;
1425
+ out[key] = value;
1426
+ }
1427
+ return out;
1428
+ }
1429
+ };
1430
+
1431
+ // src/editor/core/themes/migrations/2026-08-15-line-height-scale-rename.ts
1432
+ var RENAMED = {
1433
+ "--line-height-xs": "--line-height-none",
1434
+ "--line-height-sm": "--line-height-tighter",
1435
+ "--line-height-md": "--line-height-normal",
1436
+ "--line-height-lg": "--line-height-relaxed",
1437
+ "--line-height-xl": "--line-height-relaxed"
1438
+ };
1439
+ var REFERENCE = /--line-height-(?:xs|sm|md|lg|xl)(?![\w-])/g;
1440
+ function renameValues(rawVars) {
1441
+ const out = {};
1442
+ for (const [key, value] of Object.entries(rawVars)) {
1443
+ out[key] = typeof value === "string" ? value.replace(REFERENCE, (name) => RENAMED[name]) : value;
1444
+ }
1445
+ return out;
1446
+ }
1447
+ var colorsAndTypeMigration_2026_08_15_lineHeightScaleRename = {
1448
+ id: "2026-08-15-line-height-scale-rename-colors-and-type",
1449
+ fromVersion: 4,
1450
+ toVersion: 5,
1451
+ appliesTo: "colors-and-type",
1452
+ apply: renameValues
1453
+ };
1454
+ var componentMigration_2026_08_15_lineHeightScaleRename = {
1455
+ id: "2026-08-15-line-height-scale-rename-component",
1456
+ fromVersion: 20,
1457
+ toVersion: 21,
1458
+ appliesTo: "component-config",
1459
+ apply: renameValues
1460
+ };
1461
+
1462
+ // src/editor/core/themes/migrations/index.ts
1463
+ var MIGRATIONS = [
1464
+ colorsAndTypeMigration_2026_04_24_legacyKeysAndBgToCanvas,
1465
+ colorsAndTypeMigration_2026_05_13_primaryToBrand,
1466
+ componentMigration_2026_04_24_prefixAndSuffixRenames,
1467
+ componentMigration_2026_04_27_segmentedcontrolDisabledFlatten,
1468
+ componentMigration_2026_05_08_collapsiblesectionVariantNamespace,
1469
+ componentMigration_2026_05_08_collapsiblesectionFrameAndCleanup,
1470
+ componentMigration_2026_05_10_sectiondividerGradientStops,
1471
+ componentMigration_2026_05_13_primaryToBrand,
1472
+ componentMigration_2026_05_19_collapsiblesectionDropFrameSurface,
1473
+ componentMigration_2026_05_19_sectiondividerRichGradient,
1474
+ componentMigration_2026_05_20_sectiondividerSlimVariants,
1475
+ componentMigration_2026_05_21_sectiondividerSpacingToPadding,
1476
+ componentMigration_2026_05_22_sectiondividerIntrinsicsToCss,
1477
+ componentMigration_2026_05_24_segmentedcontrolDividerInset,
1478
+ componentMigration_2026_05_24_promoteStateSharedTokens,
1479
+ componentMigration_2026_05_24_progressbarCollapseVariants,
1480
+ componentMigration_2026_05_24_collapsiblesectionDropActiveState,
1481
+ componentMigration_2026_05_25_cornerbadgeFlattenVariants,
1482
+ colorsAndTypeMigration_2026_05_26_dropOverlayExtraStops,
1483
+ componentMigration_2026_05_26_dropOverlayExtraStops,
1484
+ componentMigration_2026_05_29_tabbarIndicatorThicknessToPerStateWidth,
1485
+ componentMigration_2026_05_29_segmentedcontrolSmallDividerRename,
1486
+ componentMigration_2026_05_29_toggleDeriveTrackFromThumb,
1487
+ colorsAndTypeMigration_2026_08_13_dropLegacyShapeSpaceKeys,
1488
+ colorsAndTypeMigration_2026_08_15_lineHeightScaleRename,
1489
+ componentMigration_2026_08_15_lineHeightScaleRename
1490
+ ];
1491
+ function countFor(kind) {
1492
+ return MIGRATIONS.filter((m) => m.appliesTo === kind).length;
1493
+ }
1494
+ var CURRENT_COLORS_AND_TYPE_SCHEMA_VERSION = countFor("colors-and-type");
1495
+ var CURRENT_COMPONENT_SCHEMA_VERSION = countFor("component-config");
1496
+ function runMigrations(kind, fileVersion, rawVars, meta = {}) {
1497
+ const applicable = MIGRATIONS.filter((m) => m.appliesTo === kind && m.fromVersion >= fileVersion).sort((a, b) => a.toVersion - b.toVersion);
1498
+ let out = { ...rawVars };
1499
+ for (const m of applicable) {
1500
+ out = m.apply(out, meta);
1501
+ }
1502
+ return out;
1503
+ }
1504
+
645
1505
  // vite-plugin/files/versionedFileResourceServer.ts
646
1506
  var import_fs = __toESM(require("fs"), 1);
647
1507
  var import_path = __toESM(require("path"), 1);
@@ -801,8 +1661,141 @@ async function dispatch(req, res, routes) {
801
1661
  return false;
802
1662
  }
803
1663
 
1664
+ // src/editor/core/themes/migrateComponentConfig.ts
1665
+ function migrateComponentConfig(component, aliasesIn, rawConfig, fileVersion) {
1666
+ const stringPart = {};
1667
+ const objectPart = {};
1668
+ for (const [k, v] of Object.entries(aliasesIn)) {
1669
+ if (typeof v === "string") stringPart[k] = v;
1670
+ else objectPart[k] = v;
1671
+ }
1672
+ const configOut = {};
1673
+ for (const [k, v] of Object.entries(rawConfig ?? {})) {
1674
+ if (typeof v === "string") stringPart[k] = v;
1675
+ else configOut[k] = v;
1676
+ }
1677
+ if (component === "sectiondivider") {
1678
+ synthesizeSectionDividerGradients(stringPart, objectPart);
1679
+ }
1680
+ const migratedString = runMigrations("component-config", fileVersion, stringPart, { component });
1681
+ const migratedObject = component === "sectiondivider" ? renameSectionDividerObjectSlots(objectPart) : objectPart;
1682
+ return {
1683
+ aliases: { ...migratedString, ...migratedObject },
1684
+ config: configOut
1685
+ };
1686
+ }
1687
+ var FAMILIES = ["canvas", "neutral", "alternate", "primary", "accent", "special"];
1688
+ var SIZE_VARIANTS = ["lg", "md", "sm"];
1689
+ function renameSectionDividerObjectSlots(objectPart) {
1690
+ const canvasSourceKeys = [
1691
+ "--sectiondivider-canvas-background",
1692
+ "--sectiondivider-canvas-gradient",
1693
+ "--sectiondivider-color-canvas-background"
1694
+ ];
1695
+ let canvasGradient;
1696
+ for (const key of canvasSourceKeys) {
1697
+ if (objectPart[key] !== void 0) {
1698
+ canvasGradient = objectPart[key];
1699
+ break;
1700
+ }
1701
+ }
1702
+ const out = {};
1703
+ for (const [key, value] of Object.entries(objectPart)) {
1704
+ let isFamilyGradient = false;
1705
+ for (const f of FAMILIES) {
1706
+ if (key === `--sectiondivider-${f}-background` || key === `--sectiondivider-${f}-gradient` || key === `--sectiondivider-color-${f}-background`) {
1707
+ isFamilyGradient = true;
1708
+ break;
1709
+ }
1710
+ }
1711
+ if (isFamilyGradient) continue;
1712
+ out[key] = value;
1713
+ }
1714
+ if (canvasGradient !== void 0) {
1715
+ for (const v of SIZE_VARIANTS) {
1716
+ out[`--sectiondivider-${v}-background`] = canvasGradient;
1717
+ }
1718
+ }
1719
+ return out;
1720
+ }
1721
+ var SECTIONDIVIDER_VARIANTS = ["canvas", "neutral", "alternate", "primary", "accent", "special"];
1722
+ function synthesizeSectionDividerGradients(stringPart, objectPart) {
1723
+ const GRADIENT_ANGLE_TOKENS = {
1724
+ "--gradient-angle-horizontal": 90,
1725
+ "--gradient-angle-vertical": 180,
1726
+ "--gradient-angle-diagonal": 135,
1727
+ "--gradient-angle-counter-diagonal": 45
1728
+ };
1729
+ const GRADIENT_STOP_POSITIONS = {
1730
+ "--gradient-stop-start": 0,
1731
+ "--gradient-stop-mid": 50,
1732
+ "--gradient-stop-end": 100
1733
+ };
1734
+ const parseAngle = (raw) => {
1735
+ if (!raw) return 135;
1736
+ if (raw.startsWith("--")) return GRADIENT_ANGLE_TOKENS[raw] ?? 135;
1737
+ const m = raw.match(/^(-?\d+(?:\.\d+)?)\s*deg$/i);
1738
+ return m ? parseFloat(m[1]) : 135;
1739
+ };
1740
+ const parsePosition = (raw, fallback) => {
1741
+ if (!raw) return fallback;
1742
+ if (raw.startsWith("--")) return GRADIENT_STOP_POSITIONS[raw] ?? fallback;
1743
+ const m = raw.match(/^(-?\d+(?:\.\d+)?)\s*%$/);
1744
+ return m ? parseFloat(m[1]) : fallback;
1745
+ };
1746
+ const parseStopColor = (raw) => {
1747
+ if (!raw) return null;
1748
+ if (raw.startsWith("--")) return { color: raw };
1749
+ const mix = raw.match(/^color-mix\(\s*in\s+srgb\s*,\s*var\((--[a-z0-9-]+)\)\s+([\d.]+)%\s*,\s*transparent\s*\)$/i);
1750
+ if (mix) return { color: mix[1], opacity: parseFloat(mix[2]) };
1751
+ return null;
1752
+ };
1753
+ for (const v of SECTIONDIVIDER_VARIANTS) {
1754
+ const slot = `--sectiondivider-${v}-gradient`;
1755
+ if (slot in objectPart) continue;
1756
+ const angleKey = `${slot}-angle`;
1757
+ const has1 = stringPart[`${slot}-stop-1-color`] !== void 0;
1758
+ const has2 = stringPart[`${slot}-stop-2-color`] !== void 0;
1759
+ const has3 = stringPart[`${slot}-stop-3-color`] !== void 0;
1760
+ if (!has1 && !has2 && !has3) continue;
1761
+ const stops = [];
1762
+ const positionDefaults = [0, 50, 100];
1763
+ for (let i = 1; i <= 3; i++) {
1764
+ const c = parseStopColor(stringPart[`${slot}-stop-${i}-color`]);
1765
+ if (!c) continue;
1766
+ const pos = parsePosition(stringPart[`${slot}-stop-${i}-position`], positionDefaults[i - 1]);
1767
+ stops.push({ position: pos, color: c.color, ...c.opacity !== void 0 ? { opacity: c.opacity } : {} });
1768
+ }
1769
+ if (stops.length < 2) continue;
1770
+ objectPart[slot] = {
1771
+ kind: "gradient",
1772
+ value: {
1773
+ type: "linear",
1774
+ angle: parseAngle(stringPart[angleKey]),
1775
+ stops
1776
+ }
1777
+ };
1778
+ }
1779
+ }
1780
+
1781
+ // src/editor/core/themes/themeTypes.ts
1782
+ var THEME_SCHEMA_VERSION = 4;
1783
+
1784
+ // src/editor/core/components/componentConfigKeys.ts
1785
+ var KNOWN_COMPONENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
1786
+ "--dialog-confirm-variant",
1787
+ "--dialog-cancel-variant",
1788
+ // SectionDivider per-variant `color-family` is editor metadata that drives
1789
+ // the family-swap rewrite on aliases. It is not a runtime CSS value, so it
1790
+ // stays in the config bucket. The other intrinsics (align, hairline,
1791
+ // eyebrow/description visibility, eyebrow text-transform) now flow through
1792
+ // the alias bucket as cascading CSS vars — see the 2026-05-22 migration.
1793
+ "--sectiondivider-lg-color-family",
1794
+ "--sectiondivider-md-color-family",
1795
+ "--sectiondivider-sm-color-family"
1796
+ ]);
1797
+
804
1798
  // vite-plugin/themes/normalizeTheme.ts
805
- var THEME_SCHEMA_VERSION = 3;
806
1799
  function asObject(value) {
807
1800
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
808
1801
  }
@@ -824,10 +1817,41 @@ function migrateEmbeddedKey(src) {
824
1817
  const { theme, ...rest } = src;
825
1818
  return { ...rest, colorsAndType: theme };
826
1819
  }
1820
+ function routeKnownConfigKeys(aliases, config) {
1821
+ const outAliases = { ...aliases };
1822
+ const outConfig = { ...config };
1823
+ for (const key of Object.keys(outAliases)) {
1824
+ if (!KNOWN_COMPONENT_CONFIG_KEYS.has(key)) continue;
1825
+ if (outConfig[key] === void 0) outConfig[key] = outAliases[key];
1826
+ delete outAliases[key];
1827
+ }
1828
+ return { aliases: outAliases, config: outConfig };
1829
+ }
1830
+ function migrateEmbeddedComponentConfig(comp, config, fileVersion) {
1831
+ const { schemaVersion: _entryStamp, aliases: rawAliases, config: rawConfig, ...rest } = config;
1832
+ const aliasesIn = asObject(rawAliases);
1833
+ if (!aliasesIn) {
1834
+ return rawConfig !== void 0 ? { ...rest, config: rawConfig } : rest;
1835
+ }
1836
+ const migratedConfig = migrateComponentConfig(
1837
+ comp,
1838
+ aliasesIn,
1839
+ asObject(rawConfig) ?? void 0,
1840
+ fileVersion
1841
+ );
1842
+ const split = routeKnownConfigKeys(migratedConfig.aliases, migratedConfig.config);
1843
+ const out = { ...rest, aliases: split.aliases };
1844
+ if (rawConfig !== void 0 || Object.keys(split.config).length > 0) {
1845
+ out.config = split.config;
1846
+ }
1847
+ return out;
1848
+ }
827
1849
  function normalizeTheme(raw, resolvers) {
828
1850
  const input = asObject(raw) ?? {};
829
1851
  const migrated = input.schemaVersion !== THEME_SCHEMA_VERSION;
830
- const src = migrated ? migrateEmbeddedKey(input) : input;
1852
+ const inputVersion = typeof input.schemaVersion === "number" ? input.schemaVersion : 0;
1853
+ const src = inputVersion < 3 ? migrateEmbeddedKey(input) : input;
1854
+ const componentSchemaVersion = typeof input.componentSchemaVersion === "number" ? input.componentSchemaVersion : 0;
831
1855
  const dropped = [];
832
1856
  const now = (/* @__PURE__ */ new Date()).toISOString();
833
1857
  const embeddedColorsAndType = asObject(src.colorsAndType);
@@ -846,18 +1870,42 @@ function normalizeTheme(raw, resolvers) {
846
1870
  const componentConfigs = {};
847
1871
  for (const [comp, value] of Object.entries(asObject(src.componentConfigs) ?? {})) {
848
1872
  const embedded = asObject(value);
1873
+ let resolvedConfig;
849
1874
  if (embedded) {
850
- componentConfigs[comp] = stripFileMarker(embedded);
1875
+ resolvedConfig = stripFileMarker(embedded);
1876
+ } else {
1877
+ const configName = asString(value, "default");
1878
+ const resolved = asObject(resolvers.readComponentConfig(comp, configName));
1879
+ if (!resolved) {
1880
+ dropped.push(`${comp}/${configName}`);
1881
+ continue;
1882
+ }
1883
+ resolvedConfig = stripFileMarker(resolved);
1884
+ }
1885
+ componentConfigs[comp] = migrateEmbeddedComponentConfig(comp, resolvedConfig, componentSchemaVersion);
1886
+ }
1887
+ const filled = { components: [], aliases: 0, orphans: 0 };
1888
+ for (const comp of resolvers.listComponentNames()) {
1889
+ const base = asObject(resolvers.readComponentConfig(comp, "default"));
1890
+ const baseAliases = base ? asObject(base.aliases) : null;
1891
+ if (!base || !baseAliases) continue;
1892
+ const entry = componentConfigs[comp];
1893
+ if (!entry) {
1894
+ const { schemaVersion: _standaloneStamp, ...rest } = stripFileMarker(base);
1895
+ componentConfigs[comp] = structuredClone(rest);
1896
+ filled.components.push(comp);
851
1897
  continue;
852
1898
  }
853
- const configName = asString(value, "default");
854
- if (configName === "default") continue;
855
- const resolved = asObject(resolvers.readComponentConfig(comp, configName));
856
- if (!resolved) {
857
- dropped.push(`${comp}/${configName}`);
858
- continue;
1899
+ const entryAliases = asObject(entry.aliases) ?? {};
1900
+ for (const key of Object.keys(baseAliases)) {
1901
+ if (key in entryAliases) continue;
1902
+ entryAliases[key] = structuredClone(baseAliases[key]);
1903
+ filled.aliases++;
1904
+ }
1905
+ entry.aliases = entryAliases;
1906
+ for (const key of Object.keys(entryAliases)) {
1907
+ if (!(key in baseAliases)) filled.orphans++;
859
1908
  }
860
- componentConfigs[comp] = stripFileMarker(resolved);
861
1909
  }
862
1910
  return {
863
1911
  theme: {
@@ -866,10 +1914,12 @@ function normalizeTheme(raw, resolvers) {
866
1914
  updatedAt: asString(src.updatedAt, now),
867
1915
  schemaVersion: THEME_SCHEMA_VERSION,
868
1916
  colorsAndType,
869
- componentConfigs
1917
+ componentConfigs,
1918
+ componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
870
1919
  },
871
1920
  dropped,
872
- migrated
1921
+ migrated,
1922
+ filled
873
1923
  };
874
1924
  }
875
1925
 
@@ -1012,7 +2062,7 @@ function ensureScale(css, opts) {
1012
2062
  lines.splice(insertAfter + 1, 0, ...block);
1013
2063
  return lines.join("\n");
1014
2064
  }
1015
- function renameToken(css, oldName, newName) {
2065
+ function renameToken2(css, oldName, newName) {
1016
2066
  const defined = collectDefinedTokens(css);
1017
2067
  if (!defined.has(oldName) || defined.has(newName)) return css;
1018
2068
  const re = new RegExp(escapeRe(oldName) + "(?![a-z0-9-])", "gi");
@@ -1231,7 +2281,7 @@ var tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions = {
1231
2281
  };
1232
2282
 
1233
2283
  // vite-plugin/tokensCssMigrations/migrations/2026-07-20-line-height-rename.ts
1234
- var RENAMES = [
2284
+ var RENAMES2 = [
1235
2285
  ["--line-height-xs", "--line-height-none"],
1236
2286
  ["--line-height-sm", "--line-height-tighter"],
1237
2287
  ["--line-height-md", "--line-height-normal"],
@@ -1251,7 +2301,7 @@ var tokensCssMigration_2026_07_20_lineHeightRename = {
1251
2301
  description: "Reshape --line-height-{xs..xl} to leading vocabulary (none/tightest/tighter/tight/normal/relaxed); retire the 2.0 slot",
1252
2302
  apply(css) {
1253
2303
  let out = css;
1254
- for (const [oldName, newName] of RENAMES) out = renameToken(out, oldName, newName);
2304
+ for (const [oldName, newName] of RENAMES2) out = renameToken2(out, oldName, newName);
1255
2305
  out = removeToken(out, "--line-height-xl");
1256
2306
  out = ensureScale(out, {
1257
2307
  anchorPrefixes: ["--line-height-", "--font-size-", "--font-weight-", "--font-"],
@@ -1419,8 +2469,29 @@ var PKG_VERSION = (() => {
1419
2469
  })();
1420
2470
  var BUNDLE_KIND_BY_VERSION = {
1421
2471
  1: "manifest-bundle",
2472
+ 3: "theme-bundle",
1422
2473
  [THEME_SCHEMA_VERSION]: "theme-bundle"
1423
2474
  };
2475
+ function extractAliasDeclarations(body) {
2476
+ const aliases = {};
2477
+ const re = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
2478
+ let m;
2479
+ while ((m = re.exec(body)) !== null) {
2480
+ const value = m[2].trim();
2481
+ const plain = value.match(/^var\((--[a-z0-9-]+)\)$/i);
2482
+ if (plain) {
2483
+ aliases[m[1]] = plain[1];
2484
+ continue;
2485
+ }
2486
+ if (parseColorOpacity(value)) {
2487
+ aliases[m[1]] = value;
2488
+ continue;
2489
+ }
2490
+ const gradient = parseGradientValue(value);
2491
+ if (gradient) aliases[m[1]] = { kind: "gradient", value: gradient };
2492
+ }
2493
+ return aliases;
2494
+ }
1424
2495
  function themeFileApi(opts) {
1425
2496
  const dataDirs = resolveDataDirs({
1426
2497
  dataDir: opts.dataDir,
@@ -1597,6 +2668,7 @@ function themeFileApi(opts) {
1597
2668
  const overrides = [];
1598
2669
  const defaultAliases = defaultCfg.aliases ?? {};
1599
2670
  for (const [varName, semanticValue] of Object.entries(prodCfg.aliases ?? {})) {
2671
+ if (!(varName in defaultAliases)) continue;
1600
2672
  if (!aliasValuesEqual(defaultAliases[varName], semanticValue)) {
1601
2673
  overrides.push([varName, semanticValue]);
1602
2674
  }
@@ -1659,18 +2731,6 @@ function themeFileApi(opts) {
1659
2731
  import_fs4.default.writeFileSync(FONTS_CSS_PATH, content);
1660
2732
  console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${import_path4.default.basename(FONTS_CSS_PATH)}`);
1661
2733
  }
1662
- function extractAliasDeclarations(body) {
1663
- const aliases = {};
1664
- const re = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
1665
- let m;
1666
- while ((m = re.exec(body)) !== null) {
1667
- const value = m[2].trim();
1668
- const plain = value.match(/^var\((--[a-z0-9-]+)\)$/i);
1669
- if (plain) aliases[m[1]] = plain[1];
1670
- else if (parseColorOpacity(value)) aliases[m[1]] = value;
1671
- }
1672
- return aliases;
1673
- }
1674
2734
  function componentNameFromFile(filePath) {
1675
2735
  return import_path4.default.basename(filePath, ".svelte").toLowerCase();
1676
2736
  }
@@ -1746,17 +2806,10 @@ ${lines.join("\n")}
1746
2806
  r.ensureDir();
1747
2807
  const defaultPath = r.filePath("default");
1748
2808
  const sourceStat = import_fs4.default.statSync(sourcePath);
1749
- if (import_fs4.default.existsSync(defaultPath)) {
1750
- const defaultStat = import_fs4.default.statSync(defaultPath);
1751
- if (defaultStat.mtimeMs >= sourceStat.mtimeMs) return;
1752
- }
1753
- const source = import_fs4.default.readFileSync(sourcePath, "utf-8");
1754
- const body = extractGlobalRootBody(source);
1755
- const aliases = extractAliasDeclarations(body);
1756
- const now = (/* @__PURE__ */ new Date()).toISOString();
1757
- let createdAt = now;
2809
+ let createdAt = (/* @__PURE__ */ new Date()).toISOString();
1758
2810
  let existingUpdatedAt;
1759
2811
  let existingAliases;
2812
+ let existingSchemaVersion;
1760
2813
  if (import_fs4.default.existsSync(defaultPath)) {
1761
2814
  try {
1762
2815
  const existing = JSON.parse(import_fs4.default.readFileSync(defaultPath, "utf-8"));
@@ -1765,25 +2818,29 @@ ${lines.join("\n")}
1765
2818
  if (existing.aliases && typeof existing.aliases === "object") {
1766
2819
  existingAliases = existing.aliases;
1767
2820
  }
2821
+ if (typeof existing.schemaVersion === "number") existingSchemaVersion = existing.schemaVersion;
1768
2822
  } catch {
1769
2823
  }
1770
- }
1771
- if (existingAliases) {
1772
- for (const [token, val] of Object.entries(existingAliases)) {
1773
- if (val !== null && typeof val === "object" && !(token in aliases)) {
1774
- aliases[token] = val;
1775
- }
2824
+ const defaultStat = import_fs4.default.statSync(defaultPath);
2825
+ if (defaultStat.mtimeMs >= sourceStat.mtimeMs && existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION) {
2826
+ return;
1776
2827
  }
1777
2828
  }
2829
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2830
+ const source = import_fs4.default.readFileSync(sourcePath, "utf-8");
2831
+ const body = extractGlobalRootBody(source);
2832
+ const aliases = extractAliasDeclarations(body);
1778
2833
  const aliasesUnchanged = existingAliases !== void 0 && JSON.stringify(existingAliases) === JSON.stringify(aliases);
2834
+ const schemaStampCurrent = existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION;
1779
2835
  const defaultConfig = {
1780
2836
  name: "default",
1781
2837
  component: comp,
1782
2838
  createdAt,
1783
2839
  updatedAt: aliasesUnchanged && existingUpdatedAt ? existingUpdatedAt : now,
1784
- aliases
2840
+ aliases,
2841
+ schemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
1785
2842
  };
1786
- if (aliasesUnchanged && existingUpdatedAt) {
2843
+ if (aliasesUnchanged && schemaStampCurrent && existingUpdatedAt) {
1787
2844
  const t = /* @__PURE__ */ new Date();
1788
2845
  import_fs4.default.utimesSync(defaultPath, t, t);
1789
2846
  return;
@@ -1831,6 +2888,7 @@ ${lines.join("\n")}
1831
2888
  const diskThemeResolvers = {
1832
2889
  readColorsAndType: (name) => colorsAndTypeResource.readJson(sanitizeFileName(name)),
1833
2890
  readComponentConfig: (comp, name) => readComponentConfig(comp, sanitizeFileName(name)),
2891
+ listComponentNames,
1834
2892
  normalizeColorsAndType: (colorsAndType) => normalizeColorsAndType(colorsAndType)
1835
2893
  };
1836
2894
  function readTheme(fileName) {
@@ -1856,7 +2914,7 @@ ${lines.join("\n")}
1856
2914
  return "corrupt";
1857
2915
  }
1858
2916
  function notAThemeError(fileName) {
1859
- return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${import_path4.default.basename(COLORS_AND_TYPE_DIR)}/ or delete it.`;
2917
+ return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${import_path4.default.basename(COLORS_AND_TYPE_DIR)}/ or delete it. To build a theme, use the theme generator skill "generate-theme".`;
1860
2918
  }
1861
2919
  function respondUnreadableTheme(res, fileName, missingError) {
1862
2920
  switch (unreadableThemeReason(fileName)) {
@@ -1918,19 +2976,26 @@ ${lines.join("\n")}
1918
2976
  const componentConfigs = {};
1919
2977
  for (const comp of listComponentNames()) {
1920
2978
  const cfg = readComponentConfig(comp, "default");
1921
- if (cfg) componentConfigs[comp] = cfg;
2979
+ if (cfg) {
2980
+ const { schemaVersion: _standaloneStamp, ...rest } = cfg;
2981
+ componentConfigs[comp] = rest;
2982
+ }
1922
2983
  }
1923
- if (existing?.schemaVersion === THEME_SCHEMA_VERSION && JSON.stringify(existing.colorsAndType) === JSON.stringify(colorsAndType) && JSON.stringify(existing.componentConfigs) === JSON.stringify(componentConfigs)) {
2984
+ const contentUnchanged = JSON.stringify(existing?.colorsAndType) === JSON.stringify(colorsAndType) && JSON.stringify(existing?.componentConfigs) === JSON.stringify(componentConfigs);
2985
+ if (existing?.schemaVersion === THEME_SCHEMA_VERSION && existing?.componentSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION && contentUnchanged) {
1924
2986
  return;
1925
2987
  }
1926
2988
  const now = (/* @__PURE__ */ new Date()).toISOString();
1927
2989
  writeTheme("default", {
1928
2990
  name: "Motion Proto",
1929
2991
  createdAt: typeof existing?.createdAt === "string" ? existing.createdAt : now,
1930
- updatedAt: now,
2992
+ // A stamp-only rewrite (componentSchemaVersion catching up) is not a
2993
+ // content change, so it doesn't bump this the way a real edit would.
2994
+ updatedAt: contentUnchanged && typeof existing?.updatedAt === "string" ? existing.updatedAt : now,
1931
2995
  schemaVersion: THEME_SCHEMA_VERSION,
1932
2996
  colorsAndType,
1933
- componentConfigs
2997
+ componentConfigs,
2998
+ componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
1934
2999
  });
1935
3000
  }
1936
3001
  function ensureThemesDir(warn) {
@@ -1962,14 +3027,24 @@ ${lines.join("\n")}
1962
3027
  if (!shipped) return null;
1963
3028
  return { data: normalizeColorsAndType(shipped), source: "default" };
1964
3029
  }
3030
+ const loggedMissingThemeEntries = /* @__PURE__ */ new Set();
3031
+ function resolveComponentConfigOrDefault(comp, theme) {
3032
+ const embedded = theme?.componentConfigs[comp];
3033
+ if (embedded) return { ...embedded, component: comp };
3034
+ if (!loggedMissingThemeEntries.has(comp)) {
3035
+ loggedMissingThemeEntries.add(comp);
3036
+ console.warn(
3037
+ `[live-tokens] "${comp}" has no entry in the open theme; resolving from its shipped default.`
3038
+ );
3039
+ }
3040
+ return readComponentConfig(comp, "default");
3041
+ }
1965
3042
  function resolveLiveComponentConfig(comp, theme) {
1966
3043
  const working = componentResource(comp).readWorking();
1967
3044
  if (working !== null) return { data: working, source: "working" };
1968
- const embedded = theme?.componentConfigs[comp];
1969
- if (embedded) return { data: { ...embedded, component: comp }, source: "theme" };
1970
- const cfg = readComponentConfig(comp, "default");
3045
+ const cfg = resolveComponentConfigOrDefault(comp, theme);
1971
3046
  if (!cfg) return null;
1972
- return { data: cfg, source: "default" };
3047
+ return { data: cfg, source: "theme" };
1973
3048
  }
1974
3049
  function resolveSavedColorsAndType(theme) {
1975
3050
  if (theme?.colorsAndType) return theme.colorsAndType;
@@ -1977,9 +3052,7 @@ ${lines.join("\n")}
1977
3052
  return shipped ? normalizeColorsAndType(shipped) : null;
1978
3053
  }
1979
3054
  function resolveSavedComponentConfig(comp, theme) {
1980
- const embedded = theme?.componentConfigs[comp];
1981
- if (embedded) return { ...embedded, component: comp };
1982
- return readComponentConfig(comp, "default");
3055
+ return resolveComponentConfigOrDefault(comp, theme);
1983
3056
  }
1984
3057
  function sameJsonValue(a, b) {
1985
3058
  if (a === null || b === null) return a === b;
@@ -2015,26 +3088,9 @@ ${lines.join("\n")}
2015
3088
  if (entry.isDirectory()) componentResource(entry.name).clearWorking();
2016
3089
  }
2017
3090
  }
2018
- function formatAliasGradient(v) {
2019
- const stopColor = (s) => {
2020
- const base = s.color.startsWith("--") ? `var(${s.color})` : s.color;
2021
- const opacity = s.opacity ?? 100;
2022
- return opacity >= 100 ? base : `color-mix(in srgb, ${base} ${opacity}%, transparent)`;
2023
- };
2024
- if (v.type === "none") return "transparent";
2025
- if (v.type === "solid") {
2026
- const first = v.stops[0];
2027
- if (!first) return "transparent";
2028
- return stopColor(first);
2029
- }
2030
- const stops = v.stops.map((s) => `${stopColor(s)} ${s.position}%`).join(", ");
2031
- if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
2032
- const radial = v.radius && v.radius > 0 ? `circle ${v.radius}px at center` : "circle";
2033
- return `radial-gradient(${radial}, ${stops})`;
2034
- }
2035
3091
  function aliasValueToCss(v) {
2036
3092
  if (typeof v === "string") return v.startsWith("--") ? `var(${v})` : v;
2037
- return formatAliasGradient(v.value);
3093
+ return formatGradientValue(v.value);
2038
3094
  }
2039
3095
  function aliasValuesEqual(a, b) {
2040
3096
  if (a === void 0 || b === void 0) return a === b;
@@ -2184,13 +3240,9 @@ ${lines.join("\n")}
2184
3240
  }
2185
3241
  }
2186
3242
  async function handleListComponents({ res }) {
2187
- const theme = activeTheme();
2188
3243
  const components = listComponentNames().map((comp) => ({
2189
3244
  name: comp,
2190
- // Presence, not a parse: one corrupt buffer must not take the listing
2191
- // down, and the door only has to say where each component's live config
2192
- // comes from.
2193
- source: componentResource(comp).hasWorking() ? "working" : theme?.componentConfigs[comp] ? "theme" : "default"
3245
+ source: componentResource(comp).hasWorking() ? "working" : "theme"
2194
3246
  }));
2195
3247
  jsonResponse(res, 200, { components });
2196
3248
  }
@@ -2201,6 +3253,7 @@ ${lines.join("\n")}
2201
3253
  }
2202
3254
  async function handleGetComponentActive({ params, res }) {
2203
3255
  const [comp] = params;
3256
+ if (rejectUnknownComponent(res, comp)) return;
2204
3257
  const resolved = resolveLiveComponentConfig(comp, activeTheme());
2205
3258
  if (!resolved) {
2206
3259
  jsonResponse(res, 404, { error: "Active config not found" });
@@ -2447,9 +3500,11 @@ ${lines.join("\n")}
2447
3500
  respondUnreadableTheme(res, fileName, "Theme not found");
2448
3501
  return;
2449
3502
  }
2450
- const { theme } = read;
3503
+ const { theme, filled } = read;
2451
3504
  if (!theme.colorsAndType) {
2452
- jsonResponse(res, 422, { error: "This theme carries no colors and type" });
3505
+ jsonResponse(res, 422, {
3506
+ error: 'This theme carries no colors and type. Build one with the theme generator skill "generate-theme".'
3507
+ });
2453
3508
  return;
2454
3509
  }
2455
3510
  clearAllWorking();
@@ -2473,7 +3528,8 @@ ${lines.join("\n")}
2473
3528
  _source: resolvedColorsAndType.source
2474
3529
  },
2475
3530
  componentConfigs: resolvedConfigs,
2476
- skippedComponents
3531
+ skippedComponents,
3532
+ filled
2477
3533
  });
2478
3534
  }
2479
3535
  async function handleExportTheme({ params, res }) {
@@ -2528,10 +3584,27 @@ ${lines.join("\n")}
2528
3584
  }
2529
3585
  const bundleResolvers = {
2530
3586
  readColorsAndType: () => bundle.theme,
2531
- readComponentConfig: (comp, name) => bundle.componentConfigs?.[`${comp}/${name}`],
3587
+ // A named ref resolves inside the bundle only — a v1 bundle's
3588
+ // `${comp}/${name}` map is itself the sender's data, and a stale name
3589
+ // must still land in `dropped` rather than quietly picking up this
3590
+ // install's copy. But `'default'` never named a bundled entry; v1
3591
+ // spelled "no override" that way (normalizeTheme.ts's pointer-
3592
+ // resolution loop treats it identically), so it always meant *this
3593
+ // install's* default, not something the bundle carries. The
3594
+ // completeness fill (Wave 2 of docs/plans/theme-completeness.md) relies
3595
+ // on that fall-through: without it, `readComponentConfig(comp,
3596
+ // 'default')` answers undefined for every component (no bundle ever
3597
+ // carries a `<comp>/default` entry), the fill loop no-ops 25 times, and
3598
+ // an incomplete bundle stays incomplete forever — exactly the drift RJC
3599
+ // 10 exists to prevent.
3600
+ readComponentConfig: (comp, name) => bundle.componentConfigs?.[`${comp}/${name}`] ?? (name === "default" ? readComponentConfig(comp, "default") : void 0),
3601
+ // Installed components are a property of this install, not of the
3602
+ // bundle being imported, so the completeness fill (Wave 2) uses the
3603
+ // same list the disk resolver does.
3604
+ listComponentNames,
2532
3605
  normalizeColorsAndType: (colorsAndType) => normalizeColorsAndType(colorsAndType)
2533
3606
  };
2534
- const { theme, dropped } = normalizeTheme(bundle.manifest, bundleResolvers);
3607
+ const { theme, dropped, filled } = normalizeTheme(bundle.manifest, bundleResolvers);
2535
3608
  if (theme.colorsAndType) theme.colorsAndType = normalizeColorsAndType(theme.colorsAndType);
2536
3609
  if (!theme.colorsAndType) {
2537
3610
  jsonResponse(res, 400, { error: "Bundle carries no theme" });
@@ -2552,7 +3625,8 @@ ${lines.join("\n")}
2552
3625
  ok: true,
2553
3626
  theme: finalName,
2554
3627
  renames,
2555
- dropped
3628
+ dropped,
3629
+ filled
2556
3630
  });
2557
3631
  }
2558
3632
  async function handleAdoptLook({ res }) {