@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
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Source adapter for the visual gradient editor.
3
+ *
4
+ * `GradientEditor.svelte` was originally hard-wired to the theme-level
5
+ * gradient library (`state.gradients.tokens`). Components that own their
6
+ * own structured gradients (e.g. SectionDivider's per-variant slot) live
7
+ * in `state.components[...].aliases[...]` as a `kind: 'gradient'` ref.
8
+ *
9
+ * Wrapping both behind one source interface lets the editor read + write
10
+ * either backing store without leaking knowledge of which it's editing.
11
+ * The store-derived `current` field reactively exposes the current value;
12
+ * the mutator methods funnel each change through `mutate`, so undo /
13
+ * persistence still cover the edit identically to direct API calls.
14
+ */
15
+ import { derived, get, type Readable } from 'svelte/store';
16
+ import { editorState } from './editorStore';
17
+ import type { GradientType, GradientTokenStop, GradientAliasValue, CssVarRef } from './editorTypes';
18
+ import {
19
+ setGradient,
20
+ setGradientType,
21
+ setGradientAngle,
22
+ setGradientCenterX,
23
+ setGradientAspect,
24
+ setGradientStop,
25
+ addGradientStop,
26
+ removeGradientStop,
27
+ } from '../themes/slices/gradients';
28
+ import { setComponentAlias } from '../themes/slices/components';
29
+ import { mutate } from './editorCore';
30
+
31
+ export interface GradientSourceSnapshot {
32
+ type: GradientType;
33
+ angle: number;
34
+ /** Horizontal center for radial gradients, 0–100. */
35
+ centerX?: number;
36
+ /** Per-axis stretch factors for the radial ellipse (1 = unscaled). */
37
+ aspectX?: number;
38
+ aspectY?: number;
39
+ stops: GradientTokenStop[];
40
+ }
41
+
42
+ export interface GradientSource {
43
+ /** Reactive readable of the current gradient value (or undefined if missing). */
44
+ current: Readable<GradientSourceSnapshot | undefined>;
45
+ setAll(next: GradientSourceSnapshot): void;
46
+ setType(type: GradientType): void;
47
+ setAngle(angle: number): void;
48
+ setCenterX(centerX: number): void;
49
+ setAspect(aspect: { x: number; y: number }): void;
50
+ setStop(index: number, partial: Partial<GradientTokenStop>): void;
51
+ addStop(stop: GradientTokenStop): void;
52
+ removeStop(index: number): void;
53
+ }
54
+
55
+ /** Adapter for `--gradient-N` library tokens in `state.gradients.tokens`. */
56
+ export function themeGradientSource(variable: string): GradientSource {
57
+ const current = derived(editorState, ($s) => {
58
+ const t = $s.gradients.tokens.find((g) => g.variable === variable);
59
+ if (!t) return undefined;
60
+ return {
61
+ type: t.type,
62
+ angle: t.angle,
63
+ centerX: t.centerX,
64
+ aspectX: t.aspectX,
65
+ aspectY: t.aspectY,
66
+ stops: t.stops.map((s) => ({ ...s })),
67
+ };
68
+ });
69
+ return {
70
+ current,
71
+ setAll: (next) => setGradient(variable, next),
72
+ setType: (t) => setGradientType(variable, t),
73
+ setAngle: (a) => setGradientAngle(variable, a),
74
+ setCenterX: (x) => setGradientCenterX(variable, x),
75
+ setAspect: (a) => setGradientAspect(variable, a),
76
+ setStop: (i, p) => setGradientStop(variable, i, p),
77
+ addStop: (s) => addGradientStop(variable, s),
78
+ removeStop: (i) => removeGradientStop(variable, i),
79
+ };
80
+ }
81
+
82
+ function readComponentGradient(component: string, varName: string): GradientAliasValue | undefined {
83
+ const ref = get(editorState).components[component]?.aliases[varName];
84
+ return ref?.kind === 'gradient' ? ref.value : undefined;
85
+ }
86
+
87
+ function writeComponentGradient(component: string, varName: string, value: GradientAliasValue): void {
88
+ const ref: CssVarRef = { kind: 'gradient', value };
89
+ setComponentAlias(component, varName, ref);
90
+ }
91
+
92
+ /** Adapter for component-owned gradients stored inline on a component alias. */
93
+ export function componentGradientSource(component: string, varName: string): GradientSource {
94
+ const current = derived(editorState, ($s) => {
95
+ const ref = $s.components[component]?.aliases[varName];
96
+ if (ref?.kind !== 'gradient') return undefined;
97
+ return {
98
+ type: ref.value.type,
99
+ angle: ref.value.angle,
100
+ centerX: ref.value.centerX,
101
+ aspectX: ref.value.aspectX,
102
+ aspectY: ref.value.aspectY,
103
+ stops: ref.value.stops.map((s) => ({ ...s })),
104
+ };
105
+ });
106
+ /** Read-modify-write through `mutate` so each edit is one history entry
107
+ * and the renderer + persistence cycle pick it up uniformly with the
108
+ * theme-gradient path. */
109
+ const update = (label: string, mutator: (g: GradientAliasValue) => void): void => {
110
+ mutate(label, (s) => {
111
+ const slice = s.components[component] ?? (s.components[component] = { activeFile: 'default', aliases: {}, config: {} });
112
+ const ref = slice.aliases[varName];
113
+ const base: GradientAliasValue =
114
+ ref?.kind === 'gradient'
115
+ ? {
116
+ type: ref.value.type,
117
+ angle: ref.value.angle,
118
+ ...(ref.value.centerX !== undefined ? { centerX: ref.value.centerX } : {}),
119
+ ...(ref.value.aspectX !== undefined ? { aspectX: ref.value.aspectX } : {}),
120
+ ...(ref.value.aspectY !== undefined ? { aspectY: ref.value.aspectY } : {}),
121
+ stops: ref.value.stops.map((st) => ({ ...st })),
122
+ }
123
+ : { type: 'linear', angle: 135, stops: [] };
124
+ mutator(base);
125
+ slice.aliases[varName] = { kind: 'gradient', value: base };
126
+ });
127
+ };
128
+ return {
129
+ current,
130
+ setAll: (next) => writeComponentGradient(component, varName, {
131
+ type: next.type,
132
+ angle: next.angle,
133
+ ...(next.centerX !== undefined ? { centerX: next.centerX } : {}),
134
+ ...(next.aspectX !== undefined ? { aspectX: next.aspectX } : {}),
135
+ ...(next.aspectY !== undefined ? { aspectY: next.aspectY } : {}),
136
+ stops: next.stops.map((s) => ({ ...s })),
137
+ }),
138
+ setType: (t) => update(`set gradient type ${varName}`, (g) => { g.type = t; }),
139
+ setAngle: (a) => update(`set gradient angle ${varName}`, (g) => { g.angle = a; }),
140
+ setCenterX: (x) => update(`set gradient center ${varName}`, (g) => { g.centerX = x; }),
141
+ setAspect: (a) => update(`set gradient aspect ${varName}`, (g) => {
142
+ // Drop axes that equal 1 so persisted JSON stays minimal and pre-aspect
143
+ // data round-trips unchanged.
144
+ if (a.x === 1) delete g.aspectX;
145
+ else g.aspectX = a.x;
146
+ if (a.y === 1) delete g.aspectY;
147
+ else g.aspectY = a.y;
148
+ }),
149
+ setStop: (i, p) => update(`set gradient stop ${varName}[${i}]`, (g) => {
150
+ const stop = g.stops[i];
151
+ if (!stop) return;
152
+ if (p.position !== undefined) stop.position = p.position;
153
+ if (p.color !== undefined) stop.color = p.color;
154
+ if (p.opacity !== undefined) stop.opacity = p.opacity;
155
+ // `monochrome: true` is the implicit default — drop the field on write
156
+ // so the persisted shape stays minimal. Only the explicit override
157
+ // (`false`) needs to live on the stop.
158
+ if (p.monochrome !== undefined) {
159
+ if (p.monochrome === false) stop.monochrome = false;
160
+ else delete stop.monochrome;
161
+ }
162
+ }),
163
+ addStop: (s) => update(`add gradient stop ${varName}`, (g) => {
164
+ g.stops.push({ ...s });
165
+ g.stops.sort((a, b) => a.position - b.position);
166
+ }),
167
+ removeStop: (i) => update(`remove gradient stop ${varName}[${i}]`, (g) => {
168
+ if (g.stops.length <= 2) return;
169
+ g.stops.splice(i, 1);
170
+ }),
171
+ };
172
+ }
173
+
174
+ /** Snapshot helper for save/cancel flows. */
175
+ export function snapshotGradient(source: GradientSource): GradientSourceSnapshot | null {
176
+ const cur = get(source.current);
177
+ if (!cur) return null;
178
+ return {
179
+ type: cur.type,
180
+ angle: cur.angle,
181
+ ...(cur.centerX !== undefined ? { centerX: cur.centerX } : {}),
182
+ ...(cur.aspectX !== undefined ? { aspectX: cur.aspectX } : {}),
183
+ ...(cur.aspectY !== undefined ? { aspectY: cur.aspectY } : {}),
184
+ stops: cur.stops.map((s) => ({ ...s })),
185
+ };
186
+ }
187
+
188
+ /** Read the component-side current gradient value used to seed UI defaults
189
+ * outside the GradientSource flow (e.g. the Monochrome checkbox derivation). */
190
+ export function readComponentGradientSync(component: string, varName: string): GradientAliasValue | undefined {
191
+ return readComponentGradient(component, varName);
192
+ }
@@ -0,0 +1,28 @@
1
+ import type { Migration } from './index';
2
+
3
+ /**
4
+ * 2026-05-19: drop `--collapsiblesection-container-frame-surface`.
5
+ *
6
+ * The frame-surface token only ever painted the outer `.es-root` background.
7
+ * Since `.section-header` covers that area with its own per-state surface, the
8
+ * frame-surface was only visible where the (typically transparent) frame
9
+ * border zone exposed it — a ring around the header. That made the token
10
+ * read as a "border" while being labeled "surface color" in the editor.
11
+ *
12
+ * Frame is now pure chrome: border color, border width, corner radius. The
13
+ * per-state header surface tokens already cover all the legitimate surface
14
+ * needs of the Container variant.
15
+ */
16
+ function dropFrameSurface(rawVars: Record<string, string>, meta: { component?: string }): Record<string, string> {
17
+ if (meta.component !== 'collapsiblesection') return rawVars;
18
+ const { '--collapsiblesection-container-frame-surface': _drop, ...rest } = rawVars;
19
+ return rest;
20
+ }
21
+
22
+ export const componentMigration_2026_05_19_collapsiblesectionDropFrameSurface: Migration = {
23
+ id: '2026-05-19-collapsiblesection-drop-frame-surface',
24
+ fromVersion: 6,
25
+ toVersion: 7,
26
+ appliesTo: 'component-config',
27
+ apply: dropFrameSurface,
28
+ };
@@ -0,0 +1,35 @@
1
+ import type { Migration } from './index';
2
+
3
+ /**
4
+ * Component-config migration (2026-05-19, sectiondivider only):
5
+ * the per-variant gradient was 7 flat tokens (angle + 3× (color + position)).
6
+ * It now lives as a single structured alias at `--sectiondivider-{v}-gradient`,
7
+ * carrying `{type, angle, stops[]}` inline on the component slice.
8
+ *
9
+ * The structured value is synthesized *before* this migration runs — see
10
+ * `synthesizeSectionDividerGradients` in `editorStore.ts`. The synthesizer
11
+ * walks the legacy 7-token shape and emits a `kind: 'gradient'` alias into
12
+ * the load path's object subset, where it survives this stripping pass and
13
+ * lands in the in-memory slice.
14
+ *
15
+ * This migration's only job is to delete the now-redundant flat tokens
16
+ * from the disk-shape string subset so a subsequent save round-trips to
17
+ * the new format.
18
+ */
19
+ const FLAT_RE = /^--sectiondivider-(canvas|neutral|alternate|primary|accent|special)-gradient-(angle|stop-[123]-(color|position))$/;
20
+
21
+ export const componentMigration_2026_05_19_sectiondividerRichGradient: Migration = {
22
+ id: '2026-05-19-sectiondivider-rich-gradient',
23
+ fromVersion: 7,
24
+ toVersion: 8,
25
+ appliesTo: 'component-config',
26
+ apply(rawVars, meta) {
27
+ if (meta.component !== 'sectiondivider') return { ...rawVars };
28
+ const out: Record<string, string> = {};
29
+ for (const [key, value] of Object.entries(rawVars)) {
30
+ if (FLAT_RE.test(key)) continue;
31
+ out[key] = value;
32
+ }
33
+ return out;
34
+ },
35
+ };
@@ -0,0 +1,82 @@
1
+ import type { Migration } from './index';
2
+
3
+ /**
4
+ * Component-config migration (2026-05-20, sectiondivider only):
5
+ * the variant axis was reshaped from six color families →
6
+ * three size presets (lg/md/sm). Each new variant owns its full token set
7
+ * (typography, geometry, AND colors / background) so the editor can author
8
+ * full visual presets per-variant without a separate color axis.
9
+ *
10
+ * Migration strategy: take **canvas** as the canonical family from the v8
11
+ * file and seed all three new variants from its values. Customisations on
12
+ * other v8 families (neutral, accent, primary, etc.) are dropped — the user
13
+ * customises each new variant from the shared canvas baseline.
14
+ *
15
+ * Two inline suffix renames also land here:
16
+ * `*-title-border-width` → `*-title-outline-width`
17
+ * `*-title-stroke-color` → `*-title-outline-color`
18
+ * `*-padding` → `*-spacing`
19
+ *
20
+ * The structured background payload moves through a companion rename in
21
+ * `migrateComponentAliases` (editorStore.ts), since the runner only sees
22
+ * string keys.
23
+ */
24
+ const FAMILIES = ['canvas', 'neutral', 'alternate', 'primary', 'accent', 'special'] as const;
25
+ type Family = typeof FAMILIES[number];
26
+ const VARIANTS = ['lg', 'md', 'sm'] as const;
27
+
28
+ const SUFFIX_RENAMES: Array<[RegExp, string]> = [
29
+ [/-title-border-width$/, '-title-outline-width'],
30
+ [/-title-stroke-color$/, '-title-outline-color'],
31
+ [/-padding$/, '-spacing'],
32
+ ];
33
+
34
+ function renameKeySuffix(key: string): string {
35
+ for (const [re, repl] of SUFFIX_RENAMES) {
36
+ if (re.test(key)) return key.replace(re, repl);
37
+ }
38
+ return key;
39
+ }
40
+
41
+ function splitFamilyKey(key: string): { family: Family; suffix: string } | null {
42
+ const m = key.match(/^--sectiondivider-(canvas|neutral|alternate|primary|accent|special)-(.+)$/);
43
+ if (!m) return null;
44
+ return { family: m[1] as Family, suffix: m[2] };
45
+ }
46
+
47
+ export const componentMigration_2026_05_20_sectiondividerSlimVariants: Migration = {
48
+ id: '2026-05-20-sectiondivider-slim-variants',
49
+ fromVersion: 8,
50
+ toVersion: 9,
51
+ appliesTo: 'component-config',
52
+ apply(rawVars, meta) {
53
+ if (meta.component !== 'sectiondivider') return { ...rawVars };
54
+ const out: Record<string, string> = {};
55
+
56
+ // Collect canvas's suffix → value mapping first; everything else gets
57
+ // pruned. (Canvas wins; other families' customisations are discarded
58
+ // because the new variants are size/preset-shaped, not color-shaped.)
59
+ const canvasValues: Record<string, string> = {};
60
+
61
+ for (const [rawKey, value] of Object.entries(rawVars)) {
62
+ const key = renameKeySuffix(rawKey);
63
+ const parts = splitFamilyKey(key);
64
+ if (!parts) {
65
+ // Non-family-prefixed key — pass through verbatim.
66
+ out[key] = value;
67
+ continue;
68
+ }
69
+ if (parts.family !== 'canvas') continue; // Drop non-canvas tokens
70
+ canvasValues[parts.suffix] = value;
71
+ }
72
+
73
+ // Seed all three size variants from canvas's values.
74
+ for (const suffix of Object.keys(canvasValues)) {
75
+ for (const v of VARIANTS) {
76
+ out[`--sectiondivider-${v}-${suffix}`] = canvasValues[suffix];
77
+ }
78
+ }
79
+
80
+ return out;
81
+ },
82
+ };
@@ -0,0 +1,24 @@
1
+ import type { Migration } from './index';
2
+
3
+ /**
4
+ * Component-config migration (2026-05-21, sectiondivider only):
5
+ * the container `*-spacing` token is renamed back to `*-padding` so it can
6
+ * use the editor's splittable padding selector (per-side control). The new
7
+ * editor also adds per-element padding tokens (title / description / eyebrow),
8
+ * but those have no v9 predecessor — they simply start unset.
9
+ */
10
+ export const componentMigration_2026_05_21_sectiondividerSpacingToPadding: Migration = {
11
+ id: '2026-05-21-sectiondivider-spacing-to-padding',
12
+ fromVersion: 9,
13
+ toVersion: 10,
14
+ appliesTo: 'component-config',
15
+ apply(rawVars, meta) {
16
+ if (meta.component !== 'sectiondivider') return { ...rawVars };
17
+ const out: Record<string, string> = {};
18
+ for (const [key, value] of Object.entries(rawVars)) {
19
+ const renamed = key.replace(/-spacing$/, '-padding');
20
+ out[renamed] = value;
21
+ }
22
+ return out;
23
+ },
24
+ };
@@ -0,0 +1,81 @@
1
+ import type { Migration } from './index';
2
+
3
+ /**
4
+ * Component-config migration (2026-05-22, sectiondivider only):
5
+ * the per-variant *intrinsics* (align, hairline visibility + position, eyebrow
6
+ * visibility + uppercase, description visibility) move from the `config`
7
+ * bucket — where the editor preview consumed them as runtime props — into
8
+ * the `aliases` bucket as cascading CSS variables. Live consumers can then
9
+ * read them off `:root` via `var(...)`, closing the editor-to-consumer sync
10
+ * gap the old config-bucket encoding had.
11
+ *
12
+ * Mapping per variant `v in (lg, md, sm)`:
13
+ * --sectiondivider-{v}-show-eyebrow ('1' | '') → --sectiondivider-{v}-eyebrow-display ('block' | 'none')
14
+ * --sectiondivider-{v}-show-description ('1' | '') → --sectiondivider-{v}-description-display ('flex' | 'none')
15
+ * --sectiondivider-{v}-show-hairline + -hairline (toggle + pos) → --sectiondivider-{v}-hairline ('none' | <position>)
16
+ * --sectiondivider-{v}-eyebrow-uppercase ('1' | '') → --sectiondivider-{v}-eyebrow-text-transform ('uppercase' | 'none')
17
+ * --sectiondivider-{v}-align → unchanged (value space is already CSS-keyword: 'start' | 'center')
18
+ * --sectiondivider-{v}-color-family → unchanged (still a config-bucket entry; drives editor's family swap)
19
+ *
20
+ * The hairline merge collapses two old keys (a boolean toggle + a position
21
+ * enum) into one. Rules (matching the legacy `getShowHairline` reader):
22
+ * - `show-hairline` explicitly '' → 'none'
23
+ * - `show-hairline` explicitly '1' → existing position, or 'above-label' fallback
24
+ * - both undefined → emit nothing (default 'none' applies via CSS)
25
+ * - `show-hairline` unset but a real position exists → preserve the position
26
+ *
27
+ * The migration touches the unified bag produced by `migrateComponentAliases`
28
+ * (which now pre-merges string-valued config entries). The post-migration
29
+ * `splitAliasesAndConfig` step routes the new keys to `aliases` (since
30
+ * `KNOWN_COMPONENT_CONFIG_KEYS` no longer contains them) while keeping
31
+ * `--sectiondivider-{v}-color-family` in `config`.
32
+ */
33
+ export const componentMigration_2026_05_22_sectiondividerIntrinsicsToCss: Migration = {
34
+ id: '2026-05-22-sectiondivider-intrinsics-to-css',
35
+ fromVersion: 10,
36
+ toVersion: 11,
37
+ appliesTo: 'component-config',
38
+ apply(rawVars, meta) {
39
+ if (meta.component !== 'sectiondivider') return { ...rawVars };
40
+ const VARIANTS = ['lg', 'md', 'sm'] as const;
41
+ const prefix = '--sectiondivider-';
42
+ const dropped = new Set<string>();
43
+ for (const v of VARIANTS) {
44
+ dropped.add(`${prefix}${v}-show-eyebrow`);
45
+ dropped.add(`${prefix}${v}-show-description`);
46
+ dropped.add(`${prefix}${v}-show-hairline`);
47
+ dropped.add(`${prefix}${v}-hairline`);
48
+ dropped.add(`${prefix}${v}-eyebrow-uppercase`);
49
+ }
50
+ const out: Record<string, string> = {};
51
+ for (const [key, value] of Object.entries(rawVars)) {
52
+ if (dropped.has(key)) continue;
53
+ out[key] = value;
54
+ }
55
+ for (const v of VARIANTS) {
56
+ const showEyebrow = rawVars[`${prefix}${v}-show-eyebrow`];
57
+ if (showEyebrow !== undefined) {
58
+ out[`${prefix}${v}-eyebrow-display`] = showEyebrow === '1' ? 'block' : 'none';
59
+ }
60
+ const showDesc = rawVars[`${prefix}${v}-show-description`];
61
+ if (showDesc !== undefined) {
62
+ out[`${prefix}${v}-description-display`] = showDesc === '1' ? 'flex' : 'none';
63
+ }
64
+ const showHair = rawVars[`${prefix}${v}-show-hairline`];
65
+ const oldHair = rawVars[`${prefix}${v}-hairline`];
66
+ const oldHairIsPos = typeof oldHair === 'string' && oldHair !== '' && oldHair !== 'off';
67
+ let newHair: string | undefined;
68
+ if (showHair === '') newHair = 'none';
69
+ else if (showHair === '1') newHair = oldHairIsPos ? oldHair : 'above-label';
70
+ else if (oldHair !== undefined) newHair = oldHairIsPos ? oldHair : 'none';
71
+ if (newHair !== undefined) {
72
+ out[`${prefix}${v}-hairline`] = newHair;
73
+ }
74
+ const upper = rawVars[`${prefix}${v}-eyebrow-uppercase`];
75
+ if (upper !== undefined) {
76
+ out[`${prefix}${v}-eyebrow-text-transform`] = upper === '1' ? 'uppercase' : 'none';
77
+ }
78
+ }
79
+ return out;
80
+ },
81
+ };
@@ -40,6 +40,11 @@ import {
40
40
  themeMigration_2026_05_13_primaryToBrand,
41
41
  componentMigration_2026_05_13_primaryToBrand,
42
42
  } from './2026-05-13-primary-to-brand';
43
+ import { componentMigration_2026_05_19_collapsiblesectionDropFrameSurface } from './2026-05-19-collapsiblesection-drop-frame-surface';
44
+ import { componentMigration_2026_05_19_sectiondividerRichGradient } from './2026-05-19-sectiondivider-rich-gradient';
45
+ import { componentMigration_2026_05_20_sectiondividerSlimVariants } from './2026-05-20-sectiondivider-slim-variants';
46
+ import { componentMigration_2026_05_21_sectiondividerSpacingToPadding } from './2026-05-21-sectiondivider-spacing-to-padding';
47
+ import { componentMigration_2026_05_22_sectiondividerIntrinsicsToCss } from './2026-05-22-sectiondivider-intrinsics-to-css';
43
48
 
44
49
  /**
45
50
  * Registered migrations. Order in this array does not matter — the runner
@@ -54,6 +59,11 @@ export const MIGRATIONS: Migration[] = [
54
59
  componentMigration_2026_05_08_collapsiblesectionFrameAndCleanup,
55
60
  componentMigration_2026_05_10_sectiondividerGradientStops,
56
61
  componentMigration_2026_05_13_primaryToBrand,
62
+ componentMigration_2026_05_19_collapsiblesectionDropFrameSurface,
63
+ componentMigration_2026_05_19_sectiondividerRichGradient,
64
+ componentMigration_2026_05_20_sectiondividerSlimVariants,
65
+ componentMigration_2026_05_21_sectiondividerSpacingToPadding,
66
+ componentMigration_2026_05_22_sectiondividerIntrinsicsToCss,
57
67
  ];
58
68
 
59
69
  function countFor(kind: 'theme' | 'component-config'): number {
@@ -4,7 +4,7 @@
4
4
  * the default we leave tokens.css in charge so the `clamp()` in
5
5
  * `--columns-gutter` survives until the editor overrides it.
6
6
  */
7
- import type { ColumnsState } from '../editorTypes';
7
+ import type { ColumnsState } from '../../store/editorTypes';
8
8
 
9
9
  export const DEFAULT_COLUMNS: ColumnsState = { count: 12, maxWidth: 1440, gutter: 16, margin: 0 };
10
10
 
@@ -50,7 +50,7 @@ export function parseColumnVars(vars: Record<string, string>): Partial<ColumnsSt
50
50
  * single-source. Mutates `next` and `rawVars` in place.
51
51
  */
52
52
  export function loadColumnsFromVars(
53
- next: import('../editorTypes').EditorState,
53
+ next: import('../../store/editorTypes').EditorState,
54
54
  rawVars: Record<string, string>,
55
55
  ): void {
56
56
  const overrides = parseColumnVars(rawVars);
@@ -30,8 +30,9 @@
30
30
  * pipeline.
31
31
  */
32
32
  import { writable, derived, get, type Readable } from 'svelte/store';
33
- import type { CssVarRef, EditorState } from '../editorTypes';
34
- import { store, mutate } from '../editorCore';
33
+ import type { CssVarRef, EditorState } from '../../store/editorTypes';
34
+ import { store, mutate } from '../../store/editorCore';
35
+ import { formatGradientValue } from './gradients';
35
36
 
36
37
  const EMPTY_COMPONENT_BASELINE = JSON.stringify({ aliases: {}, config: {} });
37
38
 
@@ -43,7 +44,9 @@ export function componentsToVars(components: EditorState['components']): Record<
43
44
  const out: Record<string, string> = {};
44
45
  for (const slice of Object.values(components)) {
45
46
  for (const [varName, ref] of Object.entries(slice.aliases)) {
46
- out[varName] = ref.kind === 'token' ? `var(${ref.name})` : ref.value;
47
+ if (ref.kind === 'token') out[varName] = `var(${ref.name})`;
48
+ else if (ref.kind === 'literal') out[varName] = ref.value;
49
+ else out[varName] = formatGradientValue(ref.value);
47
50
  }
48
51
  }
49
52
  return out;
@@ -239,9 +242,20 @@ export function getComponentPropertySiblings(component: string, varName: string)
239
242
  function cssVarRefEqual(a: CssVarRef | undefined, b: CssVarRef | undefined): boolean {
240
243
  if (!a || !b) return a === b;
241
244
  if (a.kind !== b.kind) return false;
242
- return a.kind === 'token'
243
- ? a.name === (b as { kind: 'token'; name: string }).name
244
- : a.value === (b as { kind: 'literal'; value: string }).value;
245
+ if (a.kind === 'token') return a.name === (b as { kind: 'token'; name: string }).name;
246
+ if (a.kind === 'literal') return a.value === (b as { kind: 'literal'; value: string }).value;
247
+ // gradient: structural compare on type, angle, aspect axes, and stops.
248
+ const av = a.value;
249
+ const bv = (b as { kind: 'gradient'; value: typeof a.value }).value;
250
+ if (av.type !== bv.type || av.angle !== bv.angle || av.stops.length !== bv.stops.length) return false;
251
+ if ((av.aspectX ?? 1) !== (bv.aspectX ?? 1)) return false;
252
+ if ((av.aspectY ?? 1) !== (bv.aspectY ?? 1)) return false;
253
+ for (let i = 0; i < av.stops.length; i++) {
254
+ const sa = av.stops[i];
255
+ const sb = bv.stops[i];
256
+ if (sa.position !== sb.position || sa.color !== sb.color || (sa.opacity ?? 100) !== (sb.opacity ?? 100)) return false;
257
+ }
258
+ return true;
245
259
  }
246
260
 
247
261
  /** True iff `varName` is not individually opted out, has ≥2 declared siblings,
@@ -6,7 +6,7 @@
6
6
  * after mutating.
7
7
  */
8
8
  import type { FontSource, FontStack } from '../themeTypes';
9
- import { store, mutate, persist } from '../editorCore';
9
+ import { store, mutate, persist } from '../../store/editorCore';
10
10
 
11
11
  export function setFontSources(sources: FontSource[]): void {
12
12
  mutate('update font sources', (s) => { s.fonts.sources = sources; });