@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.
- package/README.md +168 -21
- package/dist-plugin/index.cjs +823 -336
- package/dist-plugin/index.d.cts +9 -7
- package/dist-plugin/index.d.ts +9 -7
- package/dist-plugin/index.js +822 -335
- package/package.json +46 -20
- package/src/assets/newspaper.webp +0 -0
- package/src/assets/offering.webp +0 -0
- package/src/component-editor/BadgeEditor.svelte +170 -0
- package/src/component-editor/CalloutEditor.svelte +103 -0
- package/src/component-editor/CardEditor.svelte +184 -0
- package/src/component-editor/CollapsibleSectionEditor.svelte +167 -0
- package/src/component-editor/CornerBadgeEditor.svelte +207 -0
- package/src/component-editor/DialogEditor.svelte +172 -0
- package/src/component-editor/ImageEditor.svelte +72 -0
- package/src/component-editor/InlineEditActionsEditor.svelte +83 -0
- package/src/component-editor/NotificationEditor.svelte +160 -0
- package/src/component-editor/ProgressBarEditor.svelte +124 -0
- package/src/component-editor/RadioButtonEditor.svelte +140 -0
- package/src/component-editor/SectionDividerEditor.svelte +263 -0
- package/src/component-editor/SegmentedControlEditor.svelte +154 -0
- package/src/component-editor/StandardButtonsEditor.svelte +178 -0
- package/src/component-editor/TabBarEditor.svelte +137 -0
- package/src/component-editor/TableEditor.svelte +128 -0
- package/src/component-editor/TooltipEditor.svelte +122 -0
- package/src/component-editor/editorTokens.test.ts +93 -0
- package/src/component-editor/groupKeySlots.test.ts +67 -0
- package/src/component-editor/groupKeySnapshot.test.ts +52 -0
- package/src/component-editor/index.ts +5 -0
- package/src/component-editor/registry.ts +246 -0
- package/src/component-editor/scaffolding/AngleDial.svelte +185 -0
- package/src/component-editor/scaffolding/ComponentEditorBase.svelte +96 -0
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +682 -0
- package/src/component-editor/scaffolding/ComponentFileMenu.svelte +312 -0
- package/src/component-editor/scaffolding/ComponentsTab.svelte +69 -0
- package/src/component-editor/scaffolding/CopyFromMenu.svelte +246 -0
- package/src/component-editor/scaffolding/DemoHeader.svelte +21 -0
- package/src/component-editor/scaffolding/DividerEditor.svelte +81 -0
- package/src/component-editor/scaffolding/FieldsetWrapper.svelte +46 -0
- package/src/component-editor/scaffolding/GradientCard.svelte +291 -0
- package/src/component-editor/scaffolding/LinkageChart.svelte +297 -0
- package/src/component-editor/scaffolding/LinkedBlock.svelte +418 -0
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +57 -0
- package/src/component-editor/scaffolding/SaveAsDialog.svelte +177 -0
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +25 -0
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +56 -0
- package/src/component-editor/scaffolding/StateBlock.svelte +115 -0
- package/src/component-editor/scaffolding/TokenLayout.svelte +511 -0
- package/src/component-editor/scaffolding/TypeEditor.svelte +82 -0
- package/src/component-editor/scaffolding/VariantGroup.svelte +277 -0
- package/src/component-editor/scaffolding/buildTypeGroupTokens.ts +97 -0
- package/src/component-editor/scaffolding/componentSectionType.ts +8 -0
- package/src/component-editor/scaffolding/componentSources.ts +9 -0
- package/src/component-editor/scaffolding/defaultSections.ts +16 -0
- package/src/component-editor/scaffolding/editorContext.ts +44 -0
- package/src/component-editor/scaffolding/linkedBlock.ts +226 -0
- package/src/component-editor/scaffolding/siblings.ts +33 -0
- package/src/component-editor/scaffolding/types.ts +39 -0
- package/src/components/Badge.svelte +231 -42
- package/src/components/Button.svelte +324 -124
- package/src/components/Callout.svelte +145 -0
- package/src/components/Card.svelte +123 -25
- package/src/components/CollapsibleSection.svelte +213 -35
- package/src/components/CornerBadge.svelte +224 -0
- package/src/components/Dialog.svelte +137 -114
- package/src/components/Image.svelte +43 -0
- package/src/components/InlineEditActions.svelte +74 -14
- package/src/components/Notification.svelte +184 -163
- package/src/components/ProgressBar.svelte +216 -22
- package/src/components/RadioButton.svelte +110 -40
- package/src/components/SectionDivider.svelte +428 -74
- package/src/components/SegmentedControl.svelte +203 -0
- package/src/components/TabBar.svelte +146 -21
- package/src/components/Table.svelte +102 -0
- package/src/components/Tooltip.svelte +45 -19
- package/src/components/types.ts +51 -0
- package/src/data/google-fonts.json +75 -0
- package/src/lib/ColumnsOverlay.svelte +20 -7
- package/src/lib/LiveEditorOverlay.svelte +257 -78
- package/src/lib/columnsOverlay.ts +21 -17
- package/src/lib/componentConfig.test.ts +204 -0
- package/src/lib/componentConfigKeys.ts +19 -0
- package/src/lib/componentConfigService.ts +88 -0
- package/src/lib/copyPopover.ts +30 -0
- package/src/lib/cssVarSync.ts +59 -7
- package/src/lib/editorConfigStore.ts +0 -10
- package/src/lib/editorCore.ts +402 -0
- package/src/lib/editorKeybindings.ts +52 -0
- package/src/lib/editorPersistence.ts +106 -0
- package/src/lib/editorRenderer.ts +74 -0
- package/src/lib/editorStore.test.ts +328 -0
- package/src/lib/editorStore.ts +412 -0
- package/src/lib/editorTypes.ts +100 -0
- package/src/lib/editorViewStore.ts +55 -0
- package/src/lib/files/versionedFileResource.ts +140 -0
- package/src/lib/fontLoader.ts +130 -0
- package/src/lib/fontMigration.ts +140 -0
- package/src/lib/fontParse.ts +168 -0
- package/src/lib/index.ts +48 -30
- package/src/lib/lazyConfig.test.ts +54 -0
- package/src/lib/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +64 -0
- package/src/lib/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +71 -0
- package/src/lib/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +43 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +68 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +35 -0
- package/src/lib/migrations/2026-05-10-sectiondivider-gradient-stops.ts +50 -0
- package/src/lib/migrations/2026-05-13-primary-to-brand.ts +90 -0
- package/src/lib/migrations/index.ts +93 -0
- package/src/lib/migrations/migrations.test.ts +341 -0
- package/src/lib/navLinkTypes.ts +1 -0
- package/src/lib/overlayState.ts +3 -0
- package/src/lib/paletteDerivation.ts +300 -0
- package/src/lib/parentRouteStore.ts +42 -0
- package/src/lib/parsers/globalRootBlock.ts +32 -0
- package/src/lib/presetService.ts +94 -0
- package/src/lib/router.ts +42 -10
- package/src/lib/scrollSection.ts +45 -0
- package/src/lib/slices/columns.ts +59 -0
- package/src/lib/slices/components.ts +362 -0
- package/src/lib/slices/domainVars.ts +15 -0
- package/src/lib/slices/fonts.ts +30 -0
- package/src/lib/slices/gradients.ts +153 -0
- package/src/lib/slices/overlays.ts +132 -0
- package/src/lib/slices/palettes.ts +26 -0
- package/src/lib/slices/shadows.ts +123 -0
- package/src/lib/storage.ts +88 -0
- package/src/lib/themeInit.ts +74 -0
- package/src/lib/themeService.ts +101 -0
- package/src/lib/themeTypes.ts +146 -0
- package/src/lib/tokenRegistry.ts +148 -0
- package/src/pages/ComponentEditorPage.svelte +384 -0
- package/src/pages/ComponentEditorPage.svelte.d.ts +2 -0
- package/src/pages/Editor.svelte +98 -0
- package/src/pages/Editor.svelte.d.ts +2 -0
- package/src/pages/EditorShell.svelte +348 -0
- package/src/styles/_padding.scss +34 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin.woff2 +0 -0
- package/src/styles/fonts.css +22 -10
- package/src/styles/form-controls.css +14 -16
- package/src/styles/tokens.css +1322 -0
- package/src/styles/ui-editor.css +126 -0
- package/src/{showcase → ui}/BezierCurveEditor.svelte +14 -14
- package/src/{showcase → ui}/ColorEditPanel.svelte +42 -36
- package/src/ui/EditorViewSwitcher.svelte +180 -0
- package/src/ui/FontStackEditor.svelte +360 -0
- package/src/ui/GradientEditor.svelte +461 -0
- package/src/ui/GradientStopPicker.svelte +74 -0
- package/src/ui/PaletteEditor.svelte +1590 -0
- package/src/ui/PaletteEditor.test.ts +108 -0
- package/src/ui/PresetFileManager.svelte +567 -0
- package/src/ui/ProjectFontsSection.svelte +645 -0
- package/src/{showcase → ui}/SurfacesTab.svelte +39 -39
- package/src/{showcase → ui}/TextTab.svelte +27 -27
- package/src/{showcase/TokenFileManager.svelte → ui/ThemeFileManager.svelte} +196 -112
- package/src/ui/Toggle.svelte +108 -0
- package/src/ui/UICopyPopover.svelte +78 -0
- package/src/{showcase/EditorDialog.svelte → ui/UIDialog.svelte} +66 -25
- package/src/ui/UIFontFamilySelector.svelte +309 -0
- package/src/ui/UIFontSizeSelector.svelte +165 -0
- package/src/ui/UIFontWeightSelector.svelte +52 -0
- package/src/ui/UILineHeightSelector.svelte +47 -0
- package/src/ui/UILinkToggle.svelte +60 -0
- package/src/ui/UIOptionItem.svelte +74 -0
- package/src/ui/UIOptionList.svelte +27 -0
- package/src/ui/UIPaddingSelector.svelte +661 -0
- package/src/ui/UIPaletteSelector.svelte +1084 -0
- package/src/ui/UIRadio.svelte +72 -0
- package/src/ui/UIRadioGroup.svelte +59 -0
- package/src/ui/UIRelinkConfirmPopover.svelte +235 -0
- package/src/ui/UITokenSelector.svelte +509 -0
- package/src/ui/UIVariantSelector.svelte +145 -0
- package/src/ui/VariablesTab.svelte +252 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/keepInViewport.ts +84 -0
- package/src/ui/palette/GradientStopEditor.svelte +482 -0
- package/src/ui/palette/OverridesPanel.svelte +526 -0
- package/src/ui/palette/PaletteBase.svelte +165 -0
- package/src/ui/palette/ScaleCurveEditor.svelte +38 -0
- package/src/ui/palette/paletteEditorState.ts +89 -0
- package/src/ui/sections/ColumnsSection.svelte +273 -0
- package/src/ui/sections/GradientsSection.svelte +147 -0
- package/src/ui/sections/OverlaysSection.svelte +670 -0
- package/src/ui/sections/ShadowsSection.svelte +1250 -0
- package/src/ui/sections/TokenScaleTable.svelte +332 -0
- package/src/ui/sections/tokenScales.ts +81 -0
- package/src/ui/variantScales.ts +108 -0
- package/src/components/DetailNav.svelte +0 -78
- package/src/components/Toggle.svelte +0 -86
- package/src/lib/tokenInit.ts +0 -29
- package/src/lib/tokenService.ts +0 -144
- package/src/lib/tokenTypes.ts +0 -45
- package/src/pages/Admin.svelte +0 -100
- package/src/pages/ShowcasePage.svelte +0 -144
- package/src/showcase/BackupBrowser.svelte +0 -617
- package/src/showcase/ComponentsTab.svelte +0 -105
- package/src/showcase/PaletteEditor.svelte +0 -2579
- package/src/showcase/PaletteSelector.svelte +0 -627
- package/src/showcase/TokenMap.svelte +0 -54
- package/src/showcase/VariablesTab.svelte +0 -2655
- package/src/showcase/VisualsTab.svelte +0 -231
- package/src/showcase/demos/BadgeDemo.svelte +0 -56
- package/src/showcase/demos/CardDemo.svelte +0 -50
- package/src/showcase/demos/ChoiceButtonsDemo.svelte +0 -192
- package/src/showcase/demos/CollapsibleSectionDemo.svelte +0 -54
- package/src/showcase/demos/DialogDemo.svelte +0 -42
- package/src/showcase/demos/InlineEditActionsDemo.svelte +0 -25
- package/src/showcase/demos/NotificationDemo.svelte +0 -147
- package/src/showcase/demos/ProgressBarDemo.svelte +0 -54
- package/src/showcase/demos/RadioButtonDemo.svelte +0 -56
- package/src/showcase/demos/SectionDividerDemo.svelte +0 -77
- package/src/showcase/demos/StandardButtonsDemo.svelte +0 -455
- package/src/showcase/demos/TabBarDemo.svelte +0 -58
- package/src/showcase/demos/TooltipDemo.svelte +0 -52
- package/src/showcase/editor.css +0 -93
- package/src/showcase/index.ts +0 -17
- package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
- package/src/styles/fonts/Domine/OFL.txt +0 -97
- package/src/styles/fonts/Domine/README.txt +0 -66
- /package/src/{showcase → ui}/curveEngine.ts +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { createEventDispatcher, onMount } from 'svelte';
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
5
|
-
import type { ProductionInfo } from '../lib/
|
|
6
|
-
import { activeFileName
|
|
7
|
-
import
|
|
8
|
-
import
|
|
3
|
+
import type { ThemeMeta } from '../lib/themeTypes';
|
|
4
|
+
import { listThemes, deleteTheme, setActiveFile, sanitizeFileName, getProductionInfo, setProductionFile } from '../lib/themeService';
|
|
5
|
+
import type { ProductionInfo } from '../lib/themeService';
|
|
6
|
+
import { activeFileName } from '../lib/editorConfigStore';
|
|
7
|
+
import { dirty } from '../lib/editorStore';
|
|
8
|
+
import UIDialog from './UIDialog.svelte';
|
|
9
9
|
|
|
10
10
|
const dispatch = createEventDispatcher<{
|
|
11
11
|
save: { fileName: string; displayName: string };
|
|
@@ -14,28 +14,20 @@
|
|
|
14
14
|
|
|
15
15
|
export let saveStatus: 'idle' | 'saving' | 'saved' | 'error' = 'idle';
|
|
16
16
|
|
|
17
|
-
let files:
|
|
17
|
+
let files: ThemeMeta[] = [];
|
|
18
18
|
let showFileList = false;
|
|
19
19
|
let saveAsEditing = false;
|
|
20
20
|
let saveAsName = '';
|
|
21
21
|
let saveAsInput: HTMLInputElement;
|
|
22
22
|
let currentDisplayName = 'Default';
|
|
23
|
-
let showBackups = false;
|
|
24
23
|
|
|
25
24
|
// --- Production state ---
|
|
26
25
|
let productionInfo: ProductionInfo | null = null;
|
|
27
26
|
let productionUpdateStatus: 'idle' | 'updating' | 'done' | 'error' = 'idle';
|
|
28
27
|
|
|
29
|
-
// --- Dirty state tracking ---
|
|
30
|
-
let savedConfigHash = '';
|
|
31
|
-
let initialized = false;
|
|
32
|
-
|
|
33
|
-
$: configHash = JSON.stringify($editorConfigs);
|
|
34
|
-
$: unsaved = initialized && configHash !== savedConfigHash;
|
|
35
|
-
|
|
36
28
|
async function refreshFiles() {
|
|
37
29
|
try {
|
|
38
|
-
files = await
|
|
30
|
+
files = await listThemes();
|
|
39
31
|
const active = files.find(f => f.isActive);
|
|
40
32
|
if (active) {
|
|
41
33
|
$activeFileName = active.fileName;
|
|
@@ -70,16 +62,10 @@
|
|
|
70
62
|
onMount(async () => {
|
|
71
63
|
await refreshFiles();
|
|
72
64
|
await refreshProduction();
|
|
73
|
-
// Delay snapshot until editors have initialized
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
savedConfigHash = JSON.stringify($editorConfigs);
|
|
76
|
-
initialized = true;
|
|
77
|
-
}, 500);
|
|
78
65
|
});
|
|
79
66
|
|
|
80
67
|
function handleSave() {
|
|
81
68
|
dispatch('save', { fileName: $activeFileName, displayName: currentDisplayName });
|
|
82
|
-
setTimeout(() => { savedConfigHash = JSON.stringify($editorConfigs); }, 300);
|
|
83
69
|
}
|
|
84
70
|
|
|
85
71
|
function handleSaveIncrement() {
|
|
@@ -100,10 +86,7 @@
|
|
|
100
86
|
dispatch('save', { fileName, displayName });
|
|
101
87
|
$activeFileName = fileName;
|
|
102
88
|
currentDisplayName = displayName;
|
|
103
|
-
setTimeout(() =>
|
|
104
|
-
refreshFiles();
|
|
105
|
-
savedConfigHash = JSON.stringify($editorConfigs);
|
|
106
|
-
}, 500);
|
|
89
|
+
setTimeout(() => refreshFiles(), 500);
|
|
107
90
|
}
|
|
108
91
|
|
|
109
92
|
function openSaveAs() {
|
|
@@ -126,10 +109,7 @@
|
|
|
126
109
|
dispatch('save', { fileName, displayName });
|
|
127
110
|
$activeFileName = fileName;
|
|
128
111
|
currentDisplayName = displayName;
|
|
129
|
-
setTimeout(() =>
|
|
130
|
-
refreshFiles();
|
|
131
|
-
savedConfigHash = JSON.stringify($editorConfigs);
|
|
132
|
-
}, 500);
|
|
112
|
+
setTimeout(() => refreshFiles(), 500);
|
|
133
113
|
}
|
|
134
114
|
|
|
135
115
|
function cancelSaveAs() {
|
|
@@ -137,22 +117,19 @@
|
|
|
137
117
|
saveAsName = '';
|
|
138
118
|
}
|
|
139
119
|
|
|
140
|
-
async function handleLoad(file:
|
|
120
|
+
async function handleLoad(file: ThemeMeta) {
|
|
141
121
|
showFileList = false;
|
|
142
122
|
await setActiveFile(file.fileName);
|
|
143
123
|
$activeFileName = file.fileName;
|
|
144
124
|
currentDisplayName = file.name;
|
|
145
125
|
dispatch('load', { fileName: file.fileName });
|
|
146
|
-
//
|
|
147
|
-
setTimeout(() => {
|
|
148
|
-
savedConfigHash = JSON.stringify($editorConfigs);
|
|
149
|
-
}, 500);
|
|
126
|
+
// editorStore.loadFromFile clears history and resets dirty — no snapshot needed here.
|
|
150
127
|
}
|
|
151
128
|
|
|
152
|
-
async function handleDelete(file:
|
|
129
|
+
async function handleDelete(file: ThemeMeta) {
|
|
153
130
|
if (file.fileName === 'default') return;
|
|
154
131
|
try {
|
|
155
|
-
await
|
|
132
|
+
await deleteTheme(file.fileName);
|
|
156
133
|
await refreshFiles();
|
|
157
134
|
// If we deleted the active file, it reverts to default on the server
|
|
158
135
|
if (file.fileName === $activeFileName) {
|
|
@@ -176,9 +153,46 @@
|
|
|
176
153
|
if (showFileList) refreshFiles();
|
|
177
154
|
}
|
|
178
155
|
|
|
156
|
+
const dateFormatter = new Intl.DateTimeFormat(undefined, {
|
|
157
|
+
month: 'short',
|
|
158
|
+
day: 'numeric',
|
|
159
|
+
hour: 'numeric',
|
|
160
|
+
minute: '2-digit',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
function formatUpdatedAt(iso: string): string {
|
|
164
|
+
if (!iso) return '';
|
|
165
|
+
const d = new Date(iso);
|
|
166
|
+
if (Number.isNaN(d.getTime())) return '';
|
|
167
|
+
return dateFormatter.format(d);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
type SortKey = 'name' | 'updatedAt';
|
|
171
|
+
let sortKey: SortKey = 'updatedAt';
|
|
172
|
+
let sortDir: 'asc' | 'desc' = 'desc';
|
|
173
|
+
|
|
174
|
+
function toggleSort(key: SortKey) {
|
|
175
|
+
if (sortKey === key) {
|
|
176
|
+
sortDir = sortDir === 'asc' ? 'desc' : 'asc';
|
|
177
|
+
} else {
|
|
178
|
+
sortKey = key;
|
|
179
|
+
// Default: names asc, dates desc (most-recent first)
|
|
180
|
+
sortDir = key === 'name' ? 'asc' : 'desc';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
$: sortedFiles = [...files].sort((a, b) => {
|
|
185
|
+
let cmp = 0;
|
|
186
|
+
if (sortKey === 'name') {
|
|
187
|
+
cmp = a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
|
|
188
|
+
} else {
|
|
189
|
+
cmp = (a.updatedAt || '').localeCompare(b.updatedAt || '');
|
|
190
|
+
}
|
|
191
|
+
return sortDir === 'asc' ? cmp : -cmp;
|
|
192
|
+
});
|
|
179
193
|
</script>
|
|
180
194
|
|
|
181
|
-
<div class="
|
|
195
|
+
<div class="theme-file-manager">
|
|
182
196
|
<div class="active-file">
|
|
183
197
|
<span class="active-label">Theme</span>
|
|
184
198
|
<span class="active-name">{currentDisplayName}</span>
|
|
@@ -193,8 +207,8 @@
|
|
|
193
207
|
class:saved={saveStatus === 'saved'}
|
|
194
208
|
class:error={saveStatus === 'error'}
|
|
195
209
|
on:click={handleSave}
|
|
196
|
-
disabled={saveStatus === 'saving'
|
|
197
|
-
title=
|
|
210
|
+
disabled={saveStatus === 'saving'}
|
|
211
|
+
title="Save to current file"
|
|
198
212
|
>
|
|
199
213
|
<i class="fas" class:fa-save={saveStatus === 'idle'} class:fa-spinner={saveStatus === 'saving'} class:fa-check={saveStatus === 'saved'} class:fa-times={saveStatus === 'error'}></i>
|
|
200
214
|
<span>
|
|
@@ -204,7 +218,7 @@
|
|
|
204
218
|
<button
|
|
205
219
|
class="tfm-btn increment-btn"
|
|
206
220
|
on:click={handleSaveIncrement}
|
|
207
|
-
disabled={saveStatus === 'saving'
|
|
221
|
+
disabled={saveStatus === 'saving'}
|
|
208
222
|
title="Save as incremented copy"
|
|
209
223
|
>
|
|
210
224
|
<i class="fas fa-plus"></i>
|
|
@@ -245,26 +259,18 @@
|
|
|
245
259
|
class="tfm-btn"
|
|
246
260
|
class:active={showFileList}
|
|
247
261
|
on:click={toggleFileList}
|
|
248
|
-
title="Load a
|
|
262
|
+
title="Load a theme"
|
|
249
263
|
>
|
|
250
264
|
<i class="fas fa-folder-open"></i>
|
|
251
265
|
<span>Load</span>
|
|
252
266
|
</button>
|
|
253
267
|
|
|
254
|
-
<button
|
|
255
|
-
class="tfm-btn history-btn"
|
|
256
|
-
on:click={() => showBackups = true}
|
|
257
|
-
title="View backup history and restore"
|
|
258
|
-
>
|
|
259
|
-
<i class="fas fa-history"></i>
|
|
260
|
-
<span>History</span>
|
|
261
|
-
</button>
|
|
262
268
|
|
|
263
269
|
</div>
|
|
264
270
|
|
|
265
271
|
<div class="status-labels">
|
|
266
|
-
<span class="status-label" class:dirty={
|
|
267
|
-
{
|
|
272
|
+
<span class="status-label" class:dirty={$dirty} class:clean={!$dirty}>
|
|
273
|
+
{$dirty ? 'Unsaved changes' : 'Saved'}
|
|
268
274
|
</span>
|
|
269
275
|
</div>
|
|
270
276
|
|
|
@@ -282,12 +288,12 @@
|
|
|
282
288
|
class:done={productionUpdateStatus === 'done'}
|
|
283
289
|
class:error={productionUpdateStatus === 'error'}
|
|
284
290
|
on:click={handleUpdateProduction}
|
|
285
|
-
disabled={productionUpdateStatus === 'updating' ||
|
|
291
|
+
disabled={productionUpdateStatus === 'updating' || (productionInfo?.fileName === $activeFileName)}
|
|
286
292
|
title={productionInfo?.fileName === $activeFileName ? 'Already in production' : `Set "${currentDisplayName}" as production`}
|
|
287
293
|
>
|
|
288
|
-
<i class="fas" class:fa-
|
|
294
|
+
<i class="fas" class:fa-upload={productionUpdateStatus === 'idle'} class:fa-spinner={productionUpdateStatus === 'updating'} class:fa-check={productionUpdateStatus === 'done'} class:fa-times={productionUpdateStatus === 'error'}></i>
|
|
289
295
|
<span>
|
|
290
|
-
{#if productionUpdateStatus === 'idle'}
|
|
296
|
+
{#if productionUpdateStatus === 'idle'}Apply Theme{:else if productionUpdateStatus === 'updating'}Applying{:else if productionUpdateStatus === 'done'}Applied{:else}Error{/if}
|
|
291
297
|
</span>
|
|
292
298
|
</button>
|
|
293
299
|
|
|
@@ -300,18 +306,42 @@
|
|
|
300
306
|
|
|
301
307
|
</div>
|
|
302
308
|
|
|
303
|
-
<
|
|
309
|
+
<UIDialog
|
|
304
310
|
bind:show={showFileList}
|
|
305
311
|
title="Load Theme"
|
|
306
312
|
cancelLabel="Close"
|
|
307
313
|
width="420px"
|
|
308
314
|
>
|
|
309
315
|
<div class="load-list">
|
|
310
|
-
|
|
316
|
+
<div class="load-header">
|
|
317
|
+
<button
|
|
318
|
+
class="sort-btn name-col"
|
|
319
|
+
class:active-sort={sortKey === 'name'}
|
|
320
|
+
on:click={() => toggleSort('name')}
|
|
321
|
+
>
|
|
322
|
+
<span>Name</span>
|
|
323
|
+
{#if sortKey === 'name'}
|
|
324
|
+
<i class="fas {sortDir === 'asc' ? 'fa-caret-up' : 'fa-caret-down'}"></i>
|
|
325
|
+
{/if}
|
|
326
|
+
</button>
|
|
327
|
+
<button
|
|
328
|
+
class="sort-btn date-col"
|
|
329
|
+
class:active-sort={sortKey === 'updatedAt'}
|
|
330
|
+
on:click={() => toggleSort('updatedAt')}
|
|
331
|
+
>
|
|
332
|
+
<span>Date</span>
|
|
333
|
+
{#if sortKey === 'updatedAt'}
|
|
334
|
+
<i class="fas {sortDir === 'asc' ? 'fa-caret-up' : 'fa-caret-down'}"></i>
|
|
335
|
+
{/if}
|
|
336
|
+
</button>
|
|
337
|
+
<span class="header-spacer"></span>
|
|
338
|
+
</div>
|
|
339
|
+
{#each sortedFiles as file}
|
|
311
340
|
<div class="load-item" class:active={file.fileName === $activeFileName}>
|
|
312
341
|
<button class="load-name-btn" on:click={() => handleLoad(file)}>
|
|
313
342
|
{file.name}
|
|
314
343
|
</button>
|
|
344
|
+
<span class="updated-at" title={file.updatedAt}>{formatUpdatedAt(file.updatedAt)}</span>
|
|
315
345
|
{#if file.fileName === $activeFileName}
|
|
316
346
|
<span class="active-badge">active</span>
|
|
317
347
|
{/if}
|
|
@@ -330,65 +360,55 @@
|
|
|
330
360
|
<div class="load-item empty">No saved files</div>
|
|
331
361
|
{/if}
|
|
332
362
|
</div>
|
|
333
|
-
</
|
|
334
|
-
|
|
335
|
-
<BackupBrowser
|
|
336
|
-
bind:open={showBackups}
|
|
337
|
-
on:restored={(e) => {
|
|
338
|
-
if (e.detail.type === 'tokens') {
|
|
339
|
-
dispatch('load', { fileName: $activeFileName });
|
|
340
|
-
}
|
|
341
|
-
refreshFiles();
|
|
342
|
-
}}
|
|
343
|
-
/>
|
|
363
|
+
</UIDialog>
|
|
344
364
|
|
|
345
365
|
|
|
346
366
|
<style>
|
|
347
|
-
.
|
|
367
|
+
.theme-file-manager {
|
|
348
368
|
display: flex;
|
|
349
369
|
flex-direction: column;
|
|
350
|
-
gap: var(--space-8);
|
|
370
|
+
gap: var(--ui-space-8);
|
|
351
371
|
}
|
|
352
372
|
|
|
353
373
|
.active-file {
|
|
354
374
|
display: flex;
|
|
355
375
|
flex-direction: column;
|
|
356
|
-
gap: var(--space-2);
|
|
357
|
-
padding: 0 var(--space-4);
|
|
376
|
+
gap: var(--ui-space-2);
|
|
377
|
+
padding: 0 var(--ui-space-4);
|
|
358
378
|
}
|
|
359
379
|
|
|
360
380
|
.active-label {
|
|
361
|
-
font-size: var(--font-xs);
|
|
362
|
-
color: var(--ui-text-
|
|
381
|
+
font-size: var(--ui-font-size-xs);
|
|
382
|
+
color: var(--ui-text-secondary);
|
|
363
383
|
text-transform: uppercase;
|
|
364
384
|
letter-spacing: 0.05em;
|
|
365
385
|
}
|
|
366
386
|
|
|
367
387
|
.active-name {
|
|
368
|
-
font-size: var(--font-md);
|
|
369
|
-
font-weight: var(--font-weight-semibold);
|
|
388
|
+
font-size: var(--ui-font-size-md);
|
|
389
|
+
font-weight: var(--ui-font-weight-semibold);
|
|
370
390
|
color: var(--ui-text-primary);
|
|
371
391
|
}
|
|
372
392
|
|
|
373
393
|
.button-grid {
|
|
374
394
|
display: flex;
|
|
375
395
|
flex-direction: column;
|
|
376
|
-
gap: var(--space-4);
|
|
396
|
+
gap: var(--ui-space-4);
|
|
377
397
|
}
|
|
378
398
|
|
|
379
399
|
.tfm-btn {
|
|
380
400
|
display: flex;
|
|
381
401
|
align-items: center;
|
|
382
|
-
gap: var(--space-4);
|
|
402
|
+
gap: var(--ui-space-4);
|
|
383
403
|
width: 100%;
|
|
384
|
-
padding: var(--space-6) var(--space-8);
|
|
404
|
+
padding: var(--ui-space-6) var(--ui-space-8);
|
|
385
405
|
background: var(--ui-surface-low);
|
|
386
406
|
border: 1px solid var(--ui-border-subtle);
|
|
387
|
-
border-radius: var(--radius-md);
|
|
407
|
+
border-radius: var(--ui-radius-md);
|
|
388
408
|
color: var(--ui-text-secondary);
|
|
389
|
-
font-size: var(--font-md);
|
|
409
|
+
font-size: var(--ui-font-size-md);
|
|
390
410
|
cursor: pointer;
|
|
391
|
-
transition: all var(--transition-fast);
|
|
411
|
+
transition: all var(--ui-transition-fast);
|
|
392
412
|
white-space: nowrap;
|
|
393
413
|
}
|
|
394
414
|
|
|
@@ -400,7 +420,7 @@
|
|
|
400
420
|
.tfm-btn:hover:not(:disabled) {
|
|
401
421
|
background: var(--ui-surface);
|
|
402
422
|
color: var(--ui-text-primary);
|
|
403
|
-
border-color: var(--border);
|
|
423
|
+
border-color: var(--ui-border-default);
|
|
404
424
|
}
|
|
405
425
|
|
|
406
426
|
.tfm-btn:disabled {
|
|
@@ -416,7 +436,7 @@
|
|
|
416
436
|
|
|
417
437
|
.save-row {
|
|
418
438
|
display: flex;
|
|
419
|
-
gap: var(--space-4);
|
|
439
|
+
gap: var(--ui-space-4);
|
|
420
440
|
}
|
|
421
441
|
|
|
422
442
|
.save-row .save-btn {
|
|
@@ -451,24 +471,24 @@
|
|
|
451
471
|
.save-as-inline {
|
|
452
472
|
display: flex;
|
|
453
473
|
flex-direction: column;
|
|
454
|
-
gap: var(--space-4);
|
|
474
|
+
gap: var(--ui-space-4);
|
|
455
475
|
}
|
|
456
476
|
|
|
457
477
|
.save-as-actions {
|
|
458
478
|
display: flex;
|
|
459
|
-
gap: var(--space-4);
|
|
479
|
+
gap: var(--ui-space-4);
|
|
460
480
|
justify-content: flex-end;
|
|
461
481
|
}
|
|
462
482
|
|
|
463
483
|
.save-as-input {
|
|
464
484
|
flex: 1;
|
|
465
485
|
min-width: 0;
|
|
466
|
-
padding: var(--space-6) var(--space-8);
|
|
486
|
+
padding: var(--ui-space-6) var(--ui-space-8);
|
|
467
487
|
background: var(--ui-surface-lowest);
|
|
468
488
|
border: 1px solid var(--ui-border-subtle);
|
|
469
|
-
border-radius: var(--radius-md);
|
|
489
|
+
border-radius: var(--ui-radius-md);
|
|
470
490
|
color: var(--ui-text-primary);
|
|
471
|
-
font-size: var(--font-md);
|
|
491
|
+
font-size: var(--ui-font-size-md);
|
|
472
492
|
outline: none;
|
|
473
493
|
}
|
|
474
494
|
|
|
@@ -490,17 +510,17 @@
|
|
|
490
510
|
padding: 0;
|
|
491
511
|
background: var(--ui-surface-low);
|
|
492
512
|
border: 1px solid var(--ui-border-subtle);
|
|
493
|
-
border-radius: var(--radius-md);
|
|
513
|
+
border-radius: var(--ui-radius-md);
|
|
494
514
|
color: var(--ui-text-secondary);
|
|
495
|
-
font-size: var(--font-sm);
|
|
515
|
+
font-size: var(--ui-font-size-sm);
|
|
496
516
|
cursor: pointer;
|
|
497
|
-
transition: all var(--transition-fast);
|
|
517
|
+
transition: all var(--ui-transition-fast);
|
|
498
518
|
}
|
|
499
519
|
|
|
500
520
|
.inline-btn:hover:not(:disabled) {
|
|
501
521
|
background: var(--ui-surface);
|
|
502
522
|
color: var(--ui-text-primary);
|
|
503
|
-
border-color: var(--border);
|
|
523
|
+
border-color: var(--ui-border-default);
|
|
504
524
|
}
|
|
505
525
|
|
|
506
526
|
.inline-btn:disabled {
|
|
@@ -521,6 +541,62 @@
|
|
|
521
541
|
overflow-y: auto;
|
|
522
542
|
}
|
|
523
543
|
|
|
544
|
+
.load-header {
|
|
545
|
+
display: flex;
|
|
546
|
+
align-items: center;
|
|
547
|
+
gap: 6px;
|
|
548
|
+
padding: 4px 6px;
|
|
549
|
+
border-bottom: 1px solid #3a3a3a;
|
|
550
|
+
position: sticky;
|
|
551
|
+
top: 0;
|
|
552
|
+
background: var(--ui-surface, #1a1a1a);
|
|
553
|
+
z-index: 1;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.sort-btn {
|
|
557
|
+
display: inline-flex;
|
|
558
|
+
align-items: center;
|
|
559
|
+
gap: 4px;
|
|
560
|
+
padding: 4px 0;
|
|
561
|
+
background: none;
|
|
562
|
+
border: none;
|
|
563
|
+
color: #888;
|
|
564
|
+
font-size: 11px;
|
|
565
|
+
font-weight: 600;
|
|
566
|
+
text-transform: uppercase;
|
|
567
|
+
letter-spacing: 0.04em;
|
|
568
|
+
cursor: pointer;
|
|
569
|
+
text-align: left;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.sort-btn:hover {
|
|
573
|
+
color: #ccc;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.sort-btn.active-sort {
|
|
577
|
+
color: #e0e0e0;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.sort-btn i {
|
|
581
|
+
font-size: 10px;
|
|
582
|
+
opacity: 0.85;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.sort-btn.name-col {
|
|
586
|
+
flex: 1;
|
|
587
|
+
min-width: 0;
|
|
588
|
+
padding-left: 4px;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.sort-btn.date-col {
|
|
592
|
+
flex-shrink: 0;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.header-spacer {
|
|
596
|
+
flex-shrink: 0;
|
|
597
|
+
width: 24px; /* reserve space matching .file-delete-btn */
|
|
598
|
+
}
|
|
599
|
+
|
|
524
600
|
.load-item {
|
|
525
601
|
display: flex;
|
|
526
602
|
align-items: center;
|
|
@@ -565,6 +641,14 @@
|
|
|
565
641
|
font-weight: 600;
|
|
566
642
|
}
|
|
567
643
|
|
|
644
|
+
.updated-at {
|
|
645
|
+
flex-shrink: 0;
|
|
646
|
+
font-size: 12px;
|
|
647
|
+
color: #777;
|
|
648
|
+
font-variant-numeric: tabular-nums;
|
|
649
|
+
white-space: nowrap;
|
|
650
|
+
}
|
|
651
|
+
|
|
568
652
|
.active-badge {
|
|
569
653
|
flex-shrink: 0;
|
|
570
654
|
font-size: 12px;
|
|
@@ -600,21 +684,21 @@
|
|
|
600
684
|
|
|
601
685
|
.status-labels {
|
|
602
686
|
display: flex;
|
|
603
|
-
gap: var(--space-8);
|
|
604
|
-
padding: 0 var(--space-4);
|
|
687
|
+
gap: var(--ui-space-8);
|
|
688
|
+
padding: 0 var(--ui-space-4);
|
|
605
689
|
}
|
|
606
690
|
|
|
607
691
|
.status-label {
|
|
608
|
-
font-size: var(--font-xs);
|
|
692
|
+
font-size: var(--ui-font-size-xs);
|
|
609
693
|
letter-spacing: 0.02em;
|
|
610
694
|
}
|
|
611
695
|
|
|
612
696
|
.status-label.clean {
|
|
613
|
-
color: var(--ui-text-
|
|
697
|
+
color: var(--ui-text-secondary);
|
|
614
698
|
}
|
|
615
699
|
|
|
616
700
|
.status-label.dirty {
|
|
617
|
-
color: var(--ui-
|
|
701
|
+
color: var(--ui-highlight);
|
|
618
702
|
}
|
|
619
703
|
|
|
620
704
|
/* ── Production section ── */
|
|
@@ -622,28 +706,28 @@
|
|
|
622
706
|
.production-section {
|
|
623
707
|
display: flex;
|
|
624
708
|
flex-direction: column;
|
|
625
|
-
gap: var(--space-4);
|
|
626
|
-
padding-top: var(--space-8);
|
|
709
|
+
gap: var(--ui-space-4);
|
|
710
|
+
padding-top: var(--ui-space-8);
|
|
627
711
|
border-top: 1px solid var(--ui-border-subtle);
|
|
628
712
|
}
|
|
629
713
|
|
|
630
714
|
.production-header {
|
|
631
715
|
display: flex;
|
|
632
716
|
flex-direction: column;
|
|
633
|
-
gap: var(--space-2);
|
|
634
|
-
padding: 0 var(--space-4);
|
|
717
|
+
gap: var(--ui-space-2);
|
|
718
|
+
padding: 0 var(--ui-space-4);
|
|
635
719
|
}
|
|
636
720
|
|
|
637
721
|
.production-label {
|
|
638
|
-
font-size: var(--font-xs);
|
|
639
|
-
color: var(--ui-text-
|
|
722
|
+
font-size: var(--ui-font-size-xs);
|
|
723
|
+
color: var(--ui-text-secondary);
|
|
640
724
|
text-transform: uppercase;
|
|
641
725
|
letter-spacing: 0.05em;
|
|
642
726
|
}
|
|
643
727
|
|
|
644
728
|
.production-name {
|
|
645
|
-
font-size: var(--font-md);
|
|
646
|
-
font-weight: var(--font-weight-semibold);
|
|
729
|
+
font-size: var(--ui-font-size-md);
|
|
730
|
+
font-weight: var(--ui-font-weight-semibold);
|
|
647
731
|
color: var(--ui-text-primary);
|
|
648
732
|
}
|
|
649
733
|
|
|
@@ -663,16 +747,16 @@
|
|
|
663
747
|
.production-update-btn.error { color: var(--ui-text-muted); }
|
|
664
748
|
|
|
665
749
|
.production-match {
|
|
666
|
-
font-size: var(--font-xs);
|
|
667
|
-
color: var(--ui-text-
|
|
668
|
-
padding: 0 var(--space-4);
|
|
750
|
+
font-size: var(--ui-font-size-xs);
|
|
751
|
+
color: var(--ui-text-secondary);
|
|
752
|
+
padding: 0 var(--ui-space-4);
|
|
669
753
|
letter-spacing: 0.02em;
|
|
670
754
|
}
|
|
671
755
|
|
|
672
756
|
.production-diff {
|
|
673
|
-
font-size: var(--font-xs);
|
|
674
|
-
color: var(--ui-
|
|
675
|
-
padding: 0 var(--space-4);
|
|
757
|
+
font-size: var(--ui-font-size-xs);
|
|
758
|
+
color: var(--ui-highlight);
|
|
759
|
+
padding: 0 var(--ui-space-4);
|
|
676
760
|
letter-spacing: 0.02em;
|
|
677
761
|
}
|
|
678
762
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createEventDispatcher } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export let checked: boolean = false;
|
|
5
|
+
export let disabled: boolean = false;
|
|
6
|
+
export let label: string = '';
|
|
7
|
+
|
|
8
|
+
const dispatch = createEventDispatcher<{ change: boolean }>();
|
|
9
|
+
|
|
10
|
+
function toggle() {
|
|
11
|
+
if (disabled) return;
|
|
12
|
+
checked = !checked;
|
|
13
|
+
dispatch('change', checked);
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<label class="toggle" class:disabled>
|
|
18
|
+
<button
|
|
19
|
+
type="button"
|
|
20
|
+
role="switch"
|
|
21
|
+
aria-checked={checked}
|
|
22
|
+
{disabled}
|
|
23
|
+
class="toggle-track"
|
|
24
|
+
class:on={checked}
|
|
25
|
+
on:click={toggle}
|
|
26
|
+
>
|
|
27
|
+
<span class="toggle-thumb" />
|
|
28
|
+
</button>
|
|
29
|
+
{#if label}
|
|
30
|
+
<span class="toggle-label">{label}</span>
|
|
31
|
+
{/if}
|
|
32
|
+
</label>
|
|
33
|
+
|
|
34
|
+
<style lang="scss">
|
|
35
|
+
.toggle {
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: var(--space-8);
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
user-select: none;
|
|
41
|
+
|
|
42
|
+
&.disabled {
|
|
43
|
+
cursor: not-allowed;
|
|
44
|
+
|
|
45
|
+
.toggle-track {
|
|
46
|
+
background: var(--surface-neutral-lower);
|
|
47
|
+
border-color: var(--border-neutral-faint);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.toggle-thumb {
|
|
51
|
+
background: var(--text-disabled);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.toggle-label {
|
|
55
|
+
color: var(--text-disabled);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.toggle-track {
|
|
61
|
+
position: relative;
|
|
62
|
+
width: 2.25rem;
|
|
63
|
+
height: var(--space-20);
|
|
64
|
+
border-radius: var(--radius-2xl);
|
|
65
|
+
border: var(--border-width-1) solid var(--border-neutral);
|
|
66
|
+
background: var(--surface-neutral-low);
|
|
67
|
+
padding: 0;
|
|
68
|
+
cursor: inherit;
|
|
69
|
+
transition: background var(--duration-150), border-color var(--duration-150);
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
|
|
72
|
+
&.on {
|
|
73
|
+
background: var(--ui-toggle);
|
|
74
|
+
border-color: var(--ui-toggle);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&:hover:not(:disabled) {
|
|
78
|
+
border-color: var(--border-neutral-medium);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&:focus-visible {
|
|
82
|
+
outline: var(--border-width-2) solid var(--border-brand);
|
|
83
|
+
outline-offset: var(--space-2);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.toggle-thumb {
|
|
88
|
+
position: absolute;
|
|
89
|
+
top: var(--space-2);
|
|
90
|
+
left: var(--space-2);
|
|
91
|
+
width: 0.875rem;
|
|
92
|
+
height: 0.875rem;
|
|
93
|
+
border-radius: var(--radius-full);
|
|
94
|
+
background: var(--text-secondary);
|
|
95
|
+
transition: transform var(--duration-150), background var(--duration-150);
|
|
96
|
+
|
|
97
|
+
.on & {
|
|
98
|
+
transform: translateX(var(--space-16));
|
|
99
|
+
background: var(--text-primary);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.toggle-label {
|
|
104
|
+
font-size: var(--font-size-md);
|
|
105
|
+
color: var(--text-secondary);
|
|
106
|
+
line-height: 1;
|
|
107
|
+
}
|
|
108
|
+
</style>
|