@motion-proto/live-tokens 0.7.1 → 0.9.0
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.
- package/.claude/skills/live-tokens-add-component/SKILL.md +488 -0
- package/README.md +34 -0
- package/dist-plugin/index.cjs +707 -90
- package/dist-plugin/index.d.cts +1 -0
- package/dist-plugin/index.d.ts +1 -0
- package/dist-plugin/index.js +707 -90
- package/package.json +6 -2
- package/src/app/site.css +1 -1
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +34 -27
- package/src/editor/component-editor/DialogEditor.svelte +4 -4
- package/src/editor/component-editor/NotificationEditor.svelte +3 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +439 -112
- package/src/editor/component-editor/StandardButtonsEditor.svelte +13 -1
- package/src/editor/component-editor/editors.d.ts +10 -0
- package/src/editor/component-editor/index.ts +16 -1
- package/src/editor/component-editor/registry.ts +103 -26
- package/src/editor/component-editor/scaffolding/AngleDial.svelte +52 -13
- package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +10 -11
- package/src/editor/component-editor/scaffolding/ComponentsTab.svelte +2 -2
- package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +0 -1
- package/src/editor/component-editor/scaffolding/RadialShapePad.svelte +483 -0
- package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +15 -2
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +103 -15
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +9 -6
- package/src/editor/component-editor/scaffolding/TypeEditor.svelte +13 -1
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +239 -25
- package/src/editor/component-editor/scaffolding/buildTypeGroupTokens.ts +1 -0
- package/src/editor/component-editor/scaffolding/componentSources.ts +3 -3
- package/src/editor/component-editor/scaffolding/defaultSections.ts +15 -10
- package/src/editor/component-editor/scaffolding/types.ts +11 -0
- package/src/editor/core/components/componentConfigKeys.ts +22 -3
- package/src/editor/core/components/componentConfigService.ts +2 -2
- package/src/editor/core/components/componentPersist.ts +7 -5
- package/src/editor/core/manifests/manifestService.ts +58 -3
- package/src/editor/core/palettes/familySwap.ts +99 -0
- package/src/editor/core/palettes/paletteDerivation.ts +69 -0
- package/src/editor/core/palettes/tokenRegistry.ts +4 -1
- package/src/editor/core/store/editorStore.ts +206 -12
- package/src/editor/core/store/editorTypes.ts +55 -12
- package/src/editor/core/store/gradientSource.ts +192 -0
- package/src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts +28 -0
- package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +35 -0
- package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +82 -0
- package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +24 -0
- package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +81 -0
- package/src/editor/core/themes/migrations/index.ts +10 -0
- package/src/editor/core/themes/slices/components.ts +27 -4
- package/src/editor/core/themes/slices/gradients.ts +88 -13
- package/src/editor/core/themes/themeInit.ts +2 -2
- package/src/editor/core/themes/themeTypes.ts +56 -1
- package/src/editor/index.ts +10 -1
- package/src/editor/overlay/ColumnsOverlay.svelte +0 -1
- package/src/editor/overlay/LiveEditorOverlay.svelte +1 -4
- package/src/editor/pages/ComponentEditorPage.svelte +53 -3
- package/src/editor/pages/EditorShell.svelte +53 -3
- package/src/editor/styles/ui-editor.css +1 -0
- package/src/editor/styles/ui-form-controls.css +19 -20
- package/src/editor/ui/BezierCurveEditor.svelte +114 -63
- package/src/editor/ui/EditorViewSwitcher.svelte +0 -1
- package/src/editor/ui/FileLoadList.svelte +22 -5
- package/src/editor/ui/FontStackEditor.svelte +214 -76
- package/src/editor/ui/GradientEditor.svelte +435 -215
- package/src/editor/ui/GradientStopPicker.svelte +11 -3
- package/src/editor/ui/ManifestFileManager.svelte +71 -4
- package/src/editor/ui/PaletteEditor.svelte +52 -79
- package/src/editor/ui/ProjectFontsSection.svelte +328 -293
- package/src/editor/ui/ThemeFileManager.svelte +0 -4
- package/src/editor/ui/UIFontFamilySelector.svelte +0 -1
- package/src/editor/ui/UIFontSizeSelector.svelte +3 -0
- package/src/editor/ui/UIInfoPopover.svelte +0 -1
- package/src/editor/ui/UILetterSpacingSelector.svelte +65 -0
- package/src/editor/ui/UIPaletteSelector.svelte +31 -4
- package/src/editor/ui/UIPillButton.svelte +33 -3
- package/src/editor/ui/UISegmentedControl.svelte +114 -0
- package/src/editor/ui/UITokenSelector.svelte +4 -1
- package/src/editor/ui/VariablesTab.svelte +41 -35
- package/src/editor/ui/palette/OverridesPanel.svelte +14 -37
- package/src/editor/ui/palette/PaletteBase.svelte +3 -3
- package/src/editor/ui/sections/ColumnsSection.svelte +1 -2
- package/src/editor/ui/sections/GradientsSection.svelte +1 -1
- package/src/editor/ui/sections/OverlaysSection.svelte +1 -1
- package/src/editor/ui/sections/ShadowsSection.svelte +1 -1
- package/src/system/components/Button.svelte +2 -2
- package/src/system/components/Card.svelte +29 -1
- package/src/system/components/CollapsibleSection.svelte +25 -2
- package/src/system/components/Dialog.svelte +24 -4
- package/src/system/components/FloatingTokenTags.css +43 -24
- package/src/system/components/FloatingTokenTags.svelte +88 -137
- package/src/system/components/Notification.svelte +8 -1
- package/src/system/components/SectionDivider.svelte +532 -381
- package/src/system/styles/CONVENTIONS.md +1 -1
- package/src/system/styles/fonts.css +3 -16
- package/src/system/styles/tokens.css +356 -1199
- package/src/system/styles/tokens.generated.css +544 -0
- package/src/editor/component-editor/scaffolding/DividerEditor.svelte +0 -94
- package/src/editor/component-editor/scaffolding/GradientCard.svelte +0 -296
|
@@ -37,8 +37,20 @@
|
|
|
37
37
|
return tokens;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// Outline is the only variant that paints a surface tint on :active; the rest
|
|
41
|
+
// express press feedback through transform/shadow only. Expose just the one
|
|
42
|
+
// tunable property here rather than adding an active state to every variant.
|
|
43
|
+
const outlineActiveTokens: Token[] = [
|
|
44
|
+
{ label: 'surface color', groupKey: 'surface', variable: '--button-outline-active-surface' },
|
|
45
|
+
];
|
|
46
|
+
|
|
40
47
|
function variantStates(v: Variant): Record<string, Token[]> {
|
|
41
|
-
|
|
48
|
+
const out: Record<string, Token[]> = {};
|
|
49
|
+
out.default = variantStateTokens(v, 'default');
|
|
50
|
+
out.hover = variantStateTokens(v, 'hover');
|
|
51
|
+
if (v === 'outline') out.active = outlineActiveTokens;
|
|
52
|
+
out.disabled = variantStateTokens(v, 'disabled');
|
|
53
|
+
return out;
|
|
42
54
|
}
|
|
43
55
|
export const allTokens: Token[] = variants.flatMap((v) =>
|
|
44
56
|
Object.values(variantStates(v)).flat(),
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Augment the global `*.svelte` ambient module to expose the named
|
|
2
|
+
// `allTokens` export that each `<Name>Editor.svelte` declares in its
|
|
3
|
+
// `<script module>` block. The default ambient (shipped via
|
|
4
|
+
// node_modules/svelte/types/index.d.ts) declares only a default export,
|
|
5
|
+
// so without this augmentation tsc fails on the named imports in
|
|
6
|
+
// registry.ts.
|
|
7
|
+
|
|
8
|
+
declare module '*.svelte' {
|
|
9
|
+
export const allTokens: import('./scaffolding/types').Token[];
|
|
10
|
+
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
export { default as ComponentsTab } from './scaffolding/ComponentsTab.svelte';
|
|
2
2
|
export type { ComponentSection } from './scaffolding/componentSectionType';
|
|
3
|
-
export {
|
|
3
|
+
export { getDefaultSections } from './scaffolding/defaultSections';
|
|
4
4
|
|
|
5
|
+
// Editor primitives for consumer-authored components.
|
|
6
|
+
export { default as ComponentEditorBase } from './scaffolding/ComponentEditorBase.svelte';
|
|
7
|
+
export { default as VariantGroup } from './scaffolding/VariantGroup.svelte';
|
|
8
|
+
export { default as LinkedBlock } from './scaffolding/LinkedBlock.svelte';
|
|
9
|
+
export { default as TypeEditor } from './scaffolding/TypeEditor.svelte';
|
|
5
10
|
export { default as TokenLayout } from './scaffolding/TokenLayout.svelte';
|
|
11
|
+
|
|
12
|
+
// Helpers for assembling a VariantGroup's siblings and the linked-block view.
|
|
13
|
+
export { buildSiblings } from './scaffolding/siblings';
|
|
14
|
+
export type { Sibling } from './scaffolding/siblings';
|
|
15
|
+
export { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
|
|
16
|
+
export type { LinkedToken, LinkedGroup, LinkedBlockResult } from './scaffolding/linkedBlock';
|
|
17
|
+
export { buildTypeGroupTokens } from './scaffolding/buildTypeGroupTokens';
|
|
18
|
+
|
|
19
|
+
// Token schema type — the shape of an entry in an editor's `allTokens` array.
|
|
20
|
+
export type { Token } from './scaffolding/types';
|
|
@@ -21,7 +21,8 @@ import TableEditor, { allTokens as tableTokens } from './TableEditor.svelte';
|
|
|
21
21
|
import TabBarEditor, { allTokens as tabBarTokens } from './TabBarEditor.svelte';
|
|
22
22
|
import TooltipEditor, { allTokens as tooltipTokens } from './TooltipEditor.svelte';
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/** Internal narrowed union of the first-party component ids. Not exposed publicly. */
|
|
25
|
+
type BuiltInComponentId =
|
|
25
26
|
| 'segmentedcontrol'
|
|
26
27
|
| 'button'
|
|
27
28
|
| 'notification'
|
|
@@ -41,6 +42,13 @@ export type ComponentId =
|
|
|
41
42
|
| 'tooltip'
|
|
42
43
|
| 'progressbar';
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Public component id type. Widened to `string` because consumers can register
|
|
47
|
+
* their own components at runtime via `registerComponent()`. Internal code that
|
|
48
|
+
* needs to narrow to first-party ids can reference `BuiltInComponentId`.
|
|
49
|
+
*/
|
|
50
|
+
export type ComponentId = string;
|
|
51
|
+
|
|
44
52
|
export interface RegistryEntry {
|
|
45
53
|
/** Canonical id — lowercase, matches the runtime component filename + server scan + `setComponentAlias` key. */
|
|
46
54
|
id: ComponentId;
|
|
@@ -54,21 +62,18 @@ export interface RegistryEntry {
|
|
|
54
62
|
editorComponent: Component<any, any, any>;
|
|
55
63
|
/** Flat token list — the editor's declarative description of its token surface. */
|
|
56
64
|
schema: Token[];
|
|
65
|
+
/** `'system'` for first-party entries; `'custom'` for entries added via `registerComponent()`. */
|
|
66
|
+
origin: 'system' | 'custom';
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* (
|
|
64
|
-
* not affect the UI.
|
|
65
|
-
*
|
|
66
|
-
* Adding a component:
|
|
67
|
-
* 1. Author `src/components/<Name>.svelte` (declares CSS vars in `:global(:root)`)
|
|
68
|
-
* 2. Author `src/component-editor/<Name>Editor.svelte` (exports `allTokens` from a `<script context="module">` block)
|
|
70
|
+
* First-party registry. Frozen; runtime additions go in `customRegistry`.
|
|
71
|
+
* Adding a first-party component:
|
|
72
|
+
* 1. Author `src/system/components/<Name>.svelte` (declares CSS vars in `:global(:root)`)
|
|
73
|
+
* 2. Author `src/editor/component-editor/<Name>Editor.svelte` (exports `allTokens`)
|
|
69
74
|
* 3. Add an entry below.
|
|
70
75
|
*/
|
|
71
|
-
|
|
76
|
+
const builtInRegistry: Readonly<Record<BuiltInComponentId, RegistryEntry>> = Object.freeze({
|
|
72
77
|
segmentedcontrol: {
|
|
73
78
|
id: 'segmentedcontrol',
|
|
74
79
|
label: 'Segmented Control',
|
|
@@ -76,6 +81,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
76
81
|
sourceFile: 'src/system/components/SegmentedControl.svelte',
|
|
77
82
|
editorComponent: SegmentedControlEditor,
|
|
78
83
|
schema: segmentedControlTokens,
|
|
84
|
+
origin: 'system',
|
|
79
85
|
},
|
|
80
86
|
button: {
|
|
81
87
|
id: 'button',
|
|
@@ -84,6 +90,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
84
90
|
sourceFile: 'src/system/components/Button.svelte',
|
|
85
91
|
editorComponent: StandardButtonsEditor,
|
|
86
92
|
schema: buttonTokens,
|
|
93
|
+
origin: 'system',
|
|
87
94
|
},
|
|
88
95
|
notification: {
|
|
89
96
|
id: 'notification',
|
|
@@ -92,6 +99,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
92
99
|
sourceFile: 'src/system/components/Notification.svelte',
|
|
93
100
|
editorComponent: NotificationEditor,
|
|
94
101
|
schema: notificationTokens,
|
|
102
|
+
origin: 'system',
|
|
95
103
|
},
|
|
96
104
|
dialog: {
|
|
97
105
|
id: 'dialog',
|
|
@@ -100,6 +108,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
100
108
|
sourceFile: 'src/system/components/Dialog.svelte',
|
|
101
109
|
editorComponent: DialogEditor,
|
|
102
110
|
schema: dialogTokens,
|
|
111
|
+
origin: 'system',
|
|
103
112
|
},
|
|
104
113
|
radiobutton: {
|
|
105
114
|
id: 'radiobutton',
|
|
@@ -108,6 +117,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
108
117
|
sourceFile: 'src/system/components/RadioButton.svelte',
|
|
109
118
|
editorComponent: RadioButtonEditor,
|
|
110
119
|
schema: radioButtonTokens,
|
|
120
|
+
origin: 'system',
|
|
111
121
|
},
|
|
112
122
|
card: {
|
|
113
123
|
id: 'card',
|
|
@@ -116,6 +126,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
116
126
|
sourceFile: 'src/system/components/Card.svelte',
|
|
117
127
|
editorComponent: CardEditor,
|
|
118
128
|
schema: cardTokens,
|
|
129
|
+
origin: 'system',
|
|
119
130
|
},
|
|
120
131
|
badge: {
|
|
121
132
|
id: 'badge',
|
|
@@ -124,6 +135,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
124
135
|
sourceFile: 'src/system/components/Badge.svelte',
|
|
125
136
|
editorComponent: BadgeEditor,
|
|
126
137
|
schema: badgeTokens,
|
|
138
|
+
origin: 'system',
|
|
127
139
|
},
|
|
128
140
|
callout: {
|
|
129
141
|
id: 'callout',
|
|
@@ -132,6 +144,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
132
144
|
sourceFile: 'src/system/components/Callout.svelte',
|
|
133
145
|
editorComponent: CalloutEditor,
|
|
134
146
|
schema: calloutTokens,
|
|
147
|
+
origin: 'system',
|
|
135
148
|
},
|
|
136
149
|
cornerbadge: {
|
|
137
150
|
id: 'cornerbadge',
|
|
@@ -140,6 +153,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
140
153
|
sourceFile: 'src/system/components/CornerBadge.svelte',
|
|
141
154
|
editorComponent: CornerBadgeEditor,
|
|
142
155
|
schema: cornerBadgeTokens,
|
|
156
|
+
origin: 'system',
|
|
143
157
|
},
|
|
144
158
|
image: {
|
|
145
159
|
id: 'image',
|
|
@@ -148,6 +162,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
148
162
|
sourceFile: 'src/system/components/Image.svelte',
|
|
149
163
|
editorComponent: ImageEditor,
|
|
150
164
|
schema: imageTokens,
|
|
165
|
+
origin: 'system',
|
|
151
166
|
},
|
|
152
167
|
inlineeditactions: {
|
|
153
168
|
id: 'inlineeditactions',
|
|
@@ -156,6 +171,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
156
171
|
sourceFile: 'src/system/components/InlineEditActions.svelte',
|
|
157
172
|
editorComponent: InlineEditActionsEditor,
|
|
158
173
|
schema: inlineEditActionsTokens,
|
|
174
|
+
origin: 'system',
|
|
159
175
|
},
|
|
160
176
|
menuselect: {
|
|
161
177
|
id: 'menuselect',
|
|
@@ -164,6 +180,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
164
180
|
sourceFile: 'src/system/components/MenuSelect.svelte',
|
|
165
181
|
editorComponent: MenuSelectEditor,
|
|
166
182
|
schema: menuSelectTokens,
|
|
183
|
+
origin: 'system',
|
|
167
184
|
},
|
|
168
185
|
sectiondivider: {
|
|
169
186
|
id: 'sectiondivider',
|
|
@@ -172,6 +189,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
172
189
|
sourceFile: 'src/system/components/SectionDivider.svelte',
|
|
173
190
|
editorComponent: SectionDividerEditor,
|
|
174
191
|
schema: sectionDividerTokens,
|
|
192
|
+
origin: 'system',
|
|
175
193
|
},
|
|
176
194
|
collapsiblesection: {
|
|
177
195
|
id: 'collapsiblesection',
|
|
@@ -180,6 +198,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
180
198
|
sourceFile: 'src/system/components/CollapsibleSection.svelte',
|
|
181
199
|
editorComponent: CollapsibleSectionEditor,
|
|
182
200
|
schema: collapsibleSectionTokens,
|
|
201
|
+
origin: 'system',
|
|
183
202
|
},
|
|
184
203
|
table: {
|
|
185
204
|
id: 'table',
|
|
@@ -188,6 +207,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
188
207
|
sourceFile: 'src/system/components/Table.svelte',
|
|
189
208
|
editorComponent: TableEditor,
|
|
190
209
|
schema: tableTokens,
|
|
210
|
+
origin: 'system',
|
|
191
211
|
},
|
|
192
212
|
tabbar: {
|
|
193
213
|
id: 'tabbar',
|
|
@@ -196,6 +216,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
196
216
|
sourceFile: 'src/system/components/TabBar.svelte',
|
|
197
217
|
editorComponent: TabBarEditor,
|
|
198
218
|
schema: tabBarTokens,
|
|
219
|
+
origin: 'system',
|
|
199
220
|
},
|
|
200
221
|
tooltip: {
|
|
201
222
|
id: 'tooltip',
|
|
@@ -204,6 +225,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
204
225
|
sourceFile: 'src/system/components/Tooltip.svelte',
|
|
205
226
|
editorComponent: TooltipEditor,
|
|
206
227
|
schema: tooltipTokens,
|
|
228
|
+
origin: 'system',
|
|
207
229
|
},
|
|
208
230
|
progressbar: {
|
|
209
231
|
id: 'progressbar',
|
|
@@ -212,34 +234,89 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
|
|
|
212
234
|
sourceFile: 'src/system/components/ProgressBar.svelte',
|
|
213
235
|
editorComponent: ProgressBarEditor,
|
|
214
236
|
schema: progressBarTokens,
|
|
237
|
+
origin: 'system',
|
|
215
238
|
},
|
|
216
239
|
});
|
|
217
240
|
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
241
|
+
/** Mutable map of consumer-registered components, populated by `registerComponent()`. */
|
|
242
|
+
const customRegistry = new Map<string, RegistryEntry>();
|
|
243
|
+
|
|
244
|
+
/** Argument shape for `registerComponent()`. `origin` is set internally to `'custom'`. */
|
|
245
|
+
export type RegisterComponentEntry = Omit<RegistryEntry, 'origin'>;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Register a consumer-authored component at runtime. Call from `main.ts`
|
|
249
|
+
* before app mount.
|
|
250
|
+
*
|
|
251
|
+
* Collision rule: if `entry.id` matches a built-in id, a warning is logged and
|
|
252
|
+
* the custom entry wins (the custom editor and schema replace the built-in for
|
|
253
|
+
* the rest of the session).
|
|
254
|
+
*
|
|
255
|
+
* Side effect: registers the schema with the editor store so reset-to-default
|
|
256
|
+
* and sibling-group resolution work for the new component.
|
|
257
|
+
*/
|
|
258
|
+
export function registerComponent(entry: RegisterComponentEntry): void {
|
|
259
|
+
if (entry.id in builtInRegistry) {
|
|
260
|
+
console.warn(
|
|
261
|
+
`[registerComponent] custom component "${entry.id}" overrides a built-in. The custom editor will be used.`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const stored: RegistryEntry = { ...entry, origin: 'custom' };
|
|
265
|
+
customRegistry.set(entry.id, stored);
|
|
266
|
+
registerComponentSchema(entry.id, entry.schema);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Merged registry: built-ins overlaid with customs (custom wins on id collision).
|
|
271
|
+
* Recomputed on each call so callers see runtime registrations made after their
|
|
272
|
+
* own module-load order.
|
|
273
|
+
*/
|
|
274
|
+
export function getComponentRegistry(): Readonly<Record<string, RegistryEntry>> {
|
|
275
|
+
const merged: Record<string, RegistryEntry> = { ...builtInRegistry };
|
|
276
|
+
for (const [id, entry] of customRegistry) {
|
|
277
|
+
merged[id] = entry;
|
|
278
|
+
}
|
|
279
|
+
return merged;
|
|
280
|
+
}
|
|
222
281
|
|
|
223
|
-
/**
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Display-ordered entries: system first (alphabetical by label), then custom
|
|
284
|
+
* (alphabetical by label). Iteration order matches the nav rail's grouping.
|
|
285
|
+
* The nav rail renders a divider between the two groups when customs exist.
|
|
286
|
+
*/
|
|
287
|
+
export function getComponentRegistryEntries(): ReadonlyArray<RegistryEntry> {
|
|
288
|
+
const merged = getComponentRegistry();
|
|
289
|
+
const system: RegistryEntry[] = [];
|
|
290
|
+
const custom: RegistryEntry[] = [];
|
|
291
|
+
for (const entry of Object.values(merged)) {
|
|
292
|
+
(entry.origin === 'system' ? system : custom).push(entry);
|
|
293
|
+
}
|
|
294
|
+
system.sort((a, b) => a.label.localeCompare(b.label));
|
|
295
|
+
custom.sort((a, b) => a.label.localeCompare(b.label));
|
|
296
|
+
return [...system, ...custom];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** All component ids, in display order. */
|
|
300
|
+
export function getComponentIds(): ReadonlyArray<string> {
|
|
301
|
+
return getComponentRegistryEntries().map((e) => e.id);
|
|
302
|
+
}
|
|
227
303
|
|
|
228
|
-
// Eager schema registration.
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
for (const entry of
|
|
304
|
+
// Eager schema registration for built-ins. Customs register lazily inside
|
|
305
|
+
// `registerComponent()` so the store knows about every component before any
|
|
306
|
+
// editor instance mounts.
|
|
307
|
+
for (const entry of Object.values(builtInRegistry)) {
|
|
232
308
|
registerComponentSchema(entry.id, entry.schema);
|
|
233
309
|
}
|
|
234
310
|
|
|
235
311
|
/**
|
|
236
|
-
* Validate that the server's filesystem scan matches the registry's id list.
|
|
312
|
+
* Validate that the server's filesystem scan matches the merged registry's id list.
|
|
237
313
|
* Logs a warning when ids drift. Called at boot from the editor page.
|
|
238
314
|
*/
|
|
239
315
|
export function validateRegistryAgainstServerScan(serverIds: ReadonlyArray<string>): void {
|
|
240
|
-
const
|
|
316
|
+
const ids = getComponentIds();
|
|
317
|
+
const registrySet = new Set<string>(ids);
|
|
241
318
|
const serverSet = new Set<string>(serverIds);
|
|
242
|
-
const missingOnServer =
|
|
319
|
+
const missingOnServer = ids.filter((id) => !serverSet.has(id));
|
|
243
320
|
const extraOnServer = serverIds.filter((id) => !registrySet.has(id));
|
|
244
321
|
if (missingOnServer.length > 0) {
|
|
245
322
|
console.warn(
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
value?: number;
|
|
10
10
|
label?: string;
|
|
11
11
|
size?: number;
|
|
12
|
+
/** 'horizontal' (default) lays the label, dial, input, and degree mark on a
|
|
13
|
+
* single line. 'vertical' stacks the dial above the input — used when the
|
|
14
|
+
* dial sits in its own column with a section header providing the label. */
|
|
15
|
+
orientation?: 'horizontal' | 'vertical';
|
|
12
16
|
onchange?: (payload: { value: number }) => void;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
let { value = $bindable(0), label = 'Angle', size = 44, onchange }: Props = $props();
|
|
19
|
+
let { value = $bindable(0), label = 'Angle', size = 44, orientation = 'horizontal', onchange }: Props = $props();
|
|
16
20
|
|
|
17
21
|
let dialEl: HTMLDivElement | undefined = $state();
|
|
18
22
|
let dragging = $state(false);
|
|
@@ -63,8 +67,8 @@
|
|
|
63
67
|
let indicatorTransform = $derived(`rotate(${value}deg)`);
|
|
64
68
|
</script>
|
|
65
69
|
|
|
66
|
-
<div class="angle-dial-row">
|
|
67
|
-
{#if label}
|
|
70
|
+
<div class="angle-dial-row" class:vertical={orientation === 'vertical'}>
|
|
71
|
+
{#if label && orientation === 'horizontal'}
|
|
68
72
|
<span class="dial-label">{label}:</span>
|
|
69
73
|
{/if}
|
|
70
74
|
<div
|
|
@@ -90,16 +94,18 @@
|
|
|
90
94
|
<div class="indicator" style="transform: {indicatorTransform}"></div>
|
|
91
95
|
<div class="hub"></div>
|
|
92
96
|
</div>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
<div class="num-row" style={orientation === 'vertical' ? `width: ${size}px;` : ''}>
|
|
98
|
+
<input
|
|
99
|
+
class="num"
|
|
100
|
+
type="number"
|
|
101
|
+
min="0"
|
|
102
|
+
max="360"
|
|
103
|
+
step="1"
|
|
104
|
+
value={value}
|
|
105
|
+
onchange={onInputChange}
|
|
106
|
+
/>
|
|
107
|
+
<span class="suffix">°</span>
|
|
108
|
+
</div>
|
|
103
109
|
</div>
|
|
104
110
|
|
|
105
111
|
<style>
|
|
@@ -110,6 +116,39 @@
|
|
|
110
116
|
font-size: var(--ui-font-size-sm);
|
|
111
117
|
color: var(--ui-text-secondary);
|
|
112
118
|
}
|
|
119
|
+
/* Stacked layout: dial centered, input + degree mark on the row below. Used
|
|
120
|
+
when the dial sits in its own grid column with an external section label. */
|
|
121
|
+
.angle-dial-row.vertical {
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
align-items: center;
|
|
124
|
+
gap: var(--ui-space-6);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.num-row {
|
|
128
|
+
display: inline-flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: var(--ui-space-8);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Vertical mode: lock the row to the dial's width and overlay the degree
|
|
134
|
+
mark inside the input's right padding so the input fills the column
|
|
135
|
+
instead of the row growing past the dial. */
|
|
136
|
+
.angle-dial-row.vertical .num-row {
|
|
137
|
+
position: relative;
|
|
138
|
+
display: block;
|
|
139
|
+
}
|
|
140
|
+
.angle-dial-row.vertical .num {
|
|
141
|
+
width: 100%;
|
|
142
|
+
box-sizing: border-box;
|
|
143
|
+
padding-right: 1.25rem;
|
|
144
|
+
}
|
|
145
|
+
.angle-dial-row.vertical .suffix {
|
|
146
|
+
position: absolute;
|
|
147
|
+
right: var(--ui-space-6);
|
|
148
|
+
top: 50%;
|
|
149
|
+
transform: translateY(-50%);
|
|
150
|
+
pointer-events: none;
|
|
151
|
+
}
|
|
113
152
|
|
|
114
153
|
.dial-label {
|
|
115
154
|
user-select: none;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { onMount, onDestroy } from 'svelte';
|
|
7
7
|
import UIInfoPopover from '../../ui/UIInfoPopover.svelte';
|
|
8
8
|
import { get } from 'svelte/store';
|
|
9
|
-
import type { ComponentConfig, ComponentConfigMeta } from '../../core/themes/themeTypes';
|
|
9
|
+
import type { AliasDiskValue, ComponentConfig, ComponentConfigMeta } from '../../core/themes/themeTypes';
|
|
10
10
|
import { componentSourceFile } from './componentSources';
|
|
11
11
|
import {
|
|
12
12
|
loadComponentConfig,
|
|
@@ -125,15 +125,17 @@
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function
|
|
129
|
-
|
|
128
|
+
function refToDiskValue(ref: CssVarRef): AliasDiskValue {
|
|
129
|
+
if (ref.kind === 'token') return ref.name;
|
|
130
|
+
if (ref.kind === 'literal') return ref.value;
|
|
131
|
+
return { kind: 'gradient', value: ref.value };
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
function currentAliases(): Record<string,
|
|
134
|
+
function currentAliases(): Record<string, AliasDiskValue> {
|
|
133
135
|
const slice = get(editorState).components[component];
|
|
134
136
|
if (!slice) return {};
|
|
135
|
-
const out: Record<string,
|
|
136
|
-
for (const [k, ref] of Object.entries(slice.aliases)) out[k] =
|
|
137
|
+
const out: Record<string, AliasDiskValue> = {};
|
|
138
|
+
for (const [k, ref] of Object.entries(slice.aliases)) out[k] = refToDiskValue(ref);
|
|
137
139
|
return out;
|
|
138
140
|
}
|
|
139
141
|
|
|
@@ -342,7 +344,7 @@
|
|
|
342
344
|
: isApplied
|
|
343
345
|
? 'Active config is applied to production'
|
|
344
346
|
: ''}
|
|
345
|
-
style="flex: 0
|
|
347
|
+
style="flex: 0 1 11.25rem; min-width: 0; max-width: 11.25rem;"
|
|
346
348
|
/>
|
|
347
349
|
<div class="cfm-actions">
|
|
348
350
|
<ComponentFileMenu
|
|
@@ -383,7 +385,7 @@
|
|
|
383
385
|
name={productionInfo?.name ?? '—'}
|
|
384
386
|
isProtected={productionInfo?.fileName === 'default'}
|
|
385
387
|
protectedTitle="Protected system config"
|
|
386
|
-
style="flex: 0
|
|
388
|
+
style="flex: 0 1 11.25rem; min-width: 0; max-width: 11.25rem;"
|
|
387
389
|
/>
|
|
388
390
|
<div class="cfm-actions">
|
|
389
391
|
<UISquareButton
|
|
@@ -483,7 +485,6 @@
|
|
|
483
485
|
font-size: var(--ui-font-size-3xl);
|
|
484
486
|
font-weight: var(--ui-font-weight-semibold);
|
|
485
487
|
color: var(--ui-text-primary);
|
|
486
|
-
letter-spacing: -0.015em;
|
|
487
488
|
line-height: 1.1;
|
|
488
489
|
}
|
|
489
490
|
|
|
@@ -536,7 +537,6 @@
|
|
|
536
537
|
font-size: var(--ui-font-size-xs);
|
|
537
538
|
font-weight: var(--ui-font-weight-semibold);
|
|
538
539
|
text-transform: uppercase;
|
|
539
|
-
letter-spacing: 0.08em;
|
|
540
540
|
color: var(--ui-text-secondary);
|
|
541
541
|
line-height: 1.1;
|
|
542
542
|
}
|
|
@@ -546,7 +546,6 @@
|
|
|
546
546
|
align-items: center;
|
|
547
547
|
gap: var(--ui-space-4);
|
|
548
548
|
font-size: 0.75rem;
|
|
549
|
-
letter-spacing: 0.02em;
|
|
550
549
|
color: var(--ui-text-muted);
|
|
551
550
|
line-height: 1;
|
|
552
551
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ComponentSection } from './componentSectionType';
|
|
3
|
-
import {
|
|
3
|
+
import { getDefaultSections } from './defaultSections';
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
sections?: ComponentSection[];
|
|
7
7
|
selectedComponent?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
let { sections =
|
|
10
|
+
let { sections = getDefaultSections(), selectedComponent = sections[0]?.id ?? '' }: Props = $props();
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<div class="components-container">
|