@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,75 @@
1
+ {
2
+ "updatedAt": "2026-04-17",
3
+ "note": "Curated list of popular Google Fonts. Regenerate via scripts/fetch-google-fonts.ts to pull the full catalog.",
4
+ "fonts": [
5
+ { "family": "Inter", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
6
+ { "family": "Roboto", "category": "sans-serif", "variants": [100, 300, 400, 500, 700, 900] },
7
+ { "family": "Open Sans", "category": "sans-serif", "variants": [300, 400, 500, 600, 700, 800] },
8
+ { "family": "Lato", "category": "sans-serif", "variants": [100, 300, 400, 700, 900] },
9
+ { "family": "Montserrat", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
10
+ { "family": "Poppins", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
11
+ { "family": "Raleway", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
12
+ { "family": "Nunito", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700, 800, 900] },
13
+ { "family": "Nunito Sans", "category": "sans-serif", "variants": [200, 300, 400, 600, 700, 800, 900] },
14
+ { "family": "Work Sans", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
15
+ { "family": "Fira Sans", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
16
+ { "family": "Source Sans 3", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700, 800, 900] },
17
+ { "family": "DM Sans", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
18
+ { "family": "Manrope", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700, 800] },
19
+ { "family": "Space Grotesk", "category": "sans-serif", "variants": [300, 400, 500, 600, 700] },
20
+ { "family": "Plus Jakarta Sans", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700, 800] },
21
+ { "family": "Outfit", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
22
+ { "family": "Rubik", "category": "sans-serif", "variants": [300, 400, 500, 600, 700, 800, 900] },
23
+ { "family": "Mulish", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700, 800, 900, 1000] },
24
+ { "family": "Figtree", "category": "sans-serif", "variants": [300, 400, 500, 600, 700, 800, 900] },
25
+ { "family": "Ubuntu", "category": "sans-serif", "variants": [300, 400, 500, 700] },
26
+ { "family": "Barlow", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
27
+ { "family": "Karla", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700, 800] },
28
+ { "family": "Signika", "category": "sans-serif", "variants": [300, 400, 500, 600, 700] },
29
+ { "family": "Oswald", "category": "sans-serif", "variants": [200, 300, 400, 500, 600, 700] },
30
+ { "family": "PT Sans", "category": "sans-serif", "variants": [400, 700] },
31
+ { "family": "Archivo", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
32
+ { "family": "Hind", "category": "sans-serif", "variants": [300, 400, 500, 600, 700] },
33
+ { "family": "Noto Sans", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
34
+ { "family": "IBM Plex Sans", "category": "sans-serif", "variants": [100, 200, 300, 400, 500, 600, 700] },
35
+ { "family": "Faculty Glyphic", "category": "sans-serif", "variants": [400] },
36
+
37
+ { "family": "Playfair Display", "category": "serif", "variants": [400, 500, 600, 700, 800, 900] },
38
+ { "family": "Merriweather", "category": "serif", "variants": [300, 400, 700, 900] },
39
+ { "family": "Lora", "category": "serif", "variants": [400, 500, 600, 700] },
40
+ { "family": "PT Serif", "category": "serif", "variants": [400, 700] },
41
+ { "family": "Source Serif 4", "category": "serif", "variants": [200, 300, 400, 500, 600, 700, 800, 900] },
42
+ { "family": "Crimson Pro", "category": "serif", "variants": [200, 300, 400, 500, 600, 700, 800, 900] },
43
+ { "family": "Cormorant Garamond", "category": "serif", "variants": [300, 400, 500, 600, 700] },
44
+ { "family": "Libre Baskerville", "category": "serif", "variants": [400, 700] },
45
+ { "family": "EB Garamond", "category": "serif", "variants": [400, 500, 600, 700, 800] },
46
+ { "family": "Domine", "category": "serif", "variants": [400, 500, 600, 700] },
47
+ { "family": "Bitter", "category": "serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
48
+ { "family": "Noto Serif", "category": "serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
49
+ { "family": "Spectral", "category": "serif", "variants": [200, 300, 400, 500, 600, 700, 800] },
50
+ { "family": "Cardo", "category": "serif", "variants": [400, 700] },
51
+ { "family": "Fraunces", "category": "serif", "variants": [100, 200, 300, 400, 500, 600, 700, 800, 900] },
52
+ { "family": "DM Serif Display", "category": "serif", "variants": [400] },
53
+ { "family": "DM Serif Text", "category": "serif", "variants": [400] },
54
+
55
+ { "family": "Fira Code", "category": "monospace", "variants": [300, 400, 500, 600, 700] },
56
+ { "family": "JetBrains Mono", "category": "monospace", "variants": [100, 200, 300, 400, 500, 600, 700, 800] },
57
+ { "family": "Source Code Pro", "category": "monospace", "variants": [200, 300, 400, 500, 600, 700, 800, 900] },
58
+ { "family": "IBM Plex Mono", "category": "monospace", "variants": [100, 200, 300, 400, 500, 600, 700] },
59
+ { "family": "Inconsolata", "category": "monospace", "variants": [200, 300, 400, 500, 600, 700, 800, 900] },
60
+ { "family": "Roboto Mono", "category": "monospace", "variants": [100, 200, 300, 400, 500, 600, 700] },
61
+ { "family": "Space Mono", "category": "monospace", "variants": [400, 700] },
62
+ { "family": "DM Mono", "category": "monospace", "variants": [300, 400, 500] },
63
+
64
+ { "family": "Bebas Neue", "category": "display", "variants": [400] },
65
+ { "family": "Abril Fatface", "category": "display", "variants": [400] },
66
+ { "family": "Archivo Black", "category": "display", "variants": [400] },
67
+ { "family": "Alfa Slab One", "category": "display", "variants": [400] },
68
+ { "family": "Righteous", "category": "display", "variants": [400] },
69
+ { "family": "Anton", "category": "display", "variants": [400] },
70
+ { "family": "Permanent Marker", "category": "handwriting", "variants": [400] },
71
+ { "family": "Caveat", "category": "handwriting", "variants": [400, 500, 600, 700] },
72
+ { "family": "Dancing Script", "category": "handwriting", "variants": [400, 500, 600, 700] },
73
+ { "family": "Pacifico", "category": "handwriting", "variants": [400] }
74
+ ]
75
+ }
@@ -2,6 +2,10 @@
2
2
  import { onMount } from 'svelte';
3
3
  import { columnsVisible } from './columnsOverlay';
4
4
 
5
+ const isDev = import.meta.env.DEV;
6
+ const isInIframe = typeof window !== 'undefined' && window.parent !== window;
7
+ const enabled = isDev && !isInIframe;
8
+
5
9
  let count = 12;
6
10
  let gutter = '';
7
11
  let margin = '';
@@ -33,7 +37,7 @@
33
37
  });
34
38
  </script>
35
39
 
36
- {#if $columnsVisible}
40
+ {#if enabled && $columnsVisible}
37
41
  <div class="columns-overlay" aria-hidden="true">
38
42
  <div class="columns-overlay__inner">
39
43
  {#each Array(count) as _, i}
@@ -70,21 +74,30 @@
70
74
  height: 100%;
71
75
  display: grid;
72
76
  grid-template-columns: repeat(var(--columns-count, 12), 1fr);
77
+ /* Single implicit row that fills the container — without this, row
78
+ tracks default to `auto` and collapse to the content height (the
79
+ number span, ~12px), so the column fill and dashed borders are
80
+ only ~12px tall and look invisible against any backdrop. */
81
+ grid-template-rows: 1fr;
73
82
  gap: var(--columns-gutter);
74
83
  }
75
84
 
76
85
  .columns-overlay__col {
77
- background: rgba(239, 68, 68, 0.04);
78
- border-left: 1px dashed rgba(239, 68, 68, 0.22);
79
- border-right: 1px dashed rgba(239, 68, 68, 0.22);
86
+ /* Fallbacks let the overlay render on any host. --ui-* tokens are
87
+ declared in ui-editor.css which by convention is editor-scoped and
88
+ not loaded globally; without fallbacks the host site sees invisible
89
+ columns. */
90
+ background: var(--ui-overlay-fill, rgba(128, 128, 128, 0.06));
91
+ border-left: 1px dashed var(--ui-overlay-border, rgba(128, 128, 128, 0.32));
92
+ border-right: 1px dashed var(--ui-overlay-border, rgba(128, 128, 128, 0.32));
80
93
  }
81
94
 
82
95
  .columns-overlay__num {
83
96
  display: block;
84
- font-family: var(--font-mono);
97
+ font-family: var(--ui-font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace);
85
98
  font-size: 9px;
86
99
  line-height: 1;
87
- color: rgba(255, 255, 255, 0.4);
100
+ color: var(--ui-overlay-num, rgba(128, 128, 128, 0.75));
88
101
  text-align: center;
89
102
  padding-top: 3px;
90
103
  }
@@ -102,7 +115,7 @@
102
115
  -webkit-backdrop-filter: blur(6px);
103
116
  border: 1px solid rgba(255, 255, 255, 0.1);
104
117
  border-radius: 4px;
105
- font-family: var(--font-mono);
118
+ font-family: var(--ui-font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace);
106
119
  font-size: 10px;
107
120
  color: rgba(255, 255, 255, 0.75);
108
121
  letter-spacing: 0.02em;
@@ -1,28 +1,66 @@
1
- <script lang="ts" context="module">
2
- export type NavLink = { path: string; label: string; icon?: string };
1
+ <script context="module" lang="ts">
2
+ // __PROJECT_ROOT__ is injected by the themeFileApi Vite plugin as a `define`.
3
+ // Consumers don't need to configure it themselves. We declare it locally so
4
+ // this component's type-check passes in consumer projects that haven't added
5
+ // the ambient global to their tsconfig.
6
+ declare const __PROJECT_ROOT__: string | undefined;
7
+ const INJECTED_PROJECT_ROOT: string =
8
+ typeof __PROJECT_ROOT__ !== 'undefined' ? (__PROJECT_ROOT__ ?? '') : '';
3
9
  </script>
4
10
 
5
11
  <script lang="ts">
6
12
  import { onMount, onDestroy } from 'svelte';
13
+ import { fade } from 'svelte/transition';
14
+ import { cubicInOut } from 'svelte/easing';
7
15
  import { route, navigate } from './router';
8
16
  import { columnsVisible, toggleColumns } from './columnsOverlay';
9
17
  import { storageKey } from './editorConfig';
18
+ import { overlayOpen } from './overlayState';
19
+ import { quietGet, quietSet } from './storage';
20
+ import { postParentRoute } from './parentRouteStore';
21
+ import type { NavLink } from './navLinkTypes';
10
22
 
11
- export let open: boolean = false;
12
- export let editorPath: string = '/admin';
23
+ export let open: boolean | undefined = undefined;
24
+ export let editorPath: string = '/editor';
13
25
  export let navLinks: NavLink[] = [];
14
26
  export let pageSources: Record<string, string> = {};
15
- export let projectRoot: string =
16
- typeof __PROJECT_ROOT__ !== 'undefined' ? __PROJECT_ROOT__ : '';
17
-
27
+ export let hidePageSourceOn: string[] = [];
28
+ export let projectRoot: string = INJECTED_PROJECT_ROOT;
29
+
30
+ // Self-gate: only render in dev, and never inside an iframe (the /editor
31
+ // page embeds this same app in an iframe and would otherwise recursively
32
+ // mount another overlay).
33
+ const isDev = import.meta.env.DEV;
34
+ const isInIframe = typeof window !== 'undefined' && window.parent !== window;
35
+ const enabled = isDev && !isInIframe;
36
+
37
+ // Self-manage `open` when the consumer doesn't bind it. When they do, their
38
+ // binding wins and we skip our own persistence.
39
+ const OPEN_KEY = storageKey('overlay-open');
40
+ const consumerControlsOpen = open !== undefined;
41
+ if (!consumerControlsOpen) {
42
+ open = enabled && quietGet(OPEN_KEY) === '1';
43
+ }
44
+ $: if (!consumerControlsOpen && typeof window !== 'undefined') {
45
+ quietSet(OPEN_KEY, open ? '1' : '0');
46
+ }
47
+ $: overlayOpen.set(!!open);
48
+
49
+ // Hide the overlay entirely when the user is already on the editor route
50
+ // (the editor page has its own chrome).
51
+ $: onEditorPath = $route === editorPath;
18
52
  $: sourceFile = pageSources[$route];
53
+ $: showSource = !!sourceFile && !!projectRoot && !hidePageSourceOn.includes($route);
19
54
 
20
55
  // Mount the iframe the first time the editor is shown, then keep it mounted
21
56
  // across hide/show cycles so editor state (unsaved slider values, scroll
22
57
  // position, expanded sections) survives.
23
- let hasBeenOpen: boolean = open;
58
+ let hasBeenOpen: boolean = !!open;
24
59
  $: if (open) hasBeenOpen = true;
25
60
 
61
+ let editorFrame: HTMLIFrameElement | undefined;
62
+ $: postParentRoute(editorFrame?.contentWindow, $route);
63
+
26
64
  type Mode = 'docked' | 'floating';
27
65
 
28
66
  const STORAGE_KEY = storageKey('overlay-state');
@@ -43,23 +81,18 @@
43
81
  }
44
82
 
45
83
  function loadState(): OverlayState {
46
- try {
47
- const raw = localStorage.getItem(STORAGE_KEY);
48
- if (raw) {
49
- const parsed = JSON.parse(raw) as Partial<OverlayState>;
50
- return {
51
- mode: parsed.mode === 'floating' ? 'floating' : 'docked',
52
- dockedWidth: typeof parsed.dockedWidth === 'number' ? parsed.dockedWidth : DEFAULT_DOCKED_WIDTH,
53
- floating: {
54
- x: parsed.floating?.x ?? DEFAULT_FLOATING.x,
55
- y: parsed.floating?.y ?? DEFAULT_FLOATING.y,
56
- width: parsed.floating?.width ?? DEFAULT_FLOATING.width,
57
- height: parsed.floating?.height ?? DEFAULT_FLOATING.height,
58
- },
59
- };
60
- }
61
- } catch {
62
- // ignore
84
+ const parsed = quietGet<Partial<OverlayState>>(STORAGE_KEY, { parse: true });
85
+ if (parsed && typeof parsed === 'object') {
86
+ return {
87
+ mode: parsed.mode === 'floating' ? 'floating' : 'docked',
88
+ dockedWidth: typeof parsed.dockedWidth === 'number' ? parsed.dockedWidth : DEFAULT_DOCKED_WIDTH,
89
+ floating: {
90
+ x: parsed.floating?.x ?? DEFAULT_FLOATING.x,
91
+ y: parsed.floating?.y ?? DEFAULT_FLOATING.y,
92
+ width: parsed.floating?.width ?? DEFAULT_FLOATING.width,
93
+ height: parsed.floating?.height ?? DEFAULT_FLOATING.height,
94
+ },
95
+ };
63
96
  }
64
97
  return {
65
98
  mode: 'docked',
@@ -69,11 +102,7 @@
69
102
  }
70
103
 
71
104
  function persist() {
72
- try {
73
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ mode, dockedWidth, floating }));
74
- } catch {
75
- // ignore quota errors
76
- }
105
+ quietSet(STORAGE_KEY, JSON.stringify({ mode, dockedWidth, floating }));
77
106
  }
78
107
 
79
108
  const initial = loadState();
@@ -81,6 +110,20 @@
81
110
  let dockedWidth: number = Math.max(MIN_WIDTH, initial.dockedWidth);
82
111
  let floating = { ...initial.floating };
83
112
 
113
+ // Approximate natural size of the collapsed pill (Editor title + columns toggle).
114
+ // A few pixels of overshoot is fine — the panel has overflow:hidden.
115
+ const COLLAPSED_WIDTH = 184;
116
+ const COLLAPSED_HEIGHT = 38;
117
+
118
+ // Fade timing for the buttons that only render when open (float toggle, spacer,
119
+ // nav links, page-source). The bar's grow/shrink + iframe fade live in CSS vars
120
+ // at the top of the style block.
121
+ const BTN_FADE = { duration: 130, easing: cubicInOut };
122
+
123
+ // Suppress CSS transitions during gestures and mode swaps so dragging doesn't
124
+ // re-animate every frame, and floating↔docked swaps snap cleanly.
125
+ let suppressTransition = false;
126
+
84
127
  // Gesture state — a transparent scrim covers the iframe while any gesture is active
85
128
  // so pointer events land on the panel, not on content inside the iframe.
86
129
  let gesturing: 'drag' | 'resize-left' | 'resize-se' | null = null;
@@ -161,6 +204,7 @@
161
204
  }
162
205
 
163
206
  function toggleMode() {
207
+ suppressTransition = true;
164
208
  mode = mode === 'docked' ? 'floating' : 'docked';
165
209
  // Snap the floating rect back inside the viewport if it drifted off-screen since last use
166
210
  if (mode === 'floating') {
@@ -172,6 +216,7 @@
172
216
  };
173
217
  }
174
218
  persist();
219
+ requestAnimationFrame(() => requestAnimationFrame(() => { suppressTransition = false; }));
175
220
  }
176
221
 
177
222
  function toggleOpen() {
@@ -185,31 +230,25 @@
185
230
  toggleOpen();
186
231
  }
187
232
 
188
- // Messages from the editor iframe:
189
- // lt-overlay-close iframe's Close button (hides the editor)
190
- function handleMessage(ev: MessageEvent) {
191
- if (ev.origin !== window.location.origin) return;
192
- const data = ev.data;
193
- if (!data || typeof data !== 'object') return;
194
- if (data.type === 'lt-overlay-close') {
195
- open = false;
196
- }
233
+ function handleToggleRequest() {
234
+ open = !open;
197
235
  }
198
236
 
199
237
  onMount(() => {
200
- window.addEventListener('message', handleMessage);
238
+ window.addEventListener('lt-overlay-toggle', handleToggleRequest);
201
239
  });
202
240
  onDestroy(() => {
203
- window.removeEventListener('message', handleMessage);
241
+ window.removeEventListener('lt-overlay-toggle', handleToggleRequest);
204
242
  });
205
243
 
206
244
  $: panelStyle = !open
207
- ? 'position: fixed; top: 12px; right: 12px;'
245
+ ? `position: fixed; top: 12px; right: 12px; width: ${COLLAPSED_WIDTH}px; height: ${COLLAPSED_HEIGHT}px;`
208
246
  : mode === 'docked'
209
- ? `position: fixed; top: 0; right: 0; bottom: 0; width: ${dockedWidth}px;`
247
+ ? `position: fixed; top: 0; right: 0; width: ${dockedWidth}px; height: 100vh;`
210
248
  : `position: fixed; top: ${floating.y}px; left: ${floating.x}px; width: ${floating.width}px; height: ${floating.height}px;`;
211
249
  </script>
212
250
 
251
+ {#if enabled && !onEditorPath}
213
252
  <!-- svelte-ignore a11y-no-static-element-interactions -->
214
253
  <div
215
254
  class="lt-overlay"
@@ -218,6 +257,7 @@
218
257
  class:hidden={!open}
219
258
  class:docked={open && mode === 'docked'}
220
259
  class:floating={open && mode === 'floating'}
260
+ class:no-transition={!!gesturing || suppressTransition}
221
261
  >
222
262
  <div
223
263
  class="header"
@@ -225,25 +265,14 @@
225
265
  on:dblclick={handleHeaderDblClick}
226
266
  title={open ? 'Double-click to hide' : 'Double-click to show'}
227
267
  >
228
- {#if open}
229
- <span class="title">Design Editor</span>
230
- <div class="spacer"></div>
231
- {/if}
232
-
233
- {#if open && navLinks.length > 0}
234
- <div class="preview-nav">
235
- {#each navLinks as link (link.path)}
236
- <button
237
- class="hdr-btn nav"
238
- class:active={$route === link.path}
239
- on:click={() => navigate(link.path)}
240
- >
241
- {#if link.icon}<i class="fas {link.icon}"></i>{/if}
242
- <span>{link.label}</span>
243
- </button>
244
- {/each}
245
- </div>
246
- {/if}
268
+ <button
269
+ class="hdr-btn text title"
270
+ on:click={toggleOpen}
271
+ title={open ? 'Hide Editor' : 'Show Editor'}
272
+ >
273
+ <i class="fas {open ? 'fa-chevron-right' : 'fa-chevron-left'}"></i>
274
+ <span>Editor</span>
275
+ </button>
247
276
 
248
277
  <button
249
278
  class="hdr-btn icon"
@@ -254,34 +283,64 @@
254
283
  <i class="fas fa-grip-lines-vertical"></i>
255
284
  </button>
256
285
 
257
- <button class="hdr-btn text" on:click={toggleOpen}>
258
- {open ? 'Hide Editor' : 'Editor'}
259
- </button>
260
-
261
286
  {#if open}
262
- <button class="hdr-btn icon" title={mode === 'docked' ? 'Float' : 'Dock to right'} on:click={toggleMode}>
287
+ <button
288
+ class="hdr-btn icon"
289
+ title={mode === 'docked' ? 'Float' : 'Dock to right'}
290
+ on:click={toggleMode}
291
+ transition:fade={BTN_FADE}
292
+ >
263
293
  <i class={mode === 'docked' ? 'fas fa-up-right-from-square' : 'fas fa-thumbtack'}></i>
264
294
  </button>
265
295
  {/if}
266
296
 
267
- {#if sourceFile && projectRoot}
297
+ {#if open}
298
+ <div class="spacer" transition:fade={BTN_FADE}></div>
299
+ {/if}
300
+
301
+ {#if open && showSource}
268
302
  <a
269
303
  class="hdr-btn text source"
270
304
  href="vscode://file/{projectRoot}/{sourceFile}"
271
305
  title="Open {sourceFile} in VS Code"
306
+ transition:fade={BTN_FADE}
272
307
  >
273
308
  <i class="fas fa-code"></i>
274
- Page Source
309
+ Show Source
275
310
  </a>
276
311
  {/if}
312
+
313
+ {#if open && navLinks.length > 0}
314
+ <div class="seg-group" transition:fade={BTN_FADE}>
315
+ <span class="seg-label">Active Page:</span>
316
+ <div class="seg-bar" role="tablist" aria-label="Underlying page">
317
+ {#each navLinks as link (link.path)}
318
+ <button
319
+ type="button"
320
+ role="tab"
321
+ class="seg-pill"
322
+ class:active={$route === link.path}
323
+ aria-selected={$route === link.path}
324
+ disabled={link.disabled}
325
+ on:click={() => navigate(link.path)}
326
+ >
327
+ {#if link.icon}<i class="fas {link.icon}"></i>{/if}
328
+ <span>{link.label}</span>
329
+ </button>
330
+ {/each}
331
+ </div>
332
+ </div>
333
+ {/if}
277
334
  </div>
278
335
 
279
336
  {#if hasBeenOpen}
280
337
  <div class="frame-wrap">
281
338
  <iframe
282
339
  src={editorPath}
283
- title="Design editor"
340
+ title="Token editor"
284
341
  class="editor-frame"
342
+ bind:this={editorFrame}
343
+ on:load={() => postParentRoute(editorFrame?.contentWindow, $route)}
285
344
  ></iframe>
286
345
  {#if gesturing}
287
346
  <div class="gesture-scrim"></div>
@@ -295,9 +354,26 @@
295
354
  {/if}
296
355
  {/if}
297
356
  </div>
357
+ {/if}
298
358
 
299
359
  <style>
300
360
  .lt-overlay {
361
+ /* Animation knobs. bar = panel grow/shrink, pane = iframe fade.
362
+ open = collapsed to expanded, close = expanded to collapsed. */
363
+ --bar-open-dur: 240ms;
364
+ --bar-open-ease: cubic-bezier(0.65, 0, 0.35, 1);
365
+ --bar-open-delay: 0ms;
366
+ --bar-close-dur: 240ms;
367
+ --bar-close-ease: cubic-bezier(0.65, 0, 0.35, 1);
368
+ --bar-close-delay: 70ms;
369
+
370
+ --pane-open-dur: 140ms;
371
+ --pane-open-ease: cubic-bezier(0.65, 0, 0.35, 1);
372
+ --pane-open-delay: 140ms;
373
+ --pane-close-dur: 80ms;
374
+ --pane-close-ease: cubic-bezier(0.65, 0, 0.35, 1);
375
+ --pane-close-delay: 0ms;
376
+
301
377
  display: flex;
302
378
  flex-direction: column;
303
379
  background: #0a0a0a;
@@ -307,6 +383,12 @@
307
383
  overflow: hidden;
308
384
  font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
309
385
  color: #fff;
386
+ transition:
387
+ width var(--bar-open-dur) var(--bar-open-ease) var(--bar-open-delay),
388
+ height var(--bar-open-dur) var(--bar-open-ease) var(--bar-open-delay),
389
+ top var(--bar-open-dur) var(--bar-open-ease) var(--bar-open-delay),
390
+ right var(--bar-open-dur) var(--bar-open-ease) var(--bar-open-delay),
391
+ border-radius var(--bar-open-dur) var(--bar-open-ease) var(--bar-open-delay);
310
392
  }
311
393
 
312
394
  .lt-overlay.docked {
@@ -319,19 +401,28 @@
319
401
  }
320
402
 
321
403
  /* Hidden state: the editor panel is collapsed to just the header bar,
322
- pinned to the top-right. The iframe stays mounted (for instant show)
323
- but its container is display:none. */
404
+ pinned to the top-right. The iframe stays mounted; its container fades
405
+ to opacity 0 and the panel shrinks around it. */
324
406
  .lt-overlay.hidden {
325
407
  border-radius: 6px;
326
- width: auto;
408
+ transition:
409
+ width var(--bar-close-dur) var(--bar-close-ease) var(--bar-close-delay),
410
+ height var(--bar-close-dur) var(--bar-close-ease) var(--bar-close-delay),
411
+ top var(--bar-close-dur) var(--bar-close-ease) var(--bar-close-delay),
412
+ right var(--bar-close-dur) var(--bar-close-ease) var(--bar-close-delay),
413
+ border-radius var(--bar-close-dur) var(--bar-close-ease) var(--bar-close-delay);
327
414
  }
328
415
 
329
- .lt-overlay.hidden .frame-wrap,
330
416
  .lt-overlay.hidden .resize-left,
331
417
  .lt-overlay.hidden .resize-se {
332
418
  display: none;
333
419
  }
334
420
 
421
+ .lt-overlay.no-transition,
422
+ .lt-overlay.no-transition .frame-wrap {
423
+ transition: none !important;
424
+ }
425
+
335
426
  .header {
336
427
  display: flex;
337
428
  align-items: center;
@@ -353,7 +444,8 @@
353
444
  cursor: move;
354
445
  }
355
446
 
356
- .title {
447
+ .hdr-btn.title {
448
+ gap: 7px;
357
449
  font-size: 13px;
358
450
  font-weight: 600;
359
451
  color: rgba(255, 255, 255, 0.85);
@@ -391,6 +483,20 @@
391
483
  a.hdr-btn.source {
392
484
  gap: 6px;
393
485
  text-decoration: none;
486
+ background: rgba(255, 255, 255, 0.06);
487
+ border-color: rgba(255, 255, 255, 0.45);
488
+ border-radius: 999px;
489
+ padding: 0 14px;
490
+ color: rgba(255, 255, 255, 0.92);
491
+ }
492
+
493
+ a.hdr-btn.source:hover {
494
+ background: rgba(255, 255, 255, 0.14);
495
+ border-color: rgba(255, 255, 255, 0.6);
496
+ }
497
+
498
+ a.hdr-btn.source i {
499
+ color: rgba(255, 255, 255, 0.6);
394
500
  }
395
501
 
396
502
  .hdr-btn.nav {
@@ -412,17 +518,90 @@
412
518
  border-color: rgba(255, 255, 255, 0.18);
413
519
  }
414
520
 
415
- .preview-nav {
416
- display: flex;
417
- gap: 3px;
521
+ .seg-group {
522
+ display: inline-flex;
523
+ align-items: center;
524
+ gap: 8px;
525
+ margin-left: 18px;
418
526
  margin-right: 4px;
419
527
  }
420
528
 
529
+ .seg-label {
530
+ font-size: 11px;
531
+ font-weight: 600;
532
+ letter-spacing: 0.02em;
533
+ color: #fff;
534
+ }
535
+
536
+ .seg-bar {
537
+ display: inline-flex;
538
+ align-items: center;
539
+ padding: 3px;
540
+ background: rgba(0, 0, 0, 0.55);
541
+ border: 1px solid rgba(255, 255, 255, 0.28);
542
+ border-radius: 6px;
543
+ box-shadow:
544
+ inset 0 1px 0 rgba(0, 0, 0, 0.5),
545
+ 0 0 0 1px rgba(0, 0, 0, 0.4);
546
+ }
547
+
548
+ .seg-pill {
549
+ display: inline-flex;
550
+ align-items: center;
551
+ gap: 5px;
552
+ height: 22px;
553
+ padding: 0 9px;
554
+ background: transparent;
555
+ border: 1px solid transparent;
556
+ border-radius: 3px;
557
+ color: rgba(255, 255, 255, 0.6);
558
+ font-family: inherit;
559
+ font-size: 11px;
560
+ font-weight: 500;
561
+ cursor: pointer;
562
+ transition: background 0.1s, color 0.1s, border-color 0.1s;
563
+ }
564
+
565
+ .seg-pill i {
566
+ font-size: 10px;
567
+ opacity: 0.85;
568
+ }
569
+
570
+ .seg-pill:hover:not(:disabled) {
571
+ color: rgba(255, 255, 255, 0.9);
572
+ }
573
+
574
+ .seg-pill:disabled {
575
+ color: rgba(255, 255, 255, 0.28);
576
+ cursor: not-allowed;
577
+ }
578
+
579
+ .seg-pill:disabled i {
580
+ opacity: 0.5;
581
+ }
582
+
583
+ /* Outlined active — quieter than the iframe's filled switcher, so the two
584
+ segmented controls read as siblings, not twins. */
585
+ .seg-pill.active {
586
+ color: #fff;
587
+ border-color: rgba(255, 255, 255, 0.3);
588
+ background: rgba(255, 255, 255, 0.1);
589
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
590
+ }
591
+
421
592
  .frame-wrap {
422
593
  position: relative;
423
594
  flex: 1;
424
595
  min-height: 0;
425
596
  background: #000;
597
+ transition: opacity var(--pane-open-dur) var(--pane-open-ease) var(--pane-open-delay);
598
+ opacity: 1;
599
+ }
600
+
601
+ .lt-overlay.hidden .frame-wrap {
602
+ opacity: 0;
603
+ pointer-events: none;
604
+ transition: opacity var(--pane-close-dur) var(--pane-close-ease) var(--pane-close-delay);
426
605
  }
427
606
 
428
607
  .editor-frame {