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