@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,246 @@
1
+ import type { ComponentType, SvelteComponent } from 'svelte';
2
+ import type { Token } from './scaffolding/types';
3
+ import { registerComponentSchema } from '../lib/editorStore';
4
+
5
+ import BadgeEditor, { allTokens as badgeTokens } from './BadgeEditor.svelte';
6
+ import CalloutEditor, { allTokens as calloutTokens } from './CalloutEditor.svelte';
7
+ import CornerBadgeEditor, { allTokens as cornerBadgeTokens } from './CornerBadgeEditor.svelte';
8
+ import StandardButtonsEditor, { allTokens as buttonTokens } from './StandardButtonsEditor.svelte';
9
+ import CardEditor, { allTokens as cardTokens } from './CardEditor.svelte';
10
+ import CollapsibleSectionEditor, { allTokens as collapsibleSectionTokens } from './CollapsibleSectionEditor.svelte';
11
+ import DialogEditor, { allTokens as dialogTokens } from './DialogEditor.svelte';
12
+ import ImageEditor, { allTokens as imageTokens } from './ImageEditor.svelte';
13
+ import InlineEditActionsEditor, { allTokens as inlineEditActionsTokens } from './InlineEditActionsEditor.svelte';
14
+ import NotificationEditor, { allTokens as notificationTokens } from './NotificationEditor.svelte';
15
+ import ProgressBarEditor, { allTokens as progressBarTokens } from './ProgressBarEditor.svelte';
16
+ import RadioButtonEditor, { allTokens as radioButtonTokens } from './RadioButtonEditor.svelte';
17
+ import SectionDividerEditor, { allTokens as sectionDividerTokens } from './SectionDividerEditor.svelte';
18
+ import SegmentedControlEditor, { allTokens as segmentedControlTokens } from './SegmentedControlEditor.svelte';
19
+ import TableEditor, { allTokens as tableTokens } from './TableEditor.svelte';
20
+ import TabBarEditor, { allTokens as tabBarTokens } from './TabBarEditor.svelte';
21
+ import TooltipEditor, { allTokens as tooltipTokens } from './TooltipEditor.svelte';
22
+
23
+ export type ComponentId =
24
+ | 'segmentedcontrol'
25
+ | 'button'
26
+ | 'notification'
27
+ | 'dialog'
28
+ | 'radiobutton'
29
+ | 'card'
30
+ | 'badge'
31
+ | 'callout'
32
+ | 'cornerbadge'
33
+ | 'image'
34
+ | 'inlineeditactions'
35
+ | 'sectiondivider'
36
+ | 'collapsiblesection'
37
+ | 'table'
38
+ | 'tabbar'
39
+ | 'tooltip'
40
+ | 'progressbar';
41
+
42
+ export interface RegistryEntry {
43
+ /** Canonical id — lowercase, matches the runtime component filename + server scan + `setComponentAlias` key. */
44
+ id: ComponentId;
45
+ /** Human-readable label for nav and section headers. */
46
+ label: string;
47
+ /** FontAwesome icon class for the nav rail. */
48
+ icon: string;
49
+ /** Path to the runtime component, relative to repo root. */
50
+ sourceFile: string;
51
+ /** The Svelte editor component to mount in the page. */
52
+ editorComponent: ComponentType<SvelteComponent>;
53
+ /** Flat token list — the editor's declarative description of its token surface. */
54
+ schema: Token[];
55
+ }
56
+
57
+ /**
58
+ * Single source of truth for every component editor. Each entry binds the
59
+ * canonical id to its label, icon, source file, editor component, and token
60
+ * schema. Display order in the nav rail is sorted alphabetically by label
61
+ * (see `componentRegistryEntries` below) — order in this object literal does
62
+ * not affect the UI.
63
+ *
64
+ * Adding a component:
65
+ * 1. Author `src/components/<Name>.svelte` (declares CSS vars in `:global(:root)`)
66
+ * 2. Author `src/component-editor/<Name>Editor.svelte` (exports `allTokens` from a `<script context="module">` block)
67
+ * 3. Add an entry below.
68
+ */
69
+ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = Object.freeze({
70
+ segmentedcontrol: {
71
+ id: 'segmentedcontrol',
72
+ label: 'Segmented Control',
73
+ icon: 'fas fa-hand-pointer',
74
+ sourceFile: 'src/components/SegmentedControl.svelte',
75
+ editorComponent: SegmentedControlEditor,
76
+ schema: segmentedControlTokens,
77
+ },
78
+ button: {
79
+ id: 'button',
80
+ label: 'Button',
81
+ icon: 'fas fa-square',
82
+ sourceFile: 'src/components/Button.svelte',
83
+ editorComponent: StandardButtonsEditor,
84
+ schema: buttonTokens,
85
+ },
86
+ notification: {
87
+ id: 'notification',
88
+ label: 'Notification',
89
+ icon: 'fas fa-bell',
90
+ sourceFile: 'src/components/Notification.svelte',
91
+ editorComponent: NotificationEditor,
92
+ schema: notificationTokens,
93
+ },
94
+ dialog: {
95
+ id: 'dialog',
96
+ label: 'Dialog',
97
+ icon: 'fas fa-window-restore',
98
+ sourceFile: 'src/components/Dialog.svelte',
99
+ editorComponent: DialogEditor,
100
+ schema: dialogTokens,
101
+ },
102
+ radiobutton: {
103
+ id: 'radiobutton',
104
+ label: 'Radio Button',
105
+ icon: 'fas fa-dot-circle',
106
+ sourceFile: 'src/components/RadioButton.svelte',
107
+ editorComponent: RadioButtonEditor,
108
+ schema: radioButtonTokens,
109
+ },
110
+ card: {
111
+ id: 'card',
112
+ label: 'Card',
113
+ icon: 'fas fa-id-card',
114
+ sourceFile: 'src/components/Card.svelte',
115
+ editorComponent: CardEditor,
116
+ schema: cardTokens,
117
+ },
118
+ badge: {
119
+ id: 'badge',
120
+ label: 'Badge',
121
+ icon: 'fas fa-tag',
122
+ sourceFile: 'src/components/Badge.svelte',
123
+ editorComponent: BadgeEditor,
124
+ schema: badgeTokens,
125
+ },
126
+ callout: {
127
+ id: 'callout',
128
+ label: 'Callout',
129
+ icon: 'fas fa-quote-left',
130
+ sourceFile: 'src/components/Callout.svelte',
131
+ editorComponent: CalloutEditor,
132
+ schema: calloutTokens,
133
+ },
134
+ cornerbadge: {
135
+ id: 'cornerbadge',
136
+ label: 'Corner Badge',
137
+ icon: 'fas fa-tags',
138
+ sourceFile: 'src/components/CornerBadge.svelte',
139
+ editorComponent: CornerBadgeEditor,
140
+ schema: cornerBadgeTokens,
141
+ },
142
+ image: {
143
+ id: 'image',
144
+ label: 'Image',
145
+ icon: 'fas fa-image',
146
+ sourceFile: 'src/components/Image.svelte',
147
+ editorComponent: ImageEditor,
148
+ schema: imageTokens,
149
+ },
150
+ inlineeditactions: {
151
+ id: 'inlineeditactions',
152
+ label: 'Inline Edit Actions',
153
+ icon: 'fas fa-pen',
154
+ sourceFile: 'src/components/InlineEditActions.svelte',
155
+ editorComponent: InlineEditActionsEditor,
156
+ schema: inlineEditActionsTokens,
157
+ },
158
+ sectiondivider: {
159
+ id: 'sectiondivider',
160
+ label: 'Section Divider',
161
+ icon: 'fas fa-minus',
162
+ sourceFile: 'src/components/SectionDivider.svelte',
163
+ editorComponent: SectionDividerEditor,
164
+ schema: sectionDividerTokens,
165
+ },
166
+ collapsiblesection: {
167
+ id: 'collapsiblesection',
168
+ label: 'Collapsible Section',
169
+ icon: 'fas fa-chevron-down',
170
+ sourceFile: 'src/components/CollapsibleSection.svelte',
171
+ editorComponent: CollapsibleSectionEditor,
172
+ schema: collapsibleSectionTokens,
173
+ },
174
+ table: {
175
+ id: 'table',
176
+ label: 'Table',
177
+ icon: 'fas fa-table',
178
+ sourceFile: 'src/components/Table.svelte',
179
+ editorComponent: TableEditor,
180
+ schema: tableTokens,
181
+ },
182
+ tabbar: {
183
+ id: 'tabbar',
184
+ label: 'Tab Bar',
185
+ icon: 'fas fa-columns',
186
+ sourceFile: 'src/components/TabBar.svelte',
187
+ editorComponent: TabBarEditor,
188
+ schema: tabBarTokens,
189
+ },
190
+ tooltip: {
191
+ id: 'tooltip',
192
+ label: 'Tooltip',
193
+ icon: 'fas fa-comment-dots',
194
+ sourceFile: 'src/components/Tooltip.svelte',
195
+ editorComponent: TooltipEditor,
196
+ schema: tooltipTokens,
197
+ },
198
+ progressbar: {
199
+ id: 'progressbar',
200
+ label: 'Progress Bar',
201
+ icon: 'fas fa-tasks',
202
+ sourceFile: 'src/components/ProgressBar.svelte',
203
+ editorComponent: ProgressBarEditor,
204
+ schema: progressBarTokens,
205
+ },
206
+ });
207
+
208
+ /** Display-ordered list of registry entries — sorted alphabetically by label. Iteration order matches the nav rail. */
209
+ export const componentRegistryEntries: ReadonlyArray<RegistryEntry> = Object.freeze(
210
+ Object.values(componentRegistry).sort((a, b) => a.label.localeCompare(b.label)),
211
+ );
212
+
213
+ /** All canonical component ids, in display order. */
214
+ export const componentIds: ReadonlyArray<ComponentId> = Object.freeze(
215
+ componentRegistryEntries.map((e) => e.id),
216
+ );
217
+
218
+ // Eager schema registration. Replaces the side-effect-on-import pattern that
219
+ // each editor module previously used (top-of-script `registerComponentSchema(...)`).
220
+ // Runs once at module load, before any editor instance mounts.
221
+ for (const entry of componentRegistryEntries) {
222
+ registerComponentSchema(entry.id, entry.schema);
223
+ }
224
+
225
+ /**
226
+ * Validate that the server's filesystem scan matches the registry's id list.
227
+ * Logs a warning when ids drift. Called at boot from the editor page.
228
+ */
229
+ export function validateRegistryAgainstServerScan(serverIds: ReadonlyArray<string>): void {
230
+ const registrySet = new Set<string>(componentIds);
231
+ const serverSet = new Set<string>(serverIds);
232
+ const missingOnServer = componentIds.filter((id) => !serverSet.has(id));
233
+ const extraOnServer = serverIds.filter((id) => !registrySet.has(id));
234
+ if (missingOnServer.length > 0) {
235
+ console.warn(
236
+ '[componentRegistry] registered components missing from server scan:',
237
+ missingOnServer.join(', '),
238
+ );
239
+ }
240
+ if (extraOnServer.length > 0) {
241
+ console.warn(
242
+ '[componentRegistry] components on disk not in registry (will be ignored by editor):',
243
+ extraOnServer.join(', '),
244
+ );
245
+ }
246
+ }
@@ -0,0 +1,185 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Compact rotary control for an angle (0–360°). Drag the dial to rotate;
4
+ * type into the input for an exact value. The internal angle uses the CSS
5
+ * gradient convention (0deg = pointing up, increasing clockwise) so the
6
+ * displayed line orients the way the gradient axis will paint.
7
+ */
8
+ import { createEventDispatcher } from 'svelte';
9
+
10
+ export let value: number = 0;
11
+ export let label: string = 'Angle';
12
+ export let size: number = 44;
13
+
14
+ const dispatch = createEventDispatcher<{ change: { value: number } }>();
15
+
16
+ let dialEl: HTMLDivElement;
17
+ let dragging = false;
18
+
19
+ function normalize(deg: number): number {
20
+ const r = Math.round(deg) % 360;
21
+ return r < 0 ? r + 360 : r;
22
+ }
23
+
24
+ function emit(next: number) {
25
+ const n = normalize(next);
26
+ if (n === value) return;
27
+ value = n;
28
+ dispatch('change', { value: n });
29
+ }
30
+
31
+ function angleFromEvent(e: PointerEvent): number {
32
+ const rect = dialEl.getBoundingClientRect();
33
+ const cx = rect.left + rect.width / 2;
34
+ const cy = rect.top + rect.height / 2;
35
+ const dx = e.clientX - cx;
36
+ const dy = e.clientY - cy;
37
+ // atan2 yields 0 at +x axis, increasing counterclockwise. Convert to the
38
+ // CSS gradient convention (0 at +y up, clockwise positive).
39
+ return (Math.atan2(dx, -dy) * 180) / Math.PI;
40
+ }
41
+
42
+ function onPointerDown(e: PointerEvent) {
43
+ dragging = true;
44
+ dialEl.setPointerCapture(e.pointerId);
45
+ emit(angleFromEvent(e));
46
+ }
47
+ function onPointerMove(e: PointerEvent) {
48
+ if (!dragging) return;
49
+ emit(angleFromEvent(e));
50
+ }
51
+ function onPointerUp(e: PointerEvent) {
52
+ if (!dragging) return;
53
+ dragging = false;
54
+ dialEl.releasePointerCapture(e.pointerId);
55
+ }
56
+
57
+ function onInputChange(e: Event) {
58
+ const v = parseFloat((e.target as HTMLInputElement).value);
59
+ if (Number.isFinite(v)) emit(v);
60
+ }
61
+
62
+ $: indicatorTransform = `rotate(${value}deg)`;
63
+ </script>
64
+
65
+ <div class="angle-dial-row">
66
+ {#if label}
67
+ <span class="dial-label">{label}:</span>
68
+ {/if}
69
+ <div
70
+ bind:this={dialEl}
71
+ class="dial"
72
+ class:dragging
73
+ style="width: {size}px; height: {size}px;"
74
+ on:pointerdown={onPointerDown}
75
+ on:pointermove={onPointerMove}
76
+ on:pointerup={onPointerUp}
77
+ on:pointercancel={onPointerUp}
78
+ role="slider"
79
+ aria-valuemin="0"
80
+ aria-valuemax="360"
81
+ aria-valuenow={value}
82
+ aria-label={label}
83
+ tabindex="0"
84
+ on:keydown={(e) => {
85
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { e.preventDefault(); emit(value - 1); }
86
+ else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { e.preventDefault(); emit(value + 1); }
87
+ }}
88
+ >
89
+ <div class="indicator" style="transform: {indicatorTransform}"></div>
90
+ <div class="hub"></div>
91
+ </div>
92
+ <input
93
+ class="num"
94
+ type="number"
95
+ min="0"
96
+ max="360"
97
+ step="1"
98
+ value={value}
99
+ on:change={onInputChange}
100
+ />
101
+ <span class="suffix">°</span>
102
+ </div>
103
+
104
+ <style>
105
+ .angle-dial-row {
106
+ display: inline-flex;
107
+ align-items: center;
108
+ gap: var(--ui-space-8);
109
+ font-size: var(--ui-font-size-sm);
110
+ color: var(--ui-text-secondary);
111
+ }
112
+
113
+ .dial-label {
114
+ user-select: none;
115
+ }
116
+
117
+ .dial {
118
+ position: relative;
119
+ border-radius: 50%;
120
+ background: var(--ui-surface-lowest);
121
+ border: 1px solid var(--ui-border-default);
122
+ cursor: grab;
123
+ flex: none;
124
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
125
+ }
126
+
127
+ .dial:focus-visible {
128
+ outline: 2px solid var(--ui-border-focus, var(--color-brand-500));
129
+ outline-offset: 2px;
130
+ }
131
+
132
+ .dial.dragging {
133
+ cursor: grabbing;
134
+ }
135
+
136
+ /* Indicator: a 1px-wide line from center to edge. Origin is the bottom
137
+ center so rotating by N degrees points the line N degrees clockwise from
138
+ 12 o'clock, matching CSS `linear-gradient(Ndeg, ...)`. */
139
+ .indicator {
140
+ position: absolute;
141
+ left: 50%;
142
+ bottom: 50%;
143
+ width: 2px;
144
+ height: 50%;
145
+ margin-left: -1px;
146
+ background: var(--ui-text-primary);
147
+ border-radius: 1px;
148
+ transform-origin: 50% 100%;
149
+ pointer-events: none;
150
+ }
151
+
152
+ .hub {
153
+ position: absolute;
154
+ left: 50%;
155
+ top: 50%;
156
+ width: 4px;
157
+ height: 4px;
158
+ margin-left: -2px;
159
+ margin-top: -2px;
160
+ border-radius: 50%;
161
+ background: var(--ui-text-primary);
162
+ pointer-events: none;
163
+ }
164
+
165
+ .num {
166
+ width: 3.25rem;
167
+ padding: var(--ui-space-2) var(--ui-space-6);
168
+ background: var(--ui-surface-lowest);
169
+ border: 1px solid var(--ui-border-faint);
170
+ border-radius: var(--ui-radius-sm);
171
+ color: var(--ui-text-primary);
172
+ font-family: var(--ui-font-mono);
173
+ font-size: var(--ui-font-size-sm);
174
+ text-align: right;
175
+ }
176
+
177
+ .num::-webkit-outer-spin-button,
178
+ .num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
179
+
180
+ .suffix {
181
+ font-size: var(--ui-font-size-xs);
182
+ color: var(--ui-text-tertiary);
183
+ user-select: none;
184
+ }
185
+ </style>
@@ -0,0 +1,96 @@
1
+ <script lang="ts">
2
+ import DemoHeader from './DemoHeader.svelte';
3
+ import NonStylableConfig from './NonStylableConfig.svelte';
4
+ import LinkedBlock from './LinkedBlock.svelte';
5
+ import { createEditorContext } from './editorContext';
6
+ import type { Token } from './types';
7
+ import type { LinkedBlockResult } from './linkedBlock';
8
+
9
+ export let component: string;
10
+ export let title: string;
11
+ export let description: string = '';
12
+ /** Token list used to drive the reset action in the header. The editor itself
13
+ is responsible for calling `registerComponentSchema` synchronously. */
14
+ export let tokens: Token[] = [];
15
+ /** Optional linked-block result. When provided, the LinkedBlock is rendered
16
+ and hover highlights propagate to VariantGroup children via context. */
17
+ export let linked: LinkedBlockResult | null = null;
18
+ /** Canonical {value,label} list of variants in display order. When provided
19
+ with 2+ entries, a single variant tab strip is rendered that drives which
20
+ VariantGroup is focused. */
21
+ export let variants: { value: string; label: string }[] = [];
22
+
23
+ const ctx = createEditorContext();
24
+ const { focusedVariant } = ctx;
25
+
26
+ $: ctx._linkedOrder.set(linked?.linkedOrder ?? null);
27
+ $: showVariantTabs = variants.length >= 2;
28
+ $: if (showVariantTabs && ($focusedVariant === null || !variants.some((v) => v.value === $focusedVariant))) {
29
+ focusedVariant.set(variants[0].value);
30
+ }
31
+ $: resetVariables = tokens.map((t) => t.variable);
32
+ </script>
33
+
34
+ <div class="demo-block">
35
+ <DemoHeader {component} {title} {description} {resetVariables} />
36
+ {#if $$slots.config}
37
+ <NonStylableConfig>
38
+ <slot name="config" />
39
+ </NonStylableConfig>
40
+ {/if}
41
+ {#if showVariantTabs}
42
+ <div class="variant-tabs" role="tablist">
43
+ {#each variants as opt}
44
+ <button
45
+ type="button"
46
+ class="variant-tab-btn"
47
+ class:active={opt.value === $focusedVariant}
48
+ role="tab"
49
+ aria-selected={opt.value === $focusedVariant}
50
+ on:click={() => focusedVariant.set(opt.value)}
51
+ >{opt.label}</button>
52
+ {/each}
53
+ </div>
54
+ {/if}
55
+ <slot focusedVariant={$focusedVariant} />
56
+ {#if linked}
57
+ <LinkedBlock {component} {linked} on:change />
58
+ {/if}
59
+ </div>
60
+
61
+ <style>
62
+ .variant-tabs {
63
+ display: inline-flex;
64
+ flex-wrap: wrap;
65
+ gap: var(--ui-space-4);
66
+ padding: var(--ui-space-4);
67
+ background: var(--ui-surface-lowest);
68
+ border: 1px solid var(--ui-border-faint);
69
+ border-radius: var(--ui-radius-md);
70
+ align-self: flex-start;
71
+ }
72
+
73
+ .variant-tab-btn {
74
+ padding: var(--ui-space-6) var(--ui-space-12);
75
+ background: none;
76
+ border: none;
77
+ border-radius: var(--ui-radius-sm);
78
+ color: var(--ui-text-secondary);
79
+ font-size: var(--ui-font-size-md);
80
+ font-weight: var(--ui-font-weight-semibold);
81
+ text-transform: capitalize;
82
+ cursor: pointer;
83
+ transition: color var(--ui-transition-fast), background var(--ui-transition-fast);
84
+ }
85
+
86
+ .variant-tab-btn:hover:not(.active) {
87
+ color: var(--ui-text-primary);
88
+ background: var(--ui-hover);
89
+ }
90
+
91
+ .variant-tab-btn.active {
92
+ color: var(--ui-text-primary);
93
+ background: var(--ui-surface-high);
94
+ box-shadow: 0 0 0 1px var(--ui-border-default);
95
+ }
96
+ </style>