@motion-proto/live-tokens 0.3.9 → 0.6.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/README.md +47 -4
- package/package.json +18 -12
- package/src/component-editor/BadgeEditor.svelte +24 -22
- package/src/component-editor/CalloutEditor.svelte +3 -3
- package/src/component-editor/CardEditor.svelte +25 -21
- package/src/component-editor/CollapsibleSectionEditor.svelte +27 -25
- package/src/component-editor/CornerBadgeEditor.svelte +37 -35
- package/src/component-editor/DialogEditor.svelte +26 -24
- package/src/component-editor/ImageEditor.svelte +11 -9
- package/src/component-editor/InlineEditActionsEditor.svelte +17 -15
- package/src/component-editor/NotificationEditor.svelte +32 -30
- package/src/component-editor/ProgressBarEditor.svelte +3 -3
- package/src/component-editor/RadioButtonEditor.svelte +31 -29
- package/src/component-editor/SectionDividerEditor.svelte +30 -28
- package/src/component-editor/SegmentedControlEditor.svelte +29 -25
- package/src/component-editor/StandardButtonsEditor.svelte +42 -38
- package/src/component-editor/TabBarEditor.svelte +20 -18
- package/src/component-editor/TableEditor.svelte +4 -4
- package/src/component-editor/TooltipEditor.svelte +11 -9
- package/src/component-editor/registry.ts +2 -2
- package/src/component-editor/scaffolding/AngleDial.svelte +20 -19
- package/src/component-editor/scaffolding/ComponentEditorBase.svelte +44 -20
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +262 -38
- package/src/component-editor/scaffolding/ComponentFileMenu.svelte +41 -29
- package/src/component-editor/scaffolding/ComponentsTab.svelte +7 -3
- package/src/component-editor/scaffolding/CopyFromMenu.svelte +21 -12
- package/src/component-editor/scaffolding/DemoHeader.svelte +13 -4
- package/src/component-editor/scaffolding/DividerEditor.svelte +27 -14
- package/src/component-editor/scaffolding/FieldsetWrapper.svelte +10 -4
- package/src/component-editor/scaffolding/GradientCard.svelte +25 -20
- package/src/component-editor/scaffolding/LinkageChart.svelte +43 -34
- package/src/component-editor/scaffolding/LinkedBlock.svelte +24 -21
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +6 -1
- package/src/component-editor/scaffolding/SaveAsDialog.svelte +39 -35
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +21 -9
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +8 -3
- package/src/component-editor/scaffolding/StateBlock.svelte +30 -13
- package/src/component-editor/scaffolding/TokenLayout.svelte +46 -30
- package/src/component-editor/scaffolding/TypeEditor.svelte +52 -26
- package/src/component-editor/scaffolding/VariantGroup.svelte +81 -48
- package/src/component-editor/scaffolding/componentSectionType.ts +2 -2
- package/src/components/Badge.svelte +45 -26
- package/src/components/Button.svelte +44 -21
- package/src/components/Callout.svelte +17 -12
- package/src/components/Card.svelte +23 -11
- package/src/components/CollapsibleSection.svelte +56 -27
- package/src/components/CornerBadge.svelte +32 -18
- package/src/components/Dialog.svelte +55 -31
- package/src/components/Image.svelte +14 -5
- package/src/components/InlineEditActions.svelte +22 -10
- package/src/components/Notification.svelte +39 -19
- package/src/components/ProgressBar.svelte +27 -17
- package/src/components/RadioButton.svelte +27 -10
- package/src/components/SectionDivider.svelte +34 -26
- package/src/components/SegmentedControl.svelte +23 -9
- package/src/components/TabBar.svelte +23 -10
- package/src/components/Table.svelte +8 -3
- package/src/components/Tooltip.svelte +15 -5
- package/src/lib/ColumnsOverlay.svelte +3 -3
- package/src/lib/LiveEditorOverlay.svelte +57 -36
- package/src/pages/ComponentEditorPage.svelte +25 -14
- package/src/pages/Editor.svelte +8 -2
- package/src/pages/EditorShell.svelte +24 -20
- package/src/styles/site.css +138 -0
- package/src/styles/tokens.css +78 -76
- package/src/styles/ui-form-controls.css +186 -0
- package/src/ui/BezierCurveEditor.svelte +59 -43
- package/src/ui/ColorEditPanel.svelte +71 -44
- package/src/ui/EditorViewSwitcher.svelte +9 -5
- package/src/ui/FontStackEditor.svelte +17 -16
- package/src/ui/GradientEditor.svelte +42 -33
- package/src/ui/GradientStopPicker.svelte +18 -29
- package/src/ui/PaletteEditor.svelte +238 -212
- package/src/ui/PresetFileManager.svelte +20 -18
- package/src/ui/ProjectFontsSection.svelte +34 -34
- package/src/ui/SurfacesTab.svelte +3 -3
- package/src/ui/TextTab.svelte +2 -2
- package/src/ui/ThemeFileManager.svelte +38 -35
- package/src/ui/Toggle.svelte +11 -9
- package/src/ui/UICopyPopover.svelte +19 -15
- package/src/ui/UIDialog.svelte +48 -30
- package/src/ui/UIFontFamilySelector.svelte +104 -78
- package/src/ui/UIFontSizeSelector.svelte +38 -20
- package/src/ui/UIFontWeightSelector.svelte +33 -13
- package/src/ui/UILineHeightSelector.svelte +33 -13
- package/src/ui/UILinkToggle.svelte +7 -6
- package/src/ui/UIOptionItem.svelte +21 -7
- package/src/ui/UIOptionList.svelte +9 -3
- package/src/ui/UIPaddingSelector.svelte +108 -82
- package/src/ui/UIPaletteSelector.svelte +186 -161
- package/src/ui/UIRadio.svelte +23 -8
- package/src/ui/UIRadioGroup.svelte +9 -8
- package/src/ui/UIRelinkConfirmPopover.svelte +26 -16
- package/src/ui/UITokenSelector.svelte +112 -68
- package/src/ui/UIVariantSelector.svelte +79 -57
- package/src/ui/VariablesTab.svelte +15 -15
- package/src/ui/palette/GradientStopEditor.svelte +45 -26
- package/src/ui/palette/OverridesPanel.svelte +85 -49
- package/src/ui/palette/PaletteBase.svelte +60 -32
- package/src/ui/palette/ScaleCurveEditor.svelte +25 -10
- package/src/ui/sections/ColumnsSection.svelte +13 -13
- package/src/ui/sections/GradientsSection.svelte +12 -9
- package/src/ui/sections/OverlaysSection.svelte +50 -47
- package/src/ui/sections/ShadowsSection.svelte +110 -104
- package/src/ui/sections/TokenScaleTable.svelte +38 -22
- package/src/ui/sections/tokenScales.ts +2 -2
- package/src/styles/form-controls.css +0 -188
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
interface Props {
|
|
3
|
+
text?: string;
|
|
4
|
+
position?: 'top' | 'bottom';
|
|
5
|
+
open?: boolean;
|
|
6
|
+
children?: import('svelte').Snippet;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
text = '',
|
|
11
|
+
position = 'top',
|
|
12
|
+
open = false,
|
|
13
|
+
children
|
|
14
|
+
}: Props = $props();
|
|
5
15
|
</script>
|
|
6
16
|
|
|
7
17
|
<div class="tooltip-wrapper" class:open>
|
|
8
|
-
|
|
18
|
+
{@render children?.()}
|
|
9
19
|
{#if text}
|
|
10
20
|
<div class="tooltip" class:bottom={position === 'bottom'}>
|
|
11
21
|
{text}
|
|
@@ -20,7 +30,7 @@
|
|
|
20
30
|
--tooltip-text-font-family: var(--font-sans);
|
|
21
31
|
--tooltip-text-font-size: var(--font-size-sm);
|
|
22
32
|
--tooltip-text-font-weight: var(--font-weight-normal);
|
|
23
|
-
--tooltip-text-line-height: var(--line-height-
|
|
33
|
+
--tooltip-text-line-height: var(--line-height-md);
|
|
24
34
|
--tooltip-border: var(--border-neutral);
|
|
25
35
|
--tooltip-border-width: var(--border-width-0);
|
|
26
36
|
--tooltip-radius: var(--radius-md);
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
const isInIframe = typeof window !== 'undefined' && window.parent !== window;
|
|
7
7
|
const enabled = isDev && !isInIframe;
|
|
8
8
|
|
|
9
|
-
let count = 12;
|
|
10
|
-
let gutter = '';
|
|
11
|
-
let margin = '';
|
|
9
|
+
let count = $state(12);
|
|
10
|
+
let gutter = $state('');
|
|
11
|
+
let margin = $state('');
|
|
12
12
|
|
|
13
13
|
function readTokens() {
|
|
14
14
|
const cs = getComputedStyle(document.documentElement);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script
|
|
1
|
+
<script module lang="ts">
|
|
2
2
|
// __PROJECT_ROOT__ is injected by the themeFileApi Vite plugin as a `define`.
|
|
3
3
|
// Consumers don't need to configure it themselves. We declare it locally so
|
|
4
4
|
// this component's type-check passes in consumer projects that haven't added
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<script lang="ts">
|
|
15
|
+
import { run } from 'svelte/legacy';
|
|
16
|
+
|
|
15
17
|
import { onMount, onDestroy } from 'svelte';
|
|
16
18
|
import { fade } from 'svelte/transition';
|
|
17
19
|
import { cubicInOut } from 'svelte/easing';
|
|
@@ -23,12 +25,23 @@
|
|
|
23
25
|
import { postParentRoute } from './parentRouteStore';
|
|
24
26
|
import type { NavLink } from './navLinkTypes';
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
interface Props {
|
|
29
|
+
open?: boolean | undefined;
|
|
30
|
+
editorPath?: string;
|
|
31
|
+
navLinks?: NavLink[];
|
|
32
|
+
pageSources?: Record<string, string>;
|
|
33
|
+
hidePageSourceOn?: string[];
|
|
34
|
+
projectRoot?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
open = $bindable(undefined),
|
|
39
|
+
editorPath = '/editor',
|
|
40
|
+
navLinks = [],
|
|
41
|
+
pageSources = {},
|
|
42
|
+
hidePageSourceOn = [],
|
|
43
|
+
projectRoot = INJECTED_PROJECT_ROOT
|
|
44
|
+
}: Props = $props();
|
|
32
45
|
|
|
33
46
|
// Self-gate: only render in dev, and never inside an iframe (the /editor
|
|
34
47
|
// page embeds this same app in an iframe and would otherwise recursively
|
|
@@ -44,25 +57,33 @@
|
|
|
44
57
|
if (!consumerControlsOpen) {
|
|
45
58
|
open = enabled && quietGet(OPEN_KEY) === '1';
|
|
46
59
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
run(() => {
|
|
61
|
+
if (!consumerControlsOpen && typeof window !== 'undefined') {
|
|
62
|
+
quietSet(OPEN_KEY, open ? '1' : '0');
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
run(() => {
|
|
66
|
+
overlayOpen.set(!!open);
|
|
67
|
+
});
|
|
51
68
|
|
|
52
69
|
// Hide the overlay entirely when the user is already on the editor route
|
|
53
70
|
// (the editor page has its own chrome).
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
let onEditorPath = $derived($route === editorPath);
|
|
72
|
+
let sourceFile = $derived(pageSources[$route]);
|
|
73
|
+
let showSource = $derived(!!sourceFile && !!projectRoot && !hidePageSourceOn.includes($route));
|
|
57
74
|
|
|
58
75
|
// Mount the iframe the first time the editor is shown, then keep it mounted
|
|
59
76
|
// across hide/show cycles so editor state (unsaved slider values, scroll
|
|
60
77
|
// position, expanded sections) survives.
|
|
61
|
-
let hasBeenOpen: boolean = !!open;
|
|
62
|
-
|
|
78
|
+
let hasBeenOpen: boolean = $state(!!open);
|
|
79
|
+
run(() => {
|
|
80
|
+
if (open) hasBeenOpen = true;
|
|
81
|
+
});
|
|
63
82
|
|
|
64
|
-
let editorFrame: HTMLIFrameElement | undefined;
|
|
65
|
-
|
|
83
|
+
let editorFrame: HTMLIFrameElement | undefined = $state();
|
|
84
|
+
run(() => {
|
|
85
|
+
postParentRoute(editorFrame?.contentWindow, $route);
|
|
86
|
+
});
|
|
66
87
|
|
|
67
88
|
type Mode = 'docked' | 'floating';
|
|
68
89
|
|
|
@@ -109,9 +130,9 @@
|
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
const initial = loadState();
|
|
112
|
-
let mode: Mode = initial.mode;
|
|
113
|
-
let dockedWidth: number = Math.max(MIN_WIDTH, initial.dockedWidth);
|
|
114
|
-
let floating = { ...initial.floating };
|
|
133
|
+
let mode: Mode = $state(initial.mode);
|
|
134
|
+
let dockedWidth: number = $state(Math.max(MIN_WIDTH, initial.dockedWidth));
|
|
135
|
+
let floating = $state({ ...initial.floating });
|
|
115
136
|
|
|
116
137
|
// Approximate natural size of the collapsed pill (Editor title + columns toggle).
|
|
117
138
|
// A few pixels of overshoot is fine — the panel has overflow:hidden.
|
|
@@ -125,11 +146,11 @@
|
|
|
125
146
|
|
|
126
147
|
// Suppress CSS transitions during gestures and mode swaps so dragging doesn't
|
|
127
148
|
// re-animate every frame, and floating↔docked swaps snap cleanly.
|
|
128
|
-
let suppressTransition = false;
|
|
149
|
+
let suppressTransition = $state(false);
|
|
129
150
|
|
|
130
151
|
// Gesture state — a transparent scrim covers the iframe while any gesture is active
|
|
131
152
|
// so pointer events land on the panel, not on content inside the iframe.
|
|
132
|
-
let gesturing: 'drag' | 'resize-left' | 'resize-se' | null = null;
|
|
153
|
+
let gesturing: 'drag' | 'resize-left' | 'resize-se' | null = $state(null);
|
|
133
154
|
|
|
134
155
|
function startDrag(e: PointerEvent) {
|
|
135
156
|
if (!open || mode !== 'floating') return;
|
|
@@ -244,15 +265,15 @@
|
|
|
244
265
|
window.removeEventListener('lt-overlay-toggle', handleToggleRequest);
|
|
245
266
|
});
|
|
246
267
|
|
|
247
|
-
|
|
268
|
+
let panelStyle = $derived(!open
|
|
248
269
|
? `position: fixed; top: 12px; right: 12px; width: ${COLLAPSED_WIDTH}px; height: ${COLLAPSED_HEIGHT}px;`
|
|
249
270
|
: mode === 'docked'
|
|
250
271
|
? `position: fixed; top: 0; right: 0; width: ${dockedWidth}px; height: 100vh;`
|
|
251
|
-
: `position: fixed; top: ${floating.y}px; left: ${floating.x}px; width: ${floating.width}px; height: ${floating.height}px
|
|
272
|
+
: `position: fixed; top: ${floating.y}px; left: ${floating.x}px; width: ${floating.width}px; height: ${floating.height}px;`);
|
|
252
273
|
</script>
|
|
253
274
|
|
|
254
275
|
{#if enabled && !onEditorPath}
|
|
255
|
-
<!-- svelte-ignore
|
|
276
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
256
277
|
<div
|
|
257
278
|
class="lt-overlay"
|
|
258
279
|
style={panelStyle}
|
|
@@ -264,13 +285,13 @@
|
|
|
264
285
|
>
|
|
265
286
|
<div
|
|
266
287
|
class="header"
|
|
267
|
-
|
|
268
|
-
|
|
288
|
+
onpointerdown={startDrag}
|
|
289
|
+
ondblclick={handleHeaderDblClick}
|
|
269
290
|
title={open ? 'Double-click to hide' : 'Double-click to show'}
|
|
270
291
|
>
|
|
271
292
|
<button
|
|
272
293
|
class="hdr-btn text title"
|
|
273
|
-
|
|
294
|
+
onclick={toggleOpen}
|
|
274
295
|
title={open ? 'Hide Editor' : 'Show Editor'}
|
|
275
296
|
>
|
|
276
297
|
<i class="fas {open ? 'fa-chevron-right' : 'fa-chevron-left'}"></i>
|
|
@@ -280,7 +301,7 @@
|
|
|
280
301
|
<button
|
|
281
302
|
class="hdr-btn icon"
|
|
282
303
|
class:active={$columnsVisible}
|
|
283
|
-
|
|
304
|
+
onclick={toggleColumns}
|
|
284
305
|
title="{$columnsVisible ? 'Hide' : 'Show'} columns"
|
|
285
306
|
>
|
|
286
307
|
<i class="fas fa-grip-lines-vertical"></i>
|
|
@@ -290,7 +311,7 @@
|
|
|
290
311
|
<button
|
|
291
312
|
class="hdr-btn icon"
|
|
292
313
|
title={mode === 'docked' ? 'Float' : 'Dock to right'}
|
|
293
|
-
|
|
314
|
+
onclick={toggleMode}
|
|
294
315
|
transition:fade={BTN_FADE}
|
|
295
316
|
>
|
|
296
317
|
<i class={mode === 'docked' ? 'fas fa-up-right-from-square' : 'fas fa-thumbtack'}></i>
|
|
@@ -313,7 +334,7 @@
|
|
|
313
334
|
transition:fade={BTN_FADE}
|
|
314
335
|
>
|
|
315
336
|
<i class="fas fa-code"></i>
|
|
316
|
-
Show
|
|
337
|
+
Show page source
|
|
317
338
|
</a>
|
|
318
339
|
{/if}
|
|
319
340
|
|
|
@@ -329,7 +350,7 @@
|
|
|
329
350
|
class:active={$route === link.path}
|
|
330
351
|
aria-selected={$route === link.path}
|
|
331
352
|
disabled={link.disabled}
|
|
332
|
-
|
|
353
|
+
onclick={() => navigate(link.path)}
|
|
333
354
|
>
|
|
334
355
|
{#if link.icon}<i class="fas {link.icon}"></i>{/if}
|
|
335
356
|
<span>{link.label}</span>
|
|
@@ -347,7 +368,7 @@
|
|
|
347
368
|
title="Token editor"
|
|
348
369
|
class="editor-frame"
|
|
349
370
|
bind:this={editorFrame}
|
|
350
|
-
|
|
371
|
+
onload={() => postParentRoute(editorFrame?.contentWindow, $route)}
|
|
351
372
|
></iframe>
|
|
352
373
|
{#if gesturing}
|
|
353
374
|
<div class="gesture-scrim"></div>
|
|
@@ -355,9 +376,9 @@
|
|
|
355
376
|
</div>
|
|
356
377
|
|
|
357
378
|
{#if mode === 'docked'}
|
|
358
|
-
<div class="resize-left"
|
|
379
|
+
<div class="resize-left" onpointerdown={startDockedResize}></div>
|
|
359
380
|
{:else}
|
|
360
|
-
<div class="resize-se"
|
|
381
|
+
<div class="resize-se" onpointerdown={startFloatingResize}></div>
|
|
361
382
|
{/if}
|
|
362
383
|
{/if}
|
|
363
384
|
</div>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { run } from 'svelte/legacy';
|
|
3
|
+
|
|
2
4
|
import { onMount, onDestroy } from 'svelte';
|
|
3
5
|
import ComponentsTab from '../component-editor/scaffolding/ComponentsTab.svelte';
|
|
4
6
|
import PresetFileManager from '../ui/PresetFileManager.svelte';
|
|
@@ -6,20 +8,28 @@
|
|
|
6
8
|
import { componentRegistryEntries, validateRegistryAgainstServerScan } from '../component-editor/registry';
|
|
7
9
|
import { listComponents } from '../lib/componentConfigService';
|
|
8
10
|
import { selectedComponent } from '../lib/editorViewStore';
|
|
11
|
+
// Editor chrome + form controls + icon font must be JS imports (not @import
|
|
12
|
+
// inside the style block) so Vite resolves them via the module graph
|
|
13
|
+
// regardless of how the consumer compiles Svelte CSS (external ?lang.css vs
|
|
14
|
+
// injected); otherwise the @import URLs leak to the browser and 404 against
|
|
15
|
+
// the consumer's root.
|
|
16
|
+
import '../styles/ui-editor.css';
|
|
17
|
+
import '../styles/ui-form-controls.css';
|
|
18
|
+
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
9
19
|
|
|
10
|
-
let drawerOpen = true;
|
|
20
|
+
let drawerOpen = $state(true);
|
|
11
21
|
|
|
12
22
|
// Demo page is statically imported from `./Demo.svelte` in App.svelte; the
|
|
13
23
|
// glob resolves to an empty object if the file has been deleted, in which
|
|
14
24
|
// case we hide the demo option from the page-switcher.
|
|
15
25
|
const demoExists = Object.keys(import.meta.glob('./Demo.svelte')).length > 0;
|
|
16
26
|
|
|
17
|
-
let pageMenuOpen = false;
|
|
18
|
-
let pageMenuRoot: HTMLElement;
|
|
27
|
+
let pageMenuOpen = $state(false);
|
|
28
|
+
let pageMenuRoot: HTMLElement | undefined = $state();
|
|
19
29
|
|
|
20
30
|
const HINT_DELAY_MS = 80;
|
|
21
|
-
let hintLabel: string | null = null;
|
|
22
|
-
let hintTop = 0;
|
|
31
|
+
let hintLabel: string | null = $state(null);
|
|
32
|
+
let hintTop = $state(0);
|
|
23
33
|
let hintTimer: ReturnType<typeof setTimeout> | null = null;
|
|
24
34
|
|
|
25
35
|
function showHint(label: string, target: HTMLElement) {
|
|
@@ -40,7 +50,9 @@
|
|
|
40
50
|
hintLabel = null;
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
run(() => {
|
|
54
|
+
if (drawerOpen) hideHint();
|
|
55
|
+
});
|
|
44
56
|
|
|
45
57
|
function selectComponent(id: string) {
|
|
46
58
|
selectedComponent.set(id);
|
|
@@ -101,7 +113,7 @@
|
|
|
101
113
|
class="rail-toggle"
|
|
102
114
|
aria-label={drawerOpen ? 'Collapse components menu' : 'Expand components menu'}
|
|
103
115
|
aria-expanded={drawerOpen}
|
|
104
|
-
|
|
116
|
+
onclick={() => (drawerOpen = !drawerOpen)}
|
|
105
117
|
>
|
|
106
118
|
<i class="fas {drawerOpen ? 'fa-arrow-left' : 'fa-arrow-right'}"></i>
|
|
107
119
|
</button>
|
|
@@ -112,19 +124,19 @@
|
|
|
112
124
|
aria-haspopup="menu"
|
|
113
125
|
aria-expanded={pageMenuOpen}
|
|
114
126
|
tabindex={drawerOpen ? 0 : -1}
|
|
115
|
-
|
|
127
|
+
onclick={() => drawerOpen && (pageMenuOpen = !pageMenuOpen)}
|
|
116
128
|
>
|
|
117
129
|
<span class="rail-label">Components</span>
|
|
118
130
|
<i class="fas fa-chevron-down rail-chevron" class:open={pageMenuOpen}></i>
|
|
119
131
|
</button>
|
|
120
132
|
{#if pageMenuOpen && drawerOpen}
|
|
121
133
|
<div class="page-menu" role="menu">
|
|
122
|
-
<button class="page-menu-item" role="menuitem"
|
|
134
|
+
<button class="page-menu-item" role="menuitem" onclick={() => selectPage('/')}>
|
|
123
135
|
<i class="fas fa-home"></i>
|
|
124
136
|
<span>Main site</span>
|
|
125
137
|
</button>
|
|
126
138
|
{#if demoExists}
|
|
127
|
-
<button class="page-menu-item" role="menuitem"
|
|
139
|
+
<button class="page-menu-item" role="menuitem" onclick={() => selectPage('/demo')}>
|
|
128
140
|
<i class="fas fa-box-open"></i>
|
|
129
141
|
<span>Demo page</span>
|
|
130
142
|
</button>
|
|
@@ -137,9 +149,9 @@
|
|
|
137
149
|
<button
|
|
138
150
|
class="nav-item"
|
|
139
151
|
class:active={$selectedComponent === item.id}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
onmouseenter={(e) => showHint(item.label, e.currentTarget)}
|
|
153
|
+
onmouseleave={hideHint}
|
|
154
|
+
onclick={() => selectComponent(item.id)}
|
|
143
155
|
>
|
|
144
156
|
<i class={item.icon}></i>
|
|
145
157
|
<span class="rail-label">{item.label}</span>
|
|
@@ -163,7 +175,6 @@
|
|
|
163
175
|
</div>
|
|
164
176
|
|
|
165
177
|
<style>
|
|
166
|
-
@import '../styles/ui-editor.css';
|
|
167
178
|
.components-shell {
|
|
168
179
|
--rail-w: 48px;
|
|
169
180
|
display: grid;
|
package/src/pages/Editor.svelte
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
import { installEditorKeybindings } from '../lib/editorKeybindings';
|
|
6
6
|
import { initializeEditorStore } from '../lib/editorStore';
|
|
7
7
|
import { storageKey } from '../lib/editorConfig';
|
|
8
|
+
// Editor chrome + form controls + icon font must be JS imports (not @import
|
|
9
|
+
// inside the style block) so Vite resolves them via the module graph
|
|
10
|
+
// regardless of how the consumer compiles Svelte CSS (external ?lang.css vs
|
|
11
|
+
// injected); otherwise the @import URLs leak to the browser and 404 against
|
|
12
|
+
// the consumer's root.
|
|
13
|
+
import '../styles/ui-editor.css';
|
|
14
|
+
import '../styles/ui-form-controls.css';
|
|
15
|
+
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
8
16
|
|
|
9
17
|
const inOverlay = typeof window !== 'undefined' && window.parent !== window;
|
|
10
18
|
|
|
@@ -46,8 +54,6 @@
|
|
|
46
54
|
</div>
|
|
47
55
|
|
|
48
56
|
<style>
|
|
49
|
-
@import '../styles/ui-editor.css';
|
|
50
|
-
|
|
51
57
|
.editor-page {
|
|
52
58
|
min-height: 100vh;
|
|
53
59
|
background: black;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { run } from 'svelte/legacy';
|
|
3
|
+
|
|
2
4
|
import { onMount, onDestroy } from 'svelte';
|
|
3
5
|
import { get } from 'svelte/store';
|
|
4
6
|
import VariablesTab from '../ui/VariablesTab.svelte';
|
|
@@ -28,18 +30,18 @@
|
|
|
28
30
|
|
|
29
31
|
const componentNavItems = componentRegistryEntries.map(({ id, label, icon }) => ({ id, label, icon }));
|
|
30
32
|
|
|
31
|
-
let selectedTokenSection: string | null = null;
|
|
32
|
-
let saveStatus: 'idle' | 'saving' | 'saved' | 'error' = 'idle';
|
|
33
|
+
let selectedTokenSection: string | null = $state(null);
|
|
34
|
+
let saveStatus: 'idle' | 'saving' | 'saved' | 'error' = $state('idle');
|
|
33
35
|
|
|
34
|
-
let shellEl: HTMLElement | null = null;
|
|
35
|
-
let shellWidth = 1024;
|
|
36
|
+
let shellEl: HTMLElement | null = $state(null);
|
|
37
|
+
let shellWidth = $state(1024);
|
|
36
38
|
const CONDENSE_BELOW = 520;
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
let condensed = $derived($sidebarCondensed === 'auto' ? shellWidth < CONDENSE_BELOW : $sidebarCondensed);
|
|
39
41
|
|
|
40
42
|
const HINT_DELAY_MS = 80;
|
|
41
|
-
let hintLabel: string | null = null;
|
|
42
|
-
let hintTop = 0;
|
|
43
|
+
let hintLabel: string | null = $state(null);
|
|
44
|
+
let hintTop = $state(0);
|
|
43
45
|
let hintTimer: ReturnType<typeof setTimeout> | null = null;
|
|
44
46
|
|
|
45
47
|
function showHint(label: string, target: HTMLElement) {
|
|
@@ -60,7 +62,9 @@
|
|
|
60
62
|
hintLabel = null;
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
run(() => {
|
|
66
|
+
if (!condensed) hideHint();
|
|
67
|
+
});
|
|
64
68
|
|
|
65
69
|
function scrollToSection(sectionId: string) {
|
|
66
70
|
selectedTokenSection = sectionId;
|
|
@@ -80,8 +84,8 @@
|
|
|
80
84
|
});
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
async function handleSave(
|
|
84
|
-
const { fileName, displayName } =
|
|
87
|
+
async function handleSave(detail: { fileName: string; displayName: string }) {
|
|
88
|
+
const { fileName, displayName } = detail;
|
|
85
89
|
saveStatus = 'saving';
|
|
86
90
|
try {
|
|
87
91
|
await persistTheme(get(editorState), fileName, displayName);
|
|
@@ -93,9 +97,9 @@
|
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
async function handleLoad(
|
|
100
|
+
async function handleLoad(detail: { fileName: string }) {
|
|
97
101
|
try {
|
|
98
|
-
await hydrateTheme(
|
|
102
|
+
await hydrateTheme(detail.fileName);
|
|
99
103
|
} catch {
|
|
100
104
|
// silent
|
|
101
105
|
}
|
|
@@ -132,7 +136,7 @@
|
|
|
132
136
|
class="rail-toggle"
|
|
133
137
|
aria-label={condensed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
134
138
|
aria-expanded={!condensed}
|
|
135
|
-
|
|
139
|
+
onclick={toggleCondensed}
|
|
136
140
|
>
|
|
137
141
|
<i class="fas {condensed ? 'fa-arrow-right' : 'fa-arrow-left'}"></i>
|
|
138
142
|
</button>
|
|
@@ -146,9 +150,9 @@
|
|
|
146
150
|
<button
|
|
147
151
|
class="nav-item"
|
|
148
152
|
class:active={selectedTokenSection === item.id}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
onmouseenter={(e) => showHint(item.label, e.currentTarget)}
|
|
154
|
+
onmouseleave={hideHint}
|
|
155
|
+
onclick={() => scrollToSection(item.id)}
|
|
152
156
|
>
|
|
153
157
|
<i class={item.icon}></i>
|
|
154
158
|
<span class="nav-label">{item.label}</span>
|
|
@@ -157,7 +161,7 @@
|
|
|
157
161
|
</div>
|
|
158
162
|
{#if !condensed}
|
|
159
163
|
<div class="sidebar-footer">
|
|
160
|
-
<ThemeFileManager {saveStatus}
|
|
164
|
+
<ThemeFileManager {saveStatus} onsave={handleSave} onload={handleLoad} />
|
|
161
165
|
</div>
|
|
162
166
|
{/if}
|
|
163
167
|
{:else}
|
|
@@ -166,9 +170,9 @@
|
|
|
166
170
|
<button
|
|
167
171
|
class="nav-item"
|
|
168
172
|
class:active={$selectedComponent === item.id}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
onmouseenter={(e) => showHint(item.label, e.currentTarget)}
|
|
174
|
+
onmouseleave={hideHint}
|
|
175
|
+
onclick={() => selectComponent(item.id)}
|
|
172
176
|
>
|
|
173
177
|
<i class={item.icon}></i>
|
|
174
178
|
<span class="nav-label">{item.label}</span>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Site Styles — global typography for the themed pages
|
|
3
|
+
*
|
|
4
|
+
* Unscoped element selectors (h1, h2, p, a, ul li, …) consume the design
|
|
5
|
+
* tokens in tokens.css and recolor with the user's theme. Loaded globally
|
|
6
|
+
* from main.ts.
|
|
7
|
+
*
|
|
8
|
+
* Pair with: ui-editor.css (--ui-* chrome, opposite scope — neutral and
|
|
9
|
+
* theme-immune; only loaded on editor pages).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
h1 {
|
|
13
|
+
font-family: var(--font-display);
|
|
14
|
+
font-size: var(--font-size-4xl);
|
|
15
|
+
font-weight: var(--font-weight-semibold);
|
|
16
|
+
color: var(--text-primary);
|
|
17
|
+
margin: 0 0 var(--space-12);
|
|
18
|
+
line-height: var(--line-height-sm);
|
|
19
|
+
overflow-wrap: break-word;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h2 {
|
|
23
|
+
font-family: var(--font-serif);
|
|
24
|
+
font-size: var(--font-size-2xl);
|
|
25
|
+
font-weight: var(--font-weight-semibold);
|
|
26
|
+
color: var(--text-primary);
|
|
27
|
+
letter-spacing: 0.01em;
|
|
28
|
+
margin: var(--space-32) 0 var(--space-12);
|
|
29
|
+
line-height: var(--line-height-sm);
|
|
30
|
+
overflow-wrap: break-word;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h3 {
|
|
34
|
+
font-family: var(--font-serif);
|
|
35
|
+
font-size: var(--font-size-xl);
|
|
36
|
+
font-weight: var(--font-weight-normal);
|
|
37
|
+
color: var(--text-primary);
|
|
38
|
+
margin: var(--space-24) 0 var(--space-8);
|
|
39
|
+
line-height: var(--line-height-sm);
|
|
40
|
+
overflow-wrap: break-word;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (max-width: 768px) {
|
|
44
|
+
h1 {
|
|
45
|
+
line-height: 1.1;
|
|
46
|
+
margin-bottom: var(--space-8);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
h2 {
|
|
50
|
+
line-height: 1.15;
|
|
51
|
+
margin-top: var(--space-24);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
h3 {
|
|
55
|
+
line-height: 1.2;
|
|
56
|
+
margin-top: var(--space-20);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
p {
|
|
61
|
+
font-family: var(--font-serif);
|
|
62
|
+
font-size: var(--font-size-md);
|
|
63
|
+
color: var(--text-secondary);
|
|
64
|
+
line-height: var(--line-height-md);
|
|
65
|
+
margin: 0 0 14px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
p:last-child {
|
|
69
|
+
margin-bottom: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
a {
|
|
73
|
+
color: var(--text-brand);
|
|
74
|
+
text-decoration: none;
|
|
75
|
+
transition: color var(--duration-150);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
a:hover {
|
|
79
|
+
color: var(--color-brand-300);
|
|
80
|
+
text-decoration: underline;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
strong {
|
|
84
|
+
color: var(--text-primary);
|
|
85
|
+
font-weight: var(--font-weight-semibold);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ul {
|
|
89
|
+
padding-left: var(--space-24);
|
|
90
|
+
margin: var(--space-12) 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ul li {
|
|
94
|
+
font-family: var(--font-serif);
|
|
95
|
+
font-size: var(--font-size-md);
|
|
96
|
+
color: var(--text-secondary);
|
|
97
|
+
line-height: 1.75;
|
|
98
|
+
margin-bottom: var(--space-4);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
ul li::marker {
|
|
102
|
+
color: var(--text-tertiary);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
ol {
|
|
106
|
+
padding-left: var(--space-24);
|
|
107
|
+
margin: var(--space-12) 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ol li {
|
|
111
|
+
font-family: var(--font-sans);
|
|
112
|
+
font-size: var(--font-size-md);
|
|
113
|
+
color: var(--text-secondary);
|
|
114
|
+
line-height: 1.6;
|
|
115
|
+
margin-bottom: var(--space-4);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
ol li::marker {
|
|
119
|
+
color: var(--text-tertiary);
|
|
120
|
+
font-weight: var(--font-weight-semibold);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
hr {
|
|
124
|
+
border: none;
|
|
125
|
+
border-top: 1px solid var(--border-neutral-faint);
|
|
126
|
+
margin: var(--space-32) 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
blockquote {
|
|
130
|
+
margin: var(--space-16) 0;
|
|
131
|
+
padding: var(--space-12) var(--space-20);
|
|
132
|
+
border: 1px solid var(--color-brand-500);
|
|
133
|
+
border-left: 4px solid var(--color-brand-500);
|
|
134
|
+
background: var(--surface-neutral-high);
|
|
135
|
+
border-radius: 0 var(--radius-md) var(--radius-md) 0;
|
|
136
|
+
color: var(--text-secondary);
|
|
137
|
+
font-style: italic;
|
|
138
|
+
}
|