@motion-proto/live-tokens 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +14 -13
  2. package/dist-plugin/index.cjs +147 -136
  3. package/dist-plugin/index.d.cts +1 -1
  4. package/dist-plugin/index.d.ts +1 -1
  5. package/dist-plugin/index.js +145 -135
  6. package/package.json +25 -40
  7. package/src/{component-editor → editor/component-editor}/BadgeEditor.svelte +8 -82
  8. package/src/{component-editor → editor/component-editor}/CalloutEditor.svelte +4 -4
  9. package/src/{component-editor → editor/component-editor}/CardEditor.svelte +28 -76
  10. package/src/{component-editor → editor/component-editor}/CollapsibleSectionEditor.svelte +3 -3
  11. package/src/{component-editor → editor/component-editor}/CornerBadgeEditor.svelte +31 -93
  12. package/src/{component-editor → editor/component-editor}/DialogEditor.svelte +60 -57
  13. package/src/editor/component-editor/ImageEditor.svelte +30 -0
  14. package/src/{component-editor → editor/component-editor}/InlineEditActionsEditor.svelte +6 -4
  15. package/src/editor/component-editor/MenuSelectEditor.svelte +160 -0
  16. package/src/{component-editor → editor/component-editor}/NotificationEditor.svelte +64 -37
  17. package/src/{component-editor → editor/component-editor}/ProgressBarEditor.svelte +5 -4
  18. package/src/{component-editor → editor/component-editor}/RadioButtonEditor.svelte +3 -3
  19. package/src/{component-editor → editor/component-editor}/SectionDividerEditor.svelte +57 -84
  20. package/src/{component-editor → editor/component-editor}/SegmentedControlEditor.svelte +2 -2
  21. package/src/{component-editor → editor/component-editor}/StandardButtonsEditor.svelte +16 -20
  22. package/src/{component-editor → editor/component-editor}/TabBarEditor.svelte +9 -14
  23. package/src/{component-editor → editor/component-editor}/TableEditor.svelte +9 -18
  24. package/src/{component-editor → editor/component-editor}/TooltipEditor.svelte +11 -47
  25. package/src/{component-editor → editor/component-editor}/registry.ts +28 -18
  26. package/src/{component-editor → editor/component-editor}/scaffolding/AngleDial.svelte +2 -2
  27. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentEditorBase.svelte +3 -51
  28. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileManager.svelte +144 -416
  29. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileMenu.svelte +18 -170
  30. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentsTab.svelte +2 -2
  31. package/src/{component-editor → editor/component-editor}/scaffolding/CopyFromMenu.svelte +44 -4
  32. package/src/{component-editor → editor/component-editor}/scaffolding/DividerEditor.svelte +1 -1
  33. package/src/{component-editor → editor/component-editor}/scaffolding/FieldsetWrapper.svelte +1 -1
  34. package/src/{component-editor → editor/component-editor}/scaffolding/GradientCard.svelte +6 -6
  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 -11
  37. package/src/editor/component-editor/scaffolding/NonStylableConfig.svelte +38 -0
  38. package/src/{component-editor → editor/component-editor}/scaffolding/SaveAsDialog.svelte +66 -12
  39. package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +72 -0
  40. package/src/editor/component-editor/scaffolding/ShadowBackdropControls.svelte +132 -0
  41. package/src/editor/component-editor/scaffolding/StateBlock.svelte +257 -0
  42. package/src/{component-editor → editor/component-editor}/scaffolding/TokenLayout.svelte +9 -7
  43. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +644 -0
  44. package/src/{component-editor → editor/component-editor}/scaffolding/editorContext.ts +19 -9
  45. package/src/{component-editor → editor/component-editor}/scaffolding/linkedBlock.ts +2 -2
  46. package/src/{component-editor → editor/component-editor}/scaffolding/types.ts +14 -0
  47. package/src/{lib → editor/core/components}/componentConfigService.ts +2 -2
  48. package/src/{lib → editor/core/components}/componentPersist.ts +5 -5
  49. package/src/editor/core/flashStatus.ts +30 -0
  50. package/src/{lib → editor/core/fonts}/fontLoader.ts +2 -2
  51. package/src/{lib → editor/core/fonts}/fontMigration.ts +4 -4
  52. package/src/{lib → editor/core/fonts}/fontParse.ts +1 -1
  53. package/src/editor/core/manifests/manifestService.ts +116 -0
  54. package/src/{lib → editor/core/palettes}/paletteDerivation.ts +2 -2
  55. package/src/{lib → editor/core/palettes}/tokenRegistry.ts +5 -5
  56. package/src/editor/core/productionPulse.ts +37 -0
  57. package/src/{lib → editor/core/routing}/router.ts +1 -1
  58. package/src/{lib/files/versionedFileResource.ts → editor/core/storage/files/versionedFileResourceClient.ts} +8 -1
  59. package/src/{lib → editor/core/store}/editorCore.ts +24 -8
  60. package/src/{lib → editor/core/store}/editorPersistence.ts +3 -3
  61. package/src/{lib → editor/core/store}/editorRenderer.ts +2 -2
  62. package/src/{lib → editor/core/store}/editorStore.ts +17 -17
  63. package/src/{lib → editor/core/store}/editorTypes.ts +1 -1
  64. package/src/{lib → editor/core/themes}/slices/columns.ts +2 -2
  65. package/src/{lib → editor/core/themes}/slices/components.ts +2 -2
  66. package/src/{lib → editor/core/themes}/slices/fonts.ts +1 -1
  67. package/src/{lib → editor/core/themes}/slices/gradients.ts +2 -2
  68. package/src/{lib → editor/core/themes}/slices/overlays.ts +1 -1
  69. package/src/{lib → editor/core/themes}/slices/palettes.ts +1 -1
  70. package/src/{lib → editor/core/themes}/slices/shadows.ts +3 -3
  71. package/src/{lib → editor/core/themes}/themeInit.ts +6 -6
  72. package/src/{lib → editor/core/themes}/themeService.ts +6 -6
  73. package/src/{lib → editor/core/themes}/themeTypes.ts +11 -7
  74. package/src/editor/index.ts +69 -0
  75. package/src/{lib → editor/overlay}/LiveEditorOverlay.svelte +79 -125
  76. package/src/{lib → editor/overlay}/columnsOverlay.ts +2 -2
  77. package/src/{pages → editor/pages}/ComponentEditorPage.svelte +12 -12
  78. package/src/{pages → editor/pages}/Editor.svelte +4 -4
  79. package/src/{pages → editor/pages}/EditorShell.svelte +18 -36
  80. package/src/{styles → editor/styles}/ui-editor.css +41 -21
  81. package/src/{styles → editor/styles}/ui-form-controls.css +8 -8
  82. package/src/{ui → editor/ui}/BezierCurveEditor.svelte +8 -8
  83. package/src/{ui → editor/ui}/ColorEditPanel.svelte +13 -13
  84. package/src/{ui → editor/ui}/EditorViewSwitcher.svelte +8 -6
  85. package/src/editor/ui/FileLoadList.svelte +350 -0
  86. package/src/editor/ui/FilePill.svelte +80 -0
  87. package/src/{ui → editor/ui}/FontStackEditor.svelte +7 -7
  88. package/src/{ui → editor/ui}/GradientEditor.svelte +11 -11
  89. package/src/{ui → editor/ui}/GradientStopPicker.svelte +1 -1
  90. package/src/editor/ui/ManifestFileManager.svelte +371 -0
  91. package/src/{ui → editor/ui}/PaletteEditor.svelte +132 -598
  92. package/src/{ui → editor/ui}/ProjectFontsSection.svelte +102 -144
  93. package/src/{ui → editor/ui}/SurfacesTab.svelte +3 -3
  94. package/src/{ui → editor/ui}/TextTab.svelte +3 -3
  95. package/src/{ui → editor/ui}/ThemeFileManager.svelte +286 -519
  96. package/src/{ui → editor/ui}/UICopyPopover.svelte +4 -4
  97. package/src/{ui → editor/ui}/UIFontFamilySelector.svelte +6 -6
  98. package/src/{ui → editor/ui}/UIFontSizeSelector.svelte +1 -1
  99. package/src/editor/ui/UIInfoPopover.svelte +244 -0
  100. package/src/{ui → editor/ui}/UILineHeightSelector.svelte +5 -5
  101. package/src/{ui → editor/ui}/UILinkToggle.svelte +2 -2
  102. package/src/{ui → editor/ui}/UIPaddingSelector.svelte +6 -6
  103. package/src/{ui → editor/ui}/UIPaletteSelector.svelte +26 -26
  104. package/src/editor/ui/UIPillButton.svelte +138 -0
  105. package/src/{ui → editor/ui}/UIRadio.svelte +2 -2
  106. package/src/{ui → editor/ui}/UIRelinkConfirmPopover.svelte +4 -4
  107. package/src/editor/ui/UISquareButton.svelte +172 -0
  108. package/src/{ui → editor/ui}/UITokenSelector.svelte +10 -10
  109. package/src/{ui → editor/ui}/UIVariantSelector.svelte +1 -1
  110. package/src/{ui → editor/ui}/VariablesTab.svelte +31 -8
  111. package/src/{ui → editor/ui}/palette/GradientStopEditor.svelte +13 -13
  112. package/src/{ui → editor/ui}/palette/OverridesPanel.svelte +13 -13
  113. package/src/{ui → editor/ui}/palette/PaletteBase.svelte +8 -5
  114. package/src/{ui → editor/ui}/palette/paletteEditorState.ts +1 -1
  115. package/src/editor/ui/palette/paletteMath.ts +275 -0
  116. package/src/{ui → editor/ui}/sections/ColumnsSection.svelte +137 -17
  117. package/src/{ui → editor/ui}/sections/GradientsSection.svelte +7 -7
  118. package/src/{ui → editor/ui}/sections/OverlaysSection.svelte +17 -17
  119. package/src/{ui → editor/ui}/sections/ShadowsSection.svelte +22 -22
  120. package/src/{ui → editor/ui}/sections/TokenScaleTable.svelte +3 -3
  121. package/src/{components → system/components}/Badge.svelte +0 -36
  122. package/src/{components → system/components}/Card.svelte +8 -62
  123. package/src/{components → system/components}/CornerBadge.svelte +8 -24
  124. package/src/{components → system/components}/Dialog.svelte +1 -1
  125. package/src/system/components/FloatingTokenTags.css +256 -0
  126. package/src/system/components/FloatingTokenTags.svelte +592 -0
  127. package/src/{components → system/components}/InlineEditActions.svelte +6 -4
  128. package/src/system/components/MenuSelect.svelte +229 -0
  129. package/src/{components → system/components}/ProgressBar.svelte +29 -11
  130. package/src/{components → system/components}/SegmentedControl.svelte +49 -43
  131. package/src/{components → system/components}/TabBar.svelte +81 -65
  132. package/src/{components → system/components}/Table.svelte +17 -3
  133. package/src/{components → system/components}/Tooltip.svelte +6 -4
  134. package/src/system/styles/CONVENTIONS.md +178 -0
  135. package/src/{styles → system/styles}/fonts.css +6 -3
  136. package/src/{styles → system/styles}/tokens.css +149 -29
  137. package/src/component-editor/ImageEditor.svelte +0 -74
  138. package/src/component-editor/scaffolding/NonStylableConfig.svelte +0 -62
  139. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +0 -37
  140. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +0 -61
  141. package/src/component-editor/scaffolding/StateBlock.svelte +0 -132
  142. package/src/component-editor/scaffolding/VariantGroup.svelte +0 -310
  143. package/src/data/google-fonts.json +0 -75
  144. package/src/lib/index.ts +0 -68
  145. package/src/lib/presetService.ts +0 -214
  146. package/src/lib/productionPulse.ts +0 -32
  147. package/src/ui/PresetFileManager.svelte +0 -1116
  148. package/src/ui/UnsavedComponentsDialog.svelte +0 -315
  149. /package/src/{styles → app}/site.css +0 -0
  150. /package/src/{component-editor → editor/component-editor}/index.ts +0 -0
  151. /package/src/{component-editor → editor/component-editor}/scaffolding/DemoHeader.svelte +0 -0
  152. /package/src/{component-editor → editor/component-editor}/scaffolding/TypeEditor.svelte +0 -0
  153. /package/src/{component-editor → editor/component-editor}/scaffolding/buildTypeGroupTokens.ts +0 -0
  154. /package/src/{component-editor → editor/component-editor}/scaffolding/componentSectionType.ts +0 -0
  155. /package/src/{component-editor → editor/component-editor}/scaffolding/componentSources.ts +0 -0
  156. /package/src/{component-editor → editor/component-editor}/scaffolding/defaultSections.ts +0 -0
  157. /package/src/{component-editor → editor/component-editor}/scaffolding/siblings.ts +0 -0
  158. /package/src/{lib → editor/core/components}/componentConfigKeys.ts +0 -0
  159. /package/src/{lib → editor/core}/cssVarSync.ts +0 -0
  160. /package/src/{lib → editor/core/palettes}/oklch.ts +0 -0
  161. /package/src/{lib → editor/core/routing}/navLinkTypes.ts +0 -0
  162. /package/src/{lib → editor/core/routing}/parentRouteStore.ts +0 -0
  163. /package/src/{lib → editor/core/storage}/storage.ts +0 -0
  164. /package/src/{lib → editor/core/store}/editorConfig.ts +0 -0
  165. /package/src/{lib → editor/core/store}/editorConfigStore.ts +0 -0
  166. /package/src/{lib → editor/core/store}/editorKeybindings.ts +0 -0
  167. /package/src/{lib → editor/core/store}/editorViewStore.ts +0 -0
  168. /package/src/{lib → editor/core/themes}/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +0 -0
  169. /package/src/{lib → editor/core/themes}/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +0 -0
  170. /package/src/{lib → editor/core/themes}/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +0 -0
  171. /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +0 -0
  172. /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +0 -0
  173. /package/src/{lib → editor/core/themes}/migrations/2026-05-10-sectiondivider-gradient-stops.ts +0 -0
  174. /package/src/{lib → editor/core/themes}/migrations/2026-05-13-primary-to-brand.ts +0 -0
  175. /package/src/{lib → editor/core/themes}/migrations/index.ts +0 -0
  176. /package/src/{lib → editor/core/themes}/parsers/globalRootBlock.ts +0 -0
  177. /package/src/{lib → editor/core/themes}/slices/domainVars.ts +0 -0
  178. /package/src/{lib → editor/overlay}/ColumnsOverlay.svelte +0 -0
  179. /package/src/{lib → editor/overlay}/overlayState.ts +0 -0
  180. /package/src/{pages → editor/pages}/ComponentEditorPage.svelte.d.ts +0 -0
  181. /package/src/{pages → editor/pages}/Editor.svelte.d.ts +0 -0
  182. /package/src/{ui → editor/ui}/Toggle.svelte +0 -0
  183. /package/src/{ui → editor/ui}/UIDialog.svelte +0 -0
  184. /package/src/{ui → editor/ui}/UIFontWeightSelector.svelte +0 -0
  185. /package/src/{ui → editor/ui}/UIOptionItem.svelte +0 -0
  186. /package/src/{ui → editor/ui}/UIOptionList.svelte +0 -0
  187. /package/src/{ui → editor/ui}/UIRadioGroup.svelte +0 -0
  188. /package/src/{lib → editor/ui}/copyPopover.ts +0 -0
  189. /package/src/{ui → editor/ui}/curveEngine.ts +0 -0
  190. /package/src/{ui → editor/ui}/index.ts +0 -0
  191. /package/src/{ui → editor/ui}/keepInViewport.ts +0 -0
  192. /package/src/{ui → editor/ui}/palette/ScaleCurveEditor.svelte +0 -0
  193. /package/src/{lib → editor/ui}/scrollSection.ts +0 -0
  194. /package/src/{ui → editor/ui}/sections/tokenScales.ts +0 -0
  195. /package/src/{ui → editor/ui}/variantScales.ts +0 -0
  196. /package/src/{assets → system/assets}/newspaper.webp +0 -0
  197. /package/src/{assets → system/assets}/offering.webp +0 -0
  198. /package/src/{components → system/components}/Button.svelte +0 -0
  199. /package/src/{components → system/components}/Callout.svelte +0 -0
  200. /package/src/{components → system/components}/CollapsibleSection.svelte +0 -0
  201. /package/src/{components → system/components}/Image.svelte +0 -0
  202. /package/src/{components → system/components}/Notification.svelte +0 -0
  203. /package/src/{components → system/components}/RadioButton.svelte +0 -0
  204. /package/src/{components → system/components}/SectionDivider.svelte +0 -0
  205. /package/src/{components → system/components}/types.ts +0 -0
  206. /package/src/{styles → system/styles}/_padding.scss +0 -0
  207. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
  208. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
  209. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
  210. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
  211. /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
  212. /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin.woff2 +0 -0
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import ScaleCurveEditor from './ScaleCurveEditor.svelte';
3
3
  import { type CurveAnchor, lightnessCurveConfig, saturationCurveConfig, textLightnessCurveConfig } from '../curveEngine';
4
- import { scaleToCssVar } from '../../lib/paletteDerivation';
4
+ import { scaleToCssVar } from '../../core/palettes/paletteDerivation';
5
5
 
6
6
  /**
7
7
  * Per-scale derived swatch section used for Text, Surfaces, and Borders
@@ -291,7 +291,7 @@
291
291
  font-size: var(--ui-font-size-md);
292
292
  color: var(--ui-text-tertiary);
293
293
  background: none;
294
- border: 1px solid var(--ui-border-subtle);
294
+ border: 1px solid var(--ui-border-low);
295
295
  border-radius: var(--ui-radius-sm);
296
296
  padding: var(--ui-space-2) var(--ui-space-6);
297
297
  cursor: pointer;
@@ -299,12 +299,12 @@
299
299
 
300
300
  .edit-toggle:hover {
301
301
  color: var(--ui-text-primary);
302
- border-color: var(--ui-border-medium);
302
+ border-color: var(--ui-border-high);
303
303
  }
304
304
 
305
305
  .edit-toggle.active {
306
306
  color: var(--ui-text-primary);
307
- border-color: var(--ui-border-medium);
307
+ border-color: var(--ui-border-high);
308
308
  background: var(--ui-surface-high);
309
309
  }
310
310
 
@@ -368,7 +368,7 @@
368
368
  width: 100%;
369
369
  height: 2rem;
370
370
  border-radius: var(--ui-radius-sm);
371
- border: 1px solid var(--ui-border-faint);
371
+ border: 1px solid var(--ui-border-low);
372
372
  }
373
373
 
374
374
  .swatch.text-swatch {
@@ -395,7 +395,7 @@
395
395
  }
396
396
 
397
397
  .swatch.derived.clickable:hover {
398
- outline: 2px solid var(--ui-border-medium);
398
+ outline: 2px solid var(--ui-border-high);
399
399
  outline-offset: 1px;
400
400
  }
401
401
 
@@ -419,24 +419,24 @@
419
419
 
420
420
  .override-slot {
421
421
  border-style: dashed;
422
- border-color: var(--ui-border-subtle);
422
+ border-color: var(--ui-border-low);
423
423
  cursor: pointer;
424
424
  position: relative;
425
425
  overflow: hidden;
426
426
  }
427
427
 
428
428
  .override-slot:hover {
429
- border-color: var(--ui-border-medium);
429
+ border-color: var(--ui-border-high);
430
430
  }
431
431
 
432
432
  .override-slot.active {
433
- border-color: var(--ui-border-strong);
434
- outline: 1px solid var(--ui-border-medium);
433
+ border-color: var(--ui-border-higher);
434
+ outline: 1px solid var(--ui-border-high);
435
435
  outline-offset: 1px;
436
436
  }
437
437
 
438
438
  .override-slot.populated {
439
- border-color: var(--ui-border-medium);
439
+ border-color: var(--ui-border-high);
440
440
  }
441
441
 
442
442
  .override-slot.matching::after {
@@ -486,7 +486,7 @@
486
486
  z-index: 10;
487
487
  margin-top: var(--ui-space-4);
488
488
  background: var(--ui-surface-lowest);
489
- border: 1px solid var(--ui-border-medium);
489
+ border: 1px solid var(--ui-border-high);
490
490
  border-radius: var(--ui-radius-md);
491
491
  padding: var(--ui-space-4);
492
492
  display: flex;
@@ -527,7 +527,7 @@
527
527
  width: 1rem;
528
528
  height: 1rem;
529
529
  border-radius: var(--ui-radius-sm);
530
- border: 1px solid var(--ui-border-faint);
530
+ border: 1px solid var(--ui-border-low);
531
531
  flex-shrink: 0;
532
532
  }
533
533
 
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import ColorEditPanel from '../ColorEditPanel.svelte';
3
3
  import Toggle from '../Toggle.svelte';
4
- import { beginSliderGesture } from '../../lib/editorStore';
4
+ import { beginSliderGesture } from '../../core/store/editorStore';
5
5
 
6
6
 
7
7
 
@@ -137,18 +137,18 @@
137
137
  width: 4rem;
138
138
  height: 4rem;
139
139
  border-radius: var(--ui-radius-md);
140
- border: 2px solid var(--ui-border-default);
140
+ border: 2px solid var(--ui-border);
141
141
  flex-shrink: 0;
142
142
  cursor: pointer;
143
143
  }
144
144
 
145
145
  .header-swatch:hover {
146
- border-color: var(--ui-border-strong);
146
+ border-color: var(--ui-border-higher);
147
147
  }
148
148
 
149
149
  .header-swatch.active {
150
- border-color: var(--ui-border-strong);
151
- outline: 2px solid var(--ui-border-medium);
150
+ border-color: var(--ui-border-higher);
151
+ outline: 2px solid var(--ui-border-high);
152
152
  outline-offset: 1px;
153
153
  }
154
154
 
@@ -165,14 +165,17 @@
165
165
  }
166
166
 
167
167
  .clickable-hex {
168
+ align-self: flex-start;
168
169
  background: none;
169
170
  border: none;
170
171
  cursor: pointer;
171
172
  padding: var(--ui-space-2) var(--ui-space-4);
173
+ margin-left: calc(-1 * var(--ui-space-4));
172
174
  border-radius: var(--ui-radius-sm);
173
175
  font-size: var(--ui-font-size-xs);
174
176
  color: var(--ui-text-secondary);
175
177
  font-family: var(--ui-font-mono);
178
+ text-align: left;
176
179
  }
177
180
 
178
181
  .clickable-hex:hover {
@@ -30,7 +30,7 @@
30
30
  * edits don't open a panel.
31
31
  */
32
32
 
33
- import type { Scope } from '../../lib/editorStore';
33
+ import type { Scope } from '../../core/store/editorStore';
34
34
 
35
35
  /** Sentinel key for the base swatch (used as `editingKey === BASE_KEY`). */
36
36
  export const BASE_KEY = '__base__';
@@ -0,0 +1,275 @@
1
+ import { hexToOklch, oklchToHex, gamutClamp } from '../../core/palettes/oklch';
2
+ import { type CurveAnchor, makeAnchor, sampleCurve } from '../curveEngine';
3
+
4
+ export const GRAY_FALLBACK = '#808080';
5
+ export const DEFAULT_TINT_CHROMA = 0.04;
6
+
7
+ export interface Step {
8
+ name: string;
9
+ position: number;
10
+ lightness?: number;
11
+ saturation?: number;
12
+ }
13
+
14
+ export interface Scale {
15
+ title: string;
16
+ isText: boolean;
17
+ steps: Step[];
18
+ }
19
+
20
+ export interface GrayStep {
21
+ label: string;
22
+ hue: number;
23
+ saturation: number;
24
+ lightness: number;
25
+ }
26
+
27
+ export interface PaletteStepDef {
28
+ label: string;
29
+ lightness: number;
30
+ }
31
+
32
+ export type CurveOffset = Record<string, number>;
33
+ export type ScaleCurves = Record<string, { lightness: CurveAnchor[]; saturation: CurveAnchor[] }>;
34
+
35
+ export const DEFAULT_PALETTE_LIGHTNESS = (): CurveAnchor[] => [makeAnchor(0, 95, 5), makeAnchor(100, 8, 5)];
36
+ export const DEFAULT_PALETTE_SATURATION = (): CurveAnchor[] => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)];
37
+ export const DEFAULT_GRAY_LIGHTNESS = (): CurveAnchor[] => [makeAnchor(0, 92, 5), makeAnchor(100, 3, 5)];
38
+ export const DEFAULT_GRAY_SATURATION = (): CurveAnchor[] => [makeAnchor(0, 20, 30), makeAnchor(100, 20, 30)];
39
+
40
+ export const defaultScaleCurves: Record<string, { lightness: () => CurveAnchor[]; saturation: () => CurveAnchor[] }> = {
41
+ Surfaces: {
42
+ lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
43
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)],
44
+ },
45
+ Borders: {
46
+ lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
47
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)],
48
+ },
49
+ Text: {
50
+ lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
51
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)],
52
+ },
53
+ };
54
+
55
+ export function defaultScaleCurvesObject(): ScaleCurves {
56
+ return {
57
+ Surfaces: { lightness: defaultScaleCurves.Surfaces.lightness(), saturation: defaultScaleCurves.Surfaces.saturation() },
58
+ Borders: { lightness: defaultScaleCurves.Borders.lightness(), saturation: defaultScaleCurves.Borders.saturation() },
59
+ Text: { lightness: defaultScaleCurves.Text.lightness(), saturation: defaultScaleCurves.Text.saturation() },
60
+ };
61
+ }
62
+
63
+ export const paletteStepLightness: PaletteStepDef[] = [
64
+ { label: '100', lightness: 95 },
65
+ { label: '200', lightness: 88 },
66
+ { label: '300', lightness: 78 },
67
+ { label: '400', lightness: 68 },
68
+ { label: '500', lightness: 57 },
69
+ { label: '600', lightness: 49 },
70
+ { label: '700', lightness: 41 },
71
+ { label: '800', lightness: 32 },
72
+ { label: '850', lightness: 25 },
73
+ { label: '900', lightness: 17 },
74
+ { label: '950', lightness: 8 },
75
+ ];
76
+
77
+ export const graySteps: GrayStep[] = [
78
+ { label: '100', hue: 240, saturation: 5, lightness: 92 },
79
+ { label: '200', hue: 220, saturation: 13, lightness: 84 },
80
+ { label: '300', hue: 216, saturation: 12, lightness: 72 },
81
+ { label: '400', hue: 240, saturation: 5, lightness: 61 },
82
+ { label: '500', hue: 240, saturation: 5, lightness: 50 },
83
+ { label: '600', hue: 240, saturation: 5, lightness: 42 },
84
+ { label: '700', hue: 240, saturation: 5, lightness: 34 },
85
+ { label: '800', hue: 240, saturation: 10, lightness: 25 },
86
+ { label: '850', hue: 229, saturation: 20, lightness: 18 },
87
+ { label: '900', hue: 240, saturation: 30, lightness: 10 },
88
+ { label: '950', hue: 229, saturation: 34, lightness: 3 },
89
+ ];
90
+
91
+ export const scales: Scale[] = [
92
+ {
93
+ title: 'Surfaces',
94
+ isText: false,
95
+ steps: [
96
+ { name: 'lowest', position: -1 },
97
+ { name: 'lower', position: -2/3 },
98
+ { name: 'low', position: -1/3 },
99
+ { name: 'default', position: 0 },
100
+ { name: 'high', position: 1/3 },
101
+ { name: 'higher', position: 2/3 },
102
+ { name: 'highest', position: 1 },
103
+ ]
104
+ },
105
+ {
106
+ title: 'Borders',
107
+ isText: false,
108
+ steps: [
109
+ { name: 'faint', position: -1 },
110
+ { name: 'subtle', position: -0.5 },
111
+ { name: 'default', position: 0 },
112
+ { name: 'medium', position: 0.5 },
113
+ { name: 'strong', position: 1 },
114
+ ]
115
+ },
116
+ {
117
+ title: 'Text',
118
+ isText: true,
119
+ steps: [
120
+ { name: 'primary', position: 0 },
121
+ { name: 'secondary', position: 0 },
122
+ { name: 'tertiary', position: 0 },
123
+ { name: 'muted', position: 0 },
124
+ { name: 'disabled', position: 0 },
125
+ ]
126
+ }
127
+ ];
128
+
129
+ export const paletteStepKey = (label: string) => `Palette-${label}`;
130
+ export const grayStepKey = (label: string) => `gray-${label}`;
131
+ export const stepKey = (scaleTitle: string, stepName: string) => `${scaleTitle}-${stepName}`;
132
+ export const scaleCurveKey = (scaleTitle: string, channel: 'lightness' | 'saturation') => `${scaleTitle}-${channel}`;
133
+
134
+ export function stepIndexToX(index: number): number {
135
+ return (index / (paletteStepLightness.length - 1)) * 100;
136
+ }
137
+
138
+ export function grayStepToX(index: number): number {
139
+ return graySteps.length > 1 ? (index / (graySteps.length - 1)) * 100 : 50;
140
+ }
141
+
142
+ export function scaleStepToX(step: Step, scale: Scale): number {
143
+ const idx = scale.steps.indexOf(step);
144
+ return scale.steps.length > 1 ? (idx / (scale.steps.length - 1)) * 100 : 50;
145
+ }
146
+
147
+ export function injectLockedAnchor(curve: CurveAnchor[], x: number, y: number): { curve: CurveAnchor[]; idx: number; injected: boolean } {
148
+ const existing = curve.findIndex(a => Math.abs(a.x - x) < 0.5);
149
+ if (existing >= 0) {
150
+ if (curve[existing].x === x && Math.abs(curve[existing].y - y) < 0.01) return { curve, idx: existing, injected: false };
151
+ return { curve: curve.map((a, i) => i === existing ? { ...a, x, y } : a), idx: existing, injected: false };
152
+ }
153
+ let insertAt = curve.findIndex(a => a.x > x);
154
+ if (insertAt < 0) insertAt = curve.length;
155
+ return { curve: [...curve.slice(0, insertAt), makeAnchor(x, y, 15), ...curve.slice(insertAt)], idx: insertAt, injected: true };
156
+ }
157
+
158
+ export function removeLockedAnchor(curve: CurveAnchor[], idx: number | null): CurveAnchor[] {
159
+ if (idx === null || idx === 0 || idx === curve.length - 1) return curve;
160
+ return curve.filter((_, i) => i !== idx);
161
+ }
162
+
163
+ export function computeGrayColor(
164
+ index: number,
165
+ hue: number,
166
+ chroma: number,
167
+ lightnessCurve: CurveAnchor[],
168
+ saturationCurve: CurveAnchor[],
169
+ curveOffset: CurveOffset
170
+ ): string {
171
+ const xPos = grayStepToX(index);
172
+ const lOff = curveOffset['gray-lightness'] ?? 0;
173
+ const sOff = curveOffset['gray-saturation'] ?? 0;
174
+
175
+ const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + lOff)) / 100;
176
+ const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + sOff) / 100));
177
+ const targetC = chroma * satMul;
178
+
179
+ const clamped = gamutClamp(targetL, targetC, hue);
180
+ return oklchToHex(clamped.l, clamped.c, clamped.h);
181
+ }
182
+
183
+ export function computePaletteColor(
184
+ index: number,
185
+ base: string,
186
+ lightnessCurve: CurveAnchor[],
187
+ saturationCurve: CurveAnchor[],
188
+ curveOffset: CurveOffset
189
+ ): string {
190
+ const { c: baseC, h } = hexToOklch(base);
191
+ const xPos = stepIndexToX(index);
192
+
193
+ const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset['lightness'] ?? 0))) / 100;
194
+ const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset['saturation'] ?? 0)) / 100));
195
+ const targetC = baseC * satMul;
196
+
197
+ const clamped = gamutClamp(targetL, targetC, h);
198
+ return oklchToHex(clamped.l, clamped.c, clamped.h);
199
+ }
200
+
201
+ export function computeDerivedColor(
202
+ step: Step,
203
+ base: string,
204
+ scaleTitle: string,
205
+ scaleCurves: ScaleCurves,
206
+ curveOffset: CurveOffset
207
+ ): string {
208
+ const { l: baseL, c: baseC, h: baseH } = hexToOklch(base);
209
+ const scale = scales.find(s => s.title === scaleTitle)!;
210
+ const xPos = scaleStepToX(step, scale);
211
+
212
+ const lCurve = scaleCurves[scaleTitle]?.lightness ?? [];
213
+ const sCurve = scaleCurves[scaleTitle]?.saturation ?? [];
214
+ const lOff = curveOffset[scaleCurveKey(scaleTitle, 'lightness')] ?? 0;
215
+ const sOff = curveOffset[scaleCurveKey(scaleTitle, 'saturation')] ?? 0;
216
+
217
+ let targetL: number;
218
+ if (scale.isText) {
219
+ // Text: lightness curve is a multiplier (100 = 1x base lightness)
220
+ const lMul = Math.max(0, Math.min(2, (sampleCurve(lCurve, xPos) + lOff) / 100));
221
+ targetL = Math.max(0, Math.min(1, baseL * lMul));
222
+ } else {
223
+ targetL = Math.max(0, Math.min(100, sampleCurve(lCurve, xPos) + lOff)) / 100;
224
+ }
225
+
226
+ const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
227
+ const targetC = baseC * satMul;
228
+
229
+ const clamped = gamutClamp(targetL, targetC, baseH);
230
+ return oklchToHex(clamped.l, clamped.c, clamped.h);
231
+ }
232
+
233
+ interface PaletteComputed {
234
+ hex: string;
235
+ }
236
+
237
+ // Pick the contiguous window of palette steps (dark-first) whose lightness
238
+ // curve best matches this scale's derived lightness curve.
239
+ export function snapScaleToPalette(
240
+ scale: Scale,
241
+ baseColor: string,
242
+ scaleCurves: ScaleCurves,
243
+ curveOffset: CurveOffset,
244
+ paletteComputed: ReadonlyArray<PaletteComputed>
245
+ ): Record<string, string> {
246
+ const n = scale.steps.length;
247
+
248
+ const stepL = scale.steps.map(step => {
249
+ const derived = computeDerivedColor(step, baseColor, scale.title, scaleCurves, curveOffset);
250
+ return hexToOklch(derived).l;
251
+ });
252
+
253
+ const palDarkFirst = [...paletteComputed].reverse();
254
+ const palLDarkFirst = palDarkFirst.map(ps => hexToOklch(ps.hex).l);
255
+
256
+ let bestStart = 0;
257
+ let bestCost = Infinity;
258
+ for (let start = 0; start <= palDarkFirst.length - n; start++) {
259
+ let cost = 0;
260
+ for (let i = 0; i < n; i++) {
261
+ const d = stepL[i] - palLDarkFirst[start + i];
262
+ cost += d * d;
263
+ }
264
+ if (cost < bestCost) {
265
+ bestCost = cost;
266
+ bestStart = start;
267
+ }
268
+ }
269
+
270
+ const assigned: Record<string, string> = {};
271
+ for (let i = 0; i < n; i++) {
272
+ assigned[stepKey(scale.title, scale.steps[i].name)] = palDarkFirst[bestStart + i].hex;
273
+ }
274
+ return assigned;
275
+ }
@@ -1,14 +1,22 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
- import { editorState, mutate, beginSliderGesture } from '../../lib/editorStore';
4
- import type { ColumnsState } from '../../lib/editorTypes';
3
+ import { editorState, mutate, beginSliderGesture } from '../../core/store/editorStore';
4
+ import type { ColumnsState } from '../../core/store/editorTypes';
5
+
6
+ const STANDARD_COLS = 12;
7
+ const COLS_MIN = 1;
8
+ const COLS_MAX = 24;
9
+ // Pointer-events needed to break free of 12 once the drag arrives there.
10
+ // The slider is bound to state, so returning early during these trips
11
+ // keeps the thumb visually pinned at 12 even as the pointer wanders.
12
+ const DETENT_RESISTANCE = 4;
5
13
 
6
14
  function clampNum(v: number, lo: number, hi: number): number {
7
15
  return Math.max(lo, Math.min(hi, Math.round(v)));
8
16
  }
9
17
 
10
18
  function setColumnsCount(n: number) {
11
- mutate('set columns count', (s) => { s.columns.count = clampNum(n, 1, 24); });
19
+ mutate('set columns count', (s) => { s.columns.count = clampNum(n, COLS_MIN, COLS_MAX); });
12
20
  }
13
21
  function setColumnsMaxWidth(px: number) {
14
22
  mutate('set columns max-width', (s) => { s.columns.maxWidth = clampNum(px, 320, 2560); });
@@ -20,6 +28,44 @@
20
28
  mutate('set columns margin', (s) => { s.columns.margin = clampNum(px, 0, 400); });
21
29
  }
22
30
 
31
+ let colsDragging = false;
32
+ let colsDetentTrips = 0;
33
+
34
+ function onColsPointerDown() {
35
+ beginSliderGesture('drag columns count');
36
+ colsDragging = true;
37
+ colsDetentTrips = 0;
38
+ }
39
+ function onColsPointerUp() {
40
+ colsDragging = false;
41
+ colsDetentTrips = 0;
42
+ }
43
+ function handleColsInput(raw: number, target: HTMLInputElement) {
44
+ const current = $editorState.columns.count;
45
+ if (
46
+ colsDragging &&
47
+ current === STANDARD_COLS &&
48
+ Math.abs(raw - STANDARD_COLS) === 1
49
+ ) {
50
+ colsDetentTrips += 1;
51
+ if (colsDetentTrips < DETENT_RESISTANCE) {
52
+ // Force the thumb back. Svelte's controlled value may not re-sync
53
+ // when the underlying number is unchanged, so set the DOM value here.
54
+ target.value = String(STANDARD_COLS);
55
+ return;
56
+ }
57
+ colsDetentTrips = 0;
58
+ }
59
+ if (raw === STANDARD_COLS) colsDetentTrips = 0;
60
+ setColumnsCount(raw);
61
+ }
62
+
63
+ // Tick position on the slider track. Slider runs from COLS_MIN to COLS_MAX,
64
+ // so 12 sits at (12 - min) / (max - min) along the track. Inset the edges by
65
+ // approximately one thumb-radius so the tick aligns with where the thumb
66
+ // sits when value === 12.
67
+ const colsTickPct = ((STANDARD_COLS - COLS_MIN) / (COLS_MAX - COLS_MIN)) * 100;
68
+
23
69
  let initialColumns: ColumnsState | null = null;
24
70
 
25
71
  onMount(() => {
@@ -41,16 +87,28 @@
41
87
  </p>
42
88
 
43
89
  <div class="columns-controls">
44
- <div class="global-shadow-row">
90
+ <div class="global-shadow-row cols-row">
45
91
  <span class="shadow-slider-label" title="Number of columns">Cols</span>
46
- <input type="range" min="1" max="24" value={$editorState.columns.count}
47
- onpointerdown={() => beginSliderGesture('drag columns count')}
48
- oninput={(e) => setColumnsCount(+e.currentTarget.value)} />
49
- <input class="shadow-slider-input" type="number" min="1" max="24"
92
+ <div class="cols-slider-wrap">
93
+ <input type="range" min={COLS_MIN} max={COLS_MAX} value={$editorState.columns.count}
94
+ onpointerdown={onColsPointerDown}
95
+ onpointerup={onColsPointerUp}
96
+ onpointercancel={onColsPointerUp}
97
+ oninput={(e) => handleColsInput(+e.currentTarget.value, e.currentTarget)} />
98
+ <span class="cols-tick" style="left: {colsTickPct}%" aria-hidden="true"></span>
99
+ <span class="cols-tick-label" style="left: {colsTickPct}%" aria-hidden="true">12</span>
100
+ </div>
101
+ <input class="shadow-slider-input" type="number" min={COLS_MIN} max={COLS_MAX}
50
102
  value={$editorState.columns.count}
51
103
  onchange={(e) => setColumnsCount(+e.currentTarget.value)} />
52
104
  <span class="shadow-slider-unit"></span>
53
105
  </div>
106
+ {#if $editorState.columns.count !== STANDARD_COLS}
107
+ <p class="cols-consequence" role="note">
108
+ <i class="fas fa-circle-exclamation"></i>
109
+ <span>Most components assume 12 cols. Reset to restore.</span>
110
+ </p>
111
+ {/if}
54
112
  <div class="global-shadow-row">
55
113
  <span class="shadow-slider-label" title="Maximum content width">Max-Width</span>
56
114
  <input type="range" min="480" max="2560" step="10" value={$editorState.columns.maxWidth}
@@ -110,12 +168,12 @@
110
168
  }
111
169
 
112
170
  .section-title {
113
- font-size: var(--ui-font-size-lg);
171
+ font-size: var(--ui-font-size-2xl);
114
172
  font-weight: var(--ui-font-weight-semibold);
115
173
  color: var(--ui-text-primary);
116
174
  margin: 0;
117
175
  padding-bottom: var(--ui-space-8);
118
- border-bottom: 1px solid var(--ui-border-subtle);
176
+ border-bottom: 2px solid var(--ui-border-high);
119
177
  }
120
178
 
121
179
  .columns-intro {
@@ -136,7 +194,7 @@
136
194
  gap: var(--ui-space-8);
137
195
  padding: var(--ui-space-12) var(--ui-space-16);
138
196
  background: var(--ui-surface-low);
139
- border: 1px solid var(--ui-border-faint);
197
+ border: 1px solid var(--ui-border-low);
140
198
  border-radius: var(--ui-radius-md);
141
199
  }
142
200
 
@@ -145,6 +203,68 @@
145
203
  text-align: left;
146
204
  }
147
205
 
206
+ .cols-row {
207
+ /* Slightly more breathing room so the tick label below the slider
208
+ does not collide with the next row. */
209
+ padding-bottom: var(--ui-space-6);
210
+ }
211
+
212
+ .cols-slider-wrap {
213
+ flex: 1;
214
+ position: relative;
215
+ display: flex;
216
+ align-items: center;
217
+ min-width: 4rem;
218
+ }
219
+
220
+ .cols-slider-wrap input[type="range"] {
221
+ flex: 1;
222
+ min-width: 4rem;
223
+ accent-color: var(--ui-text-accent);
224
+ height: 4px;
225
+ cursor: pointer;
226
+ }
227
+
228
+ .cols-tick {
229
+ position: absolute;
230
+ top: 50%;
231
+ width: 2px;
232
+ height: 10px;
233
+ background: var(--ui-text-muted);
234
+ opacity: 0.55;
235
+ transform: translate(-50%, -50%);
236
+ pointer-events: none;
237
+ border-radius: 1px;
238
+ }
239
+
240
+ .cols-tick-label {
241
+ position: absolute;
242
+ top: calc(50% + 9px);
243
+ transform: translateX(-50%);
244
+ font-size: 9px;
245
+ font-family: var(--ui-font-mono);
246
+ color: var(--ui-text-muted);
247
+ letter-spacing: 0.04em;
248
+ pointer-events: none;
249
+ line-height: 1;
250
+ }
251
+
252
+ .cols-consequence {
253
+ display: flex;
254
+ align-items: center;
255
+ gap: var(--ui-space-6);
256
+ margin: calc(var(--ui-space-4) * -1) 0 0;
257
+ padding: var(--ui-space-4) 0 var(--ui-space-4) calc(5rem + var(--ui-space-8));
258
+ font-size: var(--ui-font-size-xs);
259
+ color: var(--ui-link-broken);
260
+ line-height: 1.3;
261
+ }
262
+
263
+ .cols-consequence i {
264
+ font-size: 10px;
265
+ opacity: 0.85;
266
+ }
267
+
148
268
  .columns-input-wide {
149
269
  width: 3.5rem;
150
270
  }
@@ -154,7 +274,7 @@
154
274
  justify-content: flex-end;
155
275
  padding-top: var(--ui-space-8);
156
276
  margin-top: var(--ui-space-4);
157
- border-top: 1px solid var(--ui-border-faint);
277
+ border-top: 1px solid var(--ui-border-low);
158
278
  }
159
279
 
160
280
  .columns-reset {
@@ -163,7 +283,7 @@
163
283
  gap: var(--ui-space-6);
164
284
  padding: var(--ui-space-4) var(--ui-space-10);
165
285
  background: transparent;
166
- border: 1px solid var(--ui-border-subtle);
286
+ border: 1px solid var(--ui-border-low);
167
287
  border-radius: var(--ui-radius-md);
168
288
  color: var(--ui-text-tertiary);
169
289
  font-family: inherit;
@@ -174,7 +294,7 @@
174
294
 
175
295
  .columns-reset:hover {
176
296
  color: var(--ui-text-primary);
177
- border-color: var(--ui-border-medium);
297
+ border-color: var(--ui-border-high);
178
298
  }
179
299
 
180
300
  .columns-reset i {
@@ -183,7 +303,7 @@
183
303
 
184
304
  .columns-preview {
185
305
  background: var(--ui-surface-lowest);
186
- border: 1px solid var(--ui-border-faint);
306
+ border: 1px solid var(--ui-border-low);
187
307
  border-radius: var(--ui-radius-md);
188
308
  padding: var(--ui-space-12) 0;
189
309
  overflow: hidden;
@@ -245,7 +365,7 @@
245
365
  text-align: right;
246
366
  flex-shrink: 0;
247
367
  background: var(--ui-surface-lowest);
248
- border: 1px solid var(--ui-border-subtle);
368
+ border: 1px solid var(--ui-border-low);
249
369
  border-radius: var(--ui-radius-sm);
250
370
  padding: var(--ui-space-2) var(--ui-space-4);
251
371
  -moz-appearance: textfield;
@@ -260,7 +380,7 @@
260
380
 
261
381
  .shadow-slider-input:focus {
262
382
  outline: none;
263
- border-color: var(--ui-border-medium);
383
+ border-color: var(--ui-border-high);
264
384
  }
265
385
 
266
386
  .shadow-slider-unit {