@newtonedev/editor 0.1.1

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 (86) hide show
  1. package/dist/Editor.d.ts +3 -0
  2. package/dist/Editor.d.ts.map +1 -0
  3. package/dist/components/CodeBlock.d.ts +7 -0
  4. package/dist/components/CodeBlock.d.ts.map +1 -0
  5. package/dist/components/EditorHeader.d.ts +16 -0
  6. package/dist/components/EditorHeader.d.ts.map +1 -0
  7. package/dist/components/EditorShell.d.ts +10 -0
  8. package/dist/components/EditorShell.d.ts.map +1 -0
  9. package/dist/components/FontPicker.d.ts +11 -0
  10. package/dist/components/FontPicker.d.ts.map +1 -0
  11. package/dist/components/PresetSelector.d.ts +14 -0
  12. package/dist/components/PresetSelector.d.ts.map +1 -0
  13. package/dist/components/PreviewWindow.d.ts +11 -0
  14. package/dist/components/PreviewWindow.d.ts.map +1 -0
  15. package/dist/components/RightSidebar.d.ts +12 -0
  16. package/dist/components/RightSidebar.d.ts.map +1 -0
  17. package/dist/components/Sidebar.d.ts +25 -0
  18. package/dist/components/Sidebar.d.ts.map +1 -0
  19. package/dist/components/TableOfContents.d.ts +9 -0
  20. package/dist/components/TableOfContents.d.ts.map +1 -0
  21. package/dist/components/ThemeBar.d.ts +8 -0
  22. package/dist/components/ThemeBar.d.ts.map +1 -0
  23. package/dist/components/sections/ColorsSection.d.ts +14 -0
  24. package/dist/components/sections/ColorsSection.d.ts.map +1 -0
  25. package/dist/components/sections/DynamicRangeSection.d.ts +9 -0
  26. package/dist/components/sections/DynamicRangeSection.d.ts.map +1 -0
  27. package/dist/components/sections/FontsSection.d.ts +9 -0
  28. package/dist/components/sections/FontsSection.d.ts.map +1 -0
  29. package/dist/components/sections/IconsSection.d.ts +9 -0
  30. package/dist/components/sections/IconsSection.d.ts.map +1 -0
  31. package/dist/components/sections/OthersSection.d.ts +9 -0
  32. package/dist/components/sections/OthersSection.d.ts.map +1 -0
  33. package/dist/components/sections/index.d.ts +6 -0
  34. package/dist/components/sections/index.d.ts.map +1 -0
  35. package/dist/hooks/useEditorState.d.ts +53 -0
  36. package/dist/hooks/useEditorState.d.ts.map +1 -0
  37. package/dist/hooks/useHover.d.ts +8 -0
  38. package/dist/hooks/useHover.d.ts.map +1 -0
  39. package/dist/hooks/usePresets.d.ts +33 -0
  40. package/dist/hooks/usePresets.d.ts.map +1 -0
  41. package/dist/index.cjs +3846 -0
  42. package/dist/index.cjs.map +1 -0
  43. package/dist/index.d.ts +22 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +3819 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/preview/CategoryView.d.ts +7 -0
  48. package/dist/preview/CategoryView.d.ts.map +1 -0
  49. package/dist/preview/ComponentDetailView.d.ts +9 -0
  50. package/dist/preview/ComponentDetailView.d.ts.map +1 -0
  51. package/dist/preview/ComponentRenderer.d.ts +7 -0
  52. package/dist/preview/ComponentRenderer.d.ts.map +1 -0
  53. package/dist/preview/OverviewView.d.ts +7 -0
  54. package/dist/preview/OverviewView.d.ts.map +1 -0
  55. package/dist/types.d.ts +69 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/utils/presets.d.ts +5 -0
  58. package/dist/utils/presets.d.ts.map +1 -0
  59. package/package.json +51 -0
  60. package/src/Editor.tsx +128 -0
  61. package/src/components/CodeBlock.tsx +58 -0
  62. package/src/components/EditorHeader.tsx +86 -0
  63. package/src/components/EditorShell.tsx +67 -0
  64. package/src/components/FontPicker.tsx +351 -0
  65. package/src/components/PresetSelector.tsx +455 -0
  66. package/src/components/PreviewWindow.tsx +69 -0
  67. package/src/components/RightSidebar.tsx +374 -0
  68. package/src/components/Sidebar.tsx +332 -0
  69. package/src/components/TableOfContents.tsx +152 -0
  70. package/src/components/ThemeBar.tsx +76 -0
  71. package/src/components/sections/ColorsSection.tsx +485 -0
  72. package/src/components/sections/DynamicRangeSection.tsx +399 -0
  73. package/src/components/sections/FontsSection.tsx +132 -0
  74. package/src/components/sections/IconsSection.tsx +66 -0
  75. package/src/components/sections/OthersSection.tsx +70 -0
  76. package/src/components/sections/index.ts +5 -0
  77. package/src/hooks/useEditorState.ts +381 -0
  78. package/src/hooks/useHover.ts +8 -0
  79. package/src/hooks/usePresets.ts +254 -0
  80. package/src/index.ts +52 -0
  81. package/src/preview/CategoryView.tsx +134 -0
  82. package/src/preview/ComponentDetailView.tsx +126 -0
  83. package/src/preview/ComponentRenderer.tsx +107 -0
  84. package/src/preview/OverviewView.tsx +177 -0
  85. package/src/types.ts +77 -0
  86. package/src/utils/presets.ts +24 -0
package/dist/index.js ADDED
@@ -0,0 +1,3819 @@
1
+ import { useState, useCallback, useRef, useMemo, useEffect } from 'react';
2
+ import { getComponent, useTokens, ColorScaleSlider, TextInput, HueSlider, Slider, Select, Toggle, GOOGLE_FONTS, SYSTEM_FONTS, Button, CATEGORIES, getComponentsByCategory, getCategory, generateComponentCode, NewtoneProvider, Card } from '@newtonedev/components';
3
+ import { useConfigurator, usePreviewColors, SEMANTIC_HUE_RANGES, useWcagValidation, traditionalHueToOklch, hexToPaletteParams } from '@newtonedev/configurator';
4
+ import { srgbToHex } from 'newtone';
5
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
+
7
+ // src/utils/presets.ts
8
+ function findPreset(presets, presetId) {
9
+ return presets.find((p) => p.id === presetId);
10
+ }
11
+ function updatePresetInArray(presets, presetId, updater) {
12
+ return presets.map((p) => p.id === presetId ? updater(p) : p);
13
+ }
14
+ function presetHasUnpublishedChanges(preset) {
15
+ if (preset.published_state === null) return true;
16
+ return JSON.stringify(preset.draft_state) !== JSON.stringify(preset.published_state);
17
+ }
18
+ function useHover() {
19
+ const [isHovered, setIsHovered] = useState(false);
20
+ const onMouseEnter = useCallback(() => setIsHovered(true), []);
21
+ const onMouseLeave = useCallback(() => setIsHovered(false), []);
22
+ return { isHovered, hoverProps: { onMouseEnter, onMouseLeave } };
23
+ }
24
+ function usePresets({
25
+ initialPresets,
26
+ initialActivePresetId,
27
+ initialPublishedPresetId,
28
+ defaultState,
29
+ onPresetSwitch,
30
+ getCurrentState,
31
+ flushPendingSave,
32
+ persistPresets
33
+ }) {
34
+ const [presets, setPresets] = useState(initialPresets);
35
+ const [activePresetId, setActivePresetId] = useState(initialActivePresetId);
36
+ const [publishedPresetId, setPublishedPresetId] = useState(
37
+ initialPublishedPresetId
38
+ );
39
+ const presetsRef = useRef(presets);
40
+ presetsRef.current = presets;
41
+ const activePreset = presets.find((p) => p.id === activePresetId) ?? presets[0];
42
+ const switchPreset = useCallback(
43
+ async (presetId) => {
44
+ if (presetId === activePresetId) return;
45
+ await flushPendingSave();
46
+ const currentState = getCurrentState();
47
+ const updatedPresets = presetsRef.current.map(
48
+ (p) => p.id === activePresetId ? { ...p, draft_state: currentState } : p
49
+ );
50
+ const target = updatedPresets.find((p) => p.id === presetId);
51
+ if (!target) return;
52
+ setPresets(updatedPresets);
53
+ presetsRef.current = updatedPresets;
54
+ setActivePresetId(presetId);
55
+ onPresetSwitch(target.draft_state);
56
+ await persistPresets({
57
+ presets: updatedPresets,
58
+ activePresetId: presetId,
59
+ publishedPresetId
60
+ });
61
+ },
62
+ [
63
+ activePresetId,
64
+ publishedPresetId,
65
+ flushPendingSave,
66
+ getCurrentState,
67
+ onPresetSwitch,
68
+ persistPresets
69
+ ]
70
+ );
71
+ const createPreset = useCallback(
72
+ async (name) => {
73
+ const newPreset = {
74
+ id: crypto.randomUUID(),
75
+ name,
76
+ draft_state: defaultState,
77
+ published_state: null
78
+ };
79
+ const newPresets = [...presetsRef.current, newPreset];
80
+ setPresets(newPresets);
81
+ presetsRef.current = newPresets;
82
+ await persistPresets({
83
+ presets: newPresets,
84
+ activePresetId,
85
+ publishedPresetId
86
+ });
87
+ return newPreset.id;
88
+ },
89
+ [defaultState, activePresetId, publishedPresetId, persistPresets]
90
+ );
91
+ const duplicatePreset = useCallback(
92
+ async (presetId, newName) => {
93
+ const source = presetsRef.current.find((p) => p.id === presetId);
94
+ if (!source) throw new Error("Preset not found");
95
+ const newPreset = {
96
+ id: crypto.randomUUID(),
97
+ name: newName,
98
+ draft_state: source.draft_state,
99
+ published_state: null
100
+ };
101
+ const newPresets = [...presetsRef.current, newPreset];
102
+ setPresets(newPresets);
103
+ presetsRef.current = newPresets;
104
+ await persistPresets({
105
+ presets: newPresets,
106
+ activePresetId,
107
+ publishedPresetId
108
+ });
109
+ return newPreset.id;
110
+ },
111
+ [activePresetId, publishedPresetId, persistPresets]
112
+ );
113
+ const renamePreset = useCallback(
114
+ (presetId, name) => {
115
+ const newPresets = presetsRef.current.map(
116
+ (p) => p.id === presetId ? { ...p, name } : p
117
+ );
118
+ setPresets(newPresets);
119
+ presetsRef.current = newPresets;
120
+ persistPresets({
121
+ presets: newPresets,
122
+ activePresetId,
123
+ publishedPresetId
124
+ });
125
+ },
126
+ [activePresetId, publishedPresetId, persistPresets]
127
+ );
128
+ const deletePreset = useCallback(
129
+ async (presetId) => {
130
+ if (presetsRef.current.length <= 1) return;
131
+ const newPresets = presetsRef.current.filter((p) => p.id !== presetId);
132
+ let newActiveId = activePresetId;
133
+ let newPublishedId = publishedPresetId;
134
+ if (presetId === activePresetId) {
135
+ newActiveId = newPresets[0].id;
136
+ onPresetSwitch(newPresets[0].draft_state);
137
+ }
138
+ if (presetId === publishedPresetId) {
139
+ newPublishedId = null;
140
+ }
141
+ setPresets(newPresets);
142
+ presetsRef.current = newPresets;
143
+ setActivePresetId(newActiveId);
144
+ setPublishedPresetId(newPublishedId);
145
+ await persistPresets({
146
+ presets: newPresets,
147
+ activePresetId: newActiveId,
148
+ publishedPresetId: newPublishedId
149
+ });
150
+ },
151
+ [activePresetId, publishedPresetId, onPresetSwitch, persistPresets]
152
+ );
153
+ const updateActivePresetDraftState = useCallback(
154
+ (state) => {
155
+ const newPresets = presetsRef.current.map(
156
+ (p) => p.id === activePresetId ? { ...p, draft_state: state } : p
157
+ );
158
+ setPresets(newPresets);
159
+ presetsRef.current = newPresets;
160
+ return newPresets;
161
+ },
162
+ [activePresetId]
163
+ );
164
+ const publishActivePreset = useCallback(
165
+ (state) => {
166
+ const newPresets = presetsRef.current.map(
167
+ (p) => p.id === activePresetId ? { ...p, draft_state: state, published_state: state } : p
168
+ );
169
+ setPresets(newPresets);
170
+ presetsRef.current = newPresets;
171
+ setPublishedPresetId(activePresetId);
172
+ return newPresets;
173
+ },
174
+ [activePresetId]
175
+ );
176
+ const revertActivePreset = useCallback(() => {
177
+ return activePreset.published_state;
178
+ }, [activePreset]);
179
+ return {
180
+ presets,
181
+ activePresetId,
182
+ publishedPresetId,
183
+ activePreset,
184
+ switchPreset,
185
+ createPreset,
186
+ renamePreset,
187
+ deletePreset,
188
+ duplicatePreset,
189
+ updateActivePresetDraftState,
190
+ publishActivePreset,
191
+ revertActivePreset
192
+ };
193
+ }
194
+ function useEditorState({
195
+ initialState,
196
+ initialIsPublished,
197
+ initialPresets,
198
+ initialActivePresetId,
199
+ initialPublishedPresetId,
200
+ defaultState,
201
+ persistence,
202
+ onNavigate,
203
+ initialPreviewView
204
+ }) {
205
+ const {
206
+ state: configuratorState,
207
+ dispatch,
208
+ themeConfig
209
+ } = useConfigurator(initialState);
210
+ const previewColors = usePreviewColors(configuratorState);
211
+ const [saveStatus, setSaveStatus] = useState("saved");
212
+ const [isPublished, setIsPublished] = useState(initialIsPublished);
213
+ const [publishing, setPublishing] = useState(false);
214
+ const [colorMode, setColorMode] = useState(
215
+ initialState.preview.mode
216
+ );
217
+ const [activeTheme, setActiveTheme] = useState(
218
+ initialState.preview.theme || "neutral"
219
+ );
220
+ const [previewView, setPreviewView] = useState(
221
+ initialPreviewView ?? { kind: "overview" }
222
+ );
223
+ const [sidebarSelection, setSidebarSelection] = useState(null);
224
+ const [propOverrides, setPropOverrides] = useState(
225
+ {}
226
+ );
227
+ const debounceRef = useRef(void 0);
228
+ const latestStateRef = useRef(initialState);
229
+ const isInitialMount = useRef(true);
230
+ const initialStateRef = useRef(initialState);
231
+ const handlePresetSwitch = useCallback(
232
+ (newState) => {
233
+ dispatch({ type: "LOAD_STATE", state: newState });
234
+ initialStateRef.current = newState;
235
+ isInitialMount.current = true;
236
+ },
237
+ [dispatch]
238
+ );
239
+ const flushPendingSave = useCallback(async () => {
240
+ if (debounceRef.current) {
241
+ clearTimeout(debounceRef.current);
242
+ debounceRef.current = void 0;
243
+ }
244
+ }, []);
245
+ const {
246
+ presets,
247
+ activePresetId,
248
+ publishedPresetId,
249
+ activePreset,
250
+ switchPreset,
251
+ createPreset,
252
+ renamePreset,
253
+ deletePreset,
254
+ duplicatePreset,
255
+ updateActivePresetDraftState,
256
+ publishActivePreset,
257
+ revertActivePreset
258
+ } = usePresets({
259
+ initialPresets,
260
+ initialActivePresetId,
261
+ initialPublishedPresetId,
262
+ defaultState,
263
+ onPresetSwitch: handlePresetSwitch,
264
+ getCurrentState: () => latestStateRef.current,
265
+ flushPendingSave,
266
+ persistPresets: persistence.persistPresets
267
+ });
268
+ const isDirty = useMemo(
269
+ () => JSON.stringify(configuratorState) !== JSON.stringify(
270
+ activePreset.published_state ?? initialStateRef.current
271
+ ),
272
+ [configuratorState, activePreset.published_state]
273
+ );
274
+ const handleRevert = useCallback(() => {
275
+ const publishedState = revertActivePreset();
276
+ const revertTarget = publishedState ?? defaultState;
277
+ if (window.confirm(
278
+ "Revert all changes to the last published version of this preset?"
279
+ )) {
280
+ dispatch({ type: "LOAD_STATE", state: revertTarget });
281
+ initialStateRef.current = revertTarget;
282
+ }
283
+ }, [dispatch, revertActivePreset, defaultState]);
284
+ const initOverridesFromVariant = useCallback(
285
+ (componentId, variantId) => {
286
+ const comp = getComponent(componentId);
287
+ if (!comp) return;
288
+ const variant = variantId ? comp.variants.find((v) => v.id === variantId) : comp.variants[0];
289
+ if (variant) {
290
+ const overrides = {};
291
+ for (const prop of comp.editableProps) {
292
+ overrides[prop.name] = variant.props[prop.name] ?? prop.defaultValue;
293
+ }
294
+ setPropOverrides(overrides);
295
+ }
296
+ },
297
+ []
298
+ );
299
+ const handlePreviewNavigate = useCallback(
300
+ (view) => {
301
+ setPreviewView(view);
302
+ onNavigate?.(view);
303
+ if (view.kind === "component") {
304
+ setSidebarSelection({
305
+ scope: "component",
306
+ componentId: view.componentId
307
+ });
308
+ initOverridesFromVariant(view.componentId);
309
+ } else {
310
+ setSidebarSelection(null);
311
+ setPropOverrides({});
312
+ }
313
+ },
314
+ [onNavigate, initOverridesFromVariant]
315
+ );
316
+ const handleSelectVariant = useCallback(
317
+ (variantId) => {
318
+ if (previewView.kind === "component") {
319
+ setSidebarSelection({
320
+ scope: "variant",
321
+ componentId: previewView.componentId,
322
+ variantId
323
+ });
324
+ initOverridesFromVariant(previewView.componentId, variantId);
325
+ }
326
+ },
327
+ [previewView, initOverridesFromVariant]
328
+ );
329
+ const handleCloseSidebar = useCallback(() => {
330
+ setSidebarSelection(null);
331
+ setPropOverrides({});
332
+ }, []);
333
+ const handleScopeToComponent = useCallback(() => {
334
+ if (sidebarSelection && sidebarSelection.scope === "variant") {
335
+ setSidebarSelection({
336
+ scope: "component",
337
+ componentId: sidebarSelection.componentId
338
+ });
339
+ initOverridesFromVariant(sidebarSelection.componentId);
340
+ }
341
+ }, [sidebarSelection, initOverridesFromVariant]);
342
+ const handlePropOverride = useCallback((propName, value) => {
343
+ setPropOverrides((prev) => ({ ...prev, [propName]: value }));
344
+ }, []);
345
+ const handleResetOverrides = useCallback(() => {
346
+ if (sidebarSelection?.scope === "variant") {
347
+ initOverridesFromVariant(
348
+ sidebarSelection.componentId,
349
+ sidebarSelection.variantId
350
+ );
351
+ }
352
+ }, [sidebarSelection, initOverridesFromVariant]);
353
+ const saveDraft = useCallback(
354
+ async (state) => {
355
+ setSaveStatus("saving");
356
+ const updatedPresets = updateActivePresetDraftState(state);
357
+ const { error } = await persistence.onSaveDraft({
358
+ state,
359
+ presets: updatedPresets
360
+ });
361
+ setSaveStatus(error ? "error" : "saved");
362
+ },
363
+ [persistence, updateActivePresetDraftState]
364
+ );
365
+ const scheduleSave = useCallback(() => {
366
+ setSaveStatus("unsaved");
367
+ if (debounceRef.current) clearTimeout(debounceRef.current);
368
+ debounceRef.current = setTimeout(() => {
369
+ saveDraft(latestStateRef.current);
370
+ }, 2e3);
371
+ }, [saveDraft]);
372
+ useEffect(() => {
373
+ if (isInitialMount.current) {
374
+ isInitialMount.current = false;
375
+ return;
376
+ }
377
+ latestStateRef.current = configuratorState;
378
+ setIsPublished(false);
379
+ scheduleSave();
380
+ }, [configuratorState, scheduleSave]);
381
+ const handleThemeChange = useCallback(
382
+ (theme) => {
383
+ setActiveTheme(theme);
384
+ dispatch({ type: "SET_PREVIEW_THEME", theme });
385
+ },
386
+ [dispatch]
387
+ );
388
+ const handleColorModeChange = useCallback(
389
+ (mode) => {
390
+ setColorMode(mode);
391
+ dispatch({ type: "SET_PREVIEW_MODE", mode });
392
+ },
393
+ [dispatch]
394
+ );
395
+ const handlePublish = useCallback(async () => {
396
+ if (debounceRef.current) clearTimeout(debounceRef.current);
397
+ setPublishing(true);
398
+ const currentState = latestStateRef.current;
399
+ const updatedPresets = publishActivePreset(currentState);
400
+ const { error } = await persistence.onPublish({
401
+ state: currentState,
402
+ presets: updatedPresets,
403
+ activePresetId
404
+ });
405
+ if (!error) {
406
+ setSaveStatus("saved");
407
+ setIsPublished(true);
408
+ }
409
+ setPublishing(false);
410
+ }, [activePresetId, publishActivePreset, persistence]);
411
+ useEffect(() => {
412
+ const handleBeforeUnload = (e) => {
413
+ if (saveStatus === "unsaved" || saveStatus === "saving") {
414
+ e.preventDefault();
415
+ }
416
+ };
417
+ window.addEventListener("beforeunload", handleBeforeUnload);
418
+ return () => {
419
+ window.removeEventListener("beforeunload", handleBeforeUnload);
420
+ };
421
+ }, [saveStatus]);
422
+ useEffect(() => {
423
+ return () => {
424
+ if (debounceRef.current) {
425
+ clearTimeout(debounceRef.current);
426
+ const state = latestStateRef.current;
427
+ const flushedPresets = updateActivePresetDraftState(state);
428
+ persistence.onSaveDraft({ state, presets: flushedPresets });
429
+ }
430
+ };
431
+ }, []);
432
+ const selectedVariantId = sidebarSelection?.scope === "variant" ? sidebarSelection.variantId : null;
433
+ const selectedComponentId = sidebarSelection?.scope === "component" || sidebarSelection?.scope === "variant" ? sidebarSelection.componentId : null;
434
+ return {
435
+ // Configurator
436
+ configuratorState,
437
+ dispatch,
438
+ themeConfig,
439
+ previewColors,
440
+ // Save/publish
441
+ saveStatus,
442
+ isPublished,
443
+ publishing,
444
+ handlePublish,
445
+ saveDraft,
446
+ // Preview
447
+ previewView,
448
+ colorMode,
449
+ activeTheme,
450
+ handlePreviewNavigate,
451
+ handleSelectVariant,
452
+ handleThemeChange,
453
+ handleColorModeChange,
454
+ // Sidebar
455
+ sidebarSelection,
456
+ selectedComponentId,
457
+ selectedVariantId,
458
+ propOverrides,
459
+ handlePropOverride,
460
+ handleResetOverrides,
461
+ handleCloseSidebar,
462
+ handleScopeToComponent,
463
+ // Presets
464
+ presets,
465
+ activePresetId,
466
+ publishedPresetId,
467
+ switchPreset,
468
+ createPreset,
469
+ renamePreset,
470
+ deletePreset,
471
+ duplicatePreset,
472
+ // Revert
473
+ isDirty,
474
+ handleRevert,
475
+ // For retry button
476
+ latestStateRef
477
+ };
478
+ }
479
+ var SIDEBAR_WIDTH = 360;
480
+ function EditorShell({
481
+ sidebar,
482
+ navbar,
483
+ content,
484
+ rightPanel
485
+ }) {
486
+ const tokens = useTokens();
487
+ return /* @__PURE__ */ jsxs(
488
+ "div",
489
+ {
490
+ style: {
491
+ display: "flex",
492
+ height: "100vh",
493
+ overflow: "hidden",
494
+ backgroundColor: srgbToHex(tokens.background.srgb)
495
+ },
496
+ children: [
497
+ /* @__PURE__ */ jsxs(
498
+ "div",
499
+ {
500
+ style: {
501
+ flex: 1,
502
+ display: "flex",
503
+ flexDirection: "column",
504
+ height: "100vh",
505
+ overflow: "hidden",
506
+ minWidth: 0
507
+ },
508
+ children: [
509
+ navbar,
510
+ /* @__PURE__ */ jsx(
511
+ "div",
512
+ {
513
+ style: {
514
+ display: "flex",
515
+ flex: 1,
516
+ overflow: "hidden"
517
+ },
518
+ children: content
519
+ }
520
+ )
521
+ ]
522
+ }
523
+ ),
524
+ /* @__PURE__ */ jsxs(
525
+ "div",
526
+ {
527
+ style: {
528
+ position: "relative",
529
+ width: SIDEBAR_WIDTH,
530
+ flexShrink: 0,
531
+ overflow: "hidden"
532
+ },
533
+ children: [
534
+ sidebar,
535
+ rightPanel
536
+ ]
537
+ }
538
+ )
539
+ ]
540
+ }
541
+ );
542
+ }
543
+ var STRENGTH_OPTIONS = [
544
+ { label: "None", value: "none" },
545
+ { label: "Low", value: "low" },
546
+ { label: "Medium", value: "medium" },
547
+ { label: "Hard", value: "hard" }
548
+ ];
549
+ function getHexAtNv(previewColors, nv) {
550
+ const idx = Math.round((1 - nv) * (previewColors.length - 1));
551
+ const clamped = Math.max(0, Math.min(previewColors.length - 1, idx));
552
+ return srgbToHex(previewColors[clamped].srgb);
553
+ }
554
+ function ColorsSection({
555
+ state,
556
+ dispatch,
557
+ previewColors,
558
+ colorMode,
559
+ onColorModeChange
560
+ }) {
561
+ const tokens = useTokens();
562
+ const [activePaletteIndex, setActivePaletteIndex] = useState(0);
563
+ const [modeToggleHovered, setModeToggleHovered] = useState(false);
564
+ const palette = state.palettes[activePaletteIndex];
565
+ const hueRange = SEMANTIC_HUE_RANGES[activePaletteIndex];
566
+ const isNeutral = activePaletteIndex === 0;
567
+ const activeColor = srgbToHex(tokens.interactive.srgb);
568
+ const borderColor = srgbToHex(tokens.border.srgb);
569
+ const effectiveKeyColor = colorMode === "dark" ? palette.keyColorDark : palette.keyColor;
570
+ const setKeyColorAction = colorMode === "dark" ? "SET_PALETTE_KEY_COLOR_DARK" : "SET_PALETTE_KEY_COLOR";
571
+ const clearKeyColorAction = colorMode === "dark" ? "CLEAR_PALETTE_KEY_COLOR_DARK" : "CLEAR_PALETTE_KEY_COLOR";
572
+ const hexAction = colorMode === "dark" ? "SET_PALETTE_FROM_HEX_DARK" : "SET_PALETTE_FROM_HEX";
573
+ const wcag = useWcagValidation(state, activePaletteIndex);
574
+ const [hexText, setHexText] = useState("");
575
+ const [hexError, setHexError] = useState("");
576
+ const [isEditingHex, setIsEditingHex] = useState(false);
577
+ const [isHexUserSet, setIsHexUserSet] = useState(false);
578
+ useEffect(() => {
579
+ setHexText("");
580
+ setHexError("");
581
+ setIsEditingHex(false);
582
+ setIsHexUserSet(false);
583
+ }, [colorMode]);
584
+ const currentPreview = previewColors[activePaletteIndex];
585
+ const displayedHex = useMemo(() => {
586
+ if (!currentPreview || currentPreview.length === 0) return "";
587
+ const nv = effectiveKeyColor ?? wcag.autoNormalizedValue;
588
+ return getHexAtNv(currentPreview, nv);
589
+ }, [currentPreview, effectiveKeyColor, wcag.autoNormalizedValue]);
590
+ useEffect(() => {
591
+ if (!isEditingHex && !isHexUserSet) {
592
+ setHexText(displayedHex);
593
+ }
594
+ }, [displayedHex, isEditingHex, isHexUserSet]);
595
+ const dynamicRange = useMemo(() => {
596
+ const light = state.globalHueGrading.light.strength !== "none" ? {
597
+ hue: traditionalHueToOklch(state.globalHueGrading.light.hue),
598
+ strength: state.globalHueGrading.light.strength
599
+ } : void 0;
600
+ const dark = state.globalHueGrading.dark.strength !== "none" ? {
601
+ hue: traditionalHueToOklch(state.globalHueGrading.dark.hue),
602
+ strength: state.globalHueGrading.dark.strength
603
+ } : void 0;
604
+ const hueGrading = light || dark ? { light, dark } : void 0;
605
+ return {
606
+ lightest: state.dynamicRange.lightest,
607
+ darkest: state.dynamicRange.darkest,
608
+ ...hueGrading ? { hueGrading } : {}
609
+ };
610
+ }, [state.dynamicRange, state.globalHueGrading]);
611
+ const handleHexSubmit = useCallback(() => {
612
+ setIsEditingHex(false);
613
+ const trimmed = hexText.trim();
614
+ if (!trimmed) {
615
+ setHexError("");
616
+ return;
617
+ }
618
+ const hex = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
619
+ const params = hexToPaletteParams(hex, dynamicRange);
620
+ if (!params) {
621
+ setHexError("Invalid hex color");
622
+ return;
623
+ }
624
+ setHexError("");
625
+ setIsHexUserSet(true);
626
+ dispatch({
627
+ type: hexAction,
628
+ index: activePaletteIndex,
629
+ hue: params.hue,
630
+ saturation: params.saturation,
631
+ keyColor: params.normalizedValue
632
+ });
633
+ }, [hexText, dynamicRange, dispatch, activePaletteIndex, hexAction]);
634
+ const handleClearKeyColor = useCallback(() => {
635
+ dispatch({ type: clearKeyColorAction, index: activePaletteIndex });
636
+ setHexError("");
637
+ setIsHexUserSet(false);
638
+ }, [dispatch, activePaletteIndex, clearKeyColorAction]);
639
+ const wcagWarning = useMemo(() => {
640
+ if (effectiveKeyColor === void 0 || wcag.keyColorContrast === null)
641
+ return void 0;
642
+ if (wcag.passesAA) return void 0;
643
+ const ratio = wcag.keyColorContrast.toFixed(1);
644
+ if (wcag.passesAALargeText) {
645
+ return `Contrast ${ratio}:1 \u2014 passes large text (AA) but fails normal text (requires 4.5:1)`;
646
+ }
647
+ return `Contrast ${ratio}:1 \u2014 fails WCAG AA (requires 4.5:1 for normal text, 3:1 for large text)`;
648
+ }, [effectiveKeyColor, wcag]);
649
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
650
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
651
+ state.palettes.map((_p, index) => {
652
+ const isActive = index === activePaletteIndex;
653
+ const colors = previewColors[index];
654
+ const isNeutralCircle = index === 0;
655
+ const paletteKeyColor = colorMode === "dark" ? _p.keyColorDark : _p.keyColor;
656
+ const circleColor = !isNeutralCircle && colors ? getHexAtNv(
657
+ colors,
658
+ paletteKeyColor ?? wcag.autoNormalizedValue
659
+ ) : void 0;
660
+ const ringStyle = isActive ? `0 0 0 2px ${srgbToHex(tokens.background.srgb)}, 0 0 0 4px ${activeColor}` : "none";
661
+ return /* @__PURE__ */ jsx(
662
+ "button",
663
+ {
664
+ onClick: () => setActivePaletteIndex(index),
665
+ "aria-label": _p.name,
666
+ "aria-pressed": isActive,
667
+ style: {
668
+ width: 32,
669
+ height: 32,
670
+ borderRadius: "50%",
671
+ border: "none",
672
+ cursor: "pointer",
673
+ flexShrink: 0,
674
+ boxShadow: ringStyle,
675
+ transition: "box-shadow 150ms ease",
676
+ padding: 0,
677
+ overflow: "hidden",
678
+ ...isNeutralCircle ? {
679
+ background: colors ? `linear-gradient(to right, ${srgbToHex(colors[0].srgb)} 50%, ${srgbToHex(colors[colors.length - 1].srgb)} 50%)` : `linear-gradient(to right, #ffffff 50%, #000000 50%)`
680
+ } : { backgroundColor: circleColor ?? borderColor }
681
+ }
682
+ },
683
+ index
684
+ );
685
+ }),
686
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
687
+ /* @__PURE__ */ jsxs(
688
+ "button",
689
+ {
690
+ onClick: () => onColorModeChange(colorMode === "light" ? "dark" : "light"),
691
+ onMouseEnter: () => setModeToggleHovered(true),
692
+ onMouseLeave: () => setModeToggleHovered(false),
693
+ "aria-label": colorMode === "light" ? "Switch to dark mode" : "Switch to light mode",
694
+ style: {
695
+ display: "flex",
696
+ alignItems: "center",
697
+ gap: 6,
698
+ padding: "4px 10px",
699
+ borderRadius: 6,
700
+ border: `1px solid ${borderColor}`,
701
+ background: modeToggleHovered ? `${borderColor}20` : "none",
702
+ cursor: "pointer",
703
+ fontSize: 12,
704
+ color: srgbToHex(tokens.textPrimary.srgb),
705
+ transition: "background-color 150ms ease"
706
+ },
707
+ children: [
708
+ colorMode === "light" ? "\u2600" : "\u263E",
709
+ /* @__PURE__ */ jsx("span", { children: colorMode === "light" ? "Light" : "Dark" })
710
+ ]
711
+ }
712
+ )
713
+ ] }),
714
+ currentPreview && (isNeutral ? /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 1 }, children: currentPreview.map((color, i) => /* @__PURE__ */ jsx(
715
+ "div",
716
+ {
717
+ style: {
718
+ flex: 1,
719
+ height: 64,
720
+ borderRadius: 2,
721
+ backgroundColor: srgbToHex(color.srgb)
722
+ }
723
+ },
724
+ i
725
+ )) }) : /* @__PURE__ */ jsxs(
726
+ "div",
727
+ {
728
+ style: { display: "flex", flexDirection: "column", gap: 8 },
729
+ children: [
730
+ /* @__PURE__ */ jsx(
731
+ ColorScaleSlider,
732
+ {
733
+ colors: currentPreview,
734
+ value: effectiveKeyColor ?? wcag.autoNormalizedValue,
735
+ onValueChange: (nv) => {
736
+ setIsHexUserSet(false);
737
+ dispatch({
738
+ type: setKeyColorAction,
739
+ index: activePaletteIndex,
740
+ normalizedValue: nv
741
+ });
742
+ },
743
+ trimEnds: true,
744
+ snap: true,
745
+ label: "Key Color",
746
+ warning: wcagWarning,
747
+ animateValue: true
748
+ }
749
+ ),
750
+ /* @__PURE__ */ jsxs(
751
+ "div",
752
+ {
753
+ style: {
754
+ display: "flex",
755
+ gap: 8,
756
+ alignItems: "flex-end"
757
+ },
758
+ children: [
759
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
760
+ TextInput,
761
+ {
762
+ label: "Hex",
763
+ value: hexText,
764
+ onChangeText: (text) => {
765
+ setIsEditingHex(true);
766
+ setHexText(text);
767
+ setHexError("");
768
+ },
769
+ onBlur: handleHexSubmit,
770
+ onSubmitEditing: handleHexSubmit,
771
+ placeholder: "#000000"
772
+ }
773
+ ) }),
774
+ effectiveKeyColor !== void 0 && /* @__PURE__ */ jsx(
775
+ "button",
776
+ {
777
+ onClick: handleClearKeyColor,
778
+ style: {
779
+ background: "none",
780
+ border: "none",
781
+ cursor: "pointer",
782
+ padding: "0 0 6px",
783
+ fontSize: 13,
784
+ fontWeight: 600,
785
+ color: activeColor
786
+ },
787
+ children: "Auto"
788
+ }
789
+ )
790
+ ]
791
+ }
792
+ ),
793
+ hexError && /* @__PURE__ */ jsx(
794
+ "div",
795
+ {
796
+ style: {
797
+ fontSize: 12,
798
+ fontWeight: 500,
799
+ color: srgbToHex(tokens.error.srgb)
800
+ },
801
+ children: hexError
802
+ }
803
+ )
804
+ ]
805
+ }
806
+ )),
807
+ /* @__PURE__ */ jsx(
808
+ "div",
809
+ {
810
+ style: {
811
+ height: 1,
812
+ backgroundColor: borderColor,
813
+ margin: "4px 0"
814
+ }
815
+ }
816
+ ),
817
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
818
+ /* @__PURE__ */ jsx(
819
+ HueSlider,
820
+ {
821
+ value: palette.hue,
822
+ onValueChange: (hue) => dispatch({
823
+ type: "SET_PALETTE_HUE",
824
+ index: activePaletteIndex,
825
+ hue
826
+ }),
827
+ label: "Hue",
828
+ editableValue: true,
829
+ ...hueRange ? { min: hueRange.min, max: hueRange.max } : {}
830
+ }
831
+ ),
832
+ /* @__PURE__ */ jsx(
833
+ Slider,
834
+ {
835
+ value: palette.saturation,
836
+ onValueChange: (saturation) => dispatch({
837
+ type: "SET_PALETTE_SATURATION",
838
+ index: activePaletteIndex,
839
+ saturation
840
+ }),
841
+ min: 0,
842
+ max: 100,
843
+ label: "Saturation",
844
+ editableValue: true
845
+ }
846
+ ),
847
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12, alignItems: "flex-end" }, children: [
848
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
849
+ Select,
850
+ {
851
+ options: STRENGTH_OPTIONS,
852
+ value: palette.desaturationStrength,
853
+ onValueChange: (strength) => dispatch({
854
+ type: "SET_PALETTE_DESAT_STRENGTH",
855
+ index: activePaletteIndex,
856
+ strength
857
+ }),
858
+ label: "Desaturation"
859
+ }
860
+ ) }),
861
+ palette.desaturationStrength !== "none" && /* @__PURE__ */ jsx("div", { style: { paddingBottom: 2 }, children: /* @__PURE__ */ jsx(
862
+ Toggle,
863
+ {
864
+ value: palette.desaturationDirection === "dark",
865
+ onValueChange: (v) => dispatch({
866
+ type: "SET_PALETTE_DESAT_DIRECTION",
867
+ index: activePaletteIndex,
868
+ direction: v ? "dark" : "light"
869
+ }),
870
+ label: "Invert"
871
+ }
872
+ ) })
873
+ ] }),
874
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12, alignItems: "flex-end" }, children: [
875
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
876
+ Select,
877
+ {
878
+ options: STRENGTH_OPTIONS,
879
+ value: palette.hueGradeStrength,
880
+ onValueChange: (strength) => dispatch({
881
+ type: "SET_PALETTE_HUE_GRADE_STRENGTH",
882
+ index: activePaletteIndex,
883
+ strength
884
+ }),
885
+ label: "Hue Grading"
886
+ }
887
+ ) }),
888
+ palette.hueGradeStrength !== "none" && /* @__PURE__ */ jsx("div", { style: { paddingBottom: 2 }, children: /* @__PURE__ */ jsx(
889
+ Toggle,
890
+ {
891
+ value: palette.hueGradeDirection === "dark",
892
+ onValueChange: (v) => dispatch({
893
+ type: "SET_PALETTE_HUE_GRADE_DIRECTION",
894
+ index: activePaletteIndex,
895
+ direction: v ? "dark" : "light"
896
+ }),
897
+ label: "Invert"
898
+ }
899
+ ) })
900
+ ] }),
901
+ palette.hueGradeStrength !== "none" && /* @__PURE__ */ jsx(
902
+ HueSlider,
903
+ {
904
+ value: palette.hueGradeHue,
905
+ onValueChange: (hue) => dispatch({
906
+ type: "SET_PALETTE_HUE_GRADE_HUE",
907
+ index: activePaletteIndex,
908
+ hue
909
+ }),
910
+ label: "Grade Target",
911
+ editableValue: true
912
+ }
913
+ )
914
+ ] })
915
+ ] });
916
+ }
917
+ var STRENGTH_OPTIONS2 = [
918
+ { label: "None", value: "none" },
919
+ { label: "Low", value: "low" },
920
+ { label: "Medium", value: "medium" },
921
+ { label: "Hard", value: "hard" }
922
+ ];
923
+ var TRACK_HEIGHT = 8;
924
+ var THUMB_SIZE = 18;
925
+ var ZONE_FRAC = 1 / 3;
926
+ function clamp(v, min, max) {
927
+ return Math.min(max, Math.max(min, v));
928
+ }
929
+ function internalToDisplay(internal) {
930
+ return clamp(Math.round(internal * 10), 0, 10);
931
+ }
932
+ function displayToInternal(display) {
933
+ return clamp(display, 0, 10) / 10;
934
+ }
935
+ function posToWhitesDisplay(pos) {
936
+ const ratio = clamp(pos / ZONE_FRAC, 0, 1);
937
+ return Math.round(10 * (1 - ratio));
938
+ }
939
+ function posToBlacksDisplay(pos) {
940
+ const ratio = clamp((pos - (1 - ZONE_FRAC)) / ZONE_FRAC, 0, 1);
941
+ return Math.round(ratio * 10);
942
+ }
943
+ function whitesDisplayToPos(display) {
944
+ return (10 - display) / 10 * ZONE_FRAC;
945
+ }
946
+ function blacksDisplayToPos(display) {
947
+ return 1 - ZONE_FRAC + display / 10 * ZONE_FRAC;
948
+ }
949
+ function DualRangeSlider({
950
+ whitesValue,
951
+ blacksValue,
952
+ onWhitesChange,
953
+ onBlacksChange
954
+ }) {
955
+ const tokens = useTokens();
956
+ const trackRef = useRef(null);
957
+ const [activeThumb, setActiveThumb] = useState(null);
958
+ const interactiveColor = srgbToHex(tokens.interactive.srgb);
959
+ const borderColor = srgbToHex(tokens.border.srgb);
960
+ const wDisplay = internalToDisplay(whitesValue);
961
+ const bDisplay = internalToDisplay(blacksValue);
962
+ const wPos = whitesDisplayToPos(wDisplay);
963
+ const bPos = blacksDisplayToPos(bDisplay);
964
+ const getPosRatio = useCallback((clientX) => {
965
+ if (!trackRef.current) return 0;
966
+ const rect = trackRef.current.getBoundingClientRect();
967
+ return clamp((clientX - rect.left) / rect.width, 0, 1);
968
+ }, []);
969
+ const handlePointerDown = useCallback(
970
+ (e) => {
971
+ e.preventDefault();
972
+ const pos = getPosRatio(e.clientX);
973
+ if (pos <= ZONE_FRAC) {
974
+ setActiveThumb("whites");
975
+ onWhitesChange(displayToInternal(posToWhitesDisplay(pos)));
976
+ } else if (pos >= 1 - ZONE_FRAC) {
977
+ setActiveThumb("blacks");
978
+ onBlacksChange(displayToInternal(posToBlacksDisplay(pos)));
979
+ } else {
980
+ return;
981
+ }
982
+ e.currentTarget.setPointerCapture(e.pointerId);
983
+ },
984
+ [getPosRatio, onWhitesChange, onBlacksChange]
985
+ );
986
+ const handlePointerMove = useCallback(
987
+ (e) => {
988
+ if (!activeThumb) return;
989
+ const pos = getPosRatio(e.clientX);
990
+ if (activeThumb === "whites") {
991
+ onWhitesChange(displayToInternal(posToWhitesDisplay(pos)));
992
+ } else {
993
+ onBlacksChange(displayToInternal(posToBlacksDisplay(pos)));
994
+ }
995
+ },
996
+ [activeThumb, getPosRatio, onWhitesChange, onBlacksChange]
997
+ );
998
+ const handlePointerUp = useCallback(() => {
999
+ setActiveThumb(null);
1000
+ }, []);
1001
+ const trackTop = (THUMB_SIZE - TRACK_HEIGHT) / 2;
1002
+ return /* @__PURE__ */ jsx("div", { style: { padding: `0 ${THUMB_SIZE / 2}px` }, children: /* @__PURE__ */ jsxs(
1003
+ "div",
1004
+ {
1005
+ ref: trackRef,
1006
+ onPointerDown: handlePointerDown,
1007
+ onPointerMove: handlePointerMove,
1008
+ onPointerUp: handlePointerUp,
1009
+ onPointerCancel: handlePointerUp,
1010
+ style: {
1011
+ position: "relative",
1012
+ height: THUMB_SIZE,
1013
+ cursor: activeThumb ? "grabbing" : "pointer",
1014
+ touchAction: "none",
1015
+ userSelect: "none"
1016
+ },
1017
+ children: [
1018
+ /* @__PURE__ */ jsx(
1019
+ "div",
1020
+ {
1021
+ style: {
1022
+ position: "absolute",
1023
+ left: 0,
1024
+ right: 0,
1025
+ top: trackTop,
1026
+ height: TRACK_HEIGHT,
1027
+ borderRadius: TRACK_HEIGHT / 2,
1028
+ background: "linear-gradient(to right, white, black)",
1029
+ border: `1px solid ${borderColor}`,
1030
+ boxSizing: "border-box"
1031
+ }
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsx(
1035
+ "div",
1036
+ {
1037
+ style: {
1038
+ position: "absolute",
1039
+ left: `${wPos * 100}%`,
1040
+ width: `${(bPos - wPos) * 100}%`,
1041
+ top: trackTop,
1042
+ height: TRACK_HEIGHT,
1043
+ backgroundColor: interactiveColor
1044
+ }
1045
+ }
1046
+ ),
1047
+ /* @__PURE__ */ jsx(
1048
+ "div",
1049
+ {
1050
+ style: {
1051
+ position: "absolute",
1052
+ left: `calc(${wPos * 100}% - ${THUMB_SIZE / 2}px)`,
1053
+ top: 0,
1054
+ width: THUMB_SIZE,
1055
+ height: THUMB_SIZE,
1056
+ borderRadius: THUMB_SIZE / 2,
1057
+ backgroundColor: interactiveColor,
1058
+ pointerEvents: "none",
1059
+ zIndex: activeThumb === "whites" ? 2 : 1
1060
+ }
1061
+ }
1062
+ ),
1063
+ /* @__PURE__ */ jsx(
1064
+ "div",
1065
+ {
1066
+ style: {
1067
+ position: "absolute",
1068
+ left: `calc(${bPos * 100}% - ${THUMB_SIZE / 2}px)`,
1069
+ top: 0,
1070
+ width: THUMB_SIZE,
1071
+ height: THUMB_SIZE,
1072
+ borderRadius: THUMB_SIZE / 2,
1073
+ backgroundColor: interactiveColor,
1074
+ pointerEvents: "none",
1075
+ zIndex: activeThumb === "blacks" ? 2 : 1
1076
+ }
1077
+ }
1078
+ )
1079
+ ]
1080
+ }
1081
+ ) });
1082
+ }
1083
+ function RangeInput({ display, onCommit, toInternal }) {
1084
+ const tokens = useTokens();
1085
+ const [text, setText] = useState(String(display));
1086
+ const [isEditing, setIsEditing] = useState(false);
1087
+ const displayText = isEditing ? text : String(display);
1088
+ const commit = () => {
1089
+ setIsEditing(false);
1090
+ const parsed = parseInt(text, 10);
1091
+ if (isNaN(parsed)) {
1092
+ setText(String(display));
1093
+ return;
1094
+ }
1095
+ const clamped = clamp(Math.round(parsed), 0, 10);
1096
+ onCommit(toInternal(clamped));
1097
+ setText(String(clamped));
1098
+ };
1099
+ return /* @__PURE__ */ jsx(
1100
+ "input",
1101
+ {
1102
+ type: "text",
1103
+ inputMode: "numeric",
1104
+ value: displayText,
1105
+ onChange: (e) => {
1106
+ setIsEditing(true);
1107
+ setText(e.target.value);
1108
+ },
1109
+ onBlur: commit,
1110
+ onKeyDown: (e) => {
1111
+ if (e.key === "Enter") commit();
1112
+ },
1113
+ style: {
1114
+ width: 40,
1115
+ padding: "2px 6px",
1116
+ border: `1px solid ${srgbToHex(tokens.border.srgb)}`,
1117
+ borderRadius: 4,
1118
+ backgroundColor: "transparent",
1119
+ color: srgbToHex(tokens.textPrimary.srgb),
1120
+ fontFamily: "inherit",
1121
+ fontSize: 12,
1122
+ fontWeight: 500,
1123
+ textAlign: "center",
1124
+ outline: "none"
1125
+ }
1126
+ }
1127
+ );
1128
+ }
1129
+ function DynamicRangeSection({
1130
+ state,
1131
+ dispatch
1132
+ }) {
1133
+ const tokens = useTokens();
1134
+ const labelColor = srgbToHex(tokens.textSecondary.srgb);
1135
+ const labelStyle = {
1136
+ fontSize: 11,
1137
+ fontWeight: 600,
1138
+ color: labelColor,
1139
+ textTransform: "uppercase",
1140
+ letterSpacing: 0.5
1141
+ };
1142
+ const wDisplay = internalToDisplay(state.dynamicRange.lightest);
1143
+ const bDisplay = internalToDisplay(state.dynamicRange.darkest);
1144
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
1145
+ /* @__PURE__ */ jsxs(
1146
+ "div",
1147
+ {
1148
+ style: {
1149
+ display: "flex",
1150
+ justifyContent: "space-between",
1151
+ alignItems: "center"
1152
+ },
1153
+ children: [
1154
+ /* @__PURE__ */ jsx("span", { style: labelStyle, children: "Whites" }),
1155
+ /* @__PURE__ */ jsx("span", { style: labelStyle, children: "Blacks" })
1156
+ ]
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsx(
1160
+ DualRangeSlider,
1161
+ {
1162
+ whitesValue: state.dynamicRange.lightest,
1163
+ blacksValue: state.dynamicRange.darkest,
1164
+ onWhitesChange: (v) => dispatch({ type: "SET_LIGHTEST", value: v }),
1165
+ onBlacksChange: (v) => dispatch({ type: "SET_DARKEST", value: v })
1166
+ }
1167
+ ),
1168
+ /* @__PURE__ */ jsxs(
1169
+ "div",
1170
+ {
1171
+ style: {
1172
+ display: "flex",
1173
+ justifyContent: "space-between",
1174
+ alignItems: "center"
1175
+ },
1176
+ children: [
1177
+ /* @__PURE__ */ jsx(
1178
+ RangeInput,
1179
+ {
1180
+ display: wDisplay,
1181
+ onCommit: (v) => dispatch({ type: "SET_LIGHTEST", value: v }),
1182
+ toInternal: displayToInternal
1183
+ }
1184
+ ),
1185
+ /* @__PURE__ */ jsx(
1186
+ RangeInput,
1187
+ {
1188
+ display: bDisplay,
1189
+ onCommit: (v) => dispatch({ type: "SET_DARKEST", value: v }),
1190
+ toInternal: displayToInternal
1191
+ }
1192
+ )
1193
+ ]
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsx("div", { style: { ...labelStyle, marginTop: 4 }, children: "Global Hue Grading \u2014 Light" }),
1197
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12 }, children: [
1198
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
1199
+ Select,
1200
+ {
1201
+ options: STRENGTH_OPTIONS2,
1202
+ value: state.globalHueGrading.light.strength,
1203
+ onValueChange: (s) => dispatch({
1204
+ type: "SET_GLOBAL_GRADE_LIGHT_STRENGTH",
1205
+ strength: s
1206
+ }),
1207
+ label: "Strength"
1208
+ }
1209
+ ) }),
1210
+ state.globalHueGrading.light.strength !== "none" && /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
1211
+ HueSlider,
1212
+ {
1213
+ value: state.globalHueGrading.light.hue,
1214
+ onValueChange: (hue) => dispatch({ type: "SET_GLOBAL_GRADE_LIGHT_HUE", hue }),
1215
+ label: "Target Hue",
1216
+ showValue: true
1217
+ }
1218
+ ) })
1219
+ ] }),
1220
+ /* @__PURE__ */ jsx("div", { style: { ...labelStyle, marginTop: 4 }, children: "Global Hue Grading \u2014 Dark" }),
1221
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12 }, children: [
1222
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
1223
+ Select,
1224
+ {
1225
+ options: STRENGTH_OPTIONS2,
1226
+ value: state.globalHueGrading.dark.strength,
1227
+ onValueChange: (s) => dispatch({
1228
+ type: "SET_GLOBAL_GRADE_DARK_STRENGTH",
1229
+ strength: s
1230
+ }),
1231
+ label: "Strength"
1232
+ }
1233
+ ) }),
1234
+ state.globalHueGrading.dark.strength !== "none" && /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
1235
+ HueSlider,
1236
+ {
1237
+ value: state.globalHueGrading.dark.hue,
1238
+ onValueChange: (hue) => dispatch({ type: "SET_GLOBAL_GRADE_DARK_HUE", hue }),
1239
+ label: "Target Hue",
1240
+ showValue: true
1241
+ }
1242
+ ) })
1243
+ ] })
1244
+ ] });
1245
+ }
1246
+ var ICON_VARIANT_OPTIONS = [
1247
+ { label: "Outlined", value: "outlined" },
1248
+ { label: "Rounded", value: "rounded" },
1249
+ { label: "Sharp", value: "sharp" }
1250
+ ];
1251
+ var ICON_WEIGHT_OPTIONS = [
1252
+ { label: "100", value: "100" },
1253
+ { label: "200", value: "200" },
1254
+ { label: "300", value: "300" },
1255
+ { label: "400", value: "400" },
1256
+ { label: "500", value: "500" },
1257
+ { label: "600", value: "600" },
1258
+ { label: "700", value: "700" }
1259
+ ];
1260
+ function IconsSection({ state, dispatch }) {
1261
+ const variant = state.icons?.variant ?? "rounded";
1262
+ const weight = state.icons?.weight ?? 400;
1263
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12 }, children: [
1264
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
1265
+ Select,
1266
+ {
1267
+ options: ICON_VARIANT_OPTIONS,
1268
+ value: variant,
1269
+ onValueChange: (v) => dispatch({
1270
+ type: "SET_ICON_VARIANT",
1271
+ variant: v
1272
+ }),
1273
+ label: "Variant"
1274
+ }
1275
+ ) }),
1276
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
1277
+ Select,
1278
+ {
1279
+ options: ICON_WEIGHT_OPTIONS,
1280
+ value: weight.toString(),
1281
+ onValueChange: (v) => dispatch({
1282
+ type: "SET_ICON_WEIGHT",
1283
+ weight: parseInt(v)
1284
+ }),
1285
+ label: "Weight"
1286
+ }
1287
+ ) })
1288
+ ] });
1289
+ }
1290
+ var previewLoaded = false;
1291
+ function preloadFontsForPreview() {
1292
+ if (previewLoaded || typeof document === "undefined") return;
1293
+ previewLoaded = true;
1294
+ const families = GOOGLE_FONTS.map(
1295
+ (f) => `family=${f.family.replace(/ /g, "+")}:wght@400`
1296
+ ).join("&");
1297
+ const url = `https://fonts.googleapis.com/css2?${families}&display=swap`;
1298
+ const link = document.createElement("link");
1299
+ link.rel = "stylesheet";
1300
+ link.href = url;
1301
+ document.head.appendChild(link);
1302
+ }
1303
+ function googleFontToConfig(entry) {
1304
+ return {
1305
+ type: "google",
1306
+ family: entry.family,
1307
+ fallback: entry.fallback
1308
+ };
1309
+ }
1310
+ function systemFontToConfig(entry) {
1311
+ return {
1312
+ type: "system",
1313
+ family: entry.family,
1314
+ fallback: entry.fallback
1315
+ };
1316
+ }
1317
+ var CATEGORY_LABELS = {
1318
+ "sans-serif": "Sans Serif",
1319
+ serif: "Serif",
1320
+ monospace: "Monospace",
1321
+ display: "Display"
1322
+ };
1323
+ var CATEGORY_ORDER = [
1324
+ "sans-serif",
1325
+ "serif",
1326
+ "monospace",
1327
+ "display"
1328
+ ];
1329
+ var MONO_CATEGORY_ORDER = [
1330
+ "monospace",
1331
+ "sans-serif",
1332
+ "serif",
1333
+ "display"
1334
+ ];
1335
+ function FontPicker({
1336
+ label,
1337
+ slot,
1338
+ currentFont,
1339
+ onSelect
1340
+ }) {
1341
+ const tokens = useTokens();
1342
+ const [isOpen, setIsOpen] = useState(false);
1343
+ const [search, setSearch] = useState("");
1344
+ const containerRef = useRef(null);
1345
+ const searchInputRef = useRef(null);
1346
+ const labelColor = srgbToHex(tokens.textSecondary.srgb);
1347
+ const textColor = srgbToHex(tokens.textPrimary.srgb);
1348
+ const bgColor = srgbToHex(tokens.backgroundElevated.srgb);
1349
+ const borderColor = srgbToHex(tokens.border.srgb);
1350
+ const hoverColor = srgbToHex(tokens.backgroundSunken.srgb);
1351
+ const interactiveColor = srgbToHex(tokens.interactive.srgb);
1352
+ useEffect(() => {
1353
+ if (!isOpen) return;
1354
+ function handleMouseDown(e) {
1355
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
1356
+ setIsOpen(false);
1357
+ setSearch("");
1358
+ }
1359
+ }
1360
+ document.addEventListener("mousedown", handleMouseDown);
1361
+ return () => document.removeEventListener("mousedown", handleMouseDown);
1362
+ }, [isOpen]);
1363
+ useEffect(() => {
1364
+ if (isOpen) {
1365
+ preloadFontsForPreview();
1366
+ requestAnimationFrame(() => searchInputRef.current?.focus());
1367
+ }
1368
+ }, [isOpen]);
1369
+ const categoryOrder = slot === "mono" ? MONO_CATEGORY_ORDER : CATEGORY_ORDER;
1370
+ const filteredGoogleFonts = useMemo(() => {
1371
+ const query = search.toLowerCase().trim();
1372
+ const fonts = query ? GOOGLE_FONTS.filter((f) => f.family.toLowerCase().includes(query)) : GOOGLE_FONTS;
1373
+ const grouped = {};
1374
+ for (const cat of categoryOrder) {
1375
+ const inCategory = fonts.filter((f) => f.category === cat);
1376
+ if (inCategory.length > 0) {
1377
+ grouped[cat] = inCategory;
1378
+ }
1379
+ }
1380
+ return grouped;
1381
+ }, [search, categoryOrder]);
1382
+ const filteredSystemFonts = useMemo(() => {
1383
+ const query = search.toLowerCase().trim();
1384
+ return query ? SYSTEM_FONTS.filter((f) => f.family.toLowerCase().includes(query)) : [...SYSTEM_FONTS];
1385
+ }, [search]);
1386
+ const handleSelect = useCallback(
1387
+ (font) => {
1388
+ onSelect(font);
1389
+ setIsOpen(false);
1390
+ setSearch("");
1391
+ },
1392
+ [onSelect]
1393
+ );
1394
+ const fontFamily = currentFont.family.includes(" ") ? `"${currentFont.family}"` : currentFont.family;
1395
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, style: { position: "relative" }, children: [
1396
+ /* @__PURE__ */ jsxs(
1397
+ "button",
1398
+ {
1399
+ type: "button",
1400
+ onClick: () => setIsOpen(!isOpen),
1401
+ style: {
1402
+ width: "100%",
1403
+ display: "flex",
1404
+ justifyContent: "space-between",
1405
+ alignItems: "center",
1406
+ padding: "6px 10px",
1407
+ borderRadius: 6,
1408
+ border: `1px solid ${isOpen ? interactiveColor : borderColor}`,
1409
+ background: "transparent",
1410
+ cursor: "pointer",
1411
+ outline: "none"
1412
+ },
1413
+ children: [
1414
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: labelColor }, children: label }),
1415
+ /* @__PURE__ */ jsx(
1416
+ "span",
1417
+ {
1418
+ style: {
1419
+ fontSize: 12,
1420
+ color: textColor,
1421
+ fontFamily: `${fontFamily}, ${currentFont.fallback}`,
1422
+ maxWidth: 140,
1423
+ overflow: "hidden",
1424
+ textOverflow: "ellipsis",
1425
+ whiteSpace: "nowrap"
1426
+ },
1427
+ children: currentFont.family
1428
+ }
1429
+ )
1430
+ ]
1431
+ }
1432
+ ),
1433
+ isOpen && /* @__PURE__ */ jsxs(
1434
+ "div",
1435
+ {
1436
+ style: {
1437
+ position: "absolute",
1438
+ top: "calc(100% + 4px)",
1439
+ left: 0,
1440
+ right: 0,
1441
+ zIndex: 100,
1442
+ background: bgColor,
1443
+ border: `1px solid ${borderColor}`,
1444
+ borderRadius: 8,
1445
+ boxShadow: "0 4px 16px rgba(0,0,0,0.15)",
1446
+ maxHeight: 320,
1447
+ display: "flex",
1448
+ flexDirection: "column",
1449
+ overflow: "hidden"
1450
+ },
1451
+ children: [
1452
+ /* @__PURE__ */ jsx("div", { style: { padding: "8px 8px 4px" }, children: /* @__PURE__ */ jsx(
1453
+ "input",
1454
+ {
1455
+ ref: searchInputRef,
1456
+ type: "text",
1457
+ value: search,
1458
+ onChange: (e) => setSearch(e.target.value),
1459
+ placeholder: "Search fonts...",
1460
+ style: {
1461
+ width: "100%",
1462
+ padding: "6px 8px",
1463
+ fontSize: 12,
1464
+ borderRadius: 4,
1465
+ border: `1px solid ${borderColor}`,
1466
+ background: "transparent",
1467
+ color: textColor,
1468
+ outline: "none",
1469
+ boxSizing: "border-box"
1470
+ }
1471
+ }
1472
+ ) }),
1473
+ /* @__PURE__ */ jsxs("div", { style: { overflowY: "auto", padding: "4px 0" }, children: [
1474
+ filteredSystemFonts.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
1475
+ /* @__PURE__ */ jsx(
1476
+ "div",
1477
+ {
1478
+ style: {
1479
+ fontSize: 10,
1480
+ fontWeight: 600,
1481
+ color: labelColor,
1482
+ textTransform: "uppercase",
1483
+ letterSpacing: 0.5,
1484
+ padding: "6px 12px 2px"
1485
+ },
1486
+ children: "System"
1487
+ }
1488
+ ),
1489
+ filteredSystemFonts.map((f) => /* @__PURE__ */ jsx(
1490
+ FontOption,
1491
+ {
1492
+ family: f.family,
1493
+ fallback: f.fallback,
1494
+ isSelected: currentFont.family === f.family && currentFont.type === "system",
1495
+ textColor,
1496
+ hoverColor,
1497
+ interactiveColor,
1498
+ onSelect: () => handleSelect(systemFontToConfig(f))
1499
+ },
1500
+ f.family
1501
+ ))
1502
+ ] }),
1503
+ Object.entries(filteredGoogleFonts).map(([category, fonts]) => /* @__PURE__ */ jsxs("div", { children: [
1504
+ /* @__PURE__ */ jsx(
1505
+ "div",
1506
+ {
1507
+ style: {
1508
+ fontSize: 10,
1509
+ fontWeight: 600,
1510
+ color: labelColor,
1511
+ textTransform: "uppercase",
1512
+ letterSpacing: 0.5,
1513
+ padding: "8px 12px 2px"
1514
+ },
1515
+ children: CATEGORY_LABELS[category] ?? category
1516
+ }
1517
+ ),
1518
+ fonts.map((f) => /* @__PURE__ */ jsx(
1519
+ FontOption,
1520
+ {
1521
+ family: f.family,
1522
+ fallback: f.fallback,
1523
+ isSelected: currentFont.family === f.family && currentFont.type === "google",
1524
+ textColor,
1525
+ hoverColor,
1526
+ interactiveColor,
1527
+ onSelect: () => handleSelect(googleFontToConfig(f))
1528
+ },
1529
+ f.family
1530
+ ))
1531
+ ] }, category)),
1532
+ filteredSystemFonts.length === 0 && Object.keys(filteredGoogleFonts).length === 0 && /* @__PURE__ */ jsx(
1533
+ "div",
1534
+ {
1535
+ style: {
1536
+ padding: "12px",
1537
+ fontSize: 12,
1538
+ color: labelColor,
1539
+ textAlign: "center"
1540
+ },
1541
+ children: "No fonts found"
1542
+ }
1543
+ )
1544
+ ] })
1545
+ ]
1546
+ }
1547
+ )
1548
+ ] });
1549
+ }
1550
+ function FontOption({
1551
+ family,
1552
+ fallback,
1553
+ isSelected,
1554
+ textColor,
1555
+ hoverColor,
1556
+ interactiveColor,
1557
+ onSelect
1558
+ }) {
1559
+ const [hovered, setHovered] = useState(false);
1560
+ const fontFamily = family.includes(" ") ? `"${family}"` : family;
1561
+ return /* @__PURE__ */ jsx(
1562
+ "button",
1563
+ {
1564
+ type: "button",
1565
+ onClick: onSelect,
1566
+ onMouseEnter: () => setHovered(true),
1567
+ onMouseLeave: () => setHovered(false),
1568
+ style: {
1569
+ display: "block",
1570
+ width: "100%",
1571
+ padding: "5px 12px",
1572
+ fontSize: 13,
1573
+ fontFamily: `${fontFamily}, ${fallback}`,
1574
+ color: isSelected ? interactiveColor : textColor,
1575
+ background: hovered ? hoverColor : "transparent",
1576
+ border: "none",
1577
+ cursor: "pointer",
1578
+ textAlign: "left",
1579
+ outline: "none",
1580
+ fontWeight: isSelected ? 600 : 400
1581
+ },
1582
+ children: family
1583
+ }
1584
+ );
1585
+ }
1586
+ var DEFAULT_FONT_DEFAULT = {
1587
+ type: "system",
1588
+ family: "system-ui",
1589
+ fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1590
+ };
1591
+ var DEFAULT_FONT_DISPLAY = {
1592
+ type: "system",
1593
+ family: "system-ui",
1594
+ fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1595
+ };
1596
+ var DEFAULT_FONT_MONO = {
1597
+ type: "system",
1598
+ family: "ui-monospace",
1599
+ fallback: "SFMono-Regular, Menlo, Monaco, Consolas, monospace"
1600
+ };
1601
+ function FontsSection({ state, dispatch }) {
1602
+ const tokens = useTokens();
1603
+ const baseSize = state.typography?.scale.baseSize ?? 16;
1604
+ const ratio = state.typography?.scale.ratio ?? 1.25;
1605
+ const labelColor = srgbToHex(tokens.textSecondary.srgb);
1606
+ const handleFontChange = (slot, font) => {
1607
+ const actionType = {
1608
+ default: "SET_FONT_DEFAULT",
1609
+ display: "SET_FONT_DISPLAY",
1610
+ mono: "SET_FONT_MONO"
1611
+ }[slot];
1612
+ dispatch({ type: actionType, font });
1613
+ };
1614
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
1615
+ /* @__PURE__ */ jsxs("div", { children: [
1616
+ /* @__PURE__ */ jsx(
1617
+ "div",
1618
+ {
1619
+ style: {
1620
+ fontSize: 11,
1621
+ fontWeight: 600,
1622
+ color: labelColor,
1623
+ textTransform: "uppercase",
1624
+ letterSpacing: 0.5,
1625
+ marginBottom: 8
1626
+ },
1627
+ children: "Scale"
1628
+ }
1629
+ ),
1630
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
1631
+ /* @__PURE__ */ jsx(
1632
+ Slider,
1633
+ {
1634
+ value: baseSize,
1635
+ onValueChange: (v) => dispatch({ type: "SET_TYPOGRAPHY_BASE_SIZE", baseSize: v }),
1636
+ min: 12,
1637
+ max: 24,
1638
+ step: 1,
1639
+ label: "Base Size",
1640
+ showValue: true
1641
+ }
1642
+ ),
1643
+ /* @__PURE__ */ jsx(
1644
+ Slider,
1645
+ {
1646
+ value: Math.round(ratio * 100),
1647
+ onValueChange: (v) => dispatch({ type: "SET_TYPOGRAPHY_RATIO", ratio: v / 100 }),
1648
+ min: 110,
1649
+ max: 150,
1650
+ step: 5,
1651
+ label: "Scale Ratio",
1652
+ showValue: true
1653
+ }
1654
+ )
1655
+ ] })
1656
+ ] }),
1657
+ /* @__PURE__ */ jsxs("div", { children: [
1658
+ /* @__PURE__ */ jsx(
1659
+ "div",
1660
+ {
1661
+ style: {
1662
+ fontSize: 11,
1663
+ fontWeight: 600,
1664
+ color: labelColor,
1665
+ textTransform: "uppercase",
1666
+ letterSpacing: 0.5,
1667
+ marginBottom: 8
1668
+ },
1669
+ children: "Fonts"
1670
+ }
1671
+ ),
1672
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1673
+ /* @__PURE__ */ jsx(
1674
+ FontPicker,
1675
+ {
1676
+ label: "Default",
1677
+ slot: "default",
1678
+ currentFont: state.typography?.fonts.default ?? DEFAULT_FONT_DEFAULT,
1679
+ onSelect: (font) => handleFontChange("default", font)
1680
+ }
1681
+ ),
1682
+ /* @__PURE__ */ jsx(
1683
+ FontPicker,
1684
+ {
1685
+ label: "Display",
1686
+ slot: "display",
1687
+ currentFont: state.typography?.fonts.display ?? DEFAULT_FONT_DISPLAY,
1688
+ onSelect: (font) => handleFontChange("display", font)
1689
+ }
1690
+ ),
1691
+ /* @__PURE__ */ jsx(
1692
+ FontPicker,
1693
+ {
1694
+ label: "Mono",
1695
+ slot: "mono",
1696
+ currentFont: state.typography?.fonts.mono ?? DEFAULT_FONT_MONO,
1697
+ onSelect: (font) => handleFontChange("mono", font)
1698
+ }
1699
+ )
1700
+ ] })
1701
+ ] })
1702
+ ] });
1703
+ }
1704
+ function OthersSection({ state, dispatch }) {
1705
+ const tokens = useTokens();
1706
+ const density = state.spacing?.density ?? 0.5;
1707
+ const intensity = state.roundness?.intensity ?? 0.5;
1708
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
1709
+ /* @__PURE__ */ jsxs("div", { children: [
1710
+ /* @__PURE__ */ jsx(
1711
+ "div",
1712
+ {
1713
+ style: {
1714
+ fontSize: 11,
1715
+ fontWeight: 600,
1716
+ color: srgbToHex(tokens.textSecondary.srgb),
1717
+ textTransform: "uppercase",
1718
+ letterSpacing: 0.5,
1719
+ marginBottom: 8
1720
+ },
1721
+ children: "Spacing"
1722
+ }
1723
+ ),
1724
+ /* @__PURE__ */ jsx(
1725
+ Slider,
1726
+ {
1727
+ value: Math.round(density * 100),
1728
+ onValueChange: (v) => dispatch({ type: "SET_SPACING_DENSITY", density: v / 100 }),
1729
+ min: 0,
1730
+ max: 100,
1731
+ label: "Density",
1732
+ showValue: true
1733
+ }
1734
+ )
1735
+ ] }),
1736
+ /* @__PURE__ */ jsxs("div", { children: [
1737
+ /* @__PURE__ */ jsx(
1738
+ "div",
1739
+ {
1740
+ style: {
1741
+ fontSize: 11,
1742
+ fontWeight: 600,
1743
+ color: srgbToHex(tokens.textSecondary.srgb),
1744
+ textTransform: "uppercase",
1745
+ letterSpacing: 0.5,
1746
+ marginBottom: 8
1747
+ },
1748
+ children: "Roundness"
1749
+ }
1750
+ ),
1751
+ /* @__PURE__ */ jsx(
1752
+ Slider,
1753
+ {
1754
+ value: Math.round(intensity * 100),
1755
+ onValueChange: (v) => dispatch({ type: "SET_ROUNDNESS_INTENSITY", intensity: v / 100 }),
1756
+ min: 0,
1757
+ max: 100,
1758
+ label: "Intensity",
1759
+ showValue: true
1760
+ }
1761
+ )
1762
+ ] })
1763
+ ] });
1764
+ }
1765
+ function PresetSelector({
1766
+ presets,
1767
+ activePresetId,
1768
+ publishedPresetId,
1769
+ onSwitchPreset,
1770
+ onCreatePreset,
1771
+ onRenamePreset,
1772
+ onDeletePreset,
1773
+ onDuplicatePreset
1774
+ }) {
1775
+ const tokens = useTokens();
1776
+ const [isOpen, setIsOpen] = useState(false);
1777
+ const [renamingId, setRenamingId] = useState(null);
1778
+ const [renameValue, setRenameValue] = useState("");
1779
+ const [menuOpenId, setMenuOpenId] = useState(null);
1780
+ const [hoveredId, setHoveredId] = useState(null);
1781
+ const [hoveredAction, setHoveredAction] = useState(null);
1782
+ const dropdownRef = useRef(null);
1783
+ const renameInputRef = useRef(null);
1784
+ const activePreset = presets.find((p) => p.id === activePresetId);
1785
+ const borderColor = srgbToHex(tokens.border.srgb);
1786
+ const bgColor = srgbToHex(tokens.background.srgb);
1787
+ const textPrimary = srgbToHex(tokens.textPrimary.srgb);
1788
+ const textSecondary = srgbToHex(tokens.textSecondary.srgb);
1789
+ const interactiveColor = srgbToHex(tokens.interactive.srgb);
1790
+ const warningColor = srgbToHex(tokens.warning.srgb);
1791
+ const errorColor = srgbToHex(tokens.error.srgb);
1792
+ const hoverBg = `${borderColor}18`;
1793
+ const activeBg = `${interactiveColor}14`;
1794
+ useEffect(() => {
1795
+ if (!isOpen) return;
1796
+ const handleClickOutside = (e) => {
1797
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
1798
+ setIsOpen(false);
1799
+ setMenuOpenId(null);
1800
+ setRenamingId(null);
1801
+ }
1802
+ };
1803
+ document.addEventListener("mousedown", handleClickOutside);
1804
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1805
+ }, [isOpen]);
1806
+ useEffect(() => {
1807
+ if (renamingId && renameInputRef.current) {
1808
+ renameInputRef.current.focus();
1809
+ renameInputRef.current.select();
1810
+ }
1811
+ }, [renamingId]);
1812
+ const handleCreate = useCallback(async () => {
1813
+ const name = `Preset ${presets.length + 1}`;
1814
+ const newId = await onCreatePreset(name);
1815
+ onSwitchPreset(newId);
1816
+ setIsOpen(false);
1817
+ }, [presets.length, onCreatePreset, onSwitchPreset]);
1818
+ const handleStartRename = useCallback(
1819
+ (presetId, currentName) => {
1820
+ setRenamingId(presetId);
1821
+ setRenameValue(currentName);
1822
+ setMenuOpenId(null);
1823
+ },
1824
+ []
1825
+ );
1826
+ const handleCommitRename = useCallback(() => {
1827
+ if (renamingId && renameValue.trim()) {
1828
+ onRenamePreset(renamingId, renameValue.trim());
1829
+ }
1830
+ setRenamingId(null);
1831
+ }, [renamingId, renameValue, onRenamePreset]);
1832
+ const handleDelete = useCallback(
1833
+ async (presetId) => {
1834
+ if (presets.length <= 1) return;
1835
+ if (window.confirm("Delete this preset? This cannot be undone.")) {
1836
+ await onDeletePreset(presetId);
1837
+ }
1838
+ setMenuOpenId(null);
1839
+ },
1840
+ [presets.length, onDeletePreset]
1841
+ );
1842
+ const handleDuplicate = useCallback(
1843
+ async (presetId, sourceName) => {
1844
+ const newId = await onDuplicatePreset(presetId, `${sourceName} (copy)`);
1845
+ onSwitchPreset(newId);
1846
+ setMenuOpenId(null);
1847
+ setIsOpen(false);
1848
+ },
1849
+ [onDuplicatePreset, onSwitchPreset]
1850
+ );
1851
+ return /* @__PURE__ */ jsxs("div", { ref: dropdownRef, style: { position: "relative" }, children: [
1852
+ /* @__PURE__ */ jsxs(
1853
+ "button",
1854
+ {
1855
+ onClick: () => setIsOpen(!isOpen),
1856
+ style: {
1857
+ display: "flex",
1858
+ alignItems: "center",
1859
+ gap: 6,
1860
+ padding: "4px 10px",
1861
+ borderRadius: 6,
1862
+ border: `1px solid ${borderColor}`,
1863
+ backgroundColor: "transparent",
1864
+ color: textPrimary,
1865
+ fontSize: 12,
1866
+ fontWeight: 500,
1867
+ cursor: "pointer",
1868
+ maxWidth: 160
1869
+ },
1870
+ children: [
1871
+ /* @__PURE__ */ jsx(
1872
+ "span",
1873
+ {
1874
+ style: {
1875
+ overflow: "hidden",
1876
+ textOverflow: "ellipsis",
1877
+ whiteSpace: "nowrap"
1878
+ },
1879
+ children: activePreset?.name ?? "Default"
1880
+ }
1881
+ ),
1882
+ /* @__PURE__ */ jsx(
1883
+ "svg",
1884
+ {
1885
+ width: 10,
1886
+ height: 10,
1887
+ viewBox: "0 0 24 24",
1888
+ fill: "none",
1889
+ stroke: "currentColor",
1890
+ strokeWidth: 2,
1891
+ style: {
1892
+ transform: isOpen ? "rotate(180deg)" : "none",
1893
+ transition: "transform 150ms ease",
1894
+ flexShrink: 0
1895
+ },
1896
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
1897
+ }
1898
+ )
1899
+ ]
1900
+ }
1901
+ ),
1902
+ isOpen && /* @__PURE__ */ jsxs(
1903
+ "div",
1904
+ {
1905
+ style: {
1906
+ position: "absolute",
1907
+ top: "calc(100% + 4px)",
1908
+ left: 0,
1909
+ width: 260,
1910
+ backgroundColor: bgColor,
1911
+ border: `1px solid ${borderColor}`,
1912
+ borderRadius: 8,
1913
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
1914
+ zIndex: 100,
1915
+ overflow: "hidden"
1916
+ },
1917
+ children: [
1918
+ /* @__PURE__ */ jsx("div", { style: { maxHeight: 240, overflowY: "auto", padding: "4px 0" }, children: presets.map((preset) => {
1919
+ const isActive = preset.id === activePresetId;
1920
+ const isPublishedPreset = preset.id === publishedPresetId;
1921
+ const hasChanges = presetHasUnpublishedChanges(preset);
1922
+ const isHovered = hoveredId === preset.id;
1923
+ const isRenaming = renamingId === preset.id;
1924
+ const isMenuShown = menuOpenId === preset.id;
1925
+ return /* @__PURE__ */ jsxs(
1926
+ "div",
1927
+ {
1928
+ onMouseEnter: () => setHoveredId(preset.id),
1929
+ onMouseLeave: () => setHoveredId(null),
1930
+ style: {
1931
+ display: "flex",
1932
+ alignItems: "center",
1933
+ padding: "6px 12px",
1934
+ backgroundColor: isActive ? activeBg : isHovered ? hoverBg : "transparent",
1935
+ cursor: isRenaming ? "default" : "pointer",
1936
+ transition: "background-color 100ms ease",
1937
+ position: "relative"
1938
+ },
1939
+ children: [
1940
+ isRenaming ? /* @__PURE__ */ jsx(
1941
+ "input",
1942
+ {
1943
+ ref: renameInputRef,
1944
+ value: renameValue,
1945
+ onChange: (e) => setRenameValue(e.target.value),
1946
+ onBlur: handleCommitRename,
1947
+ onKeyDown: (e) => {
1948
+ if (e.key === "Enter") handleCommitRename();
1949
+ if (e.key === "Escape") setRenamingId(null);
1950
+ },
1951
+ style: {
1952
+ flex: 1,
1953
+ fontSize: 13,
1954
+ padding: "2px 6px",
1955
+ border: `1px solid ${interactiveColor}`,
1956
+ borderRadius: 4,
1957
+ backgroundColor: bgColor,
1958
+ color: textPrimary,
1959
+ outline: "none"
1960
+ }
1961
+ }
1962
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1963
+ /* @__PURE__ */ jsxs(
1964
+ "div",
1965
+ {
1966
+ onClick: () => {
1967
+ onSwitchPreset(preset.id);
1968
+ setIsOpen(false);
1969
+ },
1970
+ style: {
1971
+ flex: 1,
1972
+ display: "flex",
1973
+ alignItems: "center",
1974
+ gap: 6,
1975
+ minWidth: 0
1976
+ },
1977
+ children: [
1978
+ /* @__PURE__ */ jsx(
1979
+ "span",
1980
+ {
1981
+ style: {
1982
+ fontSize: 13,
1983
+ fontWeight: isActive ? 600 : 400,
1984
+ color: textPrimary,
1985
+ overflow: "hidden",
1986
+ textOverflow: "ellipsis",
1987
+ whiteSpace: "nowrap"
1988
+ },
1989
+ children: preset.name
1990
+ }
1991
+ ),
1992
+ hasChanges && /* @__PURE__ */ jsx(
1993
+ "span",
1994
+ {
1995
+ title: "Unpublished changes",
1996
+ style: {
1997
+ width: 6,
1998
+ height: 6,
1999
+ borderRadius: "50%",
2000
+ backgroundColor: warningColor,
2001
+ flexShrink: 0
2002
+ }
2003
+ }
2004
+ ),
2005
+ isPublishedPreset && /* @__PURE__ */ jsx(
2006
+ "span",
2007
+ {
2008
+ style: {
2009
+ fontSize: 10,
2010
+ fontWeight: 600,
2011
+ color: interactiveColor,
2012
+ padding: "1px 4px",
2013
+ borderRadius: 3,
2014
+ border: `1px solid ${interactiveColor}`,
2015
+ flexShrink: 0,
2016
+ lineHeight: "14px"
2017
+ },
2018
+ children: "API"
2019
+ }
2020
+ )
2021
+ ]
2022
+ }
2023
+ ),
2024
+ (isHovered || isMenuShown) && /* @__PURE__ */ jsx(
2025
+ "button",
2026
+ {
2027
+ onClick: (e) => {
2028
+ e.stopPropagation();
2029
+ setMenuOpenId(isMenuShown ? null : preset.id);
2030
+ },
2031
+ style: {
2032
+ display: "flex",
2033
+ alignItems: "center",
2034
+ justifyContent: "center",
2035
+ width: 24,
2036
+ height: 24,
2037
+ border: "none",
2038
+ background: "none",
2039
+ color: textSecondary,
2040
+ cursor: "pointer",
2041
+ borderRadius: 4,
2042
+ flexShrink: 0
2043
+ },
2044
+ children: /* @__PURE__ */ jsxs(
2045
+ "svg",
2046
+ {
2047
+ width: 14,
2048
+ height: 14,
2049
+ viewBox: "0 0 24 24",
2050
+ fill: "currentColor",
2051
+ children: [
2052
+ /* @__PURE__ */ jsx("circle", { cx: 12, cy: 5, r: 2 }),
2053
+ /* @__PURE__ */ jsx("circle", { cx: 12, cy: 12, r: 2 }),
2054
+ /* @__PURE__ */ jsx("circle", { cx: 12, cy: 19, r: 2 })
2055
+ ]
2056
+ }
2057
+ )
2058
+ }
2059
+ )
2060
+ ] }),
2061
+ isMenuShown && !isRenaming && /* @__PURE__ */ jsxs(
2062
+ "div",
2063
+ {
2064
+ style: {
2065
+ position: "absolute",
2066
+ top: 0,
2067
+ right: -140,
2068
+ width: 130,
2069
+ backgroundColor: bgColor,
2070
+ border: `1px solid ${borderColor}`,
2071
+ borderRadius: 6,
2072
+ boxShadow: "0 2px 8px rgba(0,0,0,0.12)",
2073
+ zIndex: 101,
2074
+ overflow: "hidden"
2075
+ },
2076
+ children: [
2077
+ /* @__PURE__ */ jsx(
2078
+ "button",
2079
+ {
2080
+ onClick: (e) => {
2081
+ e.stopPropagation();
2082
+ handleStartRename(preset.id, preset.name);
2083
+ },
2084
+ onMouseEnter: () => setHoveredAction("rename"),
2085
+ onMouseLeave: () => setHoveredAction(null),
2086
+ style: {
2087
+ display: "block",
2088
+ width: "100%",
2089
+ padding: "8px 12px",
2090
+ border: "none",
2091
+ backgroundColor: hoveredAction === "rename" ? hoverBg : "transparent",
2092
+ color: textPrimary,
2093
+ fontSize: 12,
2094
+ textAlign: "left",
2095
+ cursor: "pointer"
2096
+ },
2097
+ children: "Rename"
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ jsx(
2101
+ "button",
2102
+ {
2103
+ onClick: (e) => {
2104
+ e.stopPropagation();
2105
+ handleDuplicate(preset.id, preset.name);
2106
+ },
2107
+ onMouseEnter: () => setHoveredAction("duplicate"),
2108
+ onMouseLeave: () => setHoveredAction(null),
2109
+ style: {
2110
+ display: "block",
2111
+ width: "100%",
2112
+ padding: "8px 12px",
2113
+ border: "none",
2114
+ backgroundColor: hoveredAction === "duplicate" ? hoverBg : "transparent",
2115
+ color: textPrimary,
2116
+ fontSize: 12,
2117
+ textAlign: "left",
2118
+ cursor: "pointer"
2119
+ },
2120
+ children: "Duplicate"
2121
+ }
2122
+ ),
2123
+ /* @__PURE__ */ jsx(
2124
+ "button",
2125
+ {
2126
+ onClick: (e) => {
2127
+ e.stopPropagation();
2128
+ handleDelete(preset.id);
2129
+ },
2130
+ onMouseEnter: () => setHoveredAction("delete"),
2131
+ onMouseLeave: () => setHoveredAction(null),
2132
+ disabled: presets.length <= 1,
2133
+ style: {
2134
+ display: "block",
2135
+ width: "100%",
2136
+ padding: "8px 12px",
2137
+ border: "none",
2138
+ backgroundColor: hoveredAction === "delete" ? hoverBg : "transparent",
2139
+ color: presets.length <= 1 ? textSecondary : errorColor,
2140
+ fontSize: 12,
2141
+ textAlign: "left",
2142
+ cursor: presets.length <= 1 ? "not-allowed" : "pointer",
2143
+ opacity: presets.length <= 1 ? 0.5 : 1
2144
+ },
2145
+ children: "Delete"
2146
+ }
2147
+ )
2148
+ ]
2149
+ }
2150
+ )
2151
+ ]
2152
+ },
2153
+ preset.id
2154
+ );
2155
+ }) }),
2156
+ /* @__PURE__ */ jsxs(
2157
+ "button",
2158
+ {
2159
+ onClick: handleCreate,
2160
+ onMouseEnter: () => setHoveredAction("create"),
2161
+ onMouseLeave: () => setHoveredAction(null),
2162
+ style: {
2163
+ display: "flex",
2164
+ alignItems: "center",
2165
+ gap: 8,
2166
+ width: "100%",
2167
+ padding: "10px 12px",
2168
+ border: "none",
2169
+ borderTop: `1px solid ${borderColor}`,
2170
+ backgroundColor: hoveredAction === "create" ? hoverBg : "transparent",
2171
+ color: textSecondary,
2172
+ fontSize: 13,
2173
+ cursor: "pointer"
2174
+ },
2175
+ children: [
2176
+ /* @__PURE__ */ jsxs(
2177
+ "svg",
2178
+ {
2179
+ width: 14,
2180
+ height: 14,
2181
+ viewBox: "0 0 24 24",
2182
+ fill: "none",
2183
+ stroke: "currentColor",
2184
+ strokeWidth: 2,
2185
+ children: [
2186
+ /* @__PURE__ */ jsx("line", { x1: 12, y1: 5, x2: 12, y2: 19 }),
2187
+ /* @__PURE__ */ jsx("line", { x1: 5, y1: 12, x2: 19, y2: 12 })
2188
+ ]
2189
+ }
2190
+ ),
2191
+ "New preset"
2192
+ ]
2193
+ }
2194
+ )
2195
+ ]
2196
+ }
2197
+ )
2198
+ ] });
2199
+ }
2200
+ var SIDEBAR_WIDTH2 = 360;
2201
+ var ACCORDION_SECTIONS = [
2202
+ { id: "dynamic-range", label: "Dynamic Range" },
2203
+ { id: "colors", label: "Colors" },
2204
+ { id: "fonts", label: "Fonts" },
2205
+ { id: "icons", label: "Icons" },
2206
+ { id: "others", label: "Others" }
2207
+ ];
2208
+ function SectionIcon({ id }) {
2209
+ const props = {
2210
+ width: 16,
2211
+ height: 16,
2212
+ viewBox: "0 0 24 24",
2213
+ fill: "none",
2214
+ stroke: "currentColor",
2215
+ strokeWidth: 2,
2216
+ strokeLinecap: "round",
2217
+ strokeLinejoin: "round"
2218
+ };
2219
+ switch (id) {
2220
+ case "dynamic-range":
2221
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
2222
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "5" }),
2223
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }),
2224
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }),
2225
+ /* @__PURE__ */ jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }),
2226
+ /* @__PURE__ */ jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }),
2227
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }),
2228
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }),
2229
+ /* @__PURE__ */ jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }),
2230
+ /* @__PURE__ */ jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })
2231
+ ] });
2232
+ case "colors":
2233
+ return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx("path", { d: "M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" }) });
2234
+ case "fonts":
2235
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
2236
+ /* @__PURE__ */ jsx("polyline", { points: "4 7 4 4 20 4 20 7" }),
2237
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "20", x2: "15", y2: "20" }),
2238
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "4", x2: "12", y2: "20" })
2239
+ ] });
2240
+ case "icons":
2241
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
2242
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7" }),
2243
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "3", width: "7", height: "7" }),
2244
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "14", width: "7", height: "7" }),
2245
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "14", width: "7", height: "7" })
2246
+ ] });
2247
+ case "others":
2248
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
2249
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "21", x2: "4", y2: "14" }),
2250
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "10", x2: "4", y2: "3" }),
2251
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "21", x2: "12", y2: "12" }),
2252
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12", y2: "3" }),
2253
+ /* @__PURE__ */ jsx("line", { x1: "20", y1: "21", x2: "20", y2: "16" }),
2254
+ /* @__PURE__ */ jsx("line", { x1: "20", y1: "12", x2: "20", y2: "3" }),
2255
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "14", x2: "7", y2: "14" }),
2256
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "8", x2: "15", y2: "8" }),
2257
+ /* @__PURE__ */ jsx("line", { x1: "17", y1: "16", x2: "23", y2: "16" })
2258
+ ] });
2259
+ default:
2260
+ return null;
2261
+ }
2262
+ }
2263
+ function Sidebar({
2264
+ state,
2265
+ dispatch,
2266
+ previewColors,
2267
+ isDirty,
2268
+ onRevert,
2269
+ presets,
2270
+ activePresetId,
2271
+ publishedPresetId,
2272
+ onSwitchPreset,
2273
+ onCreatePreset,
2274
+ onRenamePreset,
2275
+ onDeletePreset,
2276
+ onDuplicatePreset,
2277
+ colorMode,
2278
+ onColorModeChange
2279
+ }) {
2280
+ const tokens = useTokens();
2281
+ const [openSections, setOpenSections] = useState(
2282
+ /* @__PURE__ */ new Set(["dynamic-range", "colors"])
2283
+ );
2284
+ const [hoveredSectionId, setHoveredSectionId] = useState(null);
2285
+ const borderColor = srgbToHex(tokens.border.srgb);
2286
+ const bgColor = srgbToHex(tokens.background.srgb);
2287
+ const hoverBg = `${borderColor}10`;
2288
+ const toggleSection = (id) => {
2289
+ setOpenSections((prev) => {
2290
+ const next = new Set(prev);
2291
+ if (next.has(id)) next.delete(id);
2292
+ else next.add(id);
2293
+ return next;
2294
+ });
2295
+ };
2296
+ const renderSectionContent = (sectionId) => {
2297
+ switch (sectionId) {
2298
+ case "dynamic-range":
2299
+ return /* @__PURE__ */ jsx(DynamicRangeSection, { state, dispatch });
2300
+ case "colors":
2301
+ return /* @__PURE__ */ jsx(
2302
+ ColorsSection,
2303
+ {
2304
+ state,
2305
+ dispatch,
2306
+ previewColors,
2307
+ colorMode,
2308
+ onColorModeChange
2309
+ }
2310
+ );
2311
+ case "icons":
2312
+ return /* @__PURE__ */ jsx(IconsSection, { state, dispatch });
2313
+ case "fonts":
2314
+ return /* @__PURE__ */ jsx(FontsSection, { state, dispatch });
2315
+ case "others":
2316
+ return /* @__PURE__ */ jsx(OthersSection, { state, dispatch });
2317
+ default:
2318
+ return null;
2319
+ }
2320
+ };
2321
+ return /* @__PURE__ */ jsxs(
2322
+ "div",
2323
+ {
2324
+ style: {
2325
+ width: SIDEBAR_WIDTH2,
2326
+ flexShrink: 0,
2327
+ display: "flex",
2328
+ flexDirection: "column",
2329
+ height: "100vh",
2330
+ borderLeft: `1px solid ${borderColor}`,
2331
+ backgroundColor: bgColor
2332
+ },
2333
+ children: [
2334
+ /* @__PURE__ */ jsxs(
2335
+ "div",
2336
+ {
2337
+ style: {
2338
+ flexShrink: 0,
2339
+ padding: "16px 20px",
2340
+ borderBottom: `1px solid ${borderColor}`,
2341
+ display: "flex",
2342
+ alignItems: "center",
2343
+ justifyContent: "space-between"
2344
+ },
2345
+ children: [
2346
+ /* @__PURE__ */ jsx(
2347
+ "span",
2348
+ {
2349
+ style: {
2350
+ fontSize: 16,
2351
+ fontWeight: 700,
2352
+ color: srgbToHex(tokens.textPrimary.srgb)
2353
+ },
2354
+ children: "newtone"
2355
+ }
2356
+ ),
2357
+ /* @__PURE__ */ jsx(
2358
+ PresetSelector,
2359
+ {
2360
+ presets,
2361
+ activePresetId,
2362
+ publishedPresetId,
2363
+ onSwitchPreset,
2364
+ onCreatePreset,
2365
+ onRenamePreset,
2366
+ onDeletePreset,
2367
+ onDuplicatePreset
2368
+ }
2369
+ )
2370
+ ]
2371
+ }
2372
+ ),
2373
+ /* @__PURE__ */ jsx(
2374
+ "div",
2375
+ {
2376
+ style: {
2377
+ flex: 1,
2378
+ overflowY: "auto",
2379
+ overflowX: "hidden"
2380
+ },
2381
+ children: ACCORDION_SECTIONS.map((section) => {
2382
+ const isOpen = openSections.has(section.id);
2383
+ const isHovered = hoveredSectionId === section.id;
2384
+ return /* @__PURE__ */ jsxs("div", { children: [
2385
+ /* @__PURE__ */ jsxs(
2386
+ "button",
2387
+ {
2388
+ onClick: () => toggleSection(section.id),
2389
+ onMouseEnter: () => setHoveredSectionId(section.id),
2390
+ onMouseLeave: () => setHoveredSectionId(null),
2391
+ "aria-expanded": isOpen,
2392
+ "aria-controls": `section-${section.id}`,
2393
+ style: {
2394
+ display: "flex",
2395
+ alignItems: "center",
2396
+ justifyContent: "space-between",
2397
+ width: "100%",
2398
+ padding: "12px 20px",
2399
+ border: "none",
2400
+ borderBottom: `1px solid ${borderColor}`,
2401
+ background: isHovered ? hoverBg : "none",
2402
+ cursor: "pointer",
2403
+ fontSize: 14,
2404
+ fontWeight: 500,
2405
+ color: srgbToHex(tokens.textPrimary.srgb),
2406
+ transition: "background-color 100ms ease"
2407
+ },
2408
+ children: [
2409
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2410
+ /* @__PURE__ */ jsx(SectionIcon, { id: section.id }),
2411
+ section.label
2412
+ ] }),
2413
+ /* @__PURE__ */ jsx(
2414
+ "svg",
2415
+ {
2416
+ width: 12,
2417
+ height: 12,
2418
+ viewBox: "0 0 24 24",
2419
+ fill: "none",
2420
+ stroke: "currentColor",
2421
+ strokeWidth: 2,
2422
+ style: {
2423
+ transform: isOpen ? "rotate(180deg)" : "none",
2424
+ transition: "transform 150ms ease"
2425
+ },
2426
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
2427
+ }
2428
+ )
2429
+ ]
2430
+ }
2431
+ ),
2432
+ isOpen && /* @__PURE__ */ jsx(
2433
+ "div",
2434
+ {
2435
+ id: `section-${section.id}`,
2436
+ role: "region",
2437
+ "aria-label": section.label,
2438
+ style: {
2439
+ padding: "16px 20px",
2440
+ borderBottom: `1px solid ${borderColor}`
2441
+ },
2442
+ children: renderSectionContent(section.id)
2443
+ }
2444
+ )
2445
+ ] }, section.id);
2446
+ })
2447
+ }
2448
+ ),
2449
+ /* @__PURE__ */ jsx(
2450
+ "div",
2451
+ {
2452
+ style: {
2453
+ flexShrink: 0,
2454
+ padding: "12px 20px",
2455
+ borderTop: `1px solid ${borderColor}`
2456
+ },
2457
+ children: /* @__PURE__ */ jsx(
2458
+ "button",
2459
+ {
2460
+ disabled: !isDirty,
2461
+ onClick: onRevert,
2462
+ "aria-label": "Revert all changes to the last saved version",
2463
+ style: {
2464
+ width: "100%",
2465
+ padding: "8px 16px",
2466
+ borderRadius: 6,
2467
+ border: `1px solid ${borderColor}`,
2468
+ backgroundColor: "transparent",
2469
+ color: isDirty ? srgbToHex(tokens.textPrimary.srgb) : srgbToHex(tokens.textSecondary.srgb),
2470
+ fontSize: 13,
2471
+ cursor: isDirty ? "pointer" : "not-allowed",
2472
+ opacity: isDirty ? 1 : 0.5
2473
+ },
2474
+ children: "Revert Changes"
2475
+ }
2476
+ )
2477
+ }
2478
+ )
2479
+ ]
2480
+ }
2481
+ );
2482
+ }
2483
+ var STATUS_LABEL = {
2484
+ saved: "Saved",
2485
+ saving: "Saving...",
2486
+ unsaved: "Unsaved changes",
2487
+ error: "Save failed"
2488
+ };
2489
+ function EditorHeader({
2490
+ saveStatus,
2491
+ isPublished,
2492
+ publishing,
2493
+ onPublish,
2494
+ onRetry,
2495
+ headerSlots
2496
+ }) {
2497
+ const tokens = useTokens();
2498
+ const borderColor = srgbToHex(tokens.border.srgb);
2499
+ const statusColor = {
2500
+ saved: srgbToHex(tokens.success.srgb),
2501
+ saving: srgbToHex(tokens.warning.srgb),
2502
+ unsaved: srgbToHex(tokens.textSecondary.srgb),
2503
+ error: srgbToHex(tokens.error.srgb)
2504
+ };
2505
+ return /* @__PURE__ */ jsxs(
2506
+ "div",
2507
+ {
2508
+ style: {
2509
+ display: "flex",
2510
+ alignItems: "center",
2511
+ justifyContent: "space-between",
2512
+ padding: "12px 24px",
2513
+ borderBottom: `1px solid ${borderColor}`,
2514
+ backgroundColor: srgbToHex(tokens.background.srgb),
2515
+ flexShrink: 0
2516
+ },
2517
+ children: [
2518
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: 16 }, children: headerSlots?.left }),
2519
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
2520
+ /* @__PURE__ */ jsx(
2521
+ "span",
2522
+ {
2523
+ style: {
2524
+ fontSize: 12,
2525
+ color: statusColor[saveStatus],
2526
+ fontWeight: 500
2527
+ },
2528
+ children: STATUS_LABEL[saveStatus]
2529
+ }
2530
+ ),
2531
+ saveStatus === "error" && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "refresh", onPress: onRetry, children: "Retry" }),
2532
+ /* @__PURE__ */ jsx(
2533
+ Button,
2534
+ {
2535
+ variant: "primary",
2536
+ size: "sm",
2537
+ icon: "publish",
2538
+ onPress: onPublish,
2539
+ disabled: isPublished || publishing,
2540
+ children: publishing ? "Publishing..." : isPublished ? "Published" : "Publish"
2541
+ }
2542
+ ),
2543
+ headerSlots?.right
2544
+ ] })
2545
+ ]
2546
+ }
2547
+ );
2548
+ }
2549
+ var THEME_CHIPS = [
2550
+ { id: "neutral", label: "Neutral" },
2551
+ { id: "primary", label: "Primary" },
2552
+ { id: "secondary", label: "Secondary" },
2553
+ { id: "strong", label: "Strong" }
2554
+ ];
2555
+ function ThemeBar({ activeTheme, onThemeChange }) {
2556
+ const tokens = useTokens();
2557
+ const [hoveredChipId, setHoveredChipId] = useState(null);
2558
+ const borderColor = srgbToHex(tokens.border.srgb);
2559
+ const interactiveColor = srgbToHex(tokens.interactive.srgb);
2560
+ return /* @__PURE__ */ jsx(
2561
+ "div",
2562
+ {
2563
+ style: {
2564
+ display: "flex",
2565
+ alignItems: "center",
2566
+ padding: "8px 24px",
2567
+ borderBottom: `1px solid ${borderColor}`,
2568
+ backgroundColor: srgbToHex(tokens.background.srgb),
2569
+ flexShrink: 0
2570
+ },
2571
+ children: /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8 }, role: "group", "aria-label": "Theme", children: THEME_CHIPS.map((chip) => {
2572
+ const isActive = chip.id === activeTheme;
2573
+ const isHovered = hoveredChipId === chip.id;
2574
+ return /* @__PURE__ */ jsx(
2575
+ "button",
2576
+ {
2577
+ onClick: () => onThemeChange(chip.id),
2578
+ onMouseEnter: () => setHoveredChipId(chip.id),
2579
+ onMouseLeave: () => setHoveredChipId(null),
2580
+ "aria-pressed": isActive,
2581
+ style: {
2582
+ padding: "4px 12px",
2583
+ borderRadius: 16,
2584
+ border: `1px solid ${srgbToHex(
2585
+ isActive ? tokens.interactive.srgb : tokens.border.srgb
2586
+ )}`,
2587
+ backgroundColor: isActive ? interactiveColor : isHovered ? `${interactiveColor}10` : "transparent",
2588
+ color: isActive ? "#fff" : srgbToHex(tokens.textPrimary.srgb),
2589
+ fontSize: 12,
2590
+ fontWeight: 500,
2591
+ cursor: "pointer",
2592
+ transition: "background-color 150ms ease"
2593
+ },
2594
+ children: chip.label
2595
+ },
2596
+ chip.id
2597
+ );
2598
+ }) })
2599
+ }
2600
+ );
2601
+ }
2602
+ var TOC_WIDTH = 220;
2603
+ function TableOfContents({
2604
+ activeView,
2605
+ selectedComponentId,
2606
+ onNavigate
2607
+ }) {
2608
+ const tokens = useTokens();
2609
+ const [hoveredId, setHoveredId] = useState(null);
2610
+ const borderColor = srgbToHex(tokens.border.srgb);
2611
+ const activeColor = srgbToHex(tokens.interactive.srgb);
2612
+ const textPrimary = srgbToHex(tokens.textPrimary.srgb);
2613
+ const textSecondary = srgbToHex(tokens.textSecondary.srgb);
2614
+ const hoverBg = `${borderColor}20`;
2615
+ const isOverviewActive = activeView.kind === "overview";
2616
+ return /* @__PURE__ */ jsxs(
2617
+ "nav",
2618
+ {
2619
+ "aria-label": "Component navigation",
2620
+ style: {
2621
+ width: TOC_WIDTH,
2622
+ flexShrink: 0,
2623
+ overflowY: "auto",
2624
+ borderRight: `1px solid ${borderColor}`,
2625
+ padding: "16px 0",
2626
+ backgroundColor: srgbToHex(tokens.background.srgb)
2627
+ },
2628
+ children: [
2629
+ /* @__PURE__ */ jsx(
2630
+ "button",
2631
+ {
2632
+ onClick: () => onNavigate({ kind: "overview" }),
2633
+ onMouseEnter: () => setHoveredId("overview"),
2634
+ onMouseLeave: () => setHoveredId(null),
2635
+ "aria-current": isOverviewActive ? "page" : void 0,
2636
+ style: {
2637
+ display: "block",
2638
+ width: "100%",
2639
+ padding: "6px 20px",
2640
+ border: "none",
2641
+ background: isOverviewActive ? `${activeColor}14` : hoveredId === "overview" ? hoverBg : "none",
2642
+ cursor: "pointer",
2643
+ textAlign: "left",
2644
+ fontSize: 13,
2645
+ fontWeight: isOverviewActive ? 600 : 400,
2646
+ color: isOverviewActive ? activeColor : textPrimary,
2647
+ transition: "background-color 100ms ease"
2648
+ },
2649
+ children: "Overview"
2650
+ }
2651
+ ),
2652
+ CATEGORIES.map((category) => {
2653
+ const components = getComponentsByCategory(category.id);
2654
+ const isCategoryActive = activeView.kind === "category" && activeView.categoryId === category.id;
2655
+ return /* @__PURE__ */ jsxs("div", { style: { marginTop: 16 }, children: [
2656
+ /* @__PURE__ */ jsx(
2657
+ "button",
2658
+ {
2659
+ onClick: () => onNavigate({ kind: "category", categoryId: category.id }),
2660
+ onMouseEnter: () => setHoveredId(`cat-${category.id}`),
2661
+ onMouseLeave: () => setHoveredId(null),
2662
+ "aria-current": isCategoryActive ? "page" : void 0,
2663
+ style: {
2664
+ display: "block",
2665
+ width: "100%",
2666
+ padding: "6px 20px",
2667
+ border: "none",
2668
+ background: isCategoryActive ? `${activeColor}14` : hoveredId === `cat-${category.id}` ? hoverBg : "none",
2669
+ cursor: "pointer",
2670
+ textAlign: "left",
2671
+ fontSize: 11,
2672
+ fontWeight: 600,
2673
+ color: isCategoryActive ? activeColor : textSecondary,
2674
+ textTransform: "uppercase",
2675
+ letterSpacing: 0.5,
2676
+ transition: "background-color 100ms ease"
2677
+ },
2678
+ children: category.name
2679
+ }
2680
+ ),
2681
+ components.map((comp) => {
2682
+ const isComponentActive = activeView.kind === "component" && activeView.componentId === comp.id || selectedComponentId === comp.id;
2683
+ return /* @__PURE__ */ jsx(
2684
+ "button",
2685
+ {
2686
+ onClick: () => onNavigate({ kind: "component", componentId: comp.id }),
2687
+ onMouseEnter: () => setHoveredId(comp.id),
2688
+ onMouseLeave: () => setHoveredId(null),
2689
+ "aria-current": isComponentActive ? "page" : void 0,
2690
+ style: {
2691
+ display: "block",
2692
+ width: "100%",
2693
+ padding: "4px 20px 4px 32px",
2694
+ border: "none",
2695
+ background: isComponentActive ? `${activeColor}14` : hoveredId === comp.id ? hoverBg : "none",
2696
+ cursor: "pointer",
2697
+ textAlign: "left",
2698
+ fontSize: 13,
2699
+ fontWeight: isComponentActive ? 600 : 400,
2700
+ color: isComponentActive ? activeColor : textPrimary,
2701
+ transition: "background-color 100ms ease"
2702
+ },
2703
+ children: comp.name
2704
+ },
2705
+ comp.id
2706
+ );
2707
+ })
2708
+ ] }, category.id);
2709
+ })
2710
+ ]
2711
+ }
2712
+ );
2713
+ }
2714
+ function StatefulToggle(props) {
2715
+ const [value, setValue] = useState(props.value);
2716
+ return /* @__PURE__ */ jsx(Toggle, { ...props, value, onValueChange: setValue });
2717
+ }
2718
+ function StatefulSlider(props) {
2719
+ const [value, setValue] = useState(props.value);
2720
+ return /* @__PURE__ */ jsx(Slider, { ...props, value, onValueChange: setValue });
2721
+ }
2722
+ function StatefulHueSlider(props) {
2723
+ const [value, setValue] = useState(props.value);
2724
+ return /* @__PURE__ */ jsx(HueSlider, { ...props, value, onValueChange: setValue });
2725
+ }
2726
+ function StatefulTextInput(props) {
2727
+ const [value, setValue] = useState(props.value ?? "");
2728
+ return /* @__PURE__ */ jsx(TextInput, { ...props, value, onChangeText: setValue });
2729
+ }
2730
+ function StatefulSelect(props) {
2731
+ const [value, setValue] = useState(props.value);
2732
+ return /* @__PURE__ */ jsx(Select, { ...props, value, onValueChange: setValue });
2733
+ }
2734
+ function CardPreview(props) {
2735
+ const tokens = useTokens();
2736
+ return /* @__PURE__ */ jsxs(Card, { ...props, style: { padding: 20, minWidth: 200 }, children: [
2737
+ /* @__PURE__ */ jsx(
2738
+ "div",
2739
+ {
2740
+ style: {
2741
+ fontSize: 14,
2742
+ fontWeight: 600,
2743
+ color: srgbToHex(tokens.textPrimary.srgb),
2744
+ marginBottom: 8
2745
+ },
2746
+ children: "Card Title"
2747
+ }
2748
+ ),
2749
+ /* @__PURE__ */ jsxs(
2750
+ "div",
2751
+ {
2752
+ style: {
2753
+ fontSize: 13,
2754
+ color: srgbToHex(tokens.textSecondary.srgb),
2755
+ lineHeight: 1.4
2756
+ },
2757
+ children: [
2758
+ "Sample card content at elevation ",
2759
+ String(props.elevation ?? 0),
2760
+ "."
2761
+ ]
2762
+ }
2763
+ )
2764
+ ] });
2765
+ }
2766
+ function ComponentRenderer({ componentId, props }) {
2767
+ const noop = useCallback(() => {
2768
+ }, []);
2769
+ switch (componentId) {
2770
+ case "button": {
2771
+ const icon = props.icon || void 0;
2772
+ return /* @__PURE__ */ jsx(
2773
+ Button,
2774
+ {
2775
+ variant: props.variant,
2776
+ size: props.size,
2777
+ icon,
2778
+ iconPosition: props.iconPosition,
2779
+ onPress: noop,
2780
+ children: "Button"
2781
+ }
2782
+ );
2783
+ }
2784
+ case "text-input":
2785
+ return /* @__PURE__ */ jsx(StatefulTextInput, { ...props });
2786
+ case "select":
2787
+ return /* @__PURE__ */ jsx(StatefulSelect, { ...props });
2788
+ case "toggle":
2789
+ return /* @__PURE__ */ jsx(StatefulToggle, { ...props });
2790
+ case "slider":
2791
+ return /* @__PURE__ */ jsx(StatefulSlider, { ...props });
2792
+ case "hue-slider":
2793
+ return /* @__PURE__ */ jsx(StatefulHueSlider, { ...props });
2794
+ case "card":
2795
+ return /* @__PURE__ */ jsx(CardPreview, { ...props });
2796
+ default:
2797
+ return null;
2798
+ }
2799
+ }
2800
+ function OverviewView({
2801
+ onNavigateToCategory,
2802
+ onNavigateToComponent
2803
+ }) {
2804
+ const tokens = useTokens();
2805
+ return /* @__PURE__ */ jsxs("div", { style: { padding: 32 }, children: [
2806
+ /* @__PURE__ */ jsx(
2807
+ "h2",
2808
+ {
2809
+ style: {
2810
+ fontSize: 22,
2811
+ fontWeight: 700,
2812
+ color: srgbToHex(tokens.textPrimary.srgb),
2813
+ margin: 0,
2814
+ marginBottom: 4
2815
+ },
2816
+ children: "Component Library"
2817
+ }
2818
+ ),
2819
+ /* @__PURE__ */ jsx(
2820
+ "p",
2821
+ {
2822
+ style: {
2823
+ fontSize: 14,
2824
+ color: srgbToHex(tokens.textSecondary.srgb),
2825
+ margin: 0,
2826
+ marginBottom: 32
2827
+ },
2828
+ children: "Browse components to see how your color system affects them."
2829
+ }
2830
+ ),
2831
+ CATEGORIES.map((category) => {
2832
+ const components = getComponentsByCategory(category.id);
2833
+ if (components.length === 0) return null;
2834
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: 32 }, children: [
2835
+ /* @__PURE__ */ jsxs(
2836
+ "button",
2837
+ {
2838
+ onClick: () => onNavigateToCategory(category.id),
2839
+ style: {
2840
+ background: "none",
2841
+ border: "none",
2842
+ cursor: "pointer",
2843
+ padding: 0,
2844
+ margin: 0,
2845
+ marginBottom: 16,
2846
+ display: "block"
2847
+ },
2848
+ children: [
2849
+ /* @__PURE__ */ jsx(
2850
+ "span",
2851
+ {
2852
+ style: {
2853
+ fontSize: 16,
2854
+ fontWeight: 600,
2855
+ color: srgbToHex(tokens.textPrimary.srgb)
2856
+ },
2857
+ children: category.name
2858
+ }
2859
+ ),
2860
+ /* @__PURE__ */ jsx(
2861
+ "span",
2862
+ {
2863
+ style: {
2864
+ fontSize: 13,
2865
+ color: srgbToHex(tokens.textSecondary.srgb),
2866
+ marginLeft: 8
2867
+ },
2868
+ children: category.description
2869
+ }
2870
+ )
2871
+ ]
2872
+ }
2873
+ ),
2874
+ /* @__PURE__ */ jsx(
2875
+ "div",
2876
+ {
2877
+ style: {
2878
+ display: "grid",
2879
+ gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
2880
+ gap: 16
2881
+ },
2882
+ children: components.map((component) => /* @__PURE__ */ jsx(
2883
+ ComponentCard,
2884
+ {
2885
+ componentId: component.id,
2886
+ name: component.name,
2887
+ description: component.description,
2888
+ defaultVariantProps: component.variants[0]?.props ?? {},
2889
+ onClick: () => onNavigateToComponent(component.id)
2890
+ },
2891
+ component.id
2892
+ ))
2893
+ }
2894
+ )
2895
+ ] }, category.id);
2896
+ })
2897
+ ] });
2898
+ }
2899
+ function ComponentCard({
2900
+ componentId,
2901
+ name,
2902
+ description,
2903
+ defaultVariantProps,
2904
+ onClick
2905
+ }) {
2906
+ const tokens = useTokens();
2907
+ const { isHovered, hoverProps } = useHover();
2908
+ return /* @__PURE__ */ jsxs(
2909
+ "button",
2910
+ {
2911
+ onClick,
2912
+ ...hoverProps,
2913
+ style: {
2914
+ display: "flex",
2915
+ flexDirection: "column",
2916
+ padding: 20,
2917
+ borderRadius: 12,
2918
+ border: `1px solid ${srgbToHex(
2919
+ isHovered ? tokens.interactive.srgb : tokens.border.srgb
2920
+ )}`,
2921
+ backgroundColor: srgbToHex(tokens.backgroundElevated.srgb),
2922
+ cursor: "pointer",
2923
+ textAlign: "left",
2924
+ transform: isHovered ? "translateY(-1px)" : "none",
2925
+ boxShadow: isHovered ? "0 2px 8px rgba(0,0,0,0.08)" : "none",
2926
+ transition: "border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease"
2927
+ },
2928
+ children: [
2929
+ /* @__PURE__ */ jsx(
2930
+ "div",
2931
+ {
2932
+ style: {
2933
+ display: "flex",
2934
+ alignItems: "center",
2935
+ justifyContent: "center",
2936
+ padding: 16,
2937
+ marginBottom: 16,
2938
+ borderRadius: 8,
2939
+ backgroundColor: srgbToHex(tokens.background.srgb),
2940
+ minHeight: 60
2941
+ },
2942
+ children: /* @__PURE__ */ jsx(ComponentRenderer, { componentId, props: defaultVariantProps })
2943
+ }
2944
+ ),
2945
+ /* @__PURE__ */ jsx(
2946
+ "span",
2947
+ {
2948
+ style: {
2949
+ fontSize: 14,
2950
+ fontWeight: 600,
2951
+ color: srgbToHex(tokens.textPrimary.srgb),
2952
+ marginBottom: 4
2953
+ },
2954
+ children: name
2955
+ }
2956
+ ),
2957
+ /* @__PURE__ */ jsx(
2958
+ "span",
2959
+ {
2960
+ style: {
2961
+ fontSize: 12,
2962
+ color: srgbToHex(tokens.textSecondary.srgb),
2963
+ lineHeight: 1.4
2964
+ },
2965
+ children: description
2966
+ }
2967
+ )
2968
+ ]
2969
+ }
2970
+ );
2971
+ }
2972
+ function CategoryView({
2973
+ categoryId,
2974
+ onNavigateToComponent
2975
+ }) {
2976
+ const tokens = useTokens();
2977
+ const category = getCategory(categoryId);
2978
+ const components = getComponentsByCategory(categoryId);
2979
+ const [hoveredId, setHoveredId] = useState(null);
2980
+ if (!category) return null;
2981
+ return /* @__PURE__ */ jsxs("div", { style: { padding: 32 }, children: [
2982
+ /* @__PURE__ */ jsx(
2983
+ "h2",
2984
+ {
2985
+ style: {
2986
+ fontSize: 22,
2987
+ fontWeight: 700,
2988
+ color: srgbToHex(tokens.textPrimary.srgb),
2989
+ margin: 0,
2990
+ marginBottom: 4
2991
+ },
2992
+ children: category.name
2993
+ }
2994
+ ),
2995
+ /* @__PURE__ */ jsx(
2996
+ "p",
2997
+ {
2998
+ style: {
2999
+ fontSize: 14,
3000
+ color: srgbToHex(tokens.textSecondary.srgb),
3001
+ margin: 0,
3002
+ marginBottom: 32
3003
+ },
3004
+ children: category.description
3005
+ }
3006
+ ),
3007
+ /* @__PURE__ */ jsx(
3008
+ "div",
3009
+ {
3010
+ style: {
3011
+ display: "grid",
3012
+ gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
3013
+ gap: 20
3014
+ },
3015
+ children: components.map((component) => {
3016
+ const isHovered = hoveredId === component.id;
3017
+ return /* @__PURE__ */ jsxs(
3018
+ "button",
3019
+ {
3020
+ onClick: () => onNavigateToComponent(component.id),
3021
+ onMouseEnter: () => setHoveredId(component.id),
3022
+ onMouseLeave: () => setHoveredId(null),
3023
+ style: {
3024
+ display: "flex",
3025
+ flexDirection: "column",
3026
+ padding: 24,
3027
+ borderRadius: 12,
3028
+ border: `1px solid ${srgbToHex(
3029
+ isHovered ? tokens.interactive.srgb : tokens.border.srgb
3030
+ )}`,
3031
+ backgroundColor: srgbToHex(tokens.backgroundElevated.srgb),
3032
+ cursor: "pointer",
3033
+ textAlign: "left",
3034
+ transform: isHovered ? "translateY(-1px)" : "none",
3035
+ boxShadow: isHovered ? "0 2px 8px rgba(0,0,0,0.08)" : "none",
3036
+ transition: "border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease"
3037
+ },
3038
+ children: [
3039
+ /* @__PURE__ */ jsx(
3040
+ "div",
3041
+ {
3042
+ style: {
3043
+ display: "flex",
3044
+ alignItems: "center",
3045
+ justifyContent: "center",
3046
+ padding: 20,
3047
+ marginBottom: 16,
3048
+ borderRadius: 8,
3049
+ backgroundColor: srgbToHex(tokens.background.srgb),
3050
+ minHeight: 60
3051
+ },
3052
+ children: /* @__PURE__ */ jsx(
3053
+ ComponentRenderer,
3054
+ {
3055
+ componentId: component.id,
3056
+ props: component.variants[0]?.props ?? {}
3057
+ }
3058
+ )
3059
+ }
3060
+ ),
3061
+ /* @__PURE__ */ jsx(
3062
+ "span",
3063
+ {
3064
+ style: {
3065
+ fontSize: 15,
3066
+ fontWeight: 600,
3067
+ color: srgbToHex(tokens.textPrimary.srgb),
3068
+ marginBottom: 4
3069
+ },
3070
+ children: component.name
3071
+ }
3072
+ ),
3073
+ /* @__PURE__ */ jsx(
3074
+ "span",
3075
+ {
3076
+ style: {
3077
+ fontSize: 13,
3078
+ color: srgbToHex(tokens.textSecondary.srgb),
3079
+ lineHeight: 1.4
3080
+ },
3081
+ children: component.description
3082
+ }
3083
+ ),
3084
+ /* @__PURE__ */ jsxs(
3085
+ "span",
3086
+ {
3087
+ style: {
3088
+ fontSize: 12,
3089
+ color: srgbToHex(tokens.textSecondary.srgb),
3090
+ marginTop: 8
3091
+ },
3092
+ children: [
3093
+ component.variants.length,
3094
+ " variant",
3095
+ component.variants.length !== 1 ? "s" : ""
3096
+ ]
3097
+ }
3098
+ )
3099
+ ]
3100
+ },
3101
+ component.id
3102
+ );
3103
+ })
3104
+ }
3105
+ )
3106
+ ] });
3107
+ }
3108
+ function ComponentDetailView({
3109
+ componentId,
3110
+ selectedVariantId,
3111
+ propOverrides,
3112
+ onSelectVariant
3113
+ }) {
3114
+ const tokens = useTokens();
3115
+ const component = getComponent(componentId);
3116
+ const [hoveredId, setHoveredId] = useState(null);
3117
+ if (!component) return null;
3118
+ const interactiveColor = srgbToHex(tokens.interactive.srgb);
3119
+ return /* @__PURE__ */ jsxs("div", { style: { padding: 32 }, children: [
3120
+ /* @__PURE__ */ jsx(
3121
+ "h2",
3122
+ {
3123
+ style: {
3124
+ fontSize: 22,
3125
+ fontWeight: 700,
3126
+ color: srgbToHex(tokens.textPrimary.srgb),
3127
+ margin: 0,
3128
+ marginBottom: 4
3129
+ },
3130
+ children: component.name
3131
+ }
3132
+ ),
3133
+ /* @__PURE__ */ jsx(
3134
+ "p",
3135
+ {
3136
+ style: {
3137
+ fontSize: 14,
3138
+ color: srgbToHex(tokens.textSecondary.srgb),
3139
+ margin: 0,
3140
+ marginBottom: 32
3141
+ },
3142
+ children: component.description
3143
+ }
3144
+ ),
3145
+ /* @__PURE__ */ jsx(
3146
+ "div",
3147
+ {
3148
+ style: {
3149
+ display: "grid",
3150
+ gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
3151
+ gap: 16
3152
+ },
3153
+ children: component.variants.map((variant) => {
3154
+ const isSelected = selectedVariantId === variant.id;
3155
+ const isHovered = hoveredId === variant.id;
3156
+ const borderColor = isSelected ? interactiveColor : isHovered ? `${interactiveColor}66` : srgbToHex(tokens.border.srgb);
3157
+ return /* @__PURE__ */ jsxs(
3158
+ "button",
3159
+ {
3160
+ onClick: () => onSelectVariant(variant.id),
3161
+ onMouseEnter: () => setHoveredId(variant.id),
3162
+ onMouseLeave: () => setHoveredId(null),
3163
+ style: {
3164
+ display: "flex",
3165
+ flexDirection: "column",
3166
+ alignItems: "stretch",
3167
+ padding: 16,
3168
+ borderRadius: 12,
3169
+ border: `2px solid ${borderColor}`,
3170
+ backgroundColor: srgbToHex(tokens.backgroundElevated.srgb),
3171
+ cursor: "pointer",
3172
+ textAlign: "left",
3173
+ transition: "border-color 150ms ease"
3174
+ },
3175
+ children: [
3176
+ /* @__PURE__ */ jsx(
3177
+ "div",
3178
+ {
3179
+ style: {
3180
+ display: "flex",
3181
+ alignItems: "center",
3182
+ justifyContent: "center",
3183
+ padding: 20,
3184
+ marginBottom: 12,
3185
+ borderRadius: 8,
3186
+ backgroundColor: srgbToHex(tokens.background.srgb),
3187
+ minHeight: 56
3188
+ },
3189
+ children: /* @__PURE__ */ jsx(
3190
+ ComponentRenderer,
3191
+ {
3192
+ componentId,
3193
+ props: isSelected && propOverrides ? { ...variant.props, ...propOverrides } : variant.props
3194
+ }
3195
+ )
3196
+ }
3197
+ ),
3198
+ /* @__PURE__ */ jsx(
3199
+ "span",
3200
+ {
3201
+ style: {
3202
+ fontSize: 13,
3203
+ fontWeight: isSelected ? 600 : 500,
3204
+ color: isSelected ? interactiveColor : srgbToHex(tokens.textPrimary.srgb)
3205
+ },
3206
+ children: variant.label
3207
+ }
3208
+ )
3209
+ ]
3210
+ },
3211
+ variant.id
3212
+ );
3213
+ })
3214
+ }
3215
+ )
3216
+ ] });
3217
+ }
3218
+ function PreviewWindow({
3219
+ view,
3220
+ selectedVariantId,
3221
+ propOverrides,
3222
+ onNavigate,
3223
+ onSelectVariant
3224
+ }) {
3225
+ const tokens = useTokens();
3226
+ const handleNavigateToCategory = useCallback(
3227
+ (categoryId) => onNavigate({ kind: "category", categoryId }),
3228
+ [onNavigate]
3229
+ );
3230
+ const handleNavigateToComponent = useCallback(
3231
+ (componentId) => onNavigate({ kind: "component", componentId }),
3232
+ [onNavigate]
3233
+ );
3234
+ return /* @__PURE__ */ jsx(
3235
+ "div",
3236
+ {
3237
+ style: {
3238
+ display: "flex",
3239
+ flexDirection: "column",
3240
+ height: "100%",
3241
+ backgroundColor: srgbToHex(tokens.background.srgb)
3242
+ },
3243
+ children: /* @__PURE__ */ jsxs("div", { style: { flex: 1, overflowY: "auto" }, children: [
3244
+ view.kind === "overview" && /* @__PURE__ */ jsx(
3245
+ OverviewView,
3246
+ {
3247
+ onNavigateToCategory: handleNavigateToCategory,
3248
+ onNavigateToComponent: handleNavigateToComponent
3249
+ }
3250
+ ),
3251
+ view.kind === "category" && /* @__PURE__ */ jsx(
3252
+ CategoryView,
3253
+ {
3254
+ categoryId: view.categoryId,
3255
+ onNavigateToComponent: handleNavigateToComponent
3256
+ }
3257
+ ),
3258
+ view.kind === "component" && /* @__PURE__ */ jsx(
3259
+ ComponentDetailView,
3260
+ {
3261
+ componentId: view.componentId,
3262
+ selectedVariantId,
3263
+ propOverrides,
3264
+ onSelectVariant
3265
+ }
3266
+ )
3267
+ ] })
3268
+ }
3269
+ );
3270
+ }
3271
+ function CopyButton({ text }) {
3272
+ const [copied, setCopied] = useState(false);
3273
+ const handleCopy = useCallback(async () => {
3274
+ await navigator.clipboard.writeText(text);
3275
+ setCopied(true);
3276
+ setTimeout(() => setCopied(false), 2e3);
3277
+ }, [text]);
3278
+ return /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: copied ? "check" : "content_copy", onPress: handleCopy, children: copied ? "Copied!" : "Copy" });
3279
+ }
3280
+ function CodeBlock({
3281
+ code
3282
+ }) {
3283
+ const tokens = useTokens();
3284
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
3285
+ /* @__PURE__ */ jsx(
3286
+ "div",
3287
+ {
3288
+ style: {
3289
+ position: "absolute",
3290
+ top: 8,
3291
+ right: 8
3292
+ },
3293
+ children: /* @__PURE__ */ jsx(CopyButton, { text: code })
3294
+ }
3295
+ ),
3296
+ /* @__PURE__ */ jsx(
3297
+ "pre",
3298
+ {
3299
+ style: {
3300
+ backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
3301
+ border: `1px solid ${srgbToHex(tokens.border.srgb)}`,
3302
+ borderRadius: 8,
3303
+ padding: 16,
3304
+ paddingRight: 80,
3305
+ overflow: "auto",
3306
+ fontSize: 13,
3307
+ lineHeight: 1.5,
3308
+ fontFamily: "'SF Mono', 'Fira Code', 'Fira Mono', Menlo, monospace",
3309
+ color: srgbToHex(tokens.textPrimary.srgb),
3310
+ margin: 0
3311
+ },
3312
+ children: /* @__PURE__ */ jsx("code", { children: code })
3313
+ }
3314
+ )
3315
+ ] });
3316
+ }
3317
+ function RightSidebar({
3318
+ selection,
3319
+ propOverrides,
3320
+ onPropOverride,
3321
+ onResetOverrides,
3322
+ onClose,
3323
+ onScopeToComponent
3324
+ }) {
3325
+ const tokens = useTokens();
3326
+ const visible = selection !== null;
3327
+ const component = selection ? getComponent(selection.componentId) : null;
3328
+ const variant = selection?.scope === "variant" ? component?.variants.find((v) => v.id === selection.variantId) : null;
3329
+ const code = component ? generateComponentCode(component, propOverrides) : "";
3330
+ return /* @__PURE__ */ jsx(
3331
+ "aside",
3332
+ {
3333
+ "aria-label": "Component inspector",
3334
+ style: {
3335
+ position: "absolute",
3336
+ inset: 0,
3337
+ zIndex: 1,
3338
+ transform: visible ? "translateX(0)" : "translateX(100%)",
3339
+ opacity: visible ? 1 : 0,
3340
+ transition: "transform 200ms ease, opacity 150ms ease",
3341
+ display: "flex",
3342
+ flexDirection: "column",
3343
+ backgroundColor: srgbToHex(tokens.background.srgb),
3344
+ borderLeft: `1px solid ${srgbToHex(tokens.border.srgb)}`
3345
+ },
3346
+ children: selection && component && /* @__PURE__ */ jsxs(Fragment, { children: [
3347
+ /* @__PURE__ */ jsx(
3348
+ "div",
3349
+ {
3350
+ style: {
3351
+ display: "flex",
3352
+ alignItems: "center",
3353
+ justifyContent: "space-between",
3354
+ padding: "12px 16px",
3355
+ borderBottom: `1px solid ${srgbToHex(tokens.border.srgb)}`,
3356
+ flexShrink: 0
3357
+ },
3358
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, minWidth: 0 }, children: [
3359
+ /* @__PURE__ */ jsx(
3360
+ "button",
3361
+ {
3362
+ onClick: onClose,
3363
+ "aria-label": "Back to controls",
3364
+ style: {
3365
+ background: "none",
3366
+ border: "none",
3367
+ cursor: "pointer",
3368
+ padding: 4,
3369
+ color: srgbToHex(tokens.textSecondary.srgb),
3370
+ fontSize: 16,
3371
+ lineHeight: 1,
3372
+ flexShrink: 0,
3373
+ display: "flex",
3374
+ alignItems: "center"
3375
+ },
3376
+ children: /* @__PURE__ */ jsxs(
3377
+ "svg",
3378
+ {
3379
+ width: 16,
3380
+ height: 16,
3381
+ viewBox: "0 0 24 24",
3382
+ fill: "none",
3383
+ stroke: "currentColor",
3384
+ strokeWidth: 2,
3385
+ strokeLinecap: "round",
3386
+ strokeLinejoin: "round",
3387
+ children: [
3388
+ /* @__PURE__ */ jsx("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
3389
+ /* @__PURE__ */ jsx("polyline", { points: "12 19 5 12 12 5" })
3390
+ ]
3391
+ }
3392
+ )
3393
+ }
3394
+ ),
3395
+ selection.scope === "variant" && variant ? /* @__PURE__ */ jsxs(Fragment, { children: [
3396
+ /* @__PURE__ */ jsx(
3397
+ "button",
3398
+ {
3399
+ onClick: onScopeToComponent,
3400
+ "aria-label": `Back to ${component.name} overview`,
3401
+ style: {
3402
+ background: "none",
3403
+ border: "none",
3404
+ cursor: "pointer",
3405
+ padding: 0,
3406
+ fontSize: 14,
3407
+ fontWeight: 500,
3408
+ color: srgbToHex(tokens.interactive.srgb),
3409
+ whiteSpace: "nowrap"
3410
+ },
3411
+ children: component.name
3412
+ }
3413
+ ),
3414
+ /* @__PURE__ */ jsx(
3415
+ "span",
3416
+ {
3417
+ style: {
3418
+ fontSize: 13,
3419
+ color: srgbToHex(tokens.textSecondary.srgb)
3420
+ },
3421
+ children: "/"
3422
+ }
3423
+ ),
3424
+ /* @__PURE__ */ jsx(
3425
+ "span",
3426
+ {
3427
+ style: {
3428
+ fontSize: 14,
3429
+ fontWeight: 600,
3430
+ color: srgbToHex(tokens.textPrimary.srgb),
3431
+ whiteSpace: "nowrap",
3432
+ overflow: "hidden",
3433
+ textOverflow: "ellipsis"
3434
+ },
3435
+ children: variant.label
3436
+ }
3437
+ )
3438
+ ] }) : /* @__PURE__ */ jsx(
3439
+ "span",
3440
+ {
3441
+ style: {
3442
+ fontSize: 14,
3443
+ fontWeight: 600,
3444
+ color: srgbToHex(tokens.textPrimary.srgb)
3445
+ },
3446
+ children: component.name
3447
+ }
3448
+ )
3449
+ ] })
3450
+ }
3451
+ ),
3452
+ /* @__PURE__ */ jsxs(
3453
+ "div",
3454
+ {
3455
+ style: {
3456
+ flex: 1,
3457
+ overflowY: "auto",
3458
+ padding: 16
3459
+ },
3460
+ children: [
3461
+ /* @__PURE__ */ jsx(
3462
+ "h3",
3463
+ {
3464
+ style: {
3465
+ fontSize: 13,
3466
+ fontWeight: 600,
3467
+ color: srgbToHex(tokens.textSecondary.srgb),
3468
+ textTransform: "uppercase",
3469
+ letterSpacing: 0.5,
3470
+ margin: 0,
3471
+ marginBottom: 16
3472
+ },
3473
+ children: "Properties"
3474
+ }
3475
+ ),
3476
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: component.editableProps.map((prop) => /* @__PURE__ */ jsx(
3477
+ PropControl,
3478
+ {
3479
+ prop,
3480
+ value: propOverrides[prop.name] ?? prop.defaultValue,
3481
+ onChange: (value) => onPropOverride(prop.name, value)
3482
+ },
3483
+ prop.name
3484
+ )) }),
3485
+ selection.scope === "variant" && /* @__PURE__ */ jsx(
3486
+ "button",
3487
+ {
3488
+ onClick: onResetOverrides,
3489
+ "aria-label": "Reset properties to variant defaults",
3490
+ style: {
3491
+ marginTop: 20,
3492
+ width: "100%",
3493
+ padding: "8px 12px",
3494
+ borderRadius: 6,
3495
+ border: `1px solid ${srgbToHex(tokens.border.srgb)}`,
3496
+ backgroundColor: "transparent",
3497
+ color: srgbToHex(tokens.textSecondary.srgb),
3498
+ fontSize: 13,
3499
+ cursor: "pointer"
3500
+ },
3501
+ children: "Reset to variant defaults"
3502
+ }
3503
+ ),
3504
+ /* @__PURE__ */ jsx(
3505
+ "h3",
3506
+ {
3507
+ style: {
3508
+ fontSize: 13,
3509
+ fontWeight: 600,
3510
+ color: srgbToHex(tokens.textSecondary.srgb),
3511
+ textTransform: "uppercase",
3512
+ letterSpacing: 0.5,
3513
+ margin: 0,
3514
+ marginTop: 24,
3515
+ marginBottom: 12
3516
+ },
3517
+ children: "Code"
3518
+ }
3519
+ ),
3520
+ /* @__PURE__ */ jsx(CodeBlock, { code })
3521
+ ]
3522
+ }
3523
+ )
3524
+ ] })
3525
+ }
3526
+ );
3527
+ }
3528
+ function PropControl({
3529
+ prop,
3530
+ value,
3531
+ onChange
3532
+ }) {
3533
+ const tokens = useTokens();
3534
+ const handleToggleKeyDown = useCallback(
3535
+ (e) => {
3536
+ if (e.key === " " || e.key === "Enter") {
3537
+ e.preventDefault();
3538
+ onChange(!value);
3539
+ }
3540
+ },
3541
+ [onChange, value]
3542
+ );
3543
+ const inputStyle = {
3544
+ width: "100%",
3545
+ padding: "6px 10px",
3546
+ borderRadius: 6,
3547
+ border: `1px solid ${srgbToHex(tokens.border.srgb)}`,
3548
+ backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
3549
+ color: srgbToHex(tokens.textPrimary.srgb),
3550
+ fontSize: 13,
3551
+ fontFamily: "'SF Mono', 'Fira Code', Menlo, monospace",
3552
+ boxSizing: "border-box"
3553
+ };
3554
+ return /* @__PURE__ */ jsxs("div", { children: [
3555
+ /* @__PURE__ */ jsxs(
3556
+ "div",
3557
+ {
3558
+ style: {
3559
+ display: "flex",
3560
+ alignItems: "center",
3561
+ justifyContent: "space-between",
3562
+ marginBottom: 4
3563
+ },
3564
+ children: [
3565
+ /* @__PURE__ */ jsx(
3566
+ "span",
3567
+ {
3568
+ style: {
3569
+ fontSize: 12,
3570
+ fontWeight: 500,
3571
+ color: srgbToHex(tokens.textPrimary.srgb)
3572
+ },
3573
+ children: prop.label
3574
+ }
3575
+ ),
3576
+ /* @__PURE__ */ jsx(
3577
+ "span",
3578
+ {
3579
+ style: {
3580
+ fontSize: 11,
3581
+ color: srgbToHex(tokens.textSecondary.srgb),
3582
+ fontFamily: "'SF Mono', 'Fira Code', Menlo, monospace"
3583
+ },
3584
+ children: prop.control
3585
+ }
3586
+ )
3587
+ ]
3588
+ }
3589
+ ),
3590
+ prop.control === "select" && prop.options && /* @__PURE__ */ jsx(
3591
+ Select,
3592
+ {
3593
+ options: prop.options.map((o) => ({
3594
+ label: o.label,
3595
+ value: String(o.value)
3596
+ })),
3597
+ value: String(value),
3598
+ onValueChange: (v) => {
3599
+ const option = prop.options.find((o) => String(o.value) === v);
3600
+ if (option) onChange(option.value);
3601
+ },
3602
+ size: "sm"
3603
+ }
3604
+ ),
3605
+ prop.control === "text" && /* @__PURE__ */ jsx(
3606
+ "input",
3607
+ {
3608
+ type: "text",
3609
+ value: String(value),
3610
+ onChange: (e) => onChange(e.target.value),
3611
+ style: inputStyle
3612
+ }
3613
+ ),
3614
+ prop.control === "number" && /* @__PURE__ */ jsx(
3615
+ "input",
3616
+ {
3617
+ type: "number",
3618
+ value: Number(value),
3619
+ onChange: (e) => onChange(Number(e.target.value)),
3620
+ style: inputStyle
3621
+ }
3622
+ ),
3623
+ prop.control === "toggle" && /* @__PURE__ */ jsxs(
3624
+ "div",
3625
+ {
3626
+ role: "switch",
3627
+ "aria-checked": !!value,
3628
+ "aria-label": prop.label,
3629
+ tabIndex: 0,
3630
+ onClick: () => onChange(!value),
3631
+ onKeyDown: handleToggleKeyDown,
3632
+ style: {
3633
+ display: "inline-flex",
3634
+ alignItems: "center",
3635
+ gap: 8,
3636
+ cursor: "pointer"
3637
+ },
3638
+ children: [
3639
+ /* @__PURE__ */ jsx(
3640
+ "div",
3641
+ {
3642
+ style: {
3643
+ width: 36,
3644
+ height: 20,
3645
+ borderRadius: 10,
3646
+ backgroundColor: value ? srgbToHex(tokens.interactive.srgb) : srgbToHex(tokens.border.srgb),
3647
+ position: "relative",
3648
+ transition: "background-color 150ms ease",
3649
+ flexShrink: 0
3650
+ },
3651
+ children: /* @__PURE__ */ jsx(
3652
+ "div",
3653
+ {
3654
+ style: {
3655
+ width: 16,
3656
+ height: 16,
3657
+ borderRadius: 8,
3658
+ backgroundColor: "#fff",
3659
+ position: "absolute",
3660
+ top: 2,
3661
+ left: value ? 18 : 2,
3662
+ transition: "left 150ms ease"
3663
+ }
3664
+ }
3665
+ )
3666
+ }
3667
+ ),
3668
+ /* @__PURE__ */ jsx(
3669
+ "span",
3670
+ {
3671
+ style: {
3672
+ fontSize: 12,
3673
+ color: srgbToHex(tokens.textSecondary.srgb)
3674
+ },
3675
+ children: value ? "true" : "false"
3676
+ }
3677
+ )
3678
+ ]
3679
+ }
3680
+ )
3681
+ ] });
3682
+ }
3683
+ function Editor({
3684
+ initialState,
3685
+ initialIsPublished,
3686
+ initialPresets,
3687
+ initialActivePresetId,
3688
+ initialPublishedPresetId,
3689
+ defaultState,
3690
+ chromeThemeConfig,
3691
+ persistence,
3692
+ headerSlots,
3693
+ onNavigate,
3694
+ initialPreviewView
3695
+ }) {
3696
+ const editor = useEditorState({
3697
+ initialState,
3698
+ initialIsPublished,
3699
+ initialPresets,
3700
+ initialActivePresetId,
3701
+ initialPublishedPresetId,
3702
+ defaultState,
3703
+ persistence,
3704
+ onNavigate,
3705
+ initialPreviewView
3706
+ });
3707
+ return /* @__PURE__ */ jsx(NewtoneProvider, { config: chromeThemeConfig, children: /* @__PURE__ */ jsx(
3708
+ EditorShell,
3709
+ {
3710
+ sidebar: /* @__PURE__ */ jsx(
3711
+ Sidebar,
3712
+ {
3713
+ state: editor.configuratorState,
3714
+ dispatch: editor.dispatch,
3715
+ previewColors: editor.previewColors,
3716
+ isDirty: editor.isDirty,
3717
+ onRevert: editor.handleRevert,
3718
+ presets: editor.presets,
3719
+ activePresetId: editor.activePresetId,
3720
+ publishedPresetId: editor.publishedPresetId,
3721
+ onSwitchPreset: editor.switchPreset,
3722
+ onCreatePreset: editor.createPreset,
3723
+ onRenamePreset: editor.renamePreset,
3724
+ onDeletePreset: editor.deletePreset,
3725
+ onDuplicatePreset: editor.duplicatePreset,
3726
+ colorMode: editor.colorMode,
3727
+ onColorModeChange: editor.handleColorModeChange
3728
+ }
3729
+ ),
3730
+ navbar: /* @__PURE__ */ jsx(
3731
+ EditorHeader,
3732
+ {
3733
+ saveStatus: editor.saveStatus,
3734
+ isPublished: editor.isPublished,
3735
+ publishing: editor.publishing,
3736
+ onPublish: editor.handlePublish,
3737
+ onRetry: () => editor.saveDraft(editor.latestStateRef.current),
3738
+ headerSlots
3739
+ }
3740
+ ),
3741
+ content: /* @__PURE__ */ jsxs(
3742
+ "div",
3743
+ {
3744
+ style: {
3745
+ flex: 1,
3746
+ display: "flex",
3747
+ overflow: "hidden",
3748
+ minWidth: 0
3749
+ },
3750
+ children: [
3751
+ /* @__PURE__ */ jsx(
3752
+ TableOfContents,
3753
+ {
3754
+ activeView: editor.previewView,
3755
+ selectedComponentId: editor.selectedComponentId,
3756
+ onNavigate: editor.handlePreviewNavigate
3757
+ }
3758
+ ),
3759
+ /* @__PURE__ */ jsx(
3760
+ "div",
3761
+ {
3762
+ style: {
3763
+ flex: 1,
3764
+ display: "flex",
3765
+ flexDirection: "column",
3766
+ overflow: "hidden",
3767
+ minWidth: 0
3768
+ },
3769
+ children: /* @__PURE__ */ jsxs(
3770
+ NewtoneProvider,
3771
+ {
3772
+ config: editor.themeConfig,
3773
+ initialMode: editor.colorMode,
3774
+ initialTheme: editor.activeTheme,
3775
+ children: [
3776
+ /* @__PURE__ */ jsx(
3777
+ ThemeBar,
3778
+ {
3779
+ activeTheme: editor.activeTheme,
3780
+ onThemeChange: editor.handleThemeChange
3781
+ }
3782
+ ),
3783
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, overflowY: "auto", minWidth: 0 }, children: /* @__PURE__ */ jsx(
3784
+ PreviewWindow,
3785
+ {
3786
+ view: editor.previewView,
3787
+ selectedVariantId: editor.selectedVariantId,
3788
+ propOverrides: editor.propOverrides,
3789
+ onNavigate: editor.handlePreviewNavigate,
3790
+ onSelectVariant: editor.handleSelectVariant
3791
+ }
3792
+ ) })
3793
+ ]
3794
+ },
3795
+ `${editor.colorMode}-${editor.activeTheme}`
3796
+ )
3797
+ }
3798
+ )
3799
+ ]
3800
+ }
3801
+ ),
3802
+ rightPanel: /* @__PURE__ */ jsx(
3803
+ RightSidebar,
3804
+ {
3805
+ selection: editor.sidebarSelection,
3806
+ propOverrides: editor.propOverrides,
3807
+ onPropOverride: editor.handlePropOverride,
3808
+ onResetOverrides: editor.handleResetOverrides,
3809
+ onClose: editor.handleCloseSidebar,
3810
+ onScopeToComponent: editor.handleScopeToComponent
3811
+ }
3812
+ )
3813
+ }
3814
+ ) });
3815
+ }
3816
+
3817
+ export { CategoryView, CodeBlock, ColorsSection, ComponentDetailView, ComponentRenderer, CopyButton, Editor, EditorHeader, EditorShell, FontPicker, FontsSection, IconsSection, OthersSection, OverviewView, PresetSelector, PreviewWindow, RightSidebar, Sidebar, TableOfContents, ThemeBar, findPreset, presetHasUnpublishedChanges, updatePresetInArray, useEditorState, useHover, usePresets };
3818
+ //# sourceMappingURL=index.js.map
3819
+ //# sourceMappingURL=index.js.map