@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
@@ -3,8 +3,8 @@ import {
3
3
  isComponentPropertyLinked,
4
4
  getComponentPropertySiblings,
5
5
  editorState,
6
- } from '../../lib/editorStore';
7
- import type { CssVarRef } from '../../lib/editorTypes';
6
+ } from '../../core/store/editorStore';
7
+ import type { CssVarRef } from '../../core/store/editorTypes';
8
8
  import type { Token } from './types';
9
9
 
10
10
  function aliasKey(ref: CssVarRef | undefined): string {
@@ -13,6 +13,17 @@ export type Token = {
13
13
  /** When the linked block collapses several same-label same-value rows into one,
14
14
  the surviving row carries the other groupKey leads here so writes co-propagate. */
15
15
  mergeVariables?: string[];
16
+ /** When false, a padding-shaped token renders only the single-value control
17
+ (no split-to-sides affordance). For tokens consumed via a one-axis CSS
18
+ property like `padding-bottom: var(--x)`, splitting yields side values
19
+ that have nowhere to render — hide the toggle so users can't get into
20
+ that state. Defaults to true for padding-shaped tokens. */
21
+ splittable?: boolean;
22
+ /** Optional element grouping (e.g. 'frame', 'header', 'body'). When a state
23
+ has tokens or type-groups tagged with two or more distinct elements,
24
+ StateBlock partitions the panel into labeled subsections — typography
25
+ and properties for each element render together. */
26
+ element?: string;
16
27
  };
17
28
 
18
29
  /** Editor type-group: a fieldset containing a coordinated set of typography tokens
@@ -36,4 +47,7 @@ export type TypeGroupConfig = {
36
47
  outlineWidthLabel?: string;
37
48
  outlineColorVariable?: string;
38
49
  outlineColorLabel?: string;
50
+ /** See `Token.element` — when present, StateBlock groups this fieldset under
51
+ the matching element subsection. */
52
+ element?: string;
39
53
  };
@@ -1,5 +1,5 @@
1
- import type { ComponentConfig, ComponentConfigMeta } from './themeTypes';
2
- import { versionedFileResource } from './files/versionedFileResource';
1
+ import type { ComponentConfig, ComponentConfigMeta } from '../themes/themeTypes';
2
+ import { versionedFileResource } from '../storage/files/versionedFileResourceClient';
3
3
 
4
4
  /**
5
5
  * REST client for per-component config files. Parallel to `themeService.ts`
@@ -1,8 +1,8 @@
1
1
  import { get } from 'svelte/store';
2
- import type { ComponentConfig } from './themeTypes';
3
- import { editorState, markComponentSaved } from './editorStore';
4
- import type { CssVarRef } from './editorTypes';
5
- import { CURRENT_COMPONENT_SCHEMA_VERSION } from './migrations';
2
+ import type { ComponentConfig } from '../themes/themeTypes';
3
+ import { editorState, markComponentSaved } from '../store/editorStore';
4
+ import type { CssVarRef } from '../store/editorTypes';
5
+ import { CURRENT_COMPONENT_SCHEMA_VERSION } from '../themes/migrations';
6
6
  import {
7
7
  listComponentConfigs,
8
8
  saveComponentConfig,
@@ -12,7 +12,7 @@ import {
12
12
  /**
13
13
  * Save the current in-memory state of a component to its active file. Mirrors
14
14
  * the `persist` flow inside `ComponentFileManager.svelte` so callers without a
15
- * file-manager instance (e.g. the unsaved-components dialog in PresetFileManager)
15
+ * file-manager instance (e.g. an unsaved-components dialog upstream)
16
16
  * can save a dirty component without duplicating the schema-version + aliases
17
17
  * stringification logic.
18
18
  *
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Centralised "flash a transient status, then revert to idle" helper for the
3
+ * file managers (theme / component / manifest). Each pulse — saved, error,
4
+ * adopted — shares the same timing so the UI stays in sync.
5
+ */
6
+
7
+ export const FLASH_DURATION_MS = 2000;
8
+
9
+ export interface FlashOptions<S extends string> {
10
+ /** State to revert to after the pulse. Defaults to `'idle'`. */
11
+ idleState?: S;
12
+ /** How long the transient state is shown before reverting. */
13
+ durationMs?: number;
14
+ /** Side effect fired alongside the revert (e.g. clear an inline message). */
15
+ onIdle?: () => void;
16
+ }
17
+
18
+ export function flashStatus<S extends string>(
19
+ set: (state: S) => void,
20
+ transientState: S,
21
+ options: FlashOptions<S> = {},
22
+ ): void {
23
+ const idleState = options.idleState ?? ('idle' as S);
24
+ const durationMs = options.durationMs ?? FLASH_DURATION_MS;
25
+ set(transientState);
26
+ setTimeout(() => {
27
+ set(idleState);
28
+ options.onIdle?.();
29
+ }, durationMs);
30
+ }
@@ -5,8 +5,8 @@ import type {
5
5
  FontStackSlot,
6
6
  FontStackVariable,
7
7
  SystemCascadePreset,
8
- } from './themeTypes';
9
- import { setCssVar, getSyncedDocuments } from './cssVarSync';
8
+ } from '../themes/themeTypes';
9
+ import { setCssVar, getSyncedDocuments } from '../cssVarSync';
10
10
 
11
11
  export const SYSTEM_CASCADES: Record<SystemCascadePreset, string> = {
12
12
  'system-ui-sans':
@@ -1,7 +1,7 @@
1
- import type { FontFamily, FontSource, FontStack, Theme } from './themeTypes';
2
- import frauncesRomanLatin from '../styles/fonts/Fraunces/Fraunces-roman-latin.woff2?url';
3
- import frauncesItalicLatin from '../styles/fonts/Fraunces/Fraunces-italic-latin.woff2?url';
4
- import manropeLatin from '../styles/fonts/Manrope/Manrope-latin.woff2?url';
1
+ import type { FontFamily, FontSource, FontStack, Theme } from '../themes/themeTypes';
2
+ import frauncesRomanLatin from '../../../system/styles/fonts/Fraunces/Fraunces-roman-latin.woff2?url';
3
+ import frauncesItalicLatin from '../../../system/styles/fonts/Fraunces/Fraunces-italic-latin.woff2?url';
4
+ import manropeLatin from '../../../system/styles/fonts/Manrope/Manrope-latin.woff2?url';
5
5
 
6
6
  function makeId(prefix: string): string {
7
7
  return `${prefix}_${Math.random().toString(36).slice(2, 10)}`;
@@ -1,4 +1,4 @@
1
- import type { FontFamily, FontSource, FontSourceKind } from './themeTypes';
1
+ import type { FontFamily, FontSource, FontSourceKind } from '../themes/themeTypes';
2
2
 
3
3
  export interface ParsedFamily {
4
4
  name: string;
@@ -0,0 +1,116 @@
1
+ import type { Manifest, ManifestMeta, Theme, ComponentConfig } from '../themes/themeTypes';
2
+ import { versionedFileResource } from '../storage/files/versionedFileResourceClient';
3
+ import { listComponents } from '../components/componentConfigService';
4
+ import { getActiveTheme } from '../themes/themeService';
5
+
6
+ /**
7
+ * REST client for manifest files. A manifest references one theme file +
8
+ * one config file per component (by basename). The active manifest is the
9
+ * single live snapshot: theme and component Adopts patch its refs server-side.
10
+ *
11
+ * `default` is the protected baseline — cannot be overwritten or deleted, and
12
+ * Adopts return 409 ACTIVE_IS_PROTECTED while it is active.
13
+ */
14
+
15
+ const manifestsResource = versionedFileResource<Manifest, ManifestMeta, never>({
16
+ baseUrl: '/api/manifests',
17
+ });
18
+
19
+ export const listManifests = async (): Promise<ManifestMeta[]> => {
20
+ const data = await manifestsResource.list();
21
+ return data.files;
22
+ };
23
+
24
+ export const loadManifest = (fileName: string): Promise<Manifest> =>
25
+ manifestsResource.load(fileName);
26
+ export const saveManifest = (fileName: string, data: Manifest): Promise<void> =>
27
+ manifestsResource.save(fileName, data);
28
+ export const deleteManifest = (fileName: string): Promise<void> =>
29
+ manifestsResource.remove(fileName);
30
+ export const getActiveManifest = (): Promise<Manifest | null> => manifestsResource.getActive();
31
+ export const setActiveManifest = (fileName: string): Promise<void> =>
32
+ manifestsResource.setActive(fileName);
33
+
34
+ export interface ApplyManifestResult {
35
+ ok: boolean;
36
+ manifest: Manifest;
37
+ theme: Theme;
38
+ componentConfigs: Record<string, ComponentConfig>;
39
+ }
40
+
41
+ /**
42
+ * Server-side atomic apply: validate every referenced file exists, flip the
43
+ * theme + each component's `_active.json` pointer, mark the manifest active,
44
+ * and return the resolved theme + component configs in one payload. Clients
45
+ * usually follow with a full page reload — manifest load is a "blow up the
46
+ * world" action.
47
+ */
48
+ export async function applyManifest(fileName: string): Promise<ApplyManifestResult> {
49
+ const res = await fetch(`/api/manifests/${encodeURIComponent(fileName)}/apply`, {
50
+ method: 'PUT',
51
+ });
52
+ if (!res.ok) {
53
+ const err = await res.json().catch(() => ({ error: 'Apply failed' }));
54
+ throw new Error(err.error || 'Apply failed');
55
+ }
56
+ return res.json();
57
+ }
58
+
59
+ /**
60
+ * Snapshot the currently-active theme + component-config file pointers into
61
+ * a new manifest file and set it active. Used by the manifest panel's Save As
62
+ * action and by the SaveAs-then-Adopt recovery flow when active is `default`.
63
+ */
64
+ export async function saveAsManifest(
65
+ fileName: string,
66
+ displayName: string,
67
+ ): Promise<void> {
68
+ const activeTheme = await getActiveTheme();
69
+ if (!activeTheme || !activeTheme._fileName) {
70
+ throw new Error('No active theme on disk to capture');
71
+ }
72
+ const components = await listComponents();
73
+ const componentConfigs: Record<string, string> = {};
74
+ for (const c of components) {
75
+ componentConfigs[c.name] = c.activeFile || 'default';
76
+ }
77
+ const now = new Date().toISOString();
78
+ const manifest: Manifest = {
79
+ name: displayName,
80
+ createdAt: now,
81
+ updatedAt: now,
82
+ theme: activeTheme._fileName,
83
+ componentConfigs,
84
+ };
85
+ await saveManifest(fileName, manifest);
86
+ await setActiveManifest(fileName);
87
+ }
88
+
89
+ /**
90
+ * Re-snapshot the editor's current active pointers into the *currently active*
91
+ * manifest file. Used by the manifest panel's Save action. Server rejects with
92
+ * 403 if active is `default` (protected).
93
+ */
94
+ export async function saveActiveManifest(displayName?: string): Promise<void> {
95
+ const active = await getActiveManifest();
96
+ if (!active || !active._fileName) {
97
+ throw new Error('No active manifest');
98
+ }
99
+ const activeTheme = await getActiveTheme();
100
+ if (!activeTheme || !activeTheme._fileName) {
101
+ throw new Error('No active theme on disk');
102
+ }
103
+ const components = await listComponents();
104
+ const componentConfigs: Record<string, string> = {};
105
+ for (const c of components) {
106
+ componentConfigs[c.name] = c.activeFile || 'default';
107
+ }
108
+ const manifest: Manifest = {
109
+ name: displayName ?? active.name,
110
+ createdAt: active.createdAt,
111
+ updatedAt: new Date().toISOString(),
112
+ theme: activeTheme._fileName,
113
+ componentConfigs,
114
+ };
115
+ await saveManifest(active._fileName, manifest);
116
+ }
@@ -14,8 +14,8 @@
14
14
  */
15
15
 
16
16
  import { hexToOklch, oklchToHex, gamutClamp } from './oklch';
17
- import { type CurveAnchor, sampleCurve, makeAnchor } from '../ui/curveEngine';
18
- import type { PaletteConfig } from './themeTypes';
17
+ import { type CurveAnchor, sampleCurve, makeAnchor } from '../../ui/curveEngine';
18
+ import type { PaletteConfig } from '../themes/themeTypes';
19
19
 
20
20
  export type PaletteMode = 'chromatic' | 'gray';
21
21
 
@@ -19,17 +19,17 @@
19
19
  */
20
20
 
21
21
  import { derived, type Readable } from 'svelte/store';
22
- import tokensCss from '../styles/tokens.css?raw';
23
- import { editorState } from './editorStore';
24
- import type { EditorState } from './editorTypes';
25
- import { extractGlobalRootBody } from './parsers/globalRootBlock';
22
+ import tokensCss from '../../../system/styles/tokens.css?raw';
23
+ import { editorState } from '../store/editorStore';
24
+ import type { EditorState } from '../store/editorTypes';
25
+ import { extractGlobalRootBody } from '../themes/parsers/globalRootBlock';
26
26
 
27
27
  // Re-exported for tests and downstream consumers that previously imported it
28
28
  // from this module. The canonical implementation lives in `./parsers/globalRootBlock`
29
29
  // so the dev-server vite plugin can share it.
30
30
  export { extractGlobalRootBody };
31
31
 
32
- const componentSources = import.meta.glob('../components/*.svelte', {
32
+ const componentSources = import.meta.glob('../../../system/components/*.svelte', {
33
33
  query: '?raw',
34
34
  import: 'default',
35
35
  eager: true,
@@ -0,0 +1,37 @@
1
+ import { writable } from 'svelte/store';
2
+ import type { ProductionInfo } from './themes/themeService';
3
+ import type { ManifestMeta } from './themes/themeTypes';
4
+
5
+ /**
6
+ * Monotonic counter that ticks every time a production pointer flips —
7
+ * theme production or a component's production. UI surfaces that need to
8
+ * react to a sibling Adopt subscribe to this so they refresh without
9
+ * per-pair wiring.
10
+ *
11
+ * Bumpers: `ThemeFileManager.handleApplyToProduction`,
12
+ * `ComponentFileManager.handleUpdateProduction`. Anyone setting
13
+ * `_production.json` should bump.
14
+ */
15
+ export const productionRevision = writable(0);
16
+
17
+ export function bumpProductionRevision(): void {
18
+ productionRevision.update((n) => n + 1);
19
+ }
20
+
21
+ /**
22
+ * Cached production-state stores. The Theme and Manifest file managers live in
23
+ * the sidebar footer, swapping in/out of the DOM as the user toggles between
24
+ * the tokens and components views. Keeping the last-known production state in
25
+ * module-level Svelte stores means a remount renders the correct Adopt-button
26
+ * state on the first frame instead of flashing through "not in sync" while a
27
+ * fresh fetch resolves.
28
+ */
29
+ export const themeProductionInfo = writable<ProductionInfo | null>(null);
30
+
31
+ /**
32
+ * Last-known active manifest meta. Bumped by ManifestFileManager whenever the
33
+ * active manifest changes (load, save, save-as) and whenever a theme or
34
+ * component Adopt completes (the server patches the active manifest as a
35
+ * side-effect, so consumers re-read it on `productionRevision` ticks).
36
+ */
37
+ export const activeManifest = writable<ManifestMeta | null>(null);
@@ -1,5 +1,5 @@
1
1
  import { writable } from 'svelte/store';
2
- import { storageKey } from './editorConfig';
2
+ import { storageKey } from '../store/editorConfig';
3
3
 
4
4
  function prevKey(): string {
5
5
  return storageKey('prev-route');
@@ -118,7 +118,14 @@ export function versionedFileResource<TItem, TMeta, TProductionInfo>(
118
118
  body: JSON.stringify({ name: fileName }),
119
119
  });
120
120
  if (!res.ok) {
121
- throw new Error(await readJsonError(res, 'Set production failed'));
121
+ const body = await res.json().catch(() => ({}));
122
+ const err = new Error(body.error || 'Set production failed') as Error & {
123
+ status?: number;
124
+ code?: string;
125
+ };
126
+ err.status = res.status;
127
+ if (body.code) err.code = body.code;
128
+ throw err;
122
129
  }
123
130
  return res.json();
124
131
  }
@@ -260,21 +260,37 @@ export function mutate(label: string, fn: (draft: EditorState) => void): void {
260
260
  if (import.meta.env.DEV && !label) {
261
261
  console.warn('[editorStore] mutate() called without a label');
262
262
  }
263
+ // Svelte 5's `$derived` uses strict `===` for equality (see
264
+ // node_modules/svelte/src/internal/client/reactivity/deriveds.js → equality.js).
265
+ // If we mutated `s` in place and emitted the same top-level reference, any
266
+ // intermediate `$derived($editorState.someSlice.someObj)` in a component
267
+ // would return the same object reference and short-circuit the whole
268
+ // downstream chain — swatches and previews would freeze even while the
269
+ // renderer's direct `editorState.subscribe(...)` (which uses `safe_not_equal`,
270
+ // always-different for objects) continued to write CSS vars to :root.
271
+ //
272
+ // Cloning the draft before mutation guarantees every nested reference is
273
+ // fresh, so `$derived` chains everywhere propagate correctly. The previous
274
+ // live state (`current`) becomes the immutable history snapshot — no second
275
+ // structuredClone needed.
263
276
  if (transactionScope) {
264
- // Inside a non-clipping scope: don't push individually; just mark the
265
- // scope dirty and apply. The scope's commit pushes one collapsed entry.
266
277
  transactionScope.changed = true;
267
278
  if (clippingScope) clippingScope.changed = true;
268
- store.update((s) => { fn(s); return s; });
279
+ store.update((s) => {
280
+ const next = structuredClone(s);
281
+ fn(next);
282
+ return next;
283
+ });
269
284
  return;
270
285
  }
271
- // No transaction scope: each mutate is its own history entry. Inside a
272
- // clipping scope this still pushes per-mutate entries (so undo within the
273
- // scope walks them back), and the scope's commit will collapse them.
274
286
  if (clippingScope) clippingScope.changed = true;
275
287
  const current = get(store);
276
- pushPast(structuredClone(current));
277
- store.update((s) => { fn(s); return s; });
288
+ pushPast(current);
289
+ store.update((s) => {
290
+ const next = structuredClone(s);
291
+ fn(next);
292
+ return next;
293
+ });
278
294
  bumpTick();
279
295
  persistHook();
280
296
  }
@@ -17,9 +17,9 @@ import { get } from 'svelte/store';
17
17
  import type { EditorState } from './editorTypes';
18
18
  import { storageKey } from './editorConfig';
19
19
  import { store } from './editorCore';
20
- import { quietGet, quietSet } from './storage';
21
- import { makeDefaultGradients } from './slices/gradients';
22
- import { seedShadowsFromDom } from './slices/shadows';
20
+ import { quietGet, quietSet } from '../storage/storage';
21
+ import { makeDefaultGradients } from '../themes/slices/gradients';
22
+ import { seedShadowsFromDom } from '../themes/slices/shadows';
23
23
 
24
24
  // Resolve the persist key lazily (per-call) so library consumers that invoke
25
25
  // `configureEditor({storagePrefix})` before the first store write get the
@@ -12,8 +12,8 @@
12
12
  */
13
13
 
14
14
  import type { EditorState } from './editorTypes';
15
- import { setCssVar, removeCssVar } from './cssVarSync';
16
- import { palettesToVars } from './paletteDerivation';
15
+ import { setCssVar, removeCssVar } from '../cssVarSync';
16
+ import { palettesToVars } from '../palettes/paletteDerivation';
17
17
  import {
18
18
  editorState,
19
19
  columnsToVars,
@@ -19,14 +19,14 @@
19
19
  */
20
20
 
21
21
  import type { CssVarRef, EditorState } from './editorTypes';
22
- import type { Theme } from './themeTypes';
23
- import { KNOWN_COMPONENT_CONFIG_KEYS } from './componentConfigKeys';
22
+ import type { Theme } from '../themes/themeTypes';
23
+ import { KNOWN_COMPONENT_CONFIG_KEYS } from '../components/componentConfigKeys';
24
24
  import {
25
25
  CURRENT_THEME_SCHEMA_VERSION,
26
26
  CURRENT_COMPONENT_SCHEMA_VERSION,
27
27
  runMigrations,
28
- } from './migrations';
29
- import { renamePrimaryPaletteKey } from './migrations/2026-05-13-primary-to-brand';
28
+ } from '../themes/migrations';
29
+ import { renamePrimaryPaletteKey } from '../themes/migrations/2026-05-13-primary-to-brand';
30
30
  import { __resetRendererCacheForTests, installRenderer } from './editorRenderer';
31
31
  import {
32
32
  store,
@@ -47,25 +47,25 @@ import {
47
47
  columnsEqualsDefault,
48
48
  columnsToVars,
49
49
  loadColumnsFromVars,
50
- } from './slices/columns';
50
+ } from '../themes/slices/columns';
51
51
  import {
52
52
  loadOverlaysFromVars,
53
53
  makeDefaultOverlaysState,
54
54
  overlaysEqualsDefault,
55
55
  overlaysToVars,
56
- } from './slices/overlays';
56
+ } from '../themes/slices/overlays';
57
57
  import {
58
58
  loadShadowsFromVars,
59
59
  shadowsToVars,
60
- } from './slices/shadows';
61
- import { makeDefaultGradients } from './slices/gradients';
60
+ } from '../themes/slices/shadows';
61
+ import { makeDefaultGradients } from '../themes/slices/gradients';
62
62
  import {
63
63
  componentBaseline,
64
64
  loadComponentsFromVars,
65
65
  notifyComponentSavedChanged,
66
66
  setSavedComponentBaseline,
67
67
  __resetComponentsForTests,
68
- } from './slices/components';
68
+ } from '../themes/slices/components';
69
69
 
70
70
  function emptyState(): EditorState {
71
71
  return {
@@ -121,7 +121,7 @@ store.set(emptyState());
121
121
 
122
122
  export {
123
123
  DOMAIN_VAR_NAMES,
124
- } from './slices/domainVars';
124
+ } from '../themes/slices/domainVars';
125
125
 
126
126
  export {
127
127
  columnsToVars,
@@ -129,7 +129,7 @@ export {
129
129
  COLUMN_VAR_NAMES,
130
130
  DEFAULT_COLUMNS,
131
131
  parseColumnVars,
132
- } from './slices/columns';
132
+ } from '../themes/slices/columns';
133
133
 
134
134
  export {
135
135
  overlaysToVars,
@@ -141,7 +141,7 @@ export {
141
141
  parseRgba,
142
142
  RGBA_RE,
143
143
  HEX_RE,
144
- } from './slices/overlays';
144
+ } from '../themes/slices/overlays';
145
145
 
146
146
  export {
147
147
  shadowsToVars,
@@ -153,7 +153,7 @@ export {
153
153
  defaultShadowOverride,
154
154
  parseShadowCss,
155
155
  seedShadowsFromDom,
156
- } from './slices/shadows';
156
+ } from '../themes/slices/shadows';
157
157
 
158
158
  export {
159
159
  gradientsToVars,
@@ -166,7 +166,7 @@ export {
166
166
  removeGradientStop,
167
167
  addGradientToken,
168
168
  removeGradientToken,
169
- } from './slices/gradients';
169
+ } from '../themes/slices/gradients';
170
170
 
171
171
  export {
172
172
  componentsToVars,
@@ -184,18 +184,18 @@ export {
184
184
  unlinkComponentProperty,
185
185
  relinkComponentProperty,
186
186
  markComponentSaved,
187
- } from './slices/components';
187
+ } from '../themes/slices/components';
188
188
 
189
189
  export {
190
190
  setFontSources,
191
191
  setFontStacks,
192
192
  seedFontsFromTheme,
193
- } from './slices/fonts';
193
+ } from '../themes/slices/fonts';
194
194
 
195
195
  export {
196
196
  setPaletteConfig,
197
197
  seedPalettesFromTheme,
198
- } from './slices/palettes';
198
+ } from '../themes/slices/palettes';
199
199
 
200
200
  // ── Component-config load orchestration ───────────────────────────────────
201
201
  //
@@ -1,4 +1,4 @@
1
- import type { PaletteConfig, FontSource, FontStack } from './themeTypes';
1
+ import type { PaletteConfig, FontSource, FontStack } from '../themes/themeTypes';
2
2
 
3
3
  export interface ShadowGlobals {
4
4
  angle: 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,8 @@
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
35
 
36
36
  const EMPTY_COMPONENT_BASELINE = JSON.stringify({ aliases: {}, config: {} });
37
37
 
@@ -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; });
@@ -4,8 +4,8 @@
4
4
  * (`--color-brand-500`); the renderer wraps them in `var(...)` so palette
5
5
  * edits flow through.
6
6
  */
7
- import type { EditorState, GradientToken, GradientTokenStop, GradientType } from '../editorTypes';
8
- import { mutate } from '../editorCore';
7
+ import type { EditorState, GradientToken, GradientTokenStop, GradientType } from '../../store/editorTypes';
8
+ import { mutate } from '../../store/editorCore';
9
9
 
10
10
  export function makeDefaultGradients(): GradientToken[] {
11
11
  return [
@@ -4,7 +4,7 @@
4
4
  * and diverge from tokens.css by design: the editor starts with a neutral
5
5
  * palette and tokens.css continues to win until first edit.
6
6
  */
7
- import type { EditorState, OverlayToken } from '../editorTypes';
7
+ import type { EditorState, OverlayToken } from '../../store/editorTypes';
8
8
 
9
9
  export function makeDefaultOverlayTokens(): OverlayToken[] {
10
10
  return [
@@ -5,7 +5,7 @@
5
5
  * (palette derivation involves OKLCH + bezier curves) at render time.
6
6
  */
7
7
  import type { PaletteConfig } from '../themeTypes';
8
- import { store, mutate, persist } from '../editorCore';
8
+ import { store, mutate, persist } from '../../store/editorCore';
9
9
 
10
10
  export function setPaletteConfig(label: string, config: PaletteConfig): void {
11
11
  mutate(`update palette ${label}`, (s) => {
@@ -8,8 +8,8 @@
8
8
  * x/y/blur/spread/hsla fields.
9
9
  */
10
10
  import { get } from 'svelte/store';
11
- import type { EditorState, ShadowToken } from '../editorTypes';
12
- import { store, persist } from '../editorCore';
11
+ import type { EditorState, ShadowToken } from '../../store/editorTypes';
12
+ import { store, persist } from '../../store/editorCore';
13
13
 
14
14
  export const SHADOW_VAR_NAMES = [
15
15
  '--shadow-sm', '--shadow-md', '--shadow-lg', '--shadow-xl', '--shadow-2xl',
@@ -38,7 +38,7 @@ export function shadowTokenCss(t: ShadowToken): string {
38
38
  return `${t.x}px ${t.y}px ${t.blur}px ${t.spread}px hsla(${t.hue}, ${t.saturation}%, ${t.lightness}%, ${t.opacity})`;
39
39
  }
40
40
 
41
- export function defaultShadowOverride(): import('../editorTypes').ShadowOverrideFlags {
41
+ export function defaultShadowOverride(): import('../../store/editorTypes').ShadowOverrideFlags {
42
42
  return { angle: false, opacity: false, color: false, distance: false, blur: false, size: false };
43
43
  }
44
44