@motion-proto/live-tokens 0.1.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +168 -21
  2. package/dist-plugin/index.cjs +823 -336
  3. package/dist-plugin/index.d.cts +9 -7
  4. package/dist-plugin/index.d.ts +9 -7
  5. package/dist-plugin/index.js +822 -335
  6. package/package.json +46 -20
  7. package/src/assets/newspaper.webp +0 -0
  8. package/src/assets/offering.webp +0 -0
  9. package/src/component-editor/BadgeEditor.svelte +170 -0
  10. package/src/component-editor/CalloutEditor.svelte +103 -0
  11. package/src/component-editor/CardEditor.svelte +184 -0
  12. package/src/component-editor/CollapsibleSectionEditor.svelte +167 -0
  13. package/src/component-editor/CornerBadgeEditor.svelte +207 -0
  14. package/src/component-editor/DialogEditor.svelte +172 -0
  15. package/src/component-editor/ImageEditor.svelte +72 -0
  16. package/src/component-editor/InlineEditActionsEditor.svelte +83 -0
  17. package/src/component-editor/NotificationEditor.svelte +160 -0
  18. package/src/component-editor/ProgressBarEditor.svelte +124 -0
  19. package/src/component-editor/RadioButtonEditor.svelte +140 -0
  20. package/src/component-editor/SectionDividerEditor.svelte +263 -0
  21. package/src/component-editor/SegmentedControlEditor.svelte +154 -0
  22. package/src/component-editor/StandardButtonsEditor.svelte +178 -0
  23. package/src/component-editor/TabBarEditor.svelte +137 -0
  24. package/src/component-editor/TableEditor.svelte +128 -0
  25. package/src/component-editor/TooltipEditor.svelte +122 -0
  26. package/src/component-editor/editorTokens.test.ts +93 -0
  27. package/src/component-editor/groupKeySlots.test.ts +67 -0
  28. package/src/component-editor/groupKeySnapshot.test.ts +52 -0
  29. package/src/component-editor/index.ts +5 -0
  30. package/src/component-editor/registry.ts +246 -0
  31. package/src/component-editor/scaffolding/AngleDial.svelte +185 -0
  32. package/src/component-editor/scaffolding/ComponentEditorBase.svelte +96 -0
  33. package/src/component-editor/scaffolding/ComponentFileManager.svelte +682 -0
  34. package/src/component-editor/scaffolding/ComponentFileMenu.svelte +312 -0
  35. package/src/component-editor/scaffolding/ComponentsTab.svelte +69 -0
  36. package/src/component-editor/scaffolding/CopyFromMenu.svelte +246 -0
  37. package/src/component-editor/scaffolding/DemoHeader.svelte +21 -0
  38. package/src/component-editor/scaffolding/DividerEditor.svelte +81 -0
  39. package/src/component-editor/scaffolding/FieldsetWrapper.svelte +46 -0
  40. package/src/component-editor/scaffolding/GradientCard.svelte +291 -0
  41. package/src/component-editor/scaffolding/LinkageChart.svelte +297 -0
  42. package/src/component-editor/scaffolding/LinkedBlock.svelte +418 -0
  43. package/src/component-editor/scaffolding/NonStylableConfig.svelte +57 -0
  44. package/src/component-editor/scaffolding/SaveAsDialog.svelte +177 -0
  45. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +25 -0
  46. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +56 -0
  47. package/src/component-editor/scaffolding/StateBlock.svelte +115 -0
  48. package/src/component-editor/scaffolding/TokenLayout.svelte +511 -0
  49. package/src/component-editor/scaffolding/TypeEditor.svelte +82 -0
  50. package/src/component-editor/scaffolding/VariantGroup.svelte +277 -0
  51. package/src/component-editor/scaffolding/buildTypeGroupTokens.ts +97 -0
  52. package/src/component-editor/scaffolding/componentSectionType.ts +8 -0
  53. package/src/component-editor/scaffolding/componentSources.ts +9 -0
  54. package/src/component-editor/scaffolding/defaultSections.ts +16 -0
  55. package/src/component-editor/scaffolding/editorContext.ts +44 -0
  56. package/src/component-editor/scaffolding/linkedBlock.ts +226 -0
  57. package/src/component-editor/scaffolding/siblings.ts +33 -0
  58. package/src/component-editor/scaffolding/types.ts +39 -0
  59. package/src/components/Badge.svelte +231 -42
  60. package/src/components/Button.svelte +324 -124
  61. package/src/components/Callout.svelte +145 -0
  62. package/src/components/Card.svelte +123 -25
  63. package/src/components/CollapsibleSection.svelte +213 -35
  64. package/src/components/CornerBadge.svelte +224 -0
  65. package/src/components/Dialog.svelte +137 -114
  66. package/src/components/Image.svelte +43 -0
  67. package/src/components/InlineEditActions.svelte +74 -14
  68. package/src/components/Notification.svelte +184 -163
  69. package/src/components/ProgressBar.svelte +216 -22
  70. package/src/components/RadioButton.svelte +110 -40
  71. package/src/components/SectionDivider.svelte +428 -74
  72. package/src/components/SegmentedControl.svelte +203 -0
  73. package/src/components/TabBar.svelte +146 -21
  74. package/src/components/Table.svelte +102 -0
  75. package/src/components/Tooltip.svelte +45 -19
  76. package/src/components/types.ts +51 -0
  77. package/src/data/google-fonts.json +75 -0
  78. package/src/lib/ColumnsOverlay.svelte +20 -7
  79. package/src/lib/LiveEditorOverlay.svelte +257 -78
  80. package/src/lib/columnsOverlay.ts +21 -17
  81. package/src/lib/componentConfig.test.ts +204 -0
  82. package/src/lib/componentConfigKeys.ts +19 -0
  83. package/src/lib/componentConfigService.ts +88 -0
  84. package/src/lib/copyPopover.ts +30 -0
  85. package/src/lib/cssVarSync.ts +59 -7
  86. package/src/lib/editorConfigStore.ts +0 -10
  87. package/src/lib/editorCore.ts +402 -0
  88. package/src/lib/editorKeybindings.ts +52 -0
  89. package/src/lib/editorPersistence.ts +106 -0
  90. package/src/lib/editorRenderer.ts +74 -0
  91. package/src/lib/editorStore.test.ts +328 -0
  92. package/src/lib/editorStore.ts +412 -0
  93. package/src/lib/editorTypes.ts +100 -0
  94. package/src/lib/editorViewStore.ts +55 -0
  95. package/src/lib/files/versionedFileResource.ts +140 -0
  96. package/src/lib/fontLoader.ts +130 -0
  97. package/src/lib/fontMigration.ts +140 -0
  98. package/src/lib/fontParse.ts +168 -0
  99. package/src/lib/index.ts +48 -30
  100. package/src/lib/lazyConfig.test.ts +54 -0
  101. package/src/lib/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +64 -0
  102. package/src/lib/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +71 -0
  103. package/src/lib/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +43 -0
  104. package/src/lib/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +68 -0
  105. package/src/lib/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +35 -0
  106. package/src/lib/migrations/2026-05-10-sectiondivider-gradient-stops.ts +50 -0
  107. package/src/lib/migrations/2026-05-13-primary-to-brand.ts +90 -0
  108. package/src/lib/migrations/index.ts +93 -0
  109. package/src/lib/migrations/migrations.test.ts +341 -0
  110. package/src/lib/navLinkTypes.ts +1 -0
  111. package/src/lib/overlayState.ts +3 -0
  112. package/src/lib/paletteDerivation.ts +300 -0
  113. package/src/lib/parentRouteStore.ts +42 -0
  114. package/src/lib/parsers/globalRootBlock.ts +32 -0
  115. package/src/lib/presetService.ts +94 -0
  116. package/src/lib/router.ts +42 -10
  117. package/src/lib/scrollSection.ts +45 -0
  118. package/src/lib/slices/columns.ts +59 -0
  119. package/src/lib/slices/components.ts +362 -0
  120. package/src/lib/slices/domainVars.ts +15 -0
  121. package/src/lib/slices/fonts.ts +30 -0
  122. package/src/lib/slices/gradients.ts +153 -0
  123. package/src/lib/slices/overlays.ts +132 -0
  124. package/src/lib/slices/palettes.ts +26 -0
  125. package/src/lib/slices/shadows.ts +123 -0
  126. package/src/lib/storage.ts +88 -0
  127. package/src/lib/themeInit.ts +74 -0
  128. package/src/lib/themeService.ts +101 -0
  129. package/src/lib/themeTypes.ts +146 -0
  130. package/src/lib/tokenRegistry.ts +148 -0
  131. package/src/pages/ComponentEditorPage.svelte +384 -0
  132. package/src/pages/ComponentEditorPage.svelte.d.ts +2 -0
  133. package/src/pages/Editor.svelte +98 -0
  134. package/src/pages/Editor.svelte.d.ts +2 -0
  135. package/src/pages/EditorShell.svelte +348 -0
  136. package/src/styles/_padding.scss +34 -0
  137. package/src/styles/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
  138. package/src/styles/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
  139. package/src/styles/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
  140. package/src/styles/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
  141. package/src/styles/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
  142. package/src/styles/fonts/Manrope/Manrope-latin.woff2 +0 -0
  143. package/src/styles/fonts.css +22 -10
  144. package/src/styles/form-controls.css +14 -16
  145. package/src/styles/tokens.css +1322 -0
  146. package/src/styles/ui-editor.css +126 -0
  147. package/src/{showcase → ui}/BezierCurveEditor.svelte +14 -14
  148. package/src/{showcase → ui}/ColorEditPanel.svelte +42 -36
  149. package/src/ui/EditorViewSwitcher.svelte +180 -0
  150. package/src/ui/FontStackEditor.svelte +360 -0
  151. package/src/ui/GradientEditor.svelte +461 -0
  152. package/src/ui/GradientStopPicker.svelte +74 -0
  153. package/src/ui/PaletteEditor.svelte +1590 -0
  154. package/src/ui/PaletteEditor.test.ts +108 -0
  155. package/src/ui/PresetFileManager.svelte +567 -0
  156. package/src/ui/ProjectFontsSection.svelte +645 -0
  157. package/src/{showcase → ui}/SurfacesTab.svelte +39 -39
  158. package/src/{showcase → ui}/TextTab.svelte +27 -27
  159. package/src/{showcase/TokenFileManager.svelte → ui/ThemeFileManager.svelte} +196 -112
  160. package/src/ui/Toggle.svelte +108 -0
  161. package/src/ui/UICopyPopover.svelte +78 -0
  162. package/src/{showcase/EditorDialog.svelte → ui/UIDialog.svelte} +66 -25
  163. package/src/ui/UIFontFamilySelector.svelte +309 -0
  164. package/src/ui/UIFontSizeSelector.svelte +165 -0
  165. package/src/ui/UIFontWeightSelector.svelte +52 -0
  166. package/src/ui/UILineHeightSelector.svelte +47 -0
  167. package/src/ui/UILinkToggle.svelte +60 -0
  168. package/src/ui/UIOptionItem.svelte +74 -0
  169. package/src/ui/UIOptionList.svelte +27 -0
  170. package/src/ui/UIPaddingSelector.svelte +661 -0
  171. package/src/ui/UIPaletteSelector.svelte +1084 -0
  172. package/src/ui/UIRadio.svelte +72 -0
  173. package/src/ui/UIRadioGroup.svelte +59 -0
  174. package/src/ui/UIRelinkConfirmPopover.svelte +235 -0
  175. package/src/ui/UITokenSelector.svelte +509 -0
  176. package/src/ui/UIVariantSelector.svelte +145 -0
  177. package/src/ui/VariablesTab.svelte +252 -0
  178. package/src/ui/index.ts +31 -0
  179. package/src/ui/keepInViewport.ts +84 -0
  180. package/src/ui/palette/GradientStopEditor.svelte +482 -0
  181. package/src/ui/palette/OverridesPanel.svelte +526 -0
  182. package/src/ui/palette/PaletteBase.svelte +165 -0
  183. package/src/ui/palette/ScaleCurveEditor.svelte +38 -0
  184. package/src/ui/palette/paletteEditorState.ts +89 -0
  185. package/src/ui/sections/ColumnsSection.svelte +273 -0
  186. package/src/ui/sections/GradientsSection.svelte +147 -0
  187. package/src/ui/sections/OverlaysSection.svelte +670 -0
  188. package/src/ui/sections/ShadowsSection.svelte +1250 -0
  189. package/src/ui/sections/TokenScaleTable.svelte +332 -0
  190. package/src/ui/sections/tokenScales.ts +81 -0
  191. package/src/ui/variantScales.ts +108 -0
  192. package/src/components/DetailNav.svelte +0 -78
  193. package/src/components/Toggle.svelte +0 -86
  194. package/src/lib/tokenInit.ts +0 -29
  195. package/src/lib/tokenService.ts +0 -144
  196. package/src/lib/tokenTypes.ts +0 -45
  197. package/src/pages/Admin.svelte +0 -100
  198. package/src/pages/ShowcasePage.svelte +0 -144
  199. package/src/showcase/BackupBrowser.svelte +0 -617
  200. package/src/showcase/ComponentsTab.svelte +0 -105
  201. package/src/showcase/PaletteEditor.svelte +0 -2579
  202. package/src/showcase/PaletteSelector.svelte +0 -627
  203. package/src/showcase/TokenMap.svelte +0 -54
  204. package/src/showcase/VariablesTab.svelte +0 -2655
  205. package/src/showcase/VisualsTab.svelte +0 -231
  206. package/src/showcase/demos/BadgeDemo.svelte +0 -56
  207. package/src/showcase/demos/CardDemo.svelte +0 -50
  208. package/src/showcase/demos/ChoiceButtonsDemo.svelte +0 -192
  209. package/src/showcase/demos/CollapsibleSectionDemo.svelte +0 -54
  210. package/src/showcase/demos/DialogDemo.svelte +0 -42
  211. package/src/showcase/demos/InlineEditActionsDemo.svelte +0 -25
  212. package/src/showcase/demos/NotificationDemo.svelte +0 -147
  213. package/src/showcase/demos/ProgressBarDemo.svelte +0 -54
  214. package/src/showcase/demos/RadioButtonDemo.svelte +0 -56
  215. package/src/showcase/demos/SectionDividerDemo.svelte +0 -77
  216. package/src/showcase/demos/StandardButtonsDemo.svelte +0 -455
  217. package/src/showcase/demos/TabBarDemo.svelte +0 -58
  218. package/src/showcase/demos/TooltipDemo.svelte +0 -52
  219. package/src/showcase/editor.css +0 -93
  220. package/src/showcase/index.ts +0 -17
  221. package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
  222. package/src/styles/fonts/Domine/OFL.txt +0 -97
  223. package/src/styles/fonts/Domine/README.txt +0 -66
  224. /package/src/{showcase → ui}/curveEngine.ts +0 -0
@@ -1,617 +0,0 @@
1
- <script lang="ts">
2
- import { createEventDispatcher } from 'svelte';
3
- import Dialog from '../components/Dialog.svelte';
4
- import {
5
- listBackups,
6
- getBackupContent,
7
- getCurrentCss,
8
- loadTokenFile,
9
- restoreBackup,
10
- type BackupEntry,
11
- } from '../lib/tokenService';
12
- import { activeFileName } from '../lib/editorConfigStore';
13
-
14
- export let open = false;
15
-
16
- const dispatch = createEventDispatcher<{ close: void; restored: { type: string } }>();
17
-
18
- let backups: BackupEntry[] = [];
19
- let selected: BackupEntry | null = null;
20
- let backupContent = '';
21
- let currentContent = '';
22
- let diffLines: DiffLine[] = [];
23
- let loading = false;
24
- let restoring = false;
25
- let restoreConfirm = false;
26
- let filterType: 'all' | 'css' | 'tokens' = 'all';
27
-
28
- interface DiffLine {
29
- type: 'same' | 'add' | 'remove' | 'context';
30
- lineOld?: number;
31
- lineNew?: number;
32
- text: string;
33
- }
34
-
35
- $: filteredBackups = filterType === 'all'
36
- ? backups
37
- : backups.filter(b => b.type === filterType);
38
-
39
- $: if (open) loadBackups();
40
-
41
- async function loadBackups() {
42
- loading = true;
43
- try {
44
- backups = await listBackups();
45
- } catch {
46
- backups = [];
47
- }
48
- loading = false;
49
- }
50
-
51
- async function selectBackup(backup: BackupEntry) {
52
- selected = backup;
53
- restoreConfirm = false;
54
- loading = true;
55
- try {
56
- backupContent = await getBackupContent(backup.type, backup.file);
57
- if (backup.type === 'css') {
58
- currentContent = await getCurrentCss();
59
- } else {
60
- const tokenData = await loadTokenFile(backup.name);
61
- currentContent = JSON.stringify(tokenData, null, 2);
62
- try {
63
- backupContent = JSON.stringify(JSON.parse(backupContent), null, 2);
64
- } catch { /* keep as-is */ }
65
- }
66
- diffLines = computeDiff(backupContent, currentContent);
67
- } catch {
68
- diffLines = [];
69
- }
70
- loading = false;
71
- }
72
-
73
- async function handleRestore() {
74
- if (!selected) return;
75
- restoring = true;
76
- try {
77
- await restoreBackup(selected.type, selected.file);
78
- restoreConfirm = false;
79
- dispatch('restored', { type: selected.type });
80
- await loadBackups();
81
- await selectBackup(selected);
82
- } catch {
83
- // silent
84
- }
85
- restoring = false;
86
- }
87
-
88
- function close() {
89
- open = false;
90
- selected = null;
91
- diffLines = [];
92
- restoreConfirm = false;
93
- dispatch('close');
94
- }
95
-
96
- function formatTimestamp(ts: string): string {
97
- try {
98
- const d = new Date(ts);
99
- return d.toLocaleString(undefined, {
100
- month: 'short', day: 'numeric',
101
- hour: '2-digit', minute: '2-digit', second: '2-digit',
102
- });
103
- } catch {
104
- return ts;
105
- }
106
- }
107
-
108
- function formatSize(bytes: number): string {
109
- if (bytes < 1024) return `${bytes}B`;
110
- return `${(bytes / 1024).toFixed(1)}KB`;
111
- }
112
-
113
- // ── LCS-based line diff ──────────────────────────────────
114
-
115
- function computeDiff(oldText: string, newText: string): DiffLine[] {
116
- const oldLines = oldText.split('\n');
117
- const newLines = newText.split('\n');
118
- const ops = myersDiff(oldLines, newLines);
119
- const result: DiffLine[] = [];
120
- let oldIdx = 0;
121
- let newIdx = 0;
122
-
123
- for (const op of ops) {
124
- if (op === 'equal') {
125
- result.push({ type: 'same', lineOld: oldIdx + 1, lineNew: newIdx + 1, text: oldLines[oldIdx] });
126
- oldIdx++;
127
- newIdx++;
128
- } else if (op === 'delete') {
129
- result.push({ type: 'remove', lineOld: oldIdx + 1, text: oldLines[oldIdx] });
130
- oldIdx++;
131
- } else if (op === 'insert') {
132
- result.push({ type: 'add', lineNew: newIdx + 1, text: newLines[newIdx] });
133
- newIdx++;
134
- }
135
- }
136
-
137
- return collapseUnchanged(result);
138
- }
139
-
140
- function myersDiff(a: string[], b: string[]): ('equal' | 'delete' | 'insert')[] {
141
- const n = a.length;
142
- const m = b.length;
143
- const max = n + m;
144
- const v: Record<number, number> = { 1: 0 };
145
- const trace: Record<number, number>[] = [];
146
-
147
- for (let d = 0; d <= max; d++) {
148
- const vSnap: Record<number, number> = {};
149
- for (const k in v) vSnap[Number(k)] = v[Number(k)];
150
- trace.push(vSnap);
151
-
152
- for (let k = -d; k <= d; k += 2) {
153
- let x: number;
154
- if (k === -d || (k !== d && (v[k - 1] ?? -1) < (v[k + 1] ?? -1))) {
155
- x = v[k + 1] ?? 0;
156
- } else {
157
- x = (v[k - 1] ?? 0) + 1;
158
- }
159
- let y = x - k;
160
- while (x < n && y < m && a[x] === b[y]) {
161
- x++;
162
- y++;
163
- }
164
- v[k] = x;
165
- if (x >= n && y >= m) {
166
- return backtrack(trace, a, b);
167
- }
168
- }
169
- }
170
- return [];
171
- }
172
-
173
- function backtrack(trace: Record<number, number>[], a: string[], b: string[]): ('equal' | 'delete' | 'insert')[] {
174
- let x = a.length;
175
- let y = b.length;
176
- const ops: ('equal' | 'delete' | 'insert')[] = [];
177
-
178
- for (let d = trace.length - 1; d > 0; d--) {
179
- const v = trace[d - 1];
180
- const k = x - y;
181
- let prevK: number;
182
- if (k === -d || (k !== d && (v[k - 1] ?? -1) < (v[k + 1] ?? -1))) {
183
- prevK = k + 1;
184
- } else {
185
- prevK = k - 1;
186
- }
187
- const prevX = v[prevK] ?? 0;
188
- const prevY = prevX - prevK;
189
-
190
- while (x > prevX && y > prevY) {
191
- ops.push('equal');
192
- x--;
193
- y--;
194
- }
195
- if (x > prevX) {
196
- ops.push('delete');
197
- x--;
198
- } else if (y > prevY) {
199
- ops.push('insert');
200
- y--;
201
- }
202
- }
203
- while (x > 0 && y > 0) {
204
- ops.push('equal');
205
- x--;
206
- y--;
207
- }
208
- return ops.reverse();
209
- }
210
-
211
- function collapseUnchanged(lines: DiffLine[], contextSize = 3): DiffLine[] {
212
- const keep = new Array(lines.length).fill(false);
213
- for (let i = 0; i < lines.length; i++) {
214
- if (lines[i].type !== 'same') {
215
- for (let j = Math.max(0, i - contextSize); j <= Math.min(lines.length - 1, i + contextSize); j++) {
216
- keep[j] = true;
217
- }
218
- }
219
- }
220
-
221
- const result: DiffLine[] = [];
222
- let skipping = false;
223
- for (let i = 0; i < lines.length; i++) {
224
- if (keep[i]) {
225
- skipping = false;
226
- result.push(lines[i]);
227
- } else if (!skipping) {
228
- skipping = true;
229
- result.push({ type: 'context', text: '...' });
230
- }
231
- }
232
- return result;
233
- }
234
- </script>
235
-
236
- <Dialog
237
- bind:show={open}
238
- title="Backup History"
239
- showConfirm={false}
240
- showCancel={false}
241
- width="90vw"
242
- onCancel={close}
243
- >
244
- <div class="browser-header">
245
- <div class="filter-tabs">
246
- <button class:active={filterType === 'all'} on:click={() => filterType = 'all'}>All</button>
247
- <button class:active={filterType === 'css'} on:click={() => filterType = 'css'}>CSS</button>
248
- <button class:active={filterType === 'tokens'} on:click={() => filterType = 'tokens'}>Tokens</button>
249
- </div>
250
- {#if selected}
251
- <div class="diff-actions">
252
- {#if restoreConfirm}
253
- <span class="restore-warn">Restore this backup?</span>
254
- <button class="action-btn confirm" on:click={handleRestore} disabled={restoring}>
255
- {restoring ? 'Restoring...' : 'Confirm'}
256
- </button>
257
- <button class="action-btn cancel" on:click={() => restoreConfirm = false}>Cancel</button>
258
- {:else}
259
- <button class="action-btn restore" on:click={() => restoreConfirm = true}>
260
- <i class="fas fa-undo"></i> Restore
261
- </button>
262
- {/if}
263
- </div>
264
- {/if}
265
- </div>
266
-
267
- <div class="browser-body">
268
- <div class="backup-list">
269
- {#if loading && !selected}
270
- <div class="empty-state">Loading...</div>
271
- {:else if filteredBackups.length === 0}
272
- <div class="empty-state">No backups yet</div>
273
- {:else}
274
- {#each filteredBackups as backup}
275
- <button
276
- class="backup-item"
277
- class:selected={selected?.file === backup.file}
278
- on:click={() => selectBackup(backup)}
279
- >
280
- <span class="backup-badge" class:css={backup.type === 'css'} class:token={backup.type === 'tokens'}>
281
- {backup.type === 'css' ? 'CSS' : 'TOK'}
282
- </span>
283
- <div class="backup-info">
284
- <span class="backup-name">{backup.name}</span>
285
- <span class="backup-meta">{formatTimestamp(backup.timestamp)} &middot; {formatSize(backup.size)}</span>
286
- </div>
287
- </button>
288
- {/each}
289
- {/if}
290
- </div>
291
-
292
- <div class="diff-panel">
293
- {#if !selected}
294
- <div class="empty-state">Select a backup to view changes</div>
295
- {:else if loading}
296
- <div class="empty-state">Loading diff...</div>
297
- {:else}
298
- <div class="diff-meta">
299
- <span class="backup-badge" class:css={selected.type === 'css'} class:token={selected.type === 'tokens'}>
300
- {selected.type === 'css' ? 'CSS' : 'TOK'}
301
- </span>
302
- <strong>{selected.name}</strong>
303
- <span class="diff-timestamp">{formatTimestamp(selected.timestamp)}</span>
304
- </div>
305
- <div class="diff-legend">
306
- <span class="legend-item legend-remove">
307
- <span class="legend-swatch"></span> Backup (restore to this)
308
- </span>
309
- <span class="legend-item legend-add">
310
- <span class="legend-swatch"></span> Current file
311
- </span>
312
- </div>
313
- <div class="diff-content">
314
- {#each diffLines as line}
315
- {#if line.type === 'context'}
316
- <div class="diff-line context">
317
- <span class="line-num"></span>
318
- <span class="line-num"></span>
319
- <span class="line-text">{line.text}</span>
320
- </div>
321
- {:else if line.type === 'remove'}
322
- <div class="diff-line remove">
323
- <span class="line-num">{line.lineOld ?? ''}</span>
324
- <span class="line-num"></span>
325
- <span class="line-text">- {line.text}</span>
326
- </div>
327
- {:else if line.type === 'add'}
328
- <div class="diff-line add">
329
- <span class="line-num"></span>
330
- <span class="line-num">{line.lineNew ?? ''}</span>
331
- <span class="line-text">+ {line.text}</span>
332
- </div>
333
- {:else}
334
- <div class="diff-line same">
335
- <span class="line-num">{line.lineOld ?? ''}</span>
336
- <span class="line-num">{line.lineNew ?? ''}</span>
337
- <span class="line-text"> {line.text}</span>
338
- </div>
339
- {/if}
340
- {/each}
341
- {#if diffLines.length === 0}
342
- <div class="empty-state">Files are identical</div>
343
- {/if}
344
- </div>
345
- {/if}
346
- </div>
347
- </div>
348
- </Dialog>
349
-
350
- <style>
351
- .browser-header {
352
- display: flex;
353
- align-items: center;
354
- justify-content: space-between;
355
- padding-bottom: var(--space-12);
356
- border-bottom: 1px solid var(--border-neutral-subtle);
357
- margin-bottom: var(--space-12);
358
- }
359
-
360
- .filter-tabs {
361
- display: flex;
362
- gap: 2px;
363
- background: var(--surface-neutral-low, #1a1a1a);
364
- border-radius: var(--radius-md);
365
- padding: 2px;
366
- }
367
-
368
- .filter-tabs button {
369
- padding: var(--space-4) var(--space-12);
370
- font-size: var(--font-sm);
371
- background: none;
372
- border: none;
373
- color: var(--text-tertiary);
374
- border-radius: var(--radius-sm);
375
- cursor: pointer;
376
- transition: all var(--transition-fast);
377
- }
378
-
379
- .filter-tabs button:hover { color: var(--text-secondary); }
380
- .filter-tabs button.active { background: var(--surface-neutral-medium, #333); color: var(--text-primary); }
381
-
382
- .diff-actions {
383
- display: flex;
384
- align-items: center;
385
- gap: var(--space-8);
386
- }
387
-
388
- .restore-warn {
389
- font-size: var(--font-sm);
390
- color: var(--text-warning, #e6a030);
391
- }
392
-
393
- .action-btn {
394
- padding: var(--space-4) var(--space-12);
395
- font-size: var(--font-sm);
396
- font-weight: var(--font-weight-medium);
397
- border: 1px solid var(--border-neutral-subtle);
398
- border-radius: var(--radius-md);
399
- cursor: pointer;
400
- transition: all var(--transition-fast);
401
- }
402
-
403
- .action-btn.restore {
404
- background: var(--surface-success-low, #1a2a1a);
405
- color: var(--text-success, #8ecf8e);
406
- border-color: var(--border-success-subtle, #2a4a2a);
407
- }
408
- .action-btn.restore:hover { background: var(--surface-success-medium, #2a3a2a); }
409
-
410
- .action-btn.confirm {
411
- background: var(--surface-success-medium, #3a5a3a);
412
- color: var(--text-success-strong, #c0f0c0);
413
- border-color: var(--border-success-medium, #4a7a4a);
414
- }
415
- .action-btn.confirm:hover { background: var(--surface-success-high, #4a6a4a); }
416
- .action-btn.confirm:disabled { opacity: var(--opacity-disabled, 0.5); cursor: not-allowed; }
417
-
418
- .action-btn.cancel {
419
- background: var(--surface-neutral-low, #1a1a1a);
420
- color: var(--text-secondary);
421
- }
422
- .action-btn.cancel:hover { background: var(--surface-neutral-medium, #2a2a2a); }
423
-
424
- /* ── Layout ── */
425
- .browser-body {
426
- display: flex;
427
- height: 60vh;
428
- min-width: 0;
429
- border: 1px solid var(--border-neutral-subtle);
430
- border-radius: var(--radius-md);
431
- overflow: hidden;
432
- }
433
-
434
- /* ── Backup list ── */
435
- .backup-list {
436
- width: 240px;
437
- flex-shrink: 0;
438
- border-right: 1px solid var(--border-neutral-subtle);
439
- overflow-y: auto;
440
- background: var(--surface-neutral-lowest, #0d0d0d);
441
- }
442
-
443
- @media (max-width: 1280px) {
444
- .backup-list {
445
- width: 180px;
446
- }
447
- }
448
-
449
- @media (max-width: 1024px) {
450
- .backup-list {
451
- width: 140px;
452
- }
453
- .line-num {
454
- width: 32px;
455
- }
456
- }
457
-
458
- .backup-item {
459
- display: flex;
460
- align-items: center;
461
- gap: var(--space-8);
462
- width: 100%;
463
- padding: var(--space-8) var(--space-12);
464
- background: none;
465
- border: none;
466
- border-bottom: 1px solid var(--border-neutral-faint, #1a1a1a);
467
- color: var(--text-tertiary);
468
- cursor: pointer;
469
- text-align: left;
470
- transition: background var(--transition-fast);
471
- }
472
- .backup-item:hover { background: var(--hover-low, rgba(255,255,255,0.04)); }
473
- .backup-item.selected { background: var(--surface-neutral-low, #1f1f1f); color: var(--text-primary); }
474
-
475
- .backup-badge {
476
- font-size: 10px;
477
- font-weight: 700;
478
- letter-spacing: 0.05em;
479
- padding: 2px 6px;
480
- border-radius: var(--radius-sm);
481
- flex-shrink: 0;
482
- }
483
- .backup-badge.css { background: var(--surface-success-low, #1a3a2a); color: var(--text-success, #6dcf97); }
484
- .backup-badge.token { background: var(--surface-warning-low, #2a2a1a); color: var(--text-warning, #cfb86d); }
485
-
486
- .backup-info {
487
- display: flex;
488
- flex-direction: column;
489
- gap: 2px;
490
- min-width: 0;
491
- }
492
-
493
- .backup-name {
494
- font-size: var(--font-sm);
495
- font-weight: var(--font-weight-medium);
496
- white-space: nowrap;
497
- overflow: hidden;
498
- text-overflow: ellipsis;
499
- }
500
-
501
- .backup-meta {
502
- font-size: var(--font-xs);
503
- color: var(--text-muted);
504
- }
505
-
506
- /* ── Diff panel ── */
507
- .diff-panel {
508
- flex: 1;
509
- display: flex;
510
- flex-direction: column;
511
- min-width: 0;
512
- background: var(--surface-neutral-lowest, #0a0a0a);
513
- }
514
-
515
- .diff-meta {
516
- display: flex;
517
- align-items: center;
518
- gap: var(--space-8);
519
- padding: var(--space-8) var(--space-16);
520
- border-bottom: 1px solid var(--border-neutral-faint);
521
- font-size: var(--font-sm);
522
- color: var(--text-secondary);
523
- flex-shrink: 0;
524
- }
525
-
526
- .diff-timestamp {
527
- color: var(--text-muted);
528
- font-weight: 400;
529
- }
530
-
531
- .diff-legend {
532
- display: flex;
533
- gap: var(--space-16);
534
- padding: var(--space-4) var(--space-16);
535
- border-bottom: 1px solid var(--border-neutral-faint);
536
- flex-shrink: 0;
537
- }
538
-
539
- .legend-item {
540
- display: flex;
541
- align-items: center;
542
- gap: var(--space-4);
543
- font-size: var(--font-xs);
544
- color: var(--text-muted);
545
- }
546
-
547
- .legend-swatch {
548
- width: 12px;
549
- height: 12px;
550
- border-radius: 2px;
551
- }
552
-
553
- .legend-remove .legend-swatch { background: rgba(220, 80, 80, 0.25); border: 1px solid rgba(220, 80, 80, 0.4); }
554
- .legend-add .legend-swatch { background: rgba(80, 180, 80, 0.25); border: 1px solid rgba(80, 180, 80, 0.4); }
555
-
556
- .diff-content {
557
- flex: 1;
558
- overflow: auto;
559
- font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
560
- font-size: 12px;
561
- line-height: 1.5;
562
- }
563
-
564
- .diff-line {
565
- display: flex;
566
- white-space: pre;
567
- min-width: fit-content;
568
- }
569
-
570
- .line-num {
571
- width: 44px;
572
- flex-shrink: 0;
573
- text-align: right;
574
- padding-right: 8px;
575
- color: var(--text-muted, #444);
576
- user-select: none;
577
- border-right: 1px solid var(--border-neutral-faint, #1a1a1a);
578
- }
579
-
580
- .line-text {
581
- padding: 0 12px;
582
- flex: 1;
583
- }
584
-
585
- .diff-line.same { color: var(--text-muted, #777); }
586
- .diff-line.same:hover { background: rgba(255, 255, 255, 0.02); }
587
-
588
- .diff-line.remove {
589
- background: rgba(220, 80, 80, 0.12);
590
- color: #e09090;
591
- }
592
- .diff-line.remove .line-num { color: #a06060; }
593
-
594
- .diff-line.add {
595
- background: rgba(80, 180, 80, 0.12);
596
- color: #90c890;
597
- }
598
- .diff-line.add .line-num { color: #60a060; }
599
-
600
- .diff-line.context {
601
- color: var(--text-muted, #444);
602
- padding: var(--space-4) 0;
603
- justify-content: center;
604
- font-style: italic;
605
- }
606
- .diff-line.context .line-num { border: none; }
607
-
608
- .empty-state {
609
- display: flex;
610
- align-items: center;
611
- justify-content: center;
612
- height: 100%;
613
- min-height: 80px;
614
- color: var(--text-muted);
615
- font-size: var(--font-sm);
616
- }
617
- </style>