@motion-proto/live-tokens 0.6.2 → 0.8.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 (232) hide show
  1. package/README.md +14 -13
  2. package/dist-plugin/index.cjs +854 -226
  3. package/dist-plugin/index.d.cts +2 -1
  4. package/dist-plugin/index.d.ts +2 -1
  5. package/dist-plugin/index.js +852 -225
  6. package/package.json +26 -40
  7. package/src/{styles → app}/site.css +1 -1
  8. package/src/{component-editor → editor/component-editor}/BadgeEditor.svelte +8 -82
  9. package/src/{component-editor → editor/component-editor}/CalloutEditor.svelte +4 -4
  10. package/src/{component-editor → editor/component-editor}/CardEditor.svelte +28 -76
  11. package/src/{component-editor → editor/component-editor}/CollapsibleSectionEditor.svelte +37 -30
  12. package/src/{component-editor → editor/component-editor}/CornerBadgeEditor.svelte +31 -93
  13. package/src/{component-editor → editor/component-editor}/DialogEditor.svelte +60 -57
  14. package/src/editor/component-editor/ImageEditor.svelte +30 -0
  15. package/src/{component-editor → editor/component-editor}/InlineEditActionsEditor.svelte +6 -4
  16. package/src/editor/component-editor/MenuSelectEditor.svelte +160 -0
  17. package/src/{component-editor → editor/component-editor}/NotificationEditor.svelte +67 -38
  18. package/src/{component-editor → editor/component-editor}/ProgressBarEditor.svelte +5 -4
  19. package/src/{component-editor → editor/component-editor}/RadioButtonEditor.svelte +3 -3
  20. package/src/editor/component-editor/SectionDividerEditor.svelte +565 -0
  21. package/src/{component-editor → editor/component-editor}/SegmentedControlEditor.svelte +2 -2
  22. package/src/{component-editor → editor/component-editor}/StandardButtonsEditor.svelte +29 -21
  23. package/src/{component-editor → editor/component-editor}/TabBarEditor.svelte +9 -14
  24. package/src/{component-editor → editor/component-editor}/TableEditor.svelte +9 -18
  25. package/src/{component-editor → editor/component-editor}/TooltipEditor.svelte +11 -47
  26. package/src/editor/component-editor/editors.d.ts +10 -0
  27. package/src/{component-editor → editor/component-editor}/registry.ts +28 -18
  28. package/src/{component-editor → editor/component-editor}/scaffolding/AngleDial.svelte +54 -15
  29. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentEditorBase.svelte +3 -51
  30. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileManager.svelte +151 -424
  31. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileMenu.svelte +18 -170
  32. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentsTab.svelte +2 -2
  33. package/src/{component-editor → editor/component-editor}/scaffolding/CopyFromMenu.svelte +44 -4
  34. package/src/{component-editor → editor/component-editor}/scaffolding/FieldsetWrapper.svelte +1 -1
  35. package/src/{component-editor → editor/component-editor}/scaffolding/LinkageChart.svelte +6 -6
  36. package/src/{component-editor → editor/component-editor}/scaffolding/LinkedBlock.svelte +6 -12
  37. package/src/editor/component-editor/scaffolding/NonStylableConfig.svelte +38 -0
  38. package/src/editor/component-editor/scaffolding/RadialShapePad.svelte +483 -0
  39. package/src/{component-editor → editor/component-editor}/scaffolding/SaveAsDialog.svelte +66 -12
  40. package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +85 -0
  41. package/src/editor/component-editor/scaffolding/ShadowBackdropControls.svelte +132 -0
  42. package/src/editor/component-editor/scaffolding/StateBlock.svelte +345 -0
  43. package/src/{component-editor → editor/component-editor}/scaffolding/TokenLayout.svelte +17 -12
  44. package/src/{component-editor → editor/component-editor}/scaffolding/TypeEditor.svelte +13 -1
  45. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +858 -0
  46. package/src/{component-editor → editor/component-editor}/scaffolding/buildTypeGroupTokens.ts +1 -0
  47. package/src/{component-editor → editor/component-editor}/scaffolding/editorContext.ts +19 -9
  48. package/src/{component-editor → editor/component-editor}/scaffolding/linkedBlock.ts +2 -2
  49. package/src/{component-editor → editor/component-editor}/scaffolding/types.ts +25 -0
  50. package/src/{lib → editor/core/components}/componentConfigKeys.ts +8 -0
  51. package/src/{lib → editor/core/components}/componentConfigService.ts +3 -3
  52. package/src/{lib → editor/core/components}/componentPersist.ts +11 -9
  53. package/src/editor/core/flashStatus.ts +30 -0
  54. package/src/{lib → editor/core/fonts}/fontLoader.ts +2 -2
  55. package/src/{lib → editor/core/fonts}/fontMigration.ts +4 -4
  56. package/src/{lib → editor/core/fonts}/fontParse.ts +1 -1
  57. package/src/editor/core/manifests/manifestService.ts +171 -0
  58. package/src/editor/core/palettes/familySwap.ts +99 -0
  59. package/src/{lib → editor/core/palettes}/paletteDerivation.ts +71 -2
  60. package/src/{lib → editor/core/palettes}/tokenRegistry.ts +9 -6
  61. package/src/editor/core/productionPulse.ts +37 -0
  62. package/src/{lib → editor/core/routing}/router.ts +1 -1
  63. package/src/{lib/files/versionedFileResource.ts → editor/core/storage/files/versionedFileResourceClient.ts} +8 -1
  64. package/src/{lib → editor/core/store}/editorCore.ts +24 -8
  65. package/src/{lib → editor/core/store}/editorPersistence.ts +3 -3
  66. package/src/{lib → editor/core/store}/editorRenderer.ts +2 -2
  67. package/src/{lib → editor/core/store}/editorStore.ts +222 -28
  68. package/src/{lib → editor/core/store}/editorTypes.ts +56 -13
  69. package/src/editor/core/store/gradientSource.ts +192 -0
  70. package/src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts +28 -0
  71. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +35 -0
  72. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +82 -0
  73. package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +24 -0
  74. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +81 -0
  75. package/src/{lib → editor/core/themes}/migrations/index.ts +10 -0
  76. package/src/{lib → editor/core/themes}/slices/columns.ts +2 -2
  77. package/src/{lib → editor/core/themes}/slices/components.ts +20 -6
  78. package/src/{lib → editor/core/themes}/slices/fonts.ts +1 -1
  79. package/src/{lib → editor/core/themes}/slices/gradients.ts +89 -14
  80. package/src/{lib → editor/core/themes}/slices/overlays.ts +1 -1
  81. package/src/{lib → editor/core/themes}/slices/palettes.ts +1 -1
  82. package/src/{lib → editor/core/themes}/slices/shadows.ts +3 -3
  83. package/src/{lib → editor/core/themes}/themeInit.ts +8 -8
  84. package/src/{lib → editor/core/themes}/themeService.ts +6 -6
  85. package/src/{lib → editor/core/themes}/themeTypes.ts +67 -8
  86. package/src/editor/index.ts +69 -0
  87. package/src/{lib → editor/overlay}/ColumnsOverlay.svelte +0 -1
  88. package/src/{lib → editor/overlay}/LiveEditorOverlay.svelte +80 -129
  89. package/src/{lib → editor/overlay}/columnsOverlay.ts +2 -2
  90. package/src/{pages → editor/pages}/ComponentEditorPage.svelte +12 -12
  91. package/src/{pages → editor/pages}/Editor.svelte +4 -4
  92. package/src/{pages → editor/pages}/EditorShell.svelte +18 -36
  93. package/src/{styles → editor/styles}/ui-editor.css +43 -22
  94. package/src/{styles → editor/styles}/ui-form-controls.css +23 -24
  95. package/src/{ui → editor/ui}/BezierCurveEditor.svelte +119 -68
  96. package/src/{ui → editor/ui}/ColorEditPanel.svelte +13 -13
  97. package/src/{ui → editor/ui}/EditorViewSwitcher.svelte +7 -6
  98. package/src/editor/ui/FileLoadList.svelte +367 -0
  99. package/src/editor/ui/FilePill.svelte +80 -0
  100. package/src/editor/ui/FontStackEditor.svelte +499 -0
  101. package/src/editor/ui/GradientEditor.svelte +690 -0
  102. package/src/{ui → editor/ui}/GradientStopPicker.svelte +12 -4
  103. package/src/editor/ui/ManifestFileManager.svelte +438 -0
  104. package/src/{ui → editor/ui}/PaletteEditor.svelte +180 -673
  105. package/src/editor/ui/ProjectFontsSection.svelte +638 -0
  106. package/src/{ui → editor/ui}/SurfacesTab.svelte +3 -3
  107. package/src/{ui → editor/ui}/TextTab.svelte +3 -3
  108. package/src/editor/ui/ThemeFileManager.svelte +783 -0
  109. package/src/{ui → editor/ui}/UICopyPopover.svelte +4 -4
  110. package/src/{ui → editor/ui}/UIFontFamilySelector.svelte +6 -7
  111. package/src/{ui → editor/ui}/UIFontSizeSelector.svelte +4 -1
  112. package/src/editor/ui/UIInfoPopover.svelte +243 -0
  113. package/src/editor/ui/UILetterSpacingSelector.svelte +65 -0
  114. package/src/{ui → editor/ui}/UILineHeightSelector.svelte +5 -5
  115. package/src/{ui → editor/ui}/UILinkToggle.svelte +2 -2
  116. package/src/{ui → editor/ui}/UIPaddingSelector.svelte +6 -6
  117. package/src/{ui → editor/ui}/UIPaletteSelector.svelte +57 -30
  118. package/src/editor/ui/UIPillButton.svelte +168 -0
  119. package/src/{ui → editor/ui}/UIRadio.svelte +2 -2
  120. package/src/{ui → editor/ui}/UIRelinkConfirmPopover.svelte +4 -4
  121. package/src/editor/ui/UISegmentedControl.svelte +114 -0
  122. package/src/editor/ui/UISquareButton.svelte +172 -0
  123. package/src/{ui → editor/ui}/UITokenSelector.svelte +14 -11
  124. package/src/{ui → editor/ui}/UIVariantSelector.svelte +1 -1
  125. package/src/{ui → editor/ui}/VariablesTab.svelte +46 -17
  126. package/src/{ui → editor/ui}/palette/GradientStopEditor.svelte +13 -13
  127. package/src/{ui → editor/ui}/palette/OverridesPanel.svelte +24 -47
  128. package/src/{ui → editor/ui}/palette/PaletteBase.svelte +11 -8
  129. package/src/{ui → editor/ui}/palette/paletteEditorState.ts +1 -1
  130. package/src/editor/ui/palette/paletteMath.ts +275 -0
  131. package/src/{ui → editor/ui}/sections/ColumnsSection.svelte +137 -18
  132. package/src/{ui → editor/ui}/sections/GradientsSection.svelte +8 -8
  133. package/src/{ui → editor/ui}/sections/OverlaysSection.svelte +18 -18
  134. package/src/{ui → editor/ui}/sections/ShadowsSection.svelte +23 -23
  135. package/src/{ui → editor/ui}/sections/TokenScaleTable.svelte +3 -3
  136. package/src/{components → system/components}/Badge.svelte +0 -36
  137. package/src/{components → system/components}/Button.svelte +2 -2
  138. package/src/{components → system/components}/Card.svelte +34 -60
  139. package/src/{components → system/components}/CollapsibleSection.svelte +25 -2
  140. package/src/{components → system/components}/CornerBadge.svelte +8 -24
  141. package/src/{components → system/components}/Dialog.svelte +1 -1
  142. package/src/system/components/FloatingTokenTags.css +275 -0
  143. package/src/system/components/FloatingTokenTags.svelte +543 -0
  144. package/src/{components → system/components}/InlineEditActions.svelte +6 -4
  145. package/src/system/components/MenuSelect.svelte +229 -0
  146. package/src/{components → system/components}/Notification.svelte +8 -1
  147. package/src/{components → system/components}/ProgressBar.svelte +29 -11
  148. package/src/system/components/SectionDivider.svelte +560 -0
  149. package/src/{components → system/components}/SegmentedControl.svelte +49 -43
  150. package/src/{components → system/components}/TabBar.svelte +81 -65
  151. package/src/{components → system/components}/Table.svelte +17 -3
  152. package/src/{components → system/components}/Tooltip.svelte +6 -4
  153. package/src/system/styles/CONVENTIONS.md +178 -0
  154. package/src/system/styles/fonts.css +20 -0
  155. package/src/system/styles/tokens.css +601 -0
  156. package/src/system/styles/tokens.generated.css +544 -0
  157. package/src/component-editor/ImageEditor.svelte +0 -74
  158. package/src/component-editor/SectionDividerEditor.svelte +0 -265
  159. package/src/component-editor/scaffolding/DividerEditor.svelte +0 -94
  160. package/src/component-editor/scaffolding/GradientCard.svelte +0 -296
  161. package/src/component-editor/scaffolding/NonStylableConfig.svelte +0 -62
  162. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +0 -37
  163. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +0 -61
  164. package/src/component-editor/scaffolding/StateBlock.svelte +0 -132
  165. package/src/component-editor/scaffolding/VariantGroup.svelte +0 -310
  166. package/src/components/SectionDivider.svelte +0 -483
  167. package/src/data/google-fonts.json +0 -75
  168. package/src/lib/index.ts +0 -68
  169. package/src/lib/presetService.ts +0 -214
  170. package/src/lib/productionPulse.ts +0 -32
  171. package/src/styles/fonts.css +0 -30
  172. package/src/styles/tokens.css +0 -1324
  173. package/src/ui/FontStackEditor.svelte +0 -361
  174. package/src/ui/GradientEditor.svelte +0 -470
  175. package/src/ui/PresetFileManager.svelte +0 -1116
  176. package/src/ui/ProjectFontsSection.svelte +0 -645
  177. package/src/ui/ThemeFileManager.svelte +0 -1020
  178. package/src/ui/UnsavedComponentsDialog.svelte +0 -315
  179. /package/src/{component-editor → editor/component-editor}/index.ts +0 -0
  180. /package/src/{component-editor → editor/component-editor}/scaffolding/DemoHeader.svelte +0 -0
  181. /package/src/{component-editor → editor/component-editor}/scaffolding/componentSectionType.ts +0 -0
  182. /package/src/{component-editor → editor/component-editor}/scaffolding/componentSources.ts +0 -0
  183. /package/src/{component-editor → editor/component-editor}/scaffolding/defaultSections.ts +0 -0
  184. /package/src/{component-editor → editor/component-editor}/scaffolding/siblings.ts +0 -0
  185. /package/src/{lib → editor/core}/cssVarSync.ts +0 -0
  186. /package/src/{lib → editor/core/palettes}/oklch.ts +0 -0
  187. /package/src/{lib → editor/core/routing}/navLinkTypes.ts +0 -0
  188. /package/src/{lib → editor/core/routing}/parentRouteStore.ts +0 -0
  189. /package/src/{lib → editor/core/storage}/storage.ts +0 -0
  190. /package/src/{lib → editor/core/store}/editorConfig.ts +0 -0
  191. /package/src/{lib → editor/core/store}/editorConfigStore.ts +0 -0
  192. /package/src/{lib → editor/core/store}/editorKeybindings.ts +0 -0
  193. /package/src/{lib → editor/core/store}/editorViewStore.ts +0 -0
  194. /package/src/{lib → editor/core/themes}/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +0 -0
  195. /package/src/{lib → editor/core/themes}/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +0 -0
  196. /package/src/{lib → editor/core/themes}/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +0 -0
  197. /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +0 -0
  198. /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +0 -0
  199. /package/src/{lib → editor/core/themes}/migrations/2026-05-10-sectiondivider-gradient-stops.ts +0 -0
  200. /package/src/{lib → editor/core/themes}/migrations/2026-05-13-primary-to-brand.ts +0 -0
  201. /package/src/{lib → editor/core/themes}/parsers/globalRootBlock.ts +0 -0
  202. /package/src/{lib → editor/core/themes}/slices/domainVars.ts +0 -0
  203. /package/src/{lib → editor/overlay}/overlayState.ts +0 -0
  204. /package/src/{pages → editor/pages}/ComponentEditorPage.svelte.d.ts +0 -0
  205. /package/src/{pages → editor/pages}/Editor.svelte.d.ts +0 -0
  206. /package/src/{ui → editor/ui}/Toggle.svelte +0 -0
  207. /package/src/{ui → editor/ui}/UIDialog.svelte +0 -0
  208. /package/src/{ui → editor/ui}/UIFontWeightSelector.svelte +0 -0
  209. /package/src/{ui → editor/ui}/UIOptionItem.svelte +0 -0
  210. /package/src/{ui → editor/ui}/UIOptionList.svelte +0 -0
  211. /package/src/{ui → editor/ui}/UIRadioGroup.svelte +0 -0
  212. /package/src/{lib → editor/ui}/copyPopover.ts +0 -0
  213. /package/src/{ui → editor/ui}/curveEngine.ts +0 -0
  214. /package/src/{ui → editor/ui}/index.ts +0 -0
  215. /package/src/{ui → editor/ui}/keepInViewport.ts +0 -0
  216. /package/src/{ui → editor/ui}/palette/ScaleCurveEditor.svelte +0 -0
  217. /package/src/{lib → editor/ui}/scrollSection.ts +0 -0
  218. /package/src/{ui → editor/ui}/sections/tokenScales.ts +0 -0
  219. /package/src/{ui → editor/ui}/variantScales.ts +0 -0
  220. /package/src/{assets → system/assets}/newspaper.webp +0 -0
  221. /package/src/{assets → system/assets}/offering.webp +0 -0
  222. /package/src/{components → system/components}/Callout.svelte +0 -0
  223. /package/src/{components → system/components}/Image.svelte +0 -0
  224. /package/src/{components → system/components}/RadioButton.svelte +0 -0
  225. /package/src/{components → system/components}/types.ts +0 -0
  226. /package/src/{styles → system/styles}/_padding.scss +0 -0
  227. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
  228. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
  229. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
  230. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
  231. /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
  232. /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin.woff2 +0 -0
@@ -27,18 +27,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
- // src/vite-plugin/index.ts
30
+ // vite-plugin/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  themeFileApi: () => themeFileApi
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
- // src/vite-plugin/themeFileApi.ts
37
+ // vite-plugin/themeFileApi.ts
38
38
  var import_fs2 = __toESM(require("fs"), 1);
39
39
  var import_path2 = __toESM(require("path"), 1);
40
40
 
41
- // src/lib/parsers/globalRootBlock.ts
41
+ // src/editor/core/themes/parsers/globalRootBlock.ts
42
42
  function extractGlobalRootBody(source) {
43
43
  const re = /:global\(:root\)\s*\{([^}]*)\}/g;
44
44
  const bodies = [];
@@ -49,12 +49,416 @@ function extractGlobalRootBody(source) {
49
49
  return bodies.join("\n");
50
50
  }
51
51
 
52
- // src/lib/files/versionedFileResource.ts
52
+ // src/editor/core/storage/files/versionedFileResourceClient.ts
53
53
  function sanitizeFileName(name) {
54
54
  return name.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9\-_]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "") || "unnamed";
55
55
  }
56
56
 
57
- // src/vite-plugin/files/versionedFileResource.ts
57
+ // src/editor/core/palettes/oklch.ts
58
+ function srgbToLinear(c) {
59
+ return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
60
+ }
61
+ function linearToSrgb(c) {
62
+ return c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
63
+ }
64
+ function hexToLinearRgb(hex) {
65
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
66
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
67
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
68
+ return [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
69
+ }
70
+ function linearRgbToHex(r, g, b) {
71
+ const toHex = (c) => {
72
+ const v = Math.round(Math.max(0, Math.min(1, linearToSrgb(c))) * 255);
73
+ return v.toString(16).padStart(2, "0");
74
+ };
75
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
76
+ }
77
+ function linearRgbToOklab(r, g, b) {
78
+ const l_ = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
79
+ const m_ = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
80
+ const s_ = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
81
+ const l = Math.cbrt(l_);
82
+ const m = Math.cbrt(m_);
83
+ const s = Math.cbrt(s_);
84
+ return [
85
+ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
86
+ 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
87
+ 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
88
+ ];
89
+ }
90
+ function oklabToLinearRgb(L, a, b) {
91
+ const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
92
+ const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
93
+ const s_ = L - 0.0894841775 * a - 1.291485548 * b;
94
+ const l = l_ * l_ * l_;
95
+ const m = m_ * m_ * m_;
96
+ const s = s_ * s_ * s_;
97
+ return [
98
+ 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
99
+ -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
100
+ -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
101
+ ];
102
+ }
103
+ function oklabToOklch(L, a, b) {
104
+ const c = Math.sqrt(a * a + b * b);
105
+ let h = Math.atan2(b, a) * 180 / Math.PI;
106
+ if (h < 0) h += 360;
107
+ return { l: L, c, h };
108
+ }
109
+ function oklchToOklab(l, c, h) {
110
+ const hRad = h * Math.PI / 180;
111
+ return [l, c * Math.cos(hRad), c * Math.sin(hRad)];
112
+ }
113
+ function hexToOklch(hex) {
114
+ const [r, g, b] = hexToLinearRgb(hex);
115
+ const [L, a, bVal] = linearRgbToOklab(r, g, b);
116
+ return oklabToOklch(L, a, bVal);
117
+ }
118
+ function oklchToHex(l, c, h) {
119
+ const [L, a, b] = oklchToOklab(l, c, h);
120
+ const [r, g, bVal] = oklabToLinearRgb(L, a, b);
121
+ return linearRgbToHex(r, g, bVal);
122
+ }
123
+ function isInGamut(r, g, b) {
124
+ const eps = 1e-4;
125
+ return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
126
+ }
127
+ function gamutClamp(l, c, h) {
128
+ if (l <= 0) return { l: 0, c: 0, h };
129
+ if (l >= 1) return { l: 1, c: 0, h };
130
+ const [L, a, b] = oklchToOklab(l, c, h);
131
+ const [r, g, bVal] = oklabToLinearRgb(L, a, b);
132
+ if (isInGamut(r, g, bVal)) return { l, c, h };
133
+ let lo = 0;
134
+ let hi = c;
135
+ for (let i = 0; i < 20; i++) {
136
+ const mid = (lo + hi) / 2;
137
+ const [La, aa, ba] = oklchToOklab(l, mid, h);
138
+ const [rr, gg, bb] = oklabToLinearRgb(La, aa, ba);
139
+ if (isInGamut(rr, gg, bb)) {
140
+ lo = mid;
141
+ } else {
142
+ hi = mid;
143
+ }
144
+ }
145
+ return { l, c: lo, h };
146
+ }
147
+
148
+ // src/editor/ui/curveEngine.ts
149
+ function makeAnchor(x, y, tangentLen = 15) {
150
+ return { x, y, inDx: -tangentLen, inDy: 0, outDx: tangentLen, outDy: 0 };
151
+ }
152
+ function evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, t) {
153
+ const u = 1 - t, u2 = u * u, u3 = u2 * u;
154
+ const t2 = t * t, t3 = t2 * t;
155
+ return {
156
+ x: u3 * p0x + 3 * u2 * t * c0x + 3 * u * t2 * c1x + t3 * p1x,
157
+ y: u3 * p0y + 3 * u2 * t * c0y + 3 * u * t2 * c1y + t3 * p1y
158
+ };
159
+ }
160
+ function sampleCurve(anchors, xPos) {
161
+ if (anchors.length === 0) return 0;
162
+ if (anchors.length === 1) return anchors[0].y;
163
+ if (xPos <= anchors[0].x) return anchors[0].y;
164
+ if (xPos >= anchors[anchors.length - 1].x) return anchors[anchors.length - 1].y;
165
+ let seg = 0;
166
+ while (seg < anchors.length - 2 && anchors[seg + 1].x < xPos) seg++;
167
+ const a0 = anchors[seg], a1 = anchors[seg + 1];
168
+ const p0x = a0.x, p0y = a0.y;
169
+ const c0x = a0.x + a0.outDx, c0y = a0.y + a0.outDy;
170
+ const c1x = a1.x + a1.inDx, c1y = a1.y + a1.inDy;
171
+ const p1x = a1.x, p1y = a1.y;
172
+ let lo = 0, hi = 1;
173
+ for (let i = 0; i < 20; i++) {
174
+ const mid = (lo + hi) / 2;
175
+ const pt = evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, mid);
176
+ if (pt.x < xPos) lo = mid;
177
+ else hi = mid;
178
+ }
179
+ return evalBezier(p0x, p0y, c0x, c0y, c1x, c1y, p1x, p1y, (lo + hi) / 2).y;
180
+ }
181
+
182
+ // src/editor/core/palettes/paletteDerivation.ts
183
+ var PALETTE_SPECS = [
184
+ { label: "Neutral", cssNamespace: "neutral", mode: "gray", initialColor: "#808080" },
185
+ { label: "Alternate", cssNamespace: "alternate", mode: "gray", initialColor: "#808080" },
186
+ { label: "Background", cssNamespace: "canvas", mode: "chromatic", emptySelector: true, initialColor: "#1a1a2e" },
187
+ { label: "Brand", cssNamespace: "brand", mode: "chromatic", initialColor: "#c93636" },
188
+ { label: "Accent", cssNamespace: "accent", mode: "chromatic", initialColor: "#f49e0b" },
189
+ { label: "Special", cssNamespace: "special", mode: "chromatic", initialColor: "#8b5cf6" },
190
+ { label: "Info", cssNamespace: "info", mode: "chromatic", initialColor: "#3077e8" },
191
+ { label: "Success", cssNamespace: "success", mode: "chromatic", initialColor: "#21c45d" },
192
+ { label: "Warning", cssNamespace: "warning", mode: "chromatic", initialColor: "#e66e1a" },
193
+ { label: "Danger", cssNamespace: "danger", mode: "chromatic", initialColor: "#e8304f" }
194
+ ];
195
+ var PALETTE_STEPS = [
196
+ { label: "100" },
197
+ { label: "200" },
198
+ { label: "300" },
199
+ { label: "400" },
200
+ { label: "500" },
201
+ { label: "600" },
202
+ { label: "700" },
203
+ { label: "800" },
204
+ { label: "850" },
205
+ { label: "900" },
206
+ { label: "950" }
207
+ ];
208
+ var GRAY_STEPS = [
209
+ { label: "100" },
210
+ { label: "200" },
211
+ { label: "300" },
212
+ { label: "400" },
213
+ { label: "500" },
214
+ { label: "600" },
215
+ { label: "700" },
216
+ { label: "800" },
217
+ { label: "850" },
218
+ { label: "900" },
219
+ { label: "950" }
220
+ ];
221
+ var SCALES = [
222
+ {
223
+ title: "Surfaces",
224
+ isText: false,
225
+ steps: [
226
+ { name: "lowest", position: -1 },
227
+ { name: "lower", position: -2 / 3 },
228
+ { name: "low", position: -1 / 3 },
229
+ { name: "default", position: 0 },
230
+ { name: "high", position: 1 / 3 },
231
+ { name: "higher", position: 2 / 3 },
232
+ { name: "highest", position: 1 }
233
+ ]
234
+ },
235
+ {
236
+ title: "Borders",
237
+ isText: false,
238
+ steps: [
239
+ { name: "faint", position: -1 },
240
+ { name: "subtle", position: -0.5 },
241
+ { name: "default", position: 0 },
242
+ { name: "medium", position: 0.5 },
243
+ { name: "strong", position: 1 }
244
+ ]
245
+ },
246
+ {
247
+ title: "Text",
248
+ isText: true,
249
+ steps: [
250
+ { name: "primary", position: 0 },
251
+ { name: "secondary", position: 0 },
252
+ { name: "tertiary", position: 0 },
253
+ { name: "muted", position: 0 },
254
+ { name: "disabled", position: 0 }
255
+ ]
256
+ }
257
+ ];
258
+ var DEFAULT_PALETTE_LIGHTNESS = () => [makeAnchor(0, 95, 5), makeAnchor(100, 8, 5)];
259
+ var DEFAULT_PALETTE_SATURATION = () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)];
260
+ var DEFAULT_GRAY_LIGHTNESS = () => [makeAnchor(0, 92, 5), makeAnchor(100, 3, 5)];
261
+ var DEFAULT_GRAY_SATURATION = () => [makeAnchor(0, 20, 30), makeAnchor(100, 20, 30)];
262
+ var DEFAULT_TINT_CHROMA = 0.04;
263
+ var defaultScaleCurves = {
264
+ Surfaces: {
265
+ lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
266
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
267
+ },
268
+ Borders: {
269
+ lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
270
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
271
+ },
272
+ Text: {
273
+ lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
274
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
275
+ }
276
+ };
277
+ function paletteStepKey(label) {
278
+ return `Palette-${label}`;
279
+ }
280
+ function grayStepKey(label) {
281
+ return `gray-${label}`;
282
+ }
283
+ function stepKey(scaleTitle, stepName) {
284
+ return `${scaleTitle}-${stepName}`;
285
+ }
286
+ function stepIndexToX(index, total) {
287
+ return total > 1 ? index / (total - 1) * 100 : 50;
288
+ }
289
+ function computePaletteColor(index, base, lightnessCurve, saturationCurve, curveOffset) {
290
+ const { c: baseC, h } = hexToOklch(base);
291
+ const xPos = stepIndexToX(index, PALETTE_STEPS.length);
292
+ const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset.lightness ?? 0))) / 100;
293
+ const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset.saturation ?? 0)) / 100));
294
+ const targetC = baseC * satMul;
295
+ const clamped = gamutClamp(targetL, targetC, h);
296
+ return oklchToHex(clamped.l, clamped.c, clamped.h);
297
+ }
298
+ function computeGrayColor(index, hue, chroma, grayLightnessCurve, graySaturationCurve, curveOffset) {
299
+ const xPos = stepIndexToX(index, GRAY_STEPS.length);
300
+ const lOff = curveOffset["gray-lightness"] ?? 0;
301
+ const sOff = curveOffset["gray-saturation"] ?? 0;
302
+ const targetL = Math.max(0, Math.min(100, sampleCurve(grayLightnessCurve, xPos) + lOff)) / 100;
303
+ const satMul = Math.max(0, Math.min(2, (sampleCurve(graySaturationCurve, xPos) + sOff) / 100));
304
+ const targetC = chroma * satMul;
305
+ const clamped = gamutClamp(targetL, targetC, hue);
306
+ return oklchToHex(clamped.l, clamped.c, clamped.h);
307
+ }
308
+ function computeDerivedColor(step, base, scaleTitle, scaleCurves, curveOffset) {
309
+ const scale = SCALES.find((s) => s.title === scaleTitle);
310
+ const idx = scale.steps.indexOf(step);
311
+ const xPos = stepIndexToX(idx, scale.steps.length);
312
+ const defs = defaultScaleCurves[scaleTitle];
313
+ const lCurve = scaleCurves[scaleTitle]?.lightness ?? defs.lightness();
314
+ const sCurve = scaleCurves[scaleTitle]?.saturation ?? defs.saturation();
315
+ const lOff = curveOffset[`${scaleTitle}-lightness`] ?? 0;
316
+ const sOff = curveOffset[`${scaleTitle}-saturation`] ?? 0;
317
+ const { l: baseL, c: baseC, h: baseH } = hexToOklch(base);
318
+ let targetL;
319
+ if (scale.isText) {
320
+ const lMul = Math.max(0, Math.min(2, (sampleCurve(lCurve, xPos) + lOff) / 100));
321
+ targetL = Math.max(0, Math.min(1, baseL * lMul));
322
+ } else {
323
+ targetL = Math.max(0, Math.min(100, sampleCurve(lCurve, xPos) + lOff)) / 100;
324
+ }
325
+ const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
326
+ const targetC = baseC * satMul;
327
+ const clamped = gamutClamp(targetL, targetC, baseH);
328
+ return oklchToHex(clamped.l, clamped.c, clamped.h);
329
+ }
330
+ function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
331
+ if (cssNamespace === null) return null;
332
+ if (scaleTitle === "Surfaces") {
333
+ const suffix = stepName === "default" ? "" : `-${stepName}`;
334
+ return cssNamespace === "neutral" ? `--surface-neutral${suffix}` : `--surface-${cssNamespace}${suffix}`;
335
+ }
336
+ if (scaleTitle === "Borders") {
337
+ const suffix = stepName === "default" ? "" : `-${stepName}`;
338
+ return cssNamespace === "neutral" ? `--border-neutral${suffix}` : `--border-${cssNamespace}${suffix}`;
339
+ }
340
+ if (scaleTitle === "Text") {
341
+ if (cssNamespace === "neutral") return `--text-${stepName}`;
342
+ return stepName === "primary" ? `--text-${cssNamespace}` : `--text-${cssNamespace}-${stepName}`;
343
+ }
344
+ return null;
345
+ }
346
+ function derivePaletteVars(spec, config) {
347
+ const out = {};
348
+ if (!config) return out;
349
+ const baseColor = config.baseColor ?? spec.initialColor;
350
+ const overrides = config.overrides ?? {};
351
+ const curveOffset = config.curveOffset ?? {};
352
+ const scaleCurves = config.scaleCurves ?? {};
353
+ let baseForScales;
354
+ if (spec.mode === "gray") {
355
+ const grayLightnessCurve = config.grayLightnessCurve ?? DEFAULT_GRAY_LIGHTNESS();
356
+ const graySaturationCurve = config.graySaturationCurve ?? DEFAULT_GRAY_SATURATION();
357
+ const tintHue = config.tintHue ?? 240;
358
+ const tintChroma = config.tintChroma ?? DEFAULT_TINT_CHROMA;
359
+ let gray500 = "#808080";
360
+ GRAY_STEPS.forEach((step, index) => {
361
+ const k = grayStepKey(step.label);
362
+ const hex = computeGrayColor(index, tintHue, tintChroma, grayLightnessCurve, graySaturationCurve, curveOffset);
363
+ const effective = k in overrides ? overrides[k] : hex;
364
+ out[`--color-${spec.cssNamespace}-${step.label}`] = effective;
365
+ if (step.label === "500") gray500 = hex;
366
+ });
367
+ baseForScales = gray500;
368
+ } else {
369
+ const lightnessCurve = config.lightnessCurve ?? DEFAULT_PALETTE_LIGHTNESS();
370
+ const saturationCurve = config.saturationCurve ?? DEFAULT_PALETTE_SATURATION();
371
+ PALETTE_STEPS.forEach((ps, index) => {
372
+ const k = paletteStepKey(ps.label);
373
+ const hex = computePaletteColor(index, baseColor, lightnessCurve, saturationCurve, curveOffset);
374
+ const effective = k in overrides ? overrides[k] : hex;
375
+ out[`--color-${spec.cssNamespace}-${ps.label}`] = effective;
376
+ });
377
+ baseForScales = baseColor;
378
+ }
379
+ for (const scale of SCALES) {
380
+ for (const step of scale.steps) {
381
+ const k = stepKey(scale.title, step.name);
382
+ const hex = k in overrides ? overrides[k] : computeDerivedColor(step, baseForScales, scale.title, scaleCurves, curveOffset);
383
+ const varName = scaleToCssVar(scale.title, step.name, spec.cssNamespace);
384
+ if (varName) out[varName] = hex;
385
+ }
386
+ }
387
+ if (spec.emptySelector) {
388
+ const emptyMode = config.emptyMode ?? "solid";
389
+ const emptyStep = config.emptyStep ?? "850";
390
+ const gradientStyle = config.gradientStyle ?? "linear";
391
+ const gradientAngle = config.gradientAngle ?? 180;
392
+ const gradientReverse = config.gradientReverse ?? false;
393
+ const gradientSize = config.gradientSize ?? "page";
394
+ const gradientStops = config.gradientStops ?? [
395
+ { position: 0, paletteLabel: "800" },
396
+ { position: 100, paletteLabel: "950" }
397
+ ];
398
+ if (emptyMode === "solid") {
399
+ const stepHex = out[`--color-${spec.cssNamespace}-${emptyStep}`];
400
+ if (stepHex) out["--page-bg"] = stepHex;
401
+ out["--page-bg-attachment"] = "scroll";
402
+ } else {
403
+ const sortedStops = [...gradientStops].sort(
404
+ (a, b) => gradientReverse ? b.position - a.position : a.position - b.position
405
+ );
406
+ const stopsCss = sortedStops.map((s) => `${out[`--color-${spec.cssNamespace}-${s.paletteLabel}`] ?? "#000000"} ${s.position}%`).join(", ");
407
+ let gradient;
408
+ switch (gradientStyle) {
409
+ case "radial":
410
+ gradient = `radial-gradient(circle, ${stopsCss})`;
411
+ break;
412
+ case "conic":
413
+ gradient = `conic-gradient(from ${gradientAngle}deg, ${stopsCss})`;
414
+ break;
415
+ default:
416
+ gradient = `linear-gradient(${gradientAngle}deg, ${stopsCss})`;
417
+ }
418
+ out["--page-bg"] = gradient;
419
+ out["--page-bg-attachment"] = gradientSize === "window" ? "fixed" : "scroll";
420
+ }
421
+ }
422
+ return out;
423
+ }
424
+ function palettesToVars(palettes) {
425
+ const out = {};
426
+ for (const spec of PALETTE_SPECS) {
427
+ Object.assign(out, derivePaletteVars(spec, palettes[spec.label]));
428
+ }
429
+ return out;
430
+ }
431
+ var HEX_RE = /^#[0-9a-f]{6}$/i;
432
+ function reconcilePalettesFromCssVars(palettes, cssVars) {
433
+ const next = structuredClone(palettes);
434
+ const consumed = /* @__PURE__ */ new Set();
435
+ const snapped = /* @__PURE__ */ new Set();
436
+ for (const spec of PALETTE_SPECS) {
437
+ const current = next[spec.label];
438
+ if (current === void 0) continue;
439
+ if (current._imported === true) {
440
+ const anchorHex = cssVars[`--color-${spec.cssNamespace}-500`];
441
+ if (anchorHex && HEX_RE.test(anchorHex.trim())) {
442
+ const hex = anchorHex.trim();
443
+ if (spec.mode === "gray") {
444
+ const { c, h } = hexToOklch(hex);
445
+ next[spec.label] = { ...current, tintHue: h, tintChroma: c, _imported: false };
446
+ } else {
447
+ next[spec.label] = { ...current, baseColor: hex, _imported: false };
448
+ }
449
+ snapped.add(spec.label);
450
+ } else {
451
+ next[spec.label] = { ...current, _imported: false };
452
+ }
453
+ }
454
+ for (const k of Object.keys(derivePaletteVars(spec, next[spec.label]))) {
455
+ consumed.add(k);
456
+ }
457
+ }
458
+ return { palettes: next, consumed, snapped };
459
+ }
460
+
461
+ // vite-plugin/files/versionedFileResourceServer.ts
58
462
  var import_fs = __toESM(require("fs"), 1);
59
463
  var import_path = __toESM(require("path"), 1);
60
464
  function versionedFileResourceServer(opts) {
@@ -114,7 +518,7 @@ function versionedFileResourceServer(opts) {
114
518
  };
115
519
  }
116
520
 
117
- // src/vite-plugin/files/routeTable.ts
521
+ // vite-plugin/files/routeTable.ts
118
522
  async function dispatch(req, res, routes) {
119
523
  const url = req.url || "";
120
524
  const method = req.method || "GET";
@@ -142,16 +546,46 @@ async function dispatch(req, res, routes) {
142
546
  return false;
143
547
  }
144
548
 
145
- // src/vite-plugin/themeFileApi.ts
146
- var PKG_VERSION = true ? "0.6.2" : "";
549
+ // vite-plugin/files/nameAllocator.ts
550
+ function nextAvailableName(exists, baseName, maxAttempts = 1e3) {
551
+ if (!exists(baseName)) return baseName;
552
+ for (let i = 2; i < maxAttempts; i++) {
553
+ const candidate = `${baseName}-${i}`;
554
+ if (!exists(candidate)) return candidate;
555
+ }
556
+ throw new Error(`Could not allocate a non-colliding name for "${baseName}"`);
557
+ }
558
+
559
+ // vite-plugin/themeFileApi.ts
560
+ var import_node_url = require("url");
561
+ var import_meta = {};
562
+ var PKG_VERSION = (() => {
563
+ try {
564
+ let dir = import_path2.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
565
+ for (let i = 0; i < 4; i++) {
566
+ const p = import_path2.default.join(dir, "package.json");
567
+ if (import_fs2.default.existsSync(p)) {
568
+ const json = JSON.parse(import_fs2.default.readFileSync(p, "utf-8"));
569
+ if (json?.name === "@motion-proto/live-tokens") return json.version ?? "";
570
+ }
571
+ const up = import_path2.default.dirname(dir);
572
+ if (up === dir) break;
573
+ dir = up;
574
+ }
575
+ } catch {
576
+ }
577
+ return "";
578
+ })();
147
579
  function themeFileApi(opts) {
148
580
  const THEMES_DIR = import_path2.default.resolve(opts.themesDir);
149
581
  const CSS_PATH = import_path2.default.resolve(opts.tokensCssPath);
582
+ const GENERATED_CSS_PATH = opts.tokensGeneratedCssPath ? import_path2.default.resolve(opts.tokensGeneratedCssPath) : import_path2.default.join(import_path2.default.dirname(CSS_PATH), "tokens.generated.css");
150
583
  const FONTS_CSS_PATH = opts.fontsCssPath ? import_path2.default.resolve(opts.fontsCssPath) : import_path2.default.join(import_path2.default.dirname(CSS_PATH), "fonts.css");
151
584
  const API_BASE = opts.apiBase ?? "/api";
152
585
  const COMPONENT_CONFIGS_DIR = opts.componentConfigsDir ? import_path2.default.resolve(opts.componentConfigsDir) : import_path2.default.resolve("component-configs");
153
- const COMPONENTS_SRC_DIR = opts.componentsSrcDir ? import_path2.default.resolve(opts.componentsSrcDir) : import_path2.default.resolve("src/components");
154
- const PRESETS_DIR = opts.presetsDir ? import_path2.default.resolve(opts.presetsDir) : import_path2.default.resolve("presets");
586
+ const COMPONENTS_SRC_DIR = opts.componentsSrcDir ? import_path2.default.resolve(opts.componentsSrcDir) : import_path2.default.resolve("src/system/components");
587
+ const MANIFESTS_DIR = opts.manifestsDir ? import_path2.default.resolve(opts.manifestsDir) : import_path2.default.resolve("manifests");
588
+ const LEGACY_PRESETS_DIR = import_path2.default.resolve("presets");
155
589
  const themesResource = versionedFileResourceServer({
156
590
  dir: THEMES_DIR
157
591
  });
@@ -164,7 +598,7 @@ function themeFileApi(opts) {
164
598
  }
165
599
  return r;
166
600
  }
167
- const presetsResource = versionedFileResourceServer({ dir: PRESETS_DIR });
601
+ const manifestsResource = versionedFileResourceServer({ dir: MANIFESTS_DIR });
168
602
  function ensureThemesDir() {
169
603
  themesResource.ensureDir();
170
604
  if (!import_fs2.default.existsSync(import_path2.default.join(THEMES_DIR, "default.json"))) {
@@ -192,6 +626,17 @@ function themeFileApi(opts) {
192
626
  res.setHeader("Content-Type", "application/json");
193
627
  res.end(JSON.stringify(data));
194
628
  }
629
+ function normalizeTheme(theme) {
630
+ if (!theme || typeof theme !== "object") return theme;
631
+ const palettes = theme.editorConfigs ?? {};
632
+ const cssVars = theme.cssVariables ?? {};
633
+ const { palettes: nextPalettes, consumed } = reconcilePalettesFromCssVars(palettes, cssVars);
634
+ const nextCssVars = {};
635
+ for (const [k, v] of Object.entries(cssVars)) {
636
+ if (!consumed.has(k)) nextCssVars[k] = v;
637
+ }
638
+ return { ...theme, editorConfigs: nextPalettes, cssVariables: nextCssVars };
639
+ }
195
640
  const SYSTEM_CASCADES_SSR = {
196
641
  "system-ui-sans": 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
197
642
  "system-ui-serif": 'Georgia, "Times New Roman", serif',
@@ -223,42 +668,78 @@ function themeFileApi(opts) {
223
668
  }
224
669
  return out;
225
670
  }
226
- function syncTokensToCss(fileName) {
227
- const themePath = import_path2.default.join(THEMES_DIR, `${fileName}.json`);
228
- if (!import_fs2.default.existsSync(themePath)) return;
229
- const themeData = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
230
- const cssVars = { ...themeData.cssVariables || {} };
231
- const resolvedFontVars = resolveFontStacks(themeData);
232
- for (const [name, value] of Object.entries(resolvedFontVars)) {
233
- cssVars[name] = value;
234
- }
235
- if (Object.keys(cssVars).length === 0) return;
236
- const cssContent = import_fs2.default.readFileSync(CSS_PATH, "utf-8");
237
- const remaining = new Set(Object.keys(cssVars));
238
- const updatedContent = cssContent.replace(
239
- /^(\s*)(--[\w-]+):\s*(.+);/gm,
240
- (_match, indent, varName, oldValue) => {
241
- if (cssVars[varName] !== void 0) {
242
- remaining.delete(varName);
243
- return `${indent}${varName}: ${cssVars[varName]};`;
671
+ function regenerateTokensCss() {
672
+ const lines = [];
673
+ lines.push("/* Generated by themeFileApi from the production theme and component configs. Do not edit. */");
674
+ lines.push("/* tokens.css holds developer-authored defaults; this file holds editor overrides. */");
675
+ lines.push("");
676
+ const productionThemeName = themesResource.getProductionName();
677
+ const themePath = import_path2.default.join(THEMES_DIR, `${productionThemeName}.json`);
678
+ let themeVarCount = 0;
679
+ if (import_fs2.default.existsSync(themePath)) {
680
+ const themeData = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
681
+ const cssVars = { ...themeData.cssVariables || {} };
682
+ Object.assign(cssVars, palettesToVars(themeData.editorConfigs ?? {}));
683
+ const resolvedFontVars = resolveFontStacks(themeData);
684
+ for (const [name, value] of Object.entries(resolvedFontVars)) {
685
+ cssVars[name] = value;
686
+ }
687
+ themeVarCount = Object.keys(cssVars).length;
688
+ if (themeVarCount > 0) {
689
+ lines.push(`/* Production theme: ${productionThemeName} */`);
690
+ lines.push(":root:root {");
691
+ for (const [name, value] of Object.entries(cssVars)) {
692
+ lines.push(` ${name}: ${value};`);
244
693
  }
245
- return `${indent}${varName}: ${oldValue.trim()};`;
694
+ lines.push("}");
695
+ lines.push("");
246
696
  }
247
- );
248
- let finalContent = updatedContent;
249
- if (remaining.size > 0) {
250
- const newVars = [...remaining].map((name) => ` ${name}: ${cssVars[name]};`).join("\n");
251
- finalContent = finalContent.replace(
252
- /\n\}(\s*)$/,
253
- `
254
-
255
- /* Token additions */
256
- ${newVars}
257
- }$1`
258
- );
259
697
  }
260
- import_fs2.default.writeFileSync(CSS_PATH, finalContent);
261
- console.log(`[syncTokensToCss] Wrote ${Object.keys(cssVars).length} variables from "${fileName}" into tokens.css`);
698
+ let componentOverrideCount = 0;
699
+ if (import_fs2.default.existsSync(COMPONENT_CONFIGS_DIR)) {
700
+ const blocks = [];
701
+ for (const comp of listComponentNames()) {
702
+ const prod = componentResource(comp).getProductionName();
703
+ if (prod === "default") continue;
704
+ const prodCfg = readComponentConfig(comp, prod);
705
+ const defaultCfg = readComponentConfig(comp, "default");
706
+ if (!prodCfg || !defaultCfg) continue;
707
+ const overrides = [];
708
+ const defaultAliases = defaultCfg.aliases ?? {};
709
+ for (const [varName, semanticValue] of Object.entries(prodCfg.aliases ?? {})) {
710
+ if (!aliasValuesEqual(defaultAliases[varName], semanticValue)) {
711
+ overrides.push([varName, semanticValue]);
712
+ }
713
+ }
714
+ if (overrides.length === 0) continue;
715
+ const block = [` /* ${comp} (${prod}) */`];
716
+ for (const [varName, semanticValue] of overrides) {
717
+ block.push(` ${varName}: ${aliasValueToCss(semanticValue)};`);
718
+ }
719
+ blocks.push(block);
720
+ componentOverrideCount += overrides.length;
721
+ }
722
+ if (blocks.length > 0) {
723
+ lines.push("/* Component aliases (production configs differing from defaults) */");
724
+ lines.push(":root:root {");
725
+ for (let i = 0; i < blocks.length; i++) {
726
+ if (i > 0) lines.push("");
727
+ lines.push(...blocks[i]);
728
+ }
729
+ lines.push("}");
730
+ lines.push("");
731
+ }
732
+ }
733
+ if (!import_fs2.default.existsSync(import_path2.default.dirname(GENERATED_CSS_PATH))) {
734
+ import_fs2.default.mkdirSync(import_path2.default.dirname(GENERATED_CSS_PATH), { recursive: true });
735
+ }
736
+ import_fs2.default.writeFileSync(GENERATED_CSS_PATH, lines.join("\n"));
737
+ console.log(
738
+ `[regenerateTokensCss] Wrote ${import_path2.default.basename(GENERATED_CSS_PATH)} (${themeVarCount} theme vars, ${componentOverrideCount} component overrides)`
739
+ );
740
+ }
741
+ function syncTokensToCss(_fileName) {
742
+ regenerateTokensCss();
262
743
  }
263
744
  function syncFontsToCss(fileName) {
264
745
  const themePath = import_path2.default.join(THEMES_DIR, `${fileName}.json`);
@@ -270,15 +751,20 @@ ${newVars}
270
751
  lines.push("/* Generated from the production theme by syncFontsToCss. Do not edit. */");
271
752
  lines.push("/* Both fonts.css and fonts/ are in dist/, so relative paths work at runtime. */");
272
753
  lines.push("");
273
- for (const source of sources) {
754
+ const urlSources = sources.filter((s) => s.kind !== "font-face" && s.url);
755
+ const faceSources = sources.filter((s) => s.kind === "font-face" && s.cssText);
756
+ for (const source of urlSources) {
274
757
  const familyNames = source.families.map((f) => f.name).join(", ");
275
758
  const label = source.label ? `${source.label} \u2014 ${familyNames}` : familyNames;
276
759
  lines.push(`/* ${label} */`);
277
- if (source.kind === "font-face") {
278
- if (source.cssText) lines.push(source.cssText);
279
- } else if (source.url) {
280
- lines.push(`@import url('${source.url}');`);
281
- }
760
+ lines.push(`@import url('${source.url}');`);
761
+ lines.push("");
762
+ }
763
+ for (const source of faceSources) {
764
+ const familyNames = source.families.map((f) => f.name).join(", ");
765
+ const label = source.label ? `${source.label} \u2014 ${familyNames}` : familyNames;
766
+ lines.push(`/* ${label} */`);
767
+ lines.push(source.cssText);
282
768
  lines.push("");
283
769
  }
284
770
  const content = lines.join("\n");
@@ -360,25 +846,90 @@ ${newVars}
360
846
  r.ensureMeta();
361
847
  }
362
848
  }
363
- function ensurePresetsDir() {
364
- presetsResource.ensureDir();
365
- const defaultPath = import_path2.default.join(PRESETS_DIR, "default.json");
849
+ function ensureManifestsDir() {
850
+ if (!import_fs2.default.existsSync(MANIFESTS_DIR) && import_fs2.default.existsSync(LEGACY_PRESETS_DIR)) {
851
+ import_fs2.default.renameSync(LEGACY_PRESETS_DIR, MANIFESTS_DIR);
852
+ const legacyProd = import_path2.default.join(MANIFESTS_DIR, "_production.json");
853
+ if (import_fs2.default.existsSync(legacyProd)) import_fs2.default.unlinkSync(legacyProd);
854
+ }
855
+ manifestsResource.ensureDir();
856
+ const defaultPath = import_path2.default.join(MANIFESTS_DIR, "default.json");
366
857
  if (!import_fs2.default.existsSync(defaultPath)) {
367
858
  const componentConfigs = {};
368
859
  for (const comp of listComponentNames()) {
369
860
  componentConfigs[comp] = componentResource(comp).getActiveName();
370
861
  }
371
862
  const now = (/* @__PURE__ */ new Date()).toISOString();
372
- const defaultPreset = {
373
- name: "Default Preset",
863
+ const defaultManifest = {
864
+ name: "Default",
374
865
  createdAt: now,
375
866
  updatedAt: now,
376
867
  theme: themesResource.getActiveName(),
377
868
  componentConfigs
378
869
  };
379
- import_fs2.default.writeFileSync(defaultPath, JSON.stringify(defaultPreset, null, 2));
870
+ import_fs2.default.writeFileSync(defaultPath, JSON.stringify(defaultManifest, null, 2));
380
871
  }
381
- presetsResource.ensureMeta();
872
+ if (!import_fs2.default.existsSync(manifestsResource.activePath)) {
873
+ import_fs2.default.writeFileSync(
874
+ manifestsResource.activePath,
875
+ JSON.stringify({ activeFile: "default" })
876
+ );
877
+ } else {
878
+ const activeName = manifestsResource.getActiveName();
879
+ if (!import_fs2.default.existsSync(manifestsResource.filePath(activeName))) {
880
+ manifestsResource.setActiveName("default");
881
+ }
882
+ }
883
+ const stragglerProd = import_path2.default.join(MANIFESTS_DIR, "_production.json");
884
+ if (import_fs2.default.existsSync(stragglerProd)) import_fs2.default.unlinkSync(stragglerProd);
885
+ }
886
+ function patchActiveManifest(field, comp, fileName) {
887
+ const activeFile = manifestsResource.getActiveName();
888
+ if (activeFile === "default") return false;
889
+ const manifestPath = manifestsResource.filePath(activeFile);
890
+ if (!import_fs2.default.existsSync(manifestPath)) return false;
891
+ let manifest;
892
+ try {
893
+ manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
894
+ } catch {
895
+ return false;
896
+ }
897
+ if (field === "theme") {
898
+ manifest.theme = fileName;
899
+ } else if (field === "component" && comp) {
900
+ manifest.componentConfigs = manifest.componentConfigs ?? {};
901
+ manifest.componentConfigs[comp] = fileName;
902
+ }
903
+ manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
904
+ import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
905
+ return true;
906
+ }
907
+ function formatAliasGradient(v) {
908
+ const stopColor = (s) => {
909
+ const base = s.color.startsWith("--") ? `var(${s.color})` : s.color;
910
+ const opacity = s.opacity ?? 100;
911
+ return opacity >= 100 ? base : `color-mix(in srgb, ${base} ${opacity}%, transparent)`;
912
+ };
913
+ if (v.type === "none") return "transparent";
914
+ if (v.type === "solid") {
915
+ const first = v.stops[0];
916
+ if (!first) return "transparent";
917
+ return stopColor(first);
918
+ }
919
+ const stops = v.stops.map((s) => `${stopColor(s)} ${s.position}%`).join(", ");
920
+ if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
921
+ const radial = v.radius && v.radius > 0 ? `circle ${v.radius}px at center` : "circle";
922
+ return `radial-gradient(${radial}, ${stops})`;
923
+ }
924
+ function aliasValueToCss(v) {
925
+ if (typeof v === "string") return v.startsWith("--") ? `var(${v})` : v;
926
+ return formatAliasGradient(v.value);
927
+ }
928
+ function aliasValuesEqual(a, b) {
929
+ if (a === void 0 || b === void 0) return a === b;
930
+ if (typeof a === "string" && typeof b === "string") return a === b;
931
+ if (typeof a !== typeof b) return false;
932
+ return JSON.stringify(a) === JSON.stringify(b);
382
933
  }
383
934
  function readComponentConfig(comp, name) {
384
935
  const filePath = componentResource(comp).filePath(name);
@@ -389,70 +940,25 @@ ${newVars}
389
940
  return null;
390
941
  }
391
942
  }
392
- const COMPONENT_OVERRIDES_START = "/* component-aliases:start */";
393
- const COMPONENT_OVERRIDES_END = "/* component-aliases:end */";
394
943
  function syncComponentsToCss() {
395
- if (!import_fs2.default.existsSync(CSS_PATH)) return;
396
- if (!import_fs2.default.existsSync(COMPONENT_CONFIGS_DIR)) return;
397
- const lines = [];
398
- const components = listComponentNames();
399
- for (const comp of components) {
400
- const prod = componentResource(comp).getProductionName();
401
- if (prod === "default") continue;
402
- const prodCfg = readComponentConfig(comp, prod);
403
- const defaultCfg = readComponentConfig(comp, "default");
404
- if (!prodCfg || !defaultCfg) continue;
405
- const overrides = [];
406
- for (const [varName, semanticName] of Object.entries(prodCfg.aliases ?? {})) {
407
- if ((defaultCfg.aliases ?? {})[varName] !== semanticName) {
408
- overrides.push([varName, semanticName]);
409
- }
410
- }
411
- if (overrides.length === 0) continue;
412
- lines.push(` /* ${comp} (${prod}) */`);
413
- for (const [varName, semanticName] of overrides) {
414
- lines.push(` ${varName}: var(${semanticName});`);
415
- }
416
- }
417
- const block = lines.length > 0 ? `
418
-
419
- ${COMPONENT_OVERRIDES_START}
420
- :root:root {
421
- ${lines.join("\n")}
422
- }
423
- ${COMPONENT_OVERRIDES_END}
424
- ` : "";
425
- let cssContent = import_fs2.default.readFileSync(CSS_PATH, "utf-8");
426
- const startIdx = cssContent.indexOf(COMPONENT_OVERRIDES_START);
427
- const endIdx = cssContent.indexOf(COMPONENT_OVERRIDES_END);
428
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
429
- let stripStart = startIdx;
430
- while (stripStart > 0 && cssContent[stripStart - 1] === "\n") stripStart--;
431
- const after = cssContent.slice(endIdx + COMPONENT_OVERRIDES_END.length);
432
- cssContent = cssContent.slice(0, stripStart) + (block || "\n") + after.replace(/^\n+/, "");
433
- } else if (block) {
434
- cssContent = cssContent.replace(/\n*$/, "") + block;
435
- }
436
- import_fs2.default.writeFileSync(CSS_PATH, cssContent);
437
- console.log(
438
- `[syncComponentsToCss] Wrote ${lines.filter((l) => !l.trim().startsWith("/*")).length} alias override(s) to tokens.css`
439
- );
944
+ regenerateTokensCss();
440
945
  }
441
946
  const escapedBase = API_BASE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
442
947
  const THEMES_ROUTE = `${API_BASE}/themes`;
443
948
  const THEMES_ACTIVE_ROUTE = `${API_BASE}/themes/active`;
444
949
  const THEMES_PRODUCTION_ROUTE = `${API_BASE}/themes/production`;
445
950
  const COMPONENT_CONFIGS_ROUTE = `${API_BASE}/component-configs`;
446
- const PRESETS_ROUTE = `${API_BASE}/presets`;
447
- const PRESETS_ACTIVE_ROUTE = `${API_BASE}/presets/active`;
448
- const PRESETS_PRODUCTION_ROUTE = `${API_BASE}/presets/production`;
951
+ const MANIFESTS_ROUTE = `${API_BASE}/manifests`;
952
+ const MANIFESTS_ACTIVE_ROUTE = `${API_BASE}/manifests/active`;
449
953
  const THEME_BY_NAME_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)$`);
450
954
  const COMP_LIST_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)$`);
451
955
  const COMP_ACTIVE_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/active$`);
452
956
  const COMP_PRODUCTION_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/production$`);
453
957
  const COMP_BY_NAME_REGEX = new RegExp(`^${escapedBase}/component-configs/([a-z0-9\\-_]+)/([a-z0-9\\-_]+)$`);
454
- const PRESET_APPLY_REGEX = new RegExp(`^${escapedBase}/presets/([a-z0-9\\-_]+)/apply$`);
455
- const PRESET_BY_NAME_REGEX = new RegExp(`^${escapedBase}/presets/([a-z0-9\\-_]+)$`);
958
+ const MANIFEST_APPLY_REGEX = new RegExp(`^${escapedBase}/manifests/([a-z0-9\\-_]+)/apply$`);
959
+ const MANIFEST_EXPORT_REGEX = new RegExp(`^${escapedBase}/manifests/([a-z0-9\\-_]+)/export$`);
960
+ const MANIFEST_IMPORT_ROUTE = `${API_BASE}/manifests/import`;
961
+ const MANIFEST_BY_NAME_REGEX = new RegExp(`^${escapedBase}/manifests/([a-z0-9\\-_]+)$`);
456
962
  async function handleListThemes(_ctx) {
457
963
  const activeFile = themesResource.getActiveName();
458
964
  const files = import_fs2.default.readdirSync(THEMES_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
@@ -475,7 +981,7 @@ ${COMPONENT_OVERRIDES_END}
475
981
  jsonResponse(res, 404, { error: "Active theme not found" });
476
982
  return;
477
983
  }
478
- const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
984
+ const data = normalizeTheme(JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8")));
479
985
  data._fileName = activeFile;
480
986
  jsonResponse(res, 200, data);
481
987
  }
@@ -496,7 +1002,7 @@ ${COMPONENT_OVERRIDES_END}
496
1002
  jsonResponse(res, 200, { fileName: prodFile, name: prodFile, cssVariables: {} });
497
1003
  return;
498
1004
  }
499
- const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
1005
+ const data = normalizeTheme(JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8")));
500
1006
  jsonResponse(res, 200, {
501
1007
  fileName: prodFile,
502
1008
  name: data.name || prodFile,
@@ -511,10 +1017,18 @@ ${COMPONENT_OVERRIDES_END}
511
1017
  jsonResponse(res, 404, { error: "Theme not found" });
512
1018
  return;
513
1019
  }
1020
+ if (manifestsResource.getActiveName() === "default") {
1021
+ jsonResponse(res, 409, {
1022
+ error: "Active manifest is protected. Save As first.",
1023
+ code: "ACTIVE_IS_PROTECTED"
1024
+ });
1025
+ return;
1026
+ }
514
1027
  themesResource.setProductionName(fileName);
515
1028
  syncTokensToCss(fileName);
516
1029
  syncFontsToCss(fileName);
517
1030
  syncComponentsToCss();
1031
+ patchActiveManifest("theme", null, fileName);
518
1032
  const data = JSON.parse(import_fs2.default.readFileSync(themesResource.filePath(fileName), "utf-8"));
519
1033
  jsonResponse(res, 200, {
520
1034
  ok: true,
@@ -531,7 +1045,7 @@ ${COMPONENT_OVERRIDES_END}
531
1045
  jsonResponse(res, 404, { error: "Not found" });
532
1046
  return;
533
1047
  }
534
- const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
1048
+ const data = normalizeTheme(JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8")));
535
1049
  data._fileName = fileName;
536
1050
  jsonResponse(res, 200, data);
537
1051
  return;
@@ -561,6 +1075,13 @@ ${COMPONENT_OVERRIDES_END}
561
1075
  jsonResponse(res, 403, { error: "Cannot delete the default theme" });
562
1076
  return;
563
1077
  }
1078
+ if (themesResource.getProductionName() === fileName) {
1079
+ jsonResponse(res, 403, {
1080
+ error: "Cannot delete the production theme. Adopt a different theme first.",
1081
+ code: "PRODUCTION_THEME"
1082
+ });
1083
+ return;
1084
+ }
564
1085
  if (import_fs2.default.existsSync(filePath)) {
565
1086
  import_fs2.default.unlinkSync(filePath);
566
1087
  if (themesResource.getActiveName() === fileName) {
@@ -628,9 +1149,17 @@ ${COMPONENT_OVERRIDES_END}
628
1149
  jsonResponse(res, 404, { error: "Config not found" });
629
1150
  return;
630
1151
  }
1152
+ if (manifestsResource.getActiveName() === "default") {
1153
+ jsonResponse(res, 409, {
1154
+ error: "Active manifest is protected. Save As first.",
1155
+ code: "ACTIVE_IS_PROTECTED"
1156
+ });
1157
+ return;
1158
+ }
631
1159
  r.ensureDir();
632
1160
  r.setProductionName(fileName);
633
1161
  syncComponentsToCss();
1162
+ patchActiveManifest("component", comp, fileName);
634
1163
  const cfg = readComponentConfig(comp, fileName);
635
1164
  jsonResponse(res, 200, {
636
1165
  ok: true,
@@ -720,112 +1249,46 @@ ${COMPONENT_OVERRIDES_END}
720
1249
  });
721
1250
  jsonResponse(res, 200, { component: comp, files, activeFile, productionFile });
722
1251
  }
723
- async function handleListPresets({ res }) {
724
- const activeFile = presetsResource.getActiveName();
725
- const files = import_fs2.default.readdirSync(PRESETS_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
726
- const filePath = import_path2.default.join(PRESETS_DIR, f);
1252
+ async function handleListManifests({ res }) {
1253
+ const activeFile = manifestsResource.getActiveName();
1254
+ const files = import_fs2.default.readdirSync(MANIFESTS_DIR).filter((f) => f.endsWith(".json") && !f.startsWith("_")).map((f) => {
1255
+ const filePath = import_path2.default.join(MANIFESTS_DIR, f);
727
1256
  const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
728
1257
  const fileName = f.replace(".json", "");
729
1258
  return {
730
1259
  name: data.name || fileName,
731
1260
  fileName,
732
1261
  updatedAt: data.updatedAt || "",
733
- isActive: fileName === activeFile
1262
+ isActive: fileName === activeFile,
1263
+ isProtected: fileName === "default"
734
1264
  };
735
1265
  });
736
1266
  jsonResponse(res, 200, { files, activeFile });
737
1267
  }
738
- async function handleGetActivePreset({ res }) {
739
- const activeFile = presetsResource.getActiveName();
740
- const filePath = presetsResource.filePath(activeFile);
1268
+ async function handleGetActiveManifest({ res }) {
1269
+ const activeFile = manifestsResource.getActiveName();
1270
+ const filePath = manifestsResource.filePath(activeFile);
741
1271
  if (!import_fs2.default.existsSync(filePath)) {
742
- jsonResponse(res, 404, { error: "Active preset not found" });
1272
+ jsonResponse(res, 404, { error: "Active manifest not found" });
743
1273
  return;
744
1274
  }
745
1275
  const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
746
1276
  data._fileName = activeFile;
747
1277
  jsonResponse(res, 200, data);
748
1278
  }
749
- async function handleSetActivePreset({ req, res }) {
1279
+ async function handleSetActiveManifest({ req, res }) {
750
1280
  const body = JSON.parse(await readBody(req));
751
1281
  const fileName = sanitizeFileName(body.name || "default");
752
- if (!import_fs2.default.existsSync(presetsResource.filePath(fileName))) {
753
- jsonResponse(res, 404, { error: "Preset not found" });
1282
+ if (!import_fs2.default.existsSync(manifestsResource.filePath(fileName))) {
1283
+ jsonResponse(res, 404, { error: "Manifest not found" });
754
1284
  return;
755
1285
  }
756
- presetsResource.setActiveName(fileName);
1286
+ manifestsResource.setActiveName(fileName);
757
1287
  jsonResponse(res, 200, { ok: true, activeFile: fileName });
758
1288
  }
759
- async function handleGetProductionPreset({ res }) {
760
- const prodFile = presetsResource.getProductionName();
761
- const filePath = presetsResource.filePath(prodFile);
762
- if (!import_fs2.default.existsSync(filePath)) {
763
- jsonResponse(res, 200, {
764
- fileName: prodFile,
765
- name: prodFile,
766
- theme: "default",
767
- componentConfigs: {},
768
- updatedAt: ""
769
- });
770
- return;
771
- }
772
- const data = JSON.parse(import_fs2.default.readFileSync(filePath, "utf-8"));
773
- jsonResponse(res, 200, {
774
- fileName: prodFile,
775
- name: data.name || prodFile,
776
- theme: data.theme || "default",
777
- componentConfigs: data.componentConfigs || {},
778
- updatedAt: data.updatedAt || ""
779
- });
780
- }
781
- async function handleSetProductionPreset({ req, res }) {
782
- const body = JSON.parse(await readBody(req));
783
- const fileName = sanitizeFileName(body.name || "default");
784
- const presetPath = presetsResource.filePath(fileName);
785
- if (!import_fs2.default.existsSync(presetPath)) {
786
- jsonResponse(res, 404, { error: "Preset not found" });
787
- return;
788
- }
789
- const preset = JSON.parse(import_fs2.default.readFileSync(presetPath, "utf-8"));
790
- const themeName = sanitizeFileName(preset.theme || "default");
791
- if (!import_fs2.default.existsSync(themesResource.filePath(themeName))) {
792
- jsonResponse(res, 422, { error: `Preset references missing theme: ${themeName}` });
793
- return;
794
- }
795
- const knownComponents = new Set(listComponentNames());
796
- const componentConfigs = preset.componentConfigs ?? {};
797
- const apply = [];
798
- for (const [comp, configFile] of Object.entries(componentConfigs)) {
799
- if (!knownComponents.has(comp)) continue;
800
- const sanitized = sanitizeFileName(String(configFile) || "default");
801
- if (!import_fs2.default.existsSync(componentResource(comp).filePath(sanitized))) {
802
- jsonResponse(res, 422, {
803
- error: `Preset references missing config: ${comp}/${sanitized}`
804
- });
805
- return;
806
- }
807
- apply.push([comp, sanitized]);
808
- }
809
- themesResource.setProductionName(themeName);
810
- for (const [comp, configFile] of apply) {
811
- componentResource(comp).setProductionName(configFile);
812
- }
813
- presetsResource.setProductionName(fileName);
814
- syncTokensToCss(themeName);
815
- syncFontsToCss(themeName);
816
- syncComponentsToCss();
817
- jsonResponse(res, 200, {
818
- ok: true,
819
- fileName,
820
- name: preset.name || fileName,
821
- theme: themeName,
822
- componentConfigs: Object.fromEntries(apply),
823
- updatedAt: preset.updatedAt || ""
824
- });
825
- }
826
- async function handlePresetByName({ params, req, res }) {
1289
+ async function handleManifestByName({ params, req, res }) {
827
1290
  const [fileName] = params;
828
- const filePath = presetsResource.filePath(fileName);
1291
+ const filePath = manifestsResource.filePath(fileName);
829
1292
  if (req.method === "GET") {
830
1293
  if (!import_fs2.default.existsSync(filePath)) {
831
1294
  jsonResponse(res, 404, { error: "Not found" });
@@ -838,7 +1301,7 @@ ${COMPONENT_OVERRIDES_END}
838
1301
  }
839
1302
  if (req.method === "PUT") {
840
1303
  if (fileName === "default") {
841
- jsonResponse(res, 403, { error: "Cannot overwrite the default preset" });
1304
+ jsonResponse(res, 403, { error: "Cannot overwrite the default manifest" });
842
1305
  return;
843
1306
  }
844
1307
  const body = JSON.parse(await readBody(req));
@@ -857,35 +1320,35 @@ ${COMPONENT_OVERRIDES_END}
857
1320
  }
858
1321
  if (req.method === "DELETE") {
859
1322
  if (fileName === "default") {
860
- jsonResponse(res, 403, { error: "Cannot delete the default preset" });
1323
+ jsonResponse(res, 403, { error: "Cannot delete the default manifest" });
861
1324
  return;
862
1325
  }
863
1326
  if (import_fs2.default.existsSync(filePath)) {
864
1327
  import_fs2.default.unlinkSync(filePath);
865
- if (presetsResource.getActiveName() === fileName) {
866
- presetsResource.setActiveName("default");
1328
+ if (manifestsResource.getActiveName() === fileName) {
1329
+ manifestsResource.setActiveName("default");
867
1330
  }
868
1331
  }
869
1332
  jsonResponse(res, 200, { ok: true });
870
1333
  return;
871
1334
  }
872
1335
  }
873
- async function handleApplyPreset({ params, res }) {
1336
+ async function handleApplyManifest({ params, res }) {
874
1337
  const [fileName] = params;
875
- const presetPath = presetsResource.filePath(fileName);
876
- if (!import_fs2.default.existsSync(presetPath)) {
877
- jsonResponse(res, 404, { error: "Preset not found" });
1338
+ const manifestPath = manifestsResource.filePath(fileName);
1339
+ if (!import_fs2.default.existsSync(manifestPath)) {
1340
+ jsonResponse(res, 404, { error: "Manifest not found" });
878
1341
  return;
879
1342
  }
880
- const preset = JSON.parse(import_fs2.default.readFileSync(presetPath, "utf-8"));
881
- const themeName = sanitizeFileName(preset.theme || "default");
1343
+ const manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
1344
+ const themeName = sanitizeFileName(manifest.theme || "default");
882
1345
  const themePath = themesResource.filePath(themeName);
883
1346
  if (!import_fs2.default.existsSync(themePath)) {
884
- jsonResponse(res, 422, { error: `Preset references missing theme: ${themeName}` });
1347
+ jsonResponse(res, 422, { error: `Manifest references missing theme: ${themeName}` });
885
1348
  return;
886
1349
  }
887
1350
  const knownComponents = new Set(listComponentNames());
888
- const componentConfigs = preset.componentConfigs ?? {};
1351
+ const componentConfigs = manifest.componentConfigs ?? {};
889
1352
  const resolvedConfigs = {};
890
1353
  const apply = [];
891
1354
  for (const [comp, configFile] of Object.entries(componentConfigs)) {
@@ -895,18 +1358,22 @@ ${COMPONENT_OVERRIDES_END}
895
1358
  const cfgPath = r.filePath(sanitized);
896
1359
  if (!import_fs2.default.existsSync(cfgPath)) {
897
1360
  jsonResponse(res, 422, {
898
- error: `Preset references missing config: ${comp}/${sanitized}`
1361
+ error: `Manifest references missing config: ${comp}/${sanitized}`
899
1362
  });
900
1363
  return;
901
1364
  }
902
1365
  apply.push([comp, sanitized]);
903
1366
  }
904
1367
  themesResource.setActiveName(themeName);
905
- const themeData = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
1368
+ themesResource.setProductionName(themeName);
1369
+ syncTokensToCss(themeName);
1370
+ syncFontsToCss(themeName);
1371
+ const themeData = normalizeTheme(JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8")));
906
1372
  themeData._fileName = themeName;
907
1373
  for (const [comp, configFile] of apply) {
908
1374
  const r = componentResource(comp);
909
1375
  r.setActiveName(configFile);
1376
+ r.setProductionName(configFile);
910
1377
  const cfg = readComponentConfig(comp, configFile);
911
1378
  if (cfg) resolvedConfigs[comp] = { ...cfg, _fileName: configFile };
912
1379
  }
@@ -916,14 +1383,166 @@ ${COMPONENT_OVERRIDES_END}
916
1383
  const cfg = readComponentConfig(comp, activeName);
917
1384
  if (cfg) resolvedConfigs[comp] = { ...cfg, _fileName: activeName };
918
1385
  }
919
- presetsResource.setActiveName(fileName);
1386
+ syncComponentsToCss();
1387
+ manifestsResource.setActiveName(fileName);
920
1388
  jsonResponse(res, 200, {
921
1389
  ok: true,
922
- preset: { ...preset, _fileName: fileName },
1390
+ manifest: { ...manifest, _fileName: fileName },
923
1391
  theme: themeData,
924
1392
  componentConfigs: resolvedConfigs
925
1393
  });
926
1394
  }
1395
+ async function handleExportManifest({ params, res }) {
1396
+ const [fileName] = params;
1397
+ const manifestPath = manifestsResource.filePath(fileName);
1398
+ if (!import_fs2.default.existsSync(manifestPath)) {
1399
+ jsonResponse(res, 404, { error: "Manifest not found" });
1400
+ return;
1401
+ }
1402
+ const manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
1403
+ const themeName = sanitizeFileName(manifest.theme || "default");
1404
+ const themePath = themesResource.filePath(themeName);
1405
+ if (!import_fs2.default.existsSync(themePath)) {
1406
+ jsonResponse(res, 422, { error: `Manifest references missing theme: ${themeName}` });
1407
+ return;
1408
+ }
1409
+ const theme = JSON.parse(import_fs2.default.readFileSync(themePath, "utf-8"));
1410
+ const knownComponents = new Set(listComponentNames());
1411
+ const componentConfigs = {};
1412
+ for (const [comp, configFile] of Object.entries(manifest.componentConfigs ?? {})) {
1413
+ if (!knownComponents.has(comp)) continue;
1414
+ const sanitized = sanitizeFileName(String(configFile) || "default");
1415
+ if (sanitized === "default") continue;
1416
+ const cfgPath = componentResource(comp).filePath(sanitized);
1417
+ if (!import_fs2.default.existsSync(cfgPath)) {
1418
+ jsonResponse(res, 422, {
1419
+ error: `Manifest references missing config: ${comp}/${sanitized}`
1420
+ });
1421
+ return;
1422
+ }
1423
+ const cfg = JSON.parse(import_fs2.default.readFileSync(cfgPath, "utf-8"));
1424
+ componentConfigs[`${comp}/${sanitized}`] = cfg;
1425
+ }
1426
+ const bundle = {
1427
+ kind: "manifest-bundle",
1428
+ schemaVersion: 1,
1429
+ liveTokensVersion: PKG_VERSION,
1430
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
1431
+ manifest,
1432
+ theme,
1433
+ componentConfigs
1434
+ };
1435
+ res.statusCode = 200;
1436
+ res.setHeader("Content-Type", "application/json");
1437
+ res.setHeader(
1438
+ "Content-Disposition",
1439
+ `attachment; filename="${fileName}.bundle.json"`
1440
+ );
1441
+ res.end(JSON.stringify(bundle, null, 2));
1442
+ }
1443
+ function nextAvailableName2(resourceFilePath, baseName) {
1444
+ return nextAvailableName(
1445
+ (n) => import_fs2.default.existsSync(resourceFilePath(n)),
1446
+ sanitizeFileName(baseName)
1447
+ );
1448
+ }
1449
+ async function handleImportManifest({ req, res }) {
1450
+ let bundle;
1451
+ try {
1452
+ bundle = JSON.parse(await readBody(req));
1453
+ } catch {
1454
+ jsonResponse(res, 400, { error: "Body is not valid JSON" });
1455
+ return;
1456
+ }
1457
+ if (!bundle || bundle.kind !== "manifest-bundle") {
1458
+ jsonResponse(res, 400, {
1459
+ error: "Not a manifest bundle (kind discriminator missing or wrong)"
1460
+ });
1461
+ return;
1462
+ }
1463
+ if (bundle.schemaVersion !== 1) {
1464
+ jsonResponse(res, 400, {
1465
+ error: `Unsupported bundle schemaVersion: ${bundle.schemaVersion}`
1466
+ });
1467
+ return;
1468
+ }
1469
+ if (!bundle.manifest || !bundle.theme || !bundle.componentConfigs) {
1470
+ jsonResponse(res, 400, { error: "Bundle missing manifest / theme / componentConfigs" });
1471
+ return;
1472
+ }
1473
+ const renames = {};
1474
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1475
+ const originalThemeName = sanitizeFileName(bundle.manifest.theme || "default");
1476
+ const finalThemeName = nextAvailableName2(
1477
+ (n) => themesResource.filePath(n),
1478
+ originalThemeName
1479
+ );
1480
+ if (finalThemeName !== originalThemeName) {
1481
+ renames[`theme:${originalThemeName}`] = finalThemeName;
1482
+ }
1483
+ const themeBody = { ...bundle.theme };
1484
+ themeBody.updatedAt = now;
1485
+ if (!themeBody.createdAt) themeBody.createdAt = now;
1486
+ themesResource.ensureDir();
1487
+ import_fs2.default.writeFileSync(themesResource.filePath(finalThemeName), JSON.stringify(themeBody, null, 2));
1488
+ const knownComponents = new Set(listComponentNames());
1489
+ const componentRenames = {};
1490
+ for (const [key, cfgValue] of Object.entries(bundle.componentConfigs)) {
1491
+ const [comp, originalName] = key.split("/");
1492
+ if (!comp || !originalName) continue;
1493
+ if (!knownComponents.has(comp)) continue;
1494
+ const r = componentResource(comp);
1495
+ const finalName = nextAvailableName2(
1496
+ (n) => r.filePath(n),
1497
+ originalName
1498
+ );
1499
+ if (finalName !== originalName) {
1500
+ renames[`componentConfig:${comp}/${originalName}`] = finalName;
1501
+ }
1502
+ componentRenames[`${comp}/${originalName}`] = finalName;
1503
+ const cfgBody = { ...cfgValue };
1504
+ cfgBody.component = comp;
1505
+ cfgBody.name = finalName;
1506
+ cfgBody.updatedAt = now;
1507
+ if (!cfgBody.createdAt) cfgBody.createdAt = now;
1508
+ r.ensureDir();
1509
+ import_fs2.default.writeFileSync(r.filePath(finalName), JSON.stringify(cfgBody, null, 2));
1510
+ }
1511
+ const rewrittenManifest = {
1512
+ ...bundle.manifest,
1513
+ theme: finalThemeName,
1514
+ componentConfigs: {}
1515
+ };
1516
+ for (const [comp, configName] of Object.entries(bundle.manifest.componentConfigs ?? {})) {
1517
+ const original = String(configName);
1518
+ if (original === "default") {
1519
+ rewrittenManifest.componentConfigs[comp] = "default";
1520
+ continue;
1521
+ }
1522
+ const finalName = componentRenames[`${comp}/${original}`] ?? original;
1523
+ rewrittenManifest.componentConfigs[comp] = finalName;
1524
+ }
1525
+ rewrittenManifest.updatedAt = now;
1526
+ if (!rewrittenManifest.createdAt) rewrittenManifest.createdAt = now;
1527
+ const originalManifestName = sanitizeFileName(bundle.manifest.name || "imported");
1528
+ const finalManifestName = nextAvailableName2(
1529
+ (n) => manifestsResource.filePath(n),
1530
+ originalManifestName
1531
+ );
1532
+ if (finalManifestName !== originalManifestName) {
1533
+ renames[`manifest:${originalManifestName}`] = finalManifestName;
1534
+ }
1535
+ manifestsResource.ensureDir();
1536
+ import_fs2.default.writeFileSync(
1537
+ manifestsResource.filePath(finalManifestName),
1538
+ JSON.stringify(rewrittenManifest, null, 2)
1539
+ );
1540
+ jsonResponse(res, 200, {
1541
+ ok: true,
1542
+ manifest: finalManifestName,
1543
+ renames
1544
+ });
1545
+ }
927
1546
  function methodNotAllowed({ res }) {
928
1547
  jsonResponse(res, 405, { error: "Method not allowed" });
929
1548
  }
@@ -957,21 +1576,29 @@ ${COMPONENT_OVERRIDES_END}
957
1576
  { method: "GET", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
958
1577
  { method: "PUT", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
959
1578
  { method: "DELETE", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
960
- // Presets — list / active / production are exact strings, must run before regexes
961
- { method: "GET", pattern: PRESETS_ROUTE, handler: handleListPresets },
962
- { method: "GET", pattern: PRESETS_ACTIVE_ROUTE, handler: handleGetActivePreset },
963
- { method: "PUT", pattern: PRESETS_ACTIVE_ROUTE, handler: handleSetActivePreset },
964
- { method: "GET", pattern: PRESETS_PRODUCTION_ROUTE, handler: handleGetProductionPreset },
965
- { method: "PUT", pattern: PRESETS_PRODUCTION_ROUTE, handler: handleSetProductionPreset },
966
- // Presets :name/apply (more specific than :name)
967
- { method: "PUT", pattern: PRESET_APPLY_REGEX, handler: handleApplyPreset },
968
- { method: "POST", pattern: PRESET_APPLY_REGEX, handler: methodNotAllowed },
969
- { method: "GET", pattern: PRESET_APPLY_REGEX, handler: methodNotAllowed },
970
- { method: "DELETE", pattern: PRESET_APPLY_REGEX, handler: methodNotAllowed },
971
- // Presets :name CRUD (broadest preset route, runs last)
972
- { method: "GET", pattern: PRESET_BY_NAME_REGEX, handler: handlePresetByName },
973
- { method: "PUT", pattern: PRESET_BY_NAME_REGEX, handler: handlePresetByName },
974
- { method: "DELETE", pattern: PRESET_BY_NAME_REGEX, handler: handlePresetByName }
1579
+ // Manifests — list / active are exact strings, must run before regexes
1580
+ { method: "GET", pattern: MANIFESTS_ROUTE, handler: handleListManifests },
1581
+ { method: "GET", pattern: MANIFESTS_ACTIVE_ROUTE, handler: handleGetActiveManifest },
1582
+ { method: "PUT", pattern: MANIFESTS_ACTIVE_ROUTE, handler: handleSetActiveManifest },
1583
+ // Manifests exact import route runs before :name regexes
1584
+ { method: "POST", pattern: MANIFEST_IMPORT_ROUTE, handler: handleImportManifest },
1585
+ { method: "PUT", pattern: MANIFEST_IMPORT_ROUTE, handler: methodNotAllowed },
1586
+ { method: "GET", pattern: MANIFEST_IMPORT_ROUTE, handler: methodNotAllowed },
1587
+ { method: "DELETE", pattern: MANIFEST_IMPORT_ROUTE, handler: methodNotAllowed },
1588
+ // Manifests :name/apply (more specific than :name)
1589
+ { method: "PUT", pattern: MANIFEST_APPLY_REGEX, handler: handleApplyManifest },
1590
+ { method: "POST", pattern: MANIFEST_APPLY_REGEX, handler: methodNotAllowed },
1591
+ { method: "GET", pattern: MANIFEST_APPLY_REGEX, handler: methodNotAllowed },
1592
+ { method: "DELETE", pattern: MANIFEST_APPLY_REGEX, handler: methodNotAllowed },
1593
+ // Manifests :name/export (more specific than :name)
1594
+ { method: "GET", pattern: MANIFEST_EXPORT_REGEX, handler: handleExportManifest },
1595
+ { method: "PUT", pattern: MANIFEST_EXPORT_REGEX, handler: methodNotAllowed },
1596
+ { method: "POST", pattern: MANIFEST_EXPORT_REGEX, handler: methodNotAllowed },
1597
+ { method: "DELETE", pattern: MANIFEST_EXPORT_REGEX, handler: methodNotAllowed },
1598
+ // Manifests — :name CRUD (broadest manifest route, runs last)
1599
+ { method: "GET", pattern: MANIFEST_BY_NAME_REGEX, handler: handleManifestByName },
1600
+ { method: "PUT", pattern: MANIFEST_BY_NAME_REGEX, handler: handleManifestByName },
1601
+ { method: "DELETE", pattern: MANIFEST_BY_NAME_REGEX, handler: handleManifestByName }
975
1602
  ];
976
1603
  return {
977
1604
  name: "theme-file-api",
@@ -986,7 +1613,8 @@ ${COMPONENT_OVERRIDES_END}
986
1613
  configureServer(server) {
987
1614
  ensureThemesDir();
988
1615
  ensureComponentConfigsDir();
989
- ensurePresetsDir();
1616
+ ensureManifestsDir();
1617
+ regenerateTokensCss();
990
1618
  server.middlewares.use(async (req, res, next) => {
991
1619
  const handled = await dispatch(req, res, routes);
992
1620
  if (!handled) next();