@motion-proto/live-tokens 0.1.1 → 0.3.2

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 (224) hide show
  1. package/README.md +168 -21
  2. package/dist-plugin/index.cjs +823 -336
  3. package/dist-plugin/index.d.cts +9 -7
  4. package/dist-plugin/index.d.ts +9 -7
  5. package/dist-plugin/index.js +822 -335
  6. package/package.json +46 -20
  7. package/src/assets/newspaper.webp +0 -0
  8. package/src/assets/offering.webp +0 -0
  9. package/src/component-editor/BadgeEditor.svelte +170 -0
  10. package/src/component-editor/CalloutEditor.svelte +103 -0
  11. package/src/component-editor/CardEditor.svelte +184 -0
  12. package/src/component-editor/CollapsibleSectionEditor.svelte +167 -0
  13. package/src/component-editor/CornerBadgeEditor.svelte +207 -0
  14. package/src/component-editor/DialogEditor.svelte +172 -0
  15. package/src/component-editor/ImageEditor.svelte +72 -0
  16. package/src/component-editor/InlineEditActionsEditor.svelte +83 -0
  17. package/src/component-editor/NotificationEditor.svelte +160 -0
  18. package/src/component-editor/ProgressBarEditor.svelte +124 -0
  19. package/src/component-editor/RadioButtonEditor.svelte +140 -0
  20. package/src/component-editor/SectionDividerEditor.svelte +263 -0
  21. package/src/component-editor/SegmentedControlEditor.svelte +154 -0
  22. package/src/component-editor/StandardButtonsEditor.svelte +178 -0
  23. package/src/component-editor/TabBarEditor.svelte +137 -0
  24. package/src/component-editor/TableEditor.svelte +128 -0
  25. package/src/component-editor/TooltipEditor.svelte +122 -0
  26. package/src/component-editor/editorTokens.test.ts +93 -0
  27. package/src/component-editor/groupKeySlots.test.ts +67 -0
  28. package/src/component-editor/groupKeySnapshot.test.ts +52 -0
  29. package/src/component-editor/index.ts +5 -0
  30. package/src/component-editor/registry.ts +246 -0
  31. package/src/component-editor/scaffolding/AngleDial.svelte +185 -0
  32. package/src/component-editor/scaffolding/ComponentEditorBase.svelte +96 -0
  33. package/src/component-editor/scaffolding/ComponentFileManager.svelte +682 -0
  34. package/src/component-editor/scaffolding/ComponentFileMenu.svelte +312 -0
  35. package/src/component-editor/scaffolding/ComponentsTab.svelte +69 -0
  36. package/src/component-editor/scaffolding/CopyFromMenu.svelte +246 -0
  37. package/src/component-editor/scaffolding/DemoHeader.svelte +21 -0
  38. package/src/component-editor/scaffolding/DividerEditor.svelte +81 -0
  39. package/src/component-editor/scaffolding/FieldsetWrapper.svelte +46 -0
  40. package/src/component-editor/scaffolding/GradientCard.svelte +291 -0
  41. package/src/component-editor/scaffolding/LinkageChart.svelte +297 -0
  42. package/src/component-editor/scaffolding/LinkedBlock.svelte +418 -0
  43. package/src/component-editor/scaffolding/NonStylableConfig.svelte +57 -0
  44. package/src/component-editor/scaffolding/SaveAsDialog.svelte +177 -0
  45. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +25 -0
  46. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +56 -0
  47. package/src/component-editor/scaffolding/StateBlock.svelte +115 -0
  48. package/src/component-editor/scaffolding/TokenLayout.svelte +511 -0
  49. package/src/component-editor/scaffolding/TypeEditor.svelte +82 -0
  50. package/src/component-editor/scaffolding/VariantGroup.svelte +277 -0
  51. package/src/component-editor/scaffolding/buildTypeGroupTokens.ts +97 -0
  52. package/src/component-editor/scaffolding/componentSectionType.ts +8 -0
  53. package/src/component-editor/scaffolding/componentSources.ts +9 -0
  54. package/src/component-editor/scaffolding/defaultSections.ts +16 -0
  55. package/src/component-editor/scaffolding/editorContext.ts +44 -0
  56. package/src/component-editor/scaffolding/linkedBlock.ts +226 -0
  57. package/src/component-editor/scaffolding/siblings.ts +33 -0
  58. package/src/component-editor/scaffolding/types.ts +39 -0
  59. package/src/components/Badge.svelte +231 -42
  60. package/src/components/Button.svelte +324 -124
  61. package/src/components/Callout.svelte +145 -0
  62. package/src/components/Card.svelte +123 -25
  63. package/src/components/CollapsibleSection.svelte +213 -35
  64. package/src/components/CornerBadge.svelte +224 -0
  65. package/src/components/Dialog.svelte +137 -114
  66. package/src/components/Image.svelte +43 -0
  67. package/src/components/InlineEditActions.svelte +74 -14
  68. package/src/components/Notification.svelte +184 -163
  69. package/src/components/ProgressBar.svelte +216 -22
  70. package/src/components/RadioButton.svelte +110 -40
  71. package/src/components/SectionDivider.svelte +428 -74
  72. package/src/components/SegmentedControl.svelte +203 -0
  73. package/src/components/TabBar.svelte +146 -21
  74. package/src/components/Table.svelte +102 -0
  75. package/src/components/Tooltip.svelte +45 -19
  76. package/src/components/types.ts +51 -0
  77. package/src/data/google-fonts.json +75 -0
  78. package/src/lib/ColumnsOverlay.svelte +20 -7
  79. package/src/lib/LiveEditorOverlay.svelte +257 -78
  80. package/src/lib/columnsOverlay.ts +21 -17
  81. package/src/lib/componentConfig.test.ts +204 -0
  82. package/src/lib/componentConfigKeys.ts +19 -0
  83. package/src/lib/componentConfigService.ts +88 -0
  84. package/src/lib/copyPopover.ts +30 -0
  85. package/src/lib/cssVarSync.ts +59 -7
  86. package/src/lib/editorConfigStore.ts +0 -10
  87. package/src/lib/editorCore.ts +402 -0
  88. package/src/lib/editorKeybindings.ts +52 -0
  89. package/src/lib/editorPersistence.ts +106 -0
  90. package/src/lib/editorRenderer.ts +74 -0
  91. package/src/lib/editorStore.test.ts +328 -0
  92. package/src/lib/editorStore.ts +412 -0
  93. package/src/lib/editorTypes.ts +100 -0
  94. package/src/lib/editorViewStore.ts +55 -0
  95. package/src/lib/files/versionedFileResource.ts +140 -0
  96. package/src/lib/fontLoader.ts +130 -0
  97. package/src/lib/fontMigration.ts +140 -0
  98. package/src/lib/fontParse.ts +168 -0
  99. package/src/lib/index.ts +48 -30
  100. package/src/lib/lazyConfig.test.ts +54 -0
  101. package/src/lib/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +64 -0
  102. package/src/lib/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +71 -0
  103. package/src/lib/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +43 -0
  104. package/src/lib/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +68 -0
  105. package/src/lib/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +35 -0
  106. package/src/lib/migrations/2026-05-10-sectiondivider-gradient-stops.ts +50 -0
  107. package/src/lib/migrations/2026-05-13-primary-to-brand.ts +90 -0
  108. package/src/lib/migrations/index.ts +93 -0
  109. package/src/lib/migrations/migrations.test.ts +341 -0
  110. package/src/lib/navLinkTypes.ts +1 -0
  111. package/src/lib/overlayState.ts +3 -0
  112. package/src/lib/paletteDerivation.ts +300 -0
  113. package/src/lib/parentRouteStore.ts +42 -0
  114. package/src/lib/parsers/globalRootBlock.ts +32 -0
  115. package/src/lib/presetService.ts +94 -0
  116. package/src/lib/router.ts +42 -10
  117. package/src/lib/scrollSection.ts +45 -0
  118. package/src/lib/slices/columns.ts +59 -0
  119. package/src/lib/slices/components.ts +362 -0
  120. package/src/lib/slices/domainVars.ts +15 -0
  121. package/src/lib/slices/fonts.ts +30 -0
  122. package/src/lib/slices/gradients.ts +153 -0
  123. package/src/lib/slices/overlays.ts +132 -0
  124. package/src/lib/slices/palettes.ts +26 -0
  125. package/src/lib/slices/shadows.ts +123 -0
  126. package/src/lib/storage.ts +88 -0
  127. package/src/lib/themeInit.ts +74 -0
  128. package/src/lib/themeService.ts +101 -0
  129. package/src/lib/themeTypes.ts +146 -0
  130. package/src/lib/tokenRegistry.ts +148 -0
  131. package/src/pages/ComponentEditorPage.svelte +384 -0
  132. package/src/pages/ComponentEditorPage.svelte.d.ts +2 -0
  133. package/src/pages/Editor.svelte +98 -0
  134. package/src/pages/Editor.svelte.d.ts +2 -0
  135. package/src/pages/EditorShell.svelte +348 -0
  136. package/src/styles/_padding.scss +34 -0
  137. package/src/styles/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
  138. package/src/styles/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
  139. package/src/styles/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
  140. package/src/styles/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
  141. package/src/styles/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
  142. package/src/styles/fonts/Manrope/Manrope-latin.woff2 +0 -0
  143. package/src/styles/fonts.css +22 -10
  144. package/src/styles/form-controls.css +14 -16
  145. package/src/styles/tokens.css +1322 -0
  146. package/src/styles/ui-editor.css +126 -0
  147. package/src/{showcase → ui}/BezierCurveEditor.svelte +14 -14
  148. package/src/{showcase → ui}/ColorEditPanel.svelte +42 -36
  149. package/src/ui/EditorViewSwitcher.svelte +180 -0
  150. package/src/ui/FontStackEditor.svelte +360 -0
  151. package/src/ui/GradientEditor.svelte +461 -0
  152. package/src/ui/GradientStopPicker.svelte +74 -0
  153. package/src/ui/PaletteEditor.svelte +1590 -0
  154. package/src/ui/PaletteEditor.test.ts +108 -0
  155. package/src/ui/PresetFileManager.svelte +567 -0
  156. package/src/ui/ProjectFontsSection.svelte +645 -0
  157. package/src/{showcase → ui}/SurfacesTab.svelte +39 -39
  158. package/src/{showcase → ui}/TextTab.svelte +27 -27
  159. package/src/{showcase/TokenFileManager.svelte → ui/ThemeFileManager.svelte} +196 -112
  160. package/src/ui/Toggle.svelte +108 -0
  161. package/src/ui/UICopyPopover.svelte +78 -0
  162. package/src/{showcase/EditorDialog.svelte → ui/UIDialog.svelte} +66 -25
  163. package/src/ui/UIFontFamilySelector.svelte +309 -0
  164. package/src/ui/UIFontSizeSelector.svelte +165 -0
  165. package/src/ui/UIFontWeightSelector.svelte +52 -0
  166. package/src/ui/UILineHeightSelector.svelte +47 -0
  167. package/src/ui/UILinkToggle.svelte +60 -0
  168. package/src/ui/UIOptionItem.svelte +74 -0
  169. package/src/ui/UIOptionList.svelte +27 -0
  170. package/src/ui/UIPaddingSelector.svelte +661 -0
  171. package/src/ui/UIPaletteSelector.svelte +1084 -0
  172. package/src/ui/UIRadio.svelte +72 -0
  173. package/src/ui/UIRadioGroup.svelte +59 -0
  174. package/src/ui/UIRelinkConfirmPopover.svelte +235 -0
  175. package/src/ui/UITokenSelector.svelte +509 -0
  176. package/src/ui/UIVariantSelector.svelte +145 -0
  177. package/src/ui/VariablesTab.svelte +252 -0
  178. package/src/ui/index.ts +31 -0
  179. package/src/ui/keepInViewport.ts +84 -0
  180. package/src/ui/palette/GradientStopEditor.svelte +482 -0
  181. package/src/ui/palette/OverridesPanel.svelte +526 -0
  182. package/src/ui/palette/PaletteBase.svelte +165 -0
  183. package/src/ui/palette/ScaleCurveEditor.svelte +38 -0
  184. package/src/ui/palette/paletteEditorState.ts +89 -0
  185. package/src/ui/sections/ColumnsSection.svelte +273 -0
  186. package/src/ui/sections/GradientsSection.svelte +147 -0
  187. package/src/ui/sections/OverlaysSection.svelte +670 -0
  188. package/src/ui/sections/ShadowsSection.svelte +1250 -0
  189. package/src/ui/sections/TokenScaleTable.svelte +332 -0
  190. package/src/ui/sections/tokenScales.ts +81 -0
  191. package/src/ui/variantScales.ts +108 -0
  192. package/src/components/DetailNav.svelte +0 -78
  193. package/src/components/Toggle.svelte +0 -86
  194. package/src/lib/tokenInit.ts +0 -29
  195. package/src/lib/tokenService.ts +0 -144
  196. package/src/lib/tokenTypes.ts +0 -45
  197. package/src/pages/Admin.svelte +0 -100
  198. package/src/pages/ShowcasePage.svelte +0 -144
  199. package/src/showcase/BackupBrowser.svelte +0 -617
  200. package/src/showcase/ComponentsTab.svelte +0 -105
  201. package/src/showcase/PaletteEditor.svelte +0 -2579
  202. package/src/showcase/PaletteSelector.svelte +0 -627
  203. package/src/showcase/TokenMap.svelte +0 -54
  204. package/src/showcase/VariablesTab.svelte +0 -2655
  205. package/src/showcase/VisualsTab.svelte +0 -231
  206. package/src/showcase/demos/BadgeDemo.svelte +0 -56
  207. package/src/showcase/demos/CardDemo.svelte +0 -50
  208. package/src/showcase/demos/ChoiceButtonsDemo.svelte +0 -192
  209. package/src/showcase/demos/CollapsibleSectionDemo.svelte +0 -54
  210. package/src/showcase/demos/DialogDemo.svelte +0 -42
  211. package/src/showcase/demos/InlineEditActionsDemo.svelte +0 -25
  212. package/src/showcase/demos/NotificationDemo.svelte +0 -147
  213. package/src/showcase/demos/ProgressBarDemo.svelte +0 -54
  214. package/src/showcase/demos/RadioButtonDemo.svelte +0 -56
  215. package/src/showcase/demos/SectionDividerDemo.svelte +0 -77
  216. package/src/showcase/demos/StandardButtonsDemo.svelte +0 -455
  217. package/src/showcase/demos/TabBarDemo.svelte +0 -58
  218. package/src/showcase/demos/TooltipDemo.svelte +0 -52
  219. package/src/showcase/editor.css +0 -93
  220. package/src/showcase/index.ts +0 -17
  221. package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
  222. package/src/styles/fonts/Domine/OFL.txt +0 -97
  223. package/src/styles/fonts/Domine/README.txt +0 -66
  224. /package/src/{showcase → ui}/curveEngine.ts +0 -0
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import BezierCurveEditor from '../BezierCurveEditor.svelte';
3
+ import type { CurveAnchor, CurveConfig } from '../curveEngine';
4
+
5
+ /**
6
+ * Single-channel curve editor — one Bezier curve for one channel
7
+ * (lightness or saturation) of one scale (palette / gray-palette /
8
+ * Surfaces / Borders / Text). Instantiated 6+ times across PaletteEditor:
9
+ * the parent's chromatic-palette + gray-palette curves and the four
10
+ * derived-scale curve groups inside OverridesPanel.
11
+ *
12
+ * Pure presentational wrapper: it forwards props to BezierCurveEditor
13
+ * and adapts the offset key into a single `onOffsetChange(value)` so the
14
+ * parent only needs to know how to wire `(key, value)` once (via a
15
+ * closure passed in).
16
+ */
17
+
18
+ export let curveKey: string;
19
+ export let anchors: CurveAnchor[];
20
+ export let cfg: CurveConfig;
21
+ export let stepCount: number;
22
+ export let defaults: CurveAnchor[];
23
+ export let offset: number = 0;
24
+ export let lockedAnchorIndex: number | null = null;
25
+ export let onAnchorsChange: (anchors: CurveAnchor[]) => void;
26
+ export let onOffsetChange: (key: string, value: number) => void;
27
+ </script>
28
+
29
+ <BezierCurveEditor
30
+ {anchors}
31
+ {cfg}
32
+ {stepCount}
33
+ defaultAnchors={defaults}
34
+ {offset}
35
+ {lockedAnchorIndex}
36
+ {onAnchorsChange}
37
+ onOffsetChange={(v) => onOffsetChange(curveKey, v)}
38
+ />
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Editing-state machine for the PaletteEditor.
3
+ *
4
+ * Replaces five independent `let` decls (`editingKey`, `editingSnapshot`,
5
+ * `editingDraft`, `snapshotTintHue`, `snapshotTintChroma`) with a single
6
+ * discriminated union. The type-checker now enforces field validity per
7
+ * mode — e.g. `snapshotTintHue` only exists when `kind === 'editingBase'`,
8
+ * making it impossible to leak the gray-mode tint snapshot into a
9
+ * step-edit cancel path.
10
+ *
11
+ * Modes:
12
+ * - `idle` — no edit panel open.
13
+ * - `editingBase` — header swatch is active; the user is dragging the
14
+ * base hex (chromatic) or tint hue/chroma (gray). Snapshot is the
15
+ * original hex used to render the swatch; the tint snapshots are only
16
+ * relevant in gray mode and are nullable.
17
+ * - `editingStep` — a non-base swatch is active (palette / gray-step /
18
+ * derived-scale step / override slot). `stepKey` is the dot-keyed
19
+ * identifier; `snapshot` is the value at panel-open; `draft` is the
20
+ * live working colour (drives the swatch preview before commit).
21
+ *
22
+ * Locked-anchor state (`lockedLightnessIdx`, `lockedSaturationIdx`) and
23
+ * the per-toggle injected flags (`injectedLightness`, `injectedSaturation`)
24
+ * remain separate `let` decls in the parent because they are anchor-
25
+ * management state, not edit-session state — they persist across
26
+ * idle/edit transitions and have their own lifecycle tied to
27
+ * `setAnchorToBase()`. Folding them into this union (per the audit's
28
+ * `editingScale` sketch) would conflate two orthogonal axes; the audit's
29
+ * "scale" mode does not correspond to a user-visible state since curve
30
+ * edits don't open a panel.
31
+ */
32
+
33
+ import type { Scope } from '../../lib/editorStore';
34
+
35
+ /** Sentinel key for the base swatch (used as `editingKey === BASE_KEY`). */
36
+ export const BASE_KEY = '__base__';
37
+
38
+ export type EditingState =
39
+ | { kind: 'idle' }
40
+ | {
41
+ kind: 'editingBase';
42
+ snapshotHex: string;
43
+ snapshotTintHue: number | null;
44
+ snapshotTintChroma: number | null;
45
+ }
46
+ | {
47
+ kind: 'editingStep';
48
+ stepKey: string;
49
+ snapshot: string;
50
+ draft: string;
51
+ };
52
+
53
+ export const idleState: EditingState = { kind: 'idle' };
54
+
55
+ export function isIdle(s: EditingState): s is { kind: 'idle' } {
56
+ return s.kind === 'idle';
57
+ }
58
+
59
+ export function isEditingBase(s: EditingState): s is Extract<EditingState, { kind: 'editingBase' }> {
60
+ return s.kind === 'editingBase';
61
+ }
62
+
63
+ export function isEditingStep(s: EditingState): s is Extract<EditingState, { kind: 'editingStep' }> {
64
+ return s.kind === 'editingStep';
65
+ }
66
+
67
+ /**
68
+ * The currently-active edit key, or null when idle. `editingBase` returns
69
+ * the BASE_KEY sentinel so call sites that compare against `editingKey`
70
+ * (e.g. `editingKey === stepKey('Surfaces', 'low')`) keep working.
71
+ */
72
+ export function activeKey(s: EditingState): string | null {
73
+ if (s.kind === 'idle') return null;
74
+ if (s.kind === 'editingBase') return BASE_KEY;
75
+ return s.stepKey;
76
+ }
77
+
78
+ /** The current draft hex (for editingStep) or null. Editingbase draws from baseColor/gray500Hex directly. */
79
+ export function activeDraft(s: EditingState): string | null {
80
+ return s.kind === 'editingStep' ? s.draft : null;
81
+ }
82
+
83
+ /** Aggregate session handle: holds the `Scope` returned by `beginScope` so confirmEdit/cancelEdit can commit/cancel. */
84
+ export interface PaletteEditSession {
85
+ state: EditingState;
86
+ scope: Scope | null;
87
+ }
88
+
89
+ export const idleSession: PaletteEditSession = { state: idleState, scope: null };
@@ -0,0 +1,273 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { editorState, mutate, beginSliderGesture } from '../../lib/editorStore';
4
+ import type { ColumnsState } from '../../lib/editorTypes';
5
+
6
+ function clampNum(v: number, lo: number, hi: number): number {
7
+ return Math.max(lo, Math.min(hi, Math.round(v)));
8
+ }
9
+
10
+ function setColumnsCount(n: number) {
11
+ mutate('set columns count', (s) => { s.columns.count = clampNum(n, 1, 24); });
12
+ }
13
+ function setColumnsMaxWidth(px: number) {
14
+ mutate('set columns max-width', (s) => { s.columns.maxWidth = clampNum(px, 320, 2560); });
15
+ }
16
+ function setColumnsGutter(px: number) {
17
+ mutate('set columns gutter', (s) => { s.columns.gutter = clampNum(px, 0, 200); });
18
+ }
19
+ function setColumnsMargin(px: number) {
20
+ mutate('set columns margin', (s) => { s.columns.margin = clampNum(px, 0, 400); });
21
+ }
22
+
23
+ let initialColumns: ColumnsState | null = null;
24
+
25
+ onMount(() => {
26
+ initialColumns = { ...$editorState.columns };
27
+ });
28
+
29
+ function resetColumns() {
30
+ if (!initialColumns) return;
31
+ const snapshot = initialColumns;
32
+ mutate('reset columns', (s) => { s.columns = { ...snapshot }; });
33
+ }
34
+ </script>
35
+
36
+ <section class="section" id="columns">
37
+ <h2 class="section-title">Columns</h2>
38
+ <p class="columns-intro">
39
+ Layout grid for page content. Toggle the live overlay from the editor bar's
40
+ <i class="fas fa-grip-lines-vertical"></i> button to visualize.
41
+ </p>
42
+
43
+ <div class="columns-controls">
44
+ <div class="global-shadow-row">
45
+ <span class="shadow-slider-label" title="Number of columns">Cols</span>
46
+ <input type="range" min="1" max="24" value={$editorState.columns.count}
47
+ on:pointerdown={() => beginSliderGesture('drag columns count')}
48
+ on:input={(e) => setColumnsCount(+e.currentTarget.value)} />
49
+ <input class="shadow-slider-input" type="number" min="1" max="24"
50
+ value={$editorState.columns.count}
51
+ on:change={(e) => setColumnsCount(+e.currentTarget.value)} />
52
+ <span class="shadow-slider-unit"></span>
53
+ </div>
54
+ <div class="global-shadow-row">
55
+ <span class="shadow-slider-label" title="Maximum content width">Max-Width</span>
56
+ <input type="range" min="480" max="2560" step="10" value={$editorState.columns.maxWidth}
57
+ on:pointerdown={() => beginSliderGesture('drag columns max-width')}
58
+ on:input={(e) => setColumnsMaxWidth(+e.currentTarget.value)} />
59
+ <input class="shadow-slider-input columns-input-wide" type="number" min="320" max="2560"
60
+ value={$editorState.columns.maxWidth}
61
+ on:change={(e) => setColumnsMaxWidth(+e.currentTarget.value)} />
62
+ <span class="shadow-slider-unit">px</span>
63
+ </div>
64
+ <div class="global-shadow-row">
65
+ <span class="shadow-slider-label" title="Space between columns">Gutter</span>
66
+ <input type="range" min="0" max="80" value={$editorState.columns.gutter}
67
+ on:pointerdown={() => beginSliderGesture('drag columns gutter')}
68
+ on:input={(e) => setColumnsGutter(+e.currentTarget.value)} />
69
+ <input class="shadow-slider-input" type="number" min="0" max="200"
70
+ value={$editorState.columns.gutter}
71
+ on:change={(e) => setColumnsGutter(+e.currentTarget.value)} />
72
+ <span class="shadow-slider-unit">px</span>
73
+ </div>
74
+ <div class="global-shadow-row">
75
+ <span class="shadow-slider-label" title="Outer page margin (side gutters)">Margin</span>
76
+ <input type="range" min="0" max="200" value={$editorState.columns.margin}
77
+ on:pointerdown={() => beginSliderGesture('drag columns margin')}
78
+ on:input={(e) => setColumnsMargin(+e.currentTarget.value)} />
79
+ <input class="shadow-slider-input columns-input-wide" type="number" min="0" max="400"
80
+ value={$editorState.columns.margin}
81
+ on:change={(e) => setColumnsMargin(+e.currentTarget.value)} />
82
+ <span class="shadow-slider-unit">px</span>
83
+ </div>
84
+
85
+ <div class="columns-controls-footer">
86
+ <button class="columns-reset" on:click={resetColumns} title="Restore values from when this editor session opened">
87
+ <i class="fas fa-rotate-left"></i>
88
+ Reset to initial
89
+ </button>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="columns-preview">
94
+ <div
95
+ class="columns-preview-inner"
96
+ style="gap: {$editorState.columns.gutter}px; padding-inline: {$editorState.columns.margin}px; grid-template-columns: repeat({$editorState.columns.count}, 1fr);"
97
+ >
98
+ {#each Array($editorState.columns.count) as _, i}
99
+ <div class="columns-preview-col"><span>{i + 1}</span></div>
100
+ {/each}
101
+ </div>
102
+ </div>
103
+ </section>
104
+
105
+ <style>
106
+ .section {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: var(--ui-space-16);
110
+ }
111
+
112
+ .section-title {
113
+ font-size: var(--ui-font-size-lg);
114
+ font-weight: var(--ui-font-weight-semibold);
115
+ color: var(--ui-text-primary);
116
+ margin: 0;
117
+ padding-bottom: var(--ui-space-8);
118
+ border-bottom: 1px solid var(--ui-border-subtle);
119
+ }
120
+
121
+ .columns-intro {
122
+ font-size: var(--ui-font-size-sm);
123
+ color: var(--ui-text-muted);
124
+ margin: 0;
125
+ line-height: var(--ui-line-height-relaxed);
126
+ }
127
+
128
+ .columns-intro i {
129
+ margin-inline: 2px;
130
+ color: var(--ui-text-tertiary);
131
+ }
132
+
133
+ .columns-controls {
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: var(--ui-space-8);
137
+ padding: var(--ui-space-12) var(--ui-space-16);
138
+ background: var(--ui-surface-low);
139
+ border: 1px solid var(--ui-border-faint);
140
+ border-radius: var(--ui-radius-md);
141
+ }
142
+
143
+ .columns-controls .shadow-slider-label {
144
+ width: 5rem;
145
+ text-align: left;
146
+ }
147
+
148
+ .columns-input-wide {
149
+ width: 3.5rem;
150
+ }
151
+
152
+ .columns-controls-footer {
153
+ display: flex;
154
+ justify-content: flex-end;
155
+ padding-top: var(--ui-space-8);
156
+ margin-top: var(--ui-space-4);
157
+ border-top: 1px solid var(--ui-border-faint);
158
+ }
159
+
160
+ .columns-reset {
161
+ display: inline-flex;
162
+ align-items: center;
163
+ gap: var(--ui-space-6);
164
+ padding: var(--ui-space-4) var(--ui-space-10);
165
+ background: transparent;
166
+ border: 1px solid var(--ui-border-subtle);
167
+ border-radius: var(--ui-radius-md);
168
+ color: var(--ui-text-tertiary);
169
+ font-family: inherit;
170
+ font-size: var(--ui-font-size-xs);
171
+ cursor: pointer;
172
+ transition: color var(--ui-transition-fast), border-color var(--ui-transition-fast);
173
+ }
174
+
175
+ .columns-reset:hover {
176
+ color: var(--ui-text-primary);
177
+ border-color: var(--ui-border-medium);
178
+ }
179
+
180
+ .columns-reset i {
181
+ font-size: 10px;
182
+ }
183
+
184
+ .columns-preview {
185
+ background: var(--ui-surface-lowest);
186
+ border: 1px solid var(--ui-border-faint);
187
+ border-radius: var(--ui-radius-md);
188
+ padding: var(--ui-space-12) 0;
189
+ overflow: hidden;
190
+ }
191
+
192
+ .columns-preview-inner {
193
+ display: grid;
194
+ min-height: 64px;
195
+ }
196
+
197
+ .columns-preview-col {
198
+ background: rgba(239, 68, 68, 0.08);
199
+ border-left: 1px dashed rgba(239, 68, 68, 0.3);
200
+ border-right: 1px dashed rgba(239, 68, 68, 0.3);
201
+ display: flex;
202
+ align-items: flex-start;
203
+ justify-content: center;
204
+ min-height: 48px;
205
+ }
206
+
207
+ .columns-preview-col span {
208
+ font-family: var(--ui-font-mono);
209
+ font-size: 9px;
210
+ color: var(--ui-text-muted);
211
+ padding-top: 4px;
212
+ }
213
+
214
+ /* Slider rows shared with VariablesTab — duplicated locally so this section
215
+ is self-contained. Same names used because the hidden inputs/units are
216
+ positioned with the same flex layout as elsewhere in VariablesTab. */
217
+ .global-shadow-row {
218
+ display: flex;
219
+ align-items: center;
220
+ gap: var(--ui-space-8);
221
+ }
222
+
223
+ .global-shadow-row input[type="range"] {
224
+ flex: 1;
225
+ min-width: 4rem;
226
+ accent-color: var(--ui-text-accent);
227
+ height: 4px;
228
+ cursor: pointer;
229
+ }
230
+
231
+ .shadow-slider-label {
232
+ font-size: var(--ui-font-size-xs);
233
+ font-weight: var(--ui-font-weight-semibold);
234
+ color: var(--ui-text-tertiary);
235
+ width: 4rem;
236
+ text-align: right;
237
+ flex-shrink: 0;
238
+ }
239
+
240
+ .shadow-slider-input {
241
+ font-size: var(--ui-font-size-xs);
242
+ color: var(--ui-text-primary);
243
+ font-family: var(--ui-font-mono);
244
+ width: 2.5rem;
245
+ text-align: right;
246
+ flex-shrink: 0;
247
+ background: var(--ui-surface-lowest);
248
+ border: 1px solid var(--ui-border-subtle);
249
+ border-radius: var(--ui-radius-sm);
250
+ padding: var(--ui-space-2) var(--ui-space-4);
251
+ -moz-appearance: textfield;
252
+ appearance: textfield;
253
+ }
254
+
255
+ .shadow-slider-input::-webkit-inner-spin-button,
256
+ .shadow-slider-input::-webkit-outer-spin-button {
257
+ -webkit-appearance: none;
258
+ margin: 0;
259
+ }
260
+
261
+ .shadow-slider-input:focus {
262
+ outline: none;
263
+ border-color: var(--ui-border-medium);
264
+ }
265
+
266
+ .shadow-slider-unit {
267
+ font-size: var(--ui-font-size-xs);
268
+ color: var(--ui-text-muted);
269
+ font-family: var(--ui-font-mono);
270
+ width: 1rem;
271
+ flex-shrink: 0;
272
+ }
273
+ </style>
@@ -0,0 +1,147 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Gradient tokens UI lifted from VariablesTab.
4
+ *
5
+ * Reads token list from $editorState.gradients; each stop references a
6
+ * color token, so palette edits flow through. Heavy lifting (stop drag,
7
+ * direction, kind switch) lives in <GradientEditor>; this section is a
8
+ * thin grid that toggles which gradient is being edited.
9
+ */
10
+ import { createEventDispatcher } from 'svelte';
11
+ import { editorState } from '../../lib/editorStore';
12
+ import GradientEditor from '../GradientEditor.svelte';
13
+
14
+ export let copiedVar: string | null = null;
15
+
16
+ const dispatch = createEventDispatcher<{ copy: string }>();
17
+ function copy(v: string) { dispatch('copy', v); }
18
+
19
+ let editingGradient: string | null = null;
20
+ </script>
21
+
22
+ <section class="section" id="gradients">
23
+ <h2 class="section-title">Gradients</h2>
24
+ <p class="editor-intro">Each stop references a color token, so palette edits flow through. Add or remove stops; switch between linear and radial.</p>
25
+ <div class="gradients-grid">
26
+ {#each $editorState.gradients.tokens as token (token.variable)}
27
+ {@const isEditing = editingGradient === token.variable}
28
+ <div class="gradient-item" class:active={isEditing}>
29
+ {#if !isEditing}
30
+ <div class="gradient-box" style="background: var({token.variable});"></div>
31
+ {/if}
32
+ <div class="token-info">
33
+ <button class="token-variable copyable" class:copied={copiedVar === token.variable} on:click={() => copy(token.variable)}>{copiedVar === token.variable ? 'copied!' : token.variable}</button>
34
+ {#if !isEditing}
35
+ <button class="gradient-edit-btn" on:click={() => editingGradient = token.variable}>Edit</button>
36
+ {/if}
37
+ </div>
38
+ {#if isEditing}
39
+ <div class="gradient-editor-host">
40
+ <GradientEditor
41
+ variable={token.variable}
42
+ on:save={() => editingGradient = null}
43
+ on:cancel={() => editingGradient = null}
44
+ />
45
+ </div>
46
+ {/if}
47
+ </div>
48
+ {/each}
49
+ </div>
50
+ </section>
51
+
52
+ <style>
53
+ .section {
54
+ display: flex;
55
+ flex-direction: column;
56
+ gap: var(--ui-space-16);
57
+ }
58
+
59
+ .section-title {
60
+ font-size: var(--ui-font-size-lg);
61
+ font-weight: var(--ui-font-weight-semibold);
62
+ color: var(--ui-text-primary);
63
+ margin: 0;
64
+ padding-bottom: var(--ui-space-8);
65
+ border-bottom: 1px solid var(--ui-border-subtle);
66
+ }
67
+
68
+ .editor-intro {
69
+ font-size: var(--ui-font-size-md);
70
+ color: var(--ui-text-tertiary);
71
+ margin: 0;
72
+ }
73
+
74
+ .token-variable.copyable {
75
+ all: unset;
76
+ font-size: var(--ui-font-size-md);
77
+ color: var(--ui-text-tertiary);
78
+ font-family: var(--ui-font-mono);
79
+ cursor: pointer;
80
+ transition: color var(--ui-transition-fast);
81
+ }
82
+
83
+ .token-variable.copyable:hover {
84
+ color: var(--ui-text-accent);
85
+ }
86
+
87
+ .token-variable.copyable.copied {
88
+ color: var(--ui-text-success);
89
+ }
90
+
91
+ /* Gradients */
92
+ .gradients-grid {
93
+ display: grid;
94
+ grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
95
+ gap: var(--ui-space-16);
96
+ }
97
+
98
+ .gradient-item {
99
+ display: flex;
100
+ flex-direction: column;
101
+ gap: var(--ui-space-8);
102
+ padding: var(--ui-space-12);
103
+ border: 1px solid var(--ui-border-faint);
104
+ border-radius: var(--ui-radius-lg);
105
+ background: var(--ui-surface-lowest);
106
+ min-width: 0;
107
+ }
108
+
109
+ .gradient-item.active {
110
+ grid-column: 1 / -1;
111
+ border-color: var(--ui-text-primary);
112
+ }
113
+
114
+ .gradient-item .token-info {
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: space-between;
118
+ gap: var(--ui-space-8);
119
+ }
120
+
121
+ .gradient-edit-btn {
122
+ padding: var(--ui-space-2) var(--ui-space-10);
123
+ background: var(--ui-surface-low);
124
+ border: 1px solid var(--ui-border-faint);
125
+ border-radius: var(--ui-radius-sm);
126
+ color: var(--ui-text-secondary);
127
+ font-size: var(--ui-font-size-xs);
128
+ cursor: pointer;
129
+ font-family: inherit;
130
+ }
131
+
132
+ .gradient-edit-btn:hover {
133
+ color: var(--ui-text-primary);
134
+ border-color: var(--ui-border-default);
135
+ }
136
+
137
+ .gradient-editor-host {
138
+ margin-top: var(--ui-space-8);
139
+ padding-top: var(--ui-space-12);
140
+ border-top: 1px dashed var(--ui-border-faint);
141
+ }
142
+
143
+ .gradient-box {
144
+ height: 3rem;
145
+ border-radius: var(--ui-radius-md);
146
+ }
147
+ </style>