@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,312 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy, createEventDispatcher } from 'svelte';
3
+ import type { ComponentConfigMeta } from '../../lib/themeTypes';
4
+ import UIDialog from '../../ui/UIDialog.svelte';
5
+
6
+ /** Component slug used in the load-dialog title (e.g. "button"). */
7
+ export let component: string;
8
+ /** Files shown in the load dialog. */
9
+ export let files: ComponentConfigMeta[] = [];
10
+ /** Currently active file — highlighted in the load list. */
11
+ export let activeFileName: string = 'default';
12
+
13
+ const dispatch = createEventDispatcher<{
14
+ save: void;
15
+ saveAs: void;
16
+ /** Fired when the user clicks "Load…" in the menu — parent should refresh `files`. */
17
+ openLoad: void;
18
+ load: ComponentConfigMeta;
19
+ delete: ComponentConfigMeta;
20
+ }>();
21
+
22
+ let fileMenuOpen = false;
23
+ let fileMenuRoot: HTMLElement;
24
+ let showFileList = false;
25
+
26
+ onMount(() => {
27
+ document.addEventListener('click', handleDocClick, true);
28
+ });
29
+
30
+ onDestroy(() => {
31
+ document.removeEventListener('click', handleDocClick, true);
32
+ });
33
+
34
+ function handleDocClick(e: MouseEvent) {
35
+ if (!fileMenuOpen) return;
36
+ if (fileMenuRoot && !fileMenuRoot.contains(e.target as Node)) {
37
+ fileMenuOpen = false;
38
+ }
39
+ }
40
+
41
+ function handleSave() {
42
+ fileMenuOpen = false;
43
+ dispatch('save');
44
+ }
45
+
46
+ function handleSaveAs() {
47
+ fileMenuOpen = false;
48
+ dispatch('saveAs');
49
+ }
50
+
51
+ function handleOpenLoad() {
52
+ fileMenuOpen = false;
53
+ showFileList = true;
54
+ dispatch('openLoad');
55
+ }
56
+
57
+ function handleLoad(file: ComponentConfigMeta) {
58
+ showFileList = false;
59
+ dispatch('load', file);
60
+ }
61
+
62
+ function handleDelete(file: ComponentConfigMeta) {
63
+ if (file.fileName === 'default') return;
64
+ dispatch('delete', file);
65
+ }
66
+ </script>
67
+
68
+ <div class="file-menu" bind:this={fileMenuRoot}>
69
+ <button
70
+ class="cfm-btn"
71
+ class:active={fileMenuOpen}
72
+ on:click={() => (fileMenuOpen = !fileMenuOpen)}
73
+ title="File menu"
74
+ >
75
+ <i class="fas fa-file"></i>
76
+ <span>File</span>
77
+ <i class="fas fa-chevron-down chevron" class:open={fileMenuOpen}></i>
78
+ </button>
79
+ {#if fileMenuOpen}
80
+ <div class="file-menu-dropdown" role="menu">
81
+ <button class="file-menu-item" on:click={handleSave} role="menuitem">
82
+ <i class="fas fa-save"></i>
83
+ <span>Save</span>
84
+ </button>
85
+ <button class="file-menu-item" on:click={handleSaveAs} role="menuitem">
86
+ <i class="fas fa-copy"></i>
87
+ <span>Save As…</span>
88
+ </button>
89
+ <button class="file-menu-item" on:click={handleOpenLoad} role="menuitem">
90
+ <i class="fas fa-folder-open"></i>
91
+ <span>Load…</span>
92
+ </button>
93
+ </div>
94
+ {/if}
95
+ </div>
96
+
97
+ <UIDialog
98
+ bind:show={showFileList}
99
+ title="Load {component} Config"
100
+ cancelLabel="Close"
101
+ width="420px"
102
+ >
103
+ <div class="load-list">
104
+ {#each files as file}
105
+ <div class="load-item" class:active={file.fileName === activeFileName}>
106
+ <button class="load-name-btn" on:click={() => handleLoad(file)}>
107
+ {file.name}
108
+ </button>
109
+ {#if file.fileName === activeFileName}
110
+ <span class="active-badge">active</span>
111
+ {/if}
112
+ {#if file.fileName !== 'default'}
113
+ <button
114
+ class="file-delete-btn"
115
+ on:click|stopPropagation={() => handleDelete(file)}
116
+ title="Delete {file.name}"
117
+ >
118
+ <i class="fas fa-trash-alt"></i>
119
+ </button>
120
+ {/if}
121
+ </div>
122
+ {/each}
123
+ {#if files.length === 0}
124
+ <div class="load-item empty">No saved files</div>
125
+ {/if}
126
+ </div>
127
+ </UIDialog>
128
+
129
+ <style>
130
+ .cfm-btn {
131
+ display: inline-flex;
132
+ align-items: center;
133
+ gap: var(--ui-space-6);
134
+ padding: var(--ui-space-6) var(--ui-space-12);
135
+ background: var(--ui-surface);
136
+ border: 1px solid var(--ui-border-subtle);
137
+ border-radius: var(--ui-radius-md);
138
+ color: var(--ui-text-secondary);
139
+ font-size: var(--ui-font-size-md);
140
+ font-weight: var(--ui-font-weight-medium);
141
+ cursor: pointer;
142
+ transition: all var(--ui-transition-fast);
143
+ white-space: nowrap;
144
+ }
145
+
146
+ .cfm-btn i {
147
+ width: 1rem;
148
+ text-align: center;
149
+ font-size: 0.85em;
150
+ }
151
+
152
+ .cfm-btn:hover:not(:disabled) {
153
+ background: var(--ui-surface-high);
154
+ color: var(--ui-text-primary);
155
+ border-color: var(--ui-border-default);
156
+ }
157
+
158
+ .cfm-btn:disabled {
159
+ opacity: 0.45;
160
+ cursor: not-allowed;
161
+ }
162
+
163
+ .cfm-btn.active {
164
+ background: var(--ui-surface-high);
165
+ border-color: var(--ui-border-default);
166
+ color: var(--ui-text-primary);
167
+ }
168
+
169
+ @media (max-width: 640px) {
170
+ .cfm-btn > span:not(.chevron) { display: none; }
171
+ .cfm-btn { padding: var(--ui-space-6) var(--ui-space-10); }
172
+ }
173
+
174
+ .chevron {
175
+ font-size: 0.7em;
176
+ transition: transform var(--ui-transition-fast);
177
+ }
178
+
179
+ .chevron.open {
180
+ transform: rotate(180deg);
181
+ }
182
+
183
+ .file-menu {
184
+ position: relative;
185
+ }
186
+
187
+ .file-menu-dropdown {
188
+ position: absolute;
189
+ top: calc(100% + 4px);
190
+ left: 0;
191
+ min-width: 160px;
192
+ background: var(--ui-surface-low);
193
+ border: 1px solid var(--ui-border-default);
194
+ border-radius: var(--ui-radius-md);
195
+ box-shadow: var(--shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.4));
196
+ padding: var(--ui-space-4);
197
+ display: flex;
198
+ flex-direction: column;
199
+ gap: 2px;
200
+ z-index: 10;
201
+ }
202
+
203
+ .file-menu-item {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: var(--ui-space-8);
207
+ padding: var(--ui-space-6) var(--ui-space-10);
208
+ background: none;
209
+ border: none;
210
+ border-radius: var(--ui-radius-sm);
211
+ color: var(--ui-text-secondary);
212
+ font-size: var(--ui-font-size-md);
213
+ cursor: pointer;
214
+ text-align: left;
215
+ white-space: nowrap;
216
+ transition: background var(--ui-transition-fast), color var(--ui-transition-fast);
217
+ }
218
+
219
+ .file-menu-item i {
220
+ width: 1rem;
221
+ text-align: center;
222
+ }
223
+
224
+ .file-menu-item:hover {
225
+ background: var(--ui-hover);
226
+ color: var(--ui-text-primary);
227
+ }
228
+
229
+ .load-list {
230
+ display: flex;
231
+ flex-direction: column;
232
+ max-height: 60vh;
233
+ overflow-y: auto;
234
+ }
235
+
236
+ .load-item {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: 6px;
240
+ padding: 4px 6px;
241
+ border-bottom: 1px solid #2a2a2a;
242
+ }
243
+
244
+ .load-item:last-child {
245
+ border-bottom: none;
246
+ }
247
+
248
+ .load-item.empty {
249
+ padding: 16px;
250
+ color: #888;
251
+ font-size: 14px;
252
+ text-align: center;
253
+ }
254
+
255
+ .load-name-btn {
256
+ flex: 1;
257
+ min-width: 0;
258
+ overflow: hidden;
259
+ text-overflow: ellipsis;
260
+ white-space: nowrap;
261
+ padding: 6px 4px;
262
+ background: none;
263
+ border: none;
264
+ color: #aaa;
265
+ font-size: 14px;
266
+ cursor: pointer;
267
+ text-align: left;
268
+ border-radius: 3px;
269
+ }
270
+
271
+ .load-name-btn:hover {
272
+ color: #e0e0e0;
273
+ }
274
+
275
+ .load-item.active .load-name-btn {
276
+ color: #e0e0e0;
277
+ font-weight: 600;
278
+ }
279
+
280
+ .active-badge {
281
+ flex-shrink: 0;
282
+ font-size: 12px;
283
+ padding: 1px 6px;
284
+ border-radius: 3px;
285
+ background: #333;
286
+ color: #ccc;
287
+ }
288
+
289
+ .file-delete-btn {
290
+ flex-shrink: 0;
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: center;
294
+ width: 24px;
295
+ height: 24px;
296
+ padding: 0;
297
+ background: none;
298
+ border: none;
299
+ color: #555;
300
+ font-size: 12px;
301
+ cursor: pointer;
302
+ opacity: 0;
303
+ }
304
+
305
+ .load-item:hover .file-delete-btn {
306
+ opacity: 1;
307
+ }
308
+
309
+ .file-delete-btn:hover {
310
+ color: #ccc;
311
+ }
312
+ </style>
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ import type { ComponentSection } from './componentSectionType';
3
+ import { defaultSections } from './defaultSections';
4
+
5
+ export let sections: ComponentSection[] = defaultSections;
6
+ export let selectedComponent: string = sections[0]?.id ?? '';
7
+ </script>
8
+
9
+ <div class="components-container">
10
+ {#each sections as section (section.id)}
11
+ {#if selectedComponent === section.id}
12
+ <svelte:component this={section.component} {...(section.props ?? {})} />
13
+ {/if}
14
+ {/each}
15
+ </div>
16
+
17
+ <style>
18
+ .components-container {
19
+ display: flex;
20
+ flex-direction: column;
21
+ min-width: 0;
22
+ }
23
+
24
+ /* Shared editor chrome — used by every per-component editor in ../. */
25
+ :global(.components-container .demo-block) {
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: var(--ui-space-16);
29
+ min-width: 0;
30
+ padding-bottom: 20rem;
31
+ }
32
+
33
+ :global(.components-container .component-title) {
34
+ font-size: var(--ui-font-size-xl);
35
+ font-weight: var(--ui-font-weight-semibold);
36
+ color: var(--ui-text-primary);
37
+ margin: 0;
38
+ padding-bottom: var(--ui-space-8);
39
+ border-bottom: 1px solid var(--ui-border-subtle);
40
+ }
41
+
42
+ :global(.components-container .demo-description) {
43
+ font-size: var(--ui-font-size-sm);
44
+ color: var(--ui-text-tertiary);
45
+ margin: 0;
46
+ }
47
+
48
+ :global(.components-container .demo-description code) {
49
+ font-size: var(--ui-font-size-xs);
50
+ color: var(--ui-text-accent);
51
+ background: var(--ui-surface-lowest);
52
+ padding: var(--ui-space-2) var(--ui-space-4);
53
+ border-radius: var(--ui-radius-sm);
54
+ font-family: var(--ui-font-mono);
55
+ }
56
+
57
+ :global(.components-container .demo-section) {
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: var(--ui-space-8);
61
+ }
62
+
63
+ :global(.components-container .demo-subtitle) {
64
+ font-size: var(--ui-font-size-sm);
65
+ font-weight: var(--ui-font-weight-medium);
66
+ color: var(--ui-text-secondary);
67
+ margin: 0;
68
+ }
69
+ </style>
@@ -0,0 +1,246 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher, onMount, onDestroy } from 'svelte';
3
+
4
+ type Source = { name: string; label: string; states: string[] };
5
+
6
+ export let toState: string;
7
+ export let variantName: string;
8
+ export let copySources: Source[] = [];
9
+ export let placement: 'start' | 'end' = 'start';
10
+
11
+ const dispatch = createEventDispatcher<{ select: { fromVariant: string; fromState: string } }>();
12
+
13
+ let open = false;
14
+ let root: HTMLElement;
15
+
16
+ function toggle() {
17
+ open = !open;
18
+ }
19
+
20
+ function pick(fromVariant: string, fromState: string) {
21
+ open = false;
22
+ dispatch('select', { fromVariant, fromState });
23
+ }
24
+
25
+ function handleDocClick(e: MouseEvent) {
26
+ if (!open) return;
27
+ if (root && !root.contains(e.target as Node)) open = false;
28
+ }
29
+
30
+ function handleKeydown(e: KeyboardEvent) {
31
+ if (e.key === 'Escape' && open) open = false;
32
+ }
33
+
34
+ onMount(() => {
35
+ document.addEventListener('click', handleDocClick, true);
36
+ window.addEventListener('keydown', handleKeydown);
37
+ });
38
+
39
+ onDestroy(() => {
40
+ document.removeEventListener('click', handleDocClick, true);
41
+ window.removeEventListener('keydown', handleKeydown);
42
+ });
43
+ </script>
44
+
45
+ <div class="copy-from" class:placement-end={placement === 'end'} bind:this={root}>
46
+ <button
47
+ type="button"
48
+ class="copy-from-btn"
49
+ class:active={open}
50
+ on:click={toggle}
51
+ title="Copy values from another variant/state"
52
+ >
53
+ <i class="fas fa-clone"></i>
54
+ <span>Copy from</span>
55
+ </button>
56
+ {#if open}
57
+ <div class="copy-menu" role="menu">
58
+ {#each copySources as src}
59
+ {#if src.states.length === 1}
60
+ {@const onlyState = src.states[0]}
61
+ {@const isSelf = src.name === variantName && onlyState === toState}
62
+ <button
63
+ type="button"
64
+ class="copy-menu-item"
65
+ disabled={isSelf}
66
+ on:click={() => pick(src.name, onlyState)}
67
+ role="menuitem"
68
+ >
69
+ <span>{src.label}</span>
70
+ {#if isSelf}<span class="copy-menu-meta">current</span>{/if}
71
+ </button>
72
+ {:else}
73
+ <div class="copy-menu-item-parent">
74
+ <button
75
+ type="button"
76
+ class="copy-menu-item copy-menu-item-trigger"
77
+ role="menuitem"
78
+ aria-haspopup="menu"
79
+ >
80
+ <span>{src.label}</span>
81
+ <i class="fas fa-chevron-right"></i>
82
+ </button>
83
+ <div class="copy-submenu" role="menu">
84
+ {#each src.states as fromState}
85
+ {@const isSelf = src.name === variantName && fromState === toState}
86
+ <button
87
+ type="button"
88
+ class="copy-menu-item"
89
+ disabled={isSelf}
90
+ on:click={() => pick(src.name, fromState)}
91
+ role="menuitem"
92
+ >
93
+ <span>{fromState}</span>
94
+ {#if isSelf}<span class="copy-menu-meta">current</span>{/if}
95
+ </button>
96
+ {/each}
97
+ </div>
98
+ </div>
99
+ {/if}
100
+ {/each}
101
+ </div>
102
+ {/if}
103
+ </div>
104
+
105
+ <style>
106
+ .copy-from {
107
+ position: relative;
108
+ }
109
+
110
+ .copy-from-btn {
111
+ display: inline-flex;
112
+ align-items: center;
113
+ gap: var(--ui-space-6);
114
+ padding: var(--ui-space-4) var(--ui-space-6);
115
+ background: none;
116
+ border: none;
117
+ border-radius: var(--ui-radius-sm);
118
+ color: var(--ui-text-tertiary);
119
+ font-size: var(--ui-font-size-xs);
120
+ cursor: pointer;
121
+ transition: color var(--ui-transition-fast), background var(--ui-transition-fast);
122
+ white-space: nowrap;
123
+ }
124
+
125
+ .copy-from-btn:hover,
126
+ .copy-from-btn.active {
127
+ color: var(--ui-text-primary);
128
+ background: var(--ui-hover);
129
+ }
130
+
131
+ .copy-from-btn i {
132
+ font-size: 0.85em;
133
+ }
134
+
135
+ .copy-menu {
136
+ position: absolute;
137
+ top: calc(100% + 4px);
138
+ left: 0;
139
+ min-width: 12rem;
140
+ background: var(--ui-surface-low);
141
+ border: 1px solid var(--ui-border-default);
142
+ border-radius: var(--ui-radius-md);
143
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
144
+ padding: var(--ui-space-4);
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 2px;
148
+ z-index: 20;
149
+ }
150
+
151
+ .placement-end .copy-menu {
152
+ left: auto;
153
+ right: 0;
154
+ }
155
+
156
+ .copy-menu-item {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ gap: var(--ui-space-8);
161
+ padding: var(--ui-space-6) var(--ui-space-10);
162
+ background: none;
163
+ border: none;
164
+ border-radius: var(--ui-radius-sm);
165
+ color: var(--ui-text-secondary);
166
+ font-size: var(--ui-font-size-sm);
167
+ cursor: pointer;
168
+ text-align: left;
169
+ white-space: nowrap;
170
+ transition: background var(--ui-transition-fast), color var(--ui-transition-fast);
171
+ width: 100%;
172
+ }
173
+
174
+ .copy-menu-item:hover:not(:disabled) {
175
+ background: var(--ui-surface-high);
176
+ color: var(--ui-text-primary);
177
+ box-shadow: inset 0 0 0 1px var(--ui-border-faint);
178
+ }
179
+
180
+ .copy-menu-item:disabled {
181
+ opacity: 0.4;
182
+ cursor: not-allowed;
183
+ }
184
+
185
+ .copy-menu-item i {
186
+ font-size: 0.65em;
187
+ color: var(--ui-text-muted);
188
+ }
189
+
190
+ .copy-menu-meta {
191
+ font-size: var(--ui-font-size-xs);
192
+ color: var(--ui-text-muted);
193
+ font-style: italic;
194
+ }
195
+
196
+ .copy-menu-item-parent {
197
+ position: relative;
198
+ }
199
+
200
+ .copy-menu-item-parent:hover > .copy-menu-item-trigger {
201
+ background: var(--ui-surface-high);
202
+ color: var(--ui-text-primary);
203
+ box-shadow: inset 0 0 0 1px var(--ui-border-faint);
204
+ }
205
+
206
+ .copy-submenu {
207
+ display: none;
208
+ position: absolute;
209
+ top: calc(-1 * var(--ui-space-4));
210
+ left: 100%;
211
+ min-width: 10rem;
212
+ background: var(--ui-surface-low);
213
+ border: 1px solid var(--ui-border-default);
214
+ border-radius: var(--ui-radius-md);
215
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
216
+ padding: var(--ui-space-4);
217
+ flex-direction: column;
218
+ gap: 2px;
219
+ z-index: 21;
220
+ }
221
+
222
+ .placement-end .copy-submenu {
223
+ left: auto;
224
+ right: 100%;
225
+ }
226
+
227
+ /* Invisible hover bridge so the cursor doesn't lose the parent's :hover state
228
+ while traveling between the menu item and its submenu. */
229
+ .copy-submenu::before {
230
+ content: '';
231
+ position: absolute;
232
+ top: 0;
233
+ right: 100%;
234
+ width: 8px;
235
+ height: 100%;
236
+ }
237
+
238
+ .placement-end .copy-submenu::before {
239
+ right: auto;
240
+ left: 100%;
241
+ }
242
+
243
+ .copy-menu-item-parent:hover > .copy-submenu {
244
+ display: flex;
245
+ }
246
+ </style>
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import ComponentFileManager from './ComponentFileManager.svelte';
3
+
4
+ export let component: string;
5
+ export let title: string;
6
+ export let description: string = '';
7
+ export let resetVariables: string[] | null = null;
8
+ </script>
9
+
10
+ <ComponentFileManager {component} {title} {resetVariables} />
11
+
12
+ {#if description}
13
+ <p class="demo-description">{@html description}</p>
14
+ {/if}
15
+
16
+ <style>
17
+ .demo-description {
18
+ margin: 0;
19
+ color: var(--ui-text-secondary);
20
+ }
21
+ </style>