@aprovan/bobbin 0.1.0-dev.03aaf5b

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 (45) hide show
  1. package/.turbo/turbo-build.log +16 -0
  2. package/LICENSE +373 -0
  3. package/dist/index.d.ts +402 -0
  4. package/dist/index.js +3704 -0
  5. package/package.json +30 -0
  6. package/src/Bobbin.tsx +89 -0
  7. package/src/components/EditPanel/EditPanel.tsx +376 -0
  8. package/src/components/EditPanel/controls/ColorPicker.tsx +138 -0
  9. package/src/components/EditPanel/controls/QuickSelectDropdown.tsx +142 -0
  10. package/src/components/EditPanel/controls/SliderInput.tsx +94 -0
  11. package/src/components/EditPanel/controls/SpacingControl.tsx +285 -0
  12. package/src/components/EditPanel/controls/ToggleGroup.tsx +37 -0
  13. package/src/components/EditPanel/controls/TokenDropdown.tsx +33 -0
  14. package/src/components/EditPanel/sections/AnnotationSection.tsx +136 -0
  15. package/src/components/EditPanel/sections/BackgroundSection.tsx +79 -0
  16. package/src/components/EditPanel/sections/EffectsSection.tsx +85 -0
  17. package/src/components/EditPanel/sections/LayoutSection.tsx +224 -0
  18. package/src/components/EditPanel/sections/SectionWrapper.tsx +57 -0
  19. package/src/components/EditPanel/sections/SizeSection.tsx +166 -0
  20. package/src/components/EditPanel/sections/SpacingSection.tsx +69 -0
  21. package/src/components/EditPanel/sections/TypographySection.tsx +148 -0
  22. package/src/components/Inspector/Inspector.tsx +221 -0
  23. package/src/components/Overlay/ControlHandles.tsx +572 -0
  24. package/src/components/Overlay/MarginPaddingOverlay.tsx +229 -0
  25. package/src/components/Overlay/SelectionOverlay.tsx +73 -0
  26. package/src/components/Pill/Pill.tsx +155 -0
  27. package/src/components/ThemeToggle/ThemeToggle.tsx +72 -0
  28. package/src/core/changeSerializer.ts +139 -0
  29. package/src/core/useBobbin.ts +399 -0
  30. package/src/core/useChangeTracker.ts +186 -0
  31. package/src/core/useClipboard.ts +21 -0
  32. package/src/core/useElementSelection.ts +146 -0
  33. package/src/index.ts +46 -0
  34. package/src/tokens/borders.ts +19 -0
  35. package/src/tokens/colors.ts +150 -0
  36. package/src/tokens/index.ts +37 -0
  37. package/src/tokens/shadows.ts +10 -0
  38. package/src/tokens/spacing.ts +37 -0
  39. package/src/tokens/typography.ts +51 -0
  40. package/src/types.ts +157 -0
  41. package/src/utils/animation.ts +40 -0
  42. package/src/utils/dom.ts +36 -0
  43. package/src/utils/selectors.ts +76 -0
  44. package/tsconfig.json +10 -0
  45. package/tsup.config.ts +10 -0
package/dist/index.js ADDED
@@ -0,0 +1,3704 @@
1
+ import { createPortal } from 'react-dom';
2
+ import { useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect } from 'react';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+
5
+ // src/Bobbin.tsx
6
+
7
+ // src/utils/selectors.ts
8
+ function generateId() {
9
+ return Math.random().toString(36).substring(2, 15);
10
+ }
11
+ function getElementPath(el) {
12
+ const path = [];
13
+ let current = el;
14
+ while (current && current !== document.body) {
15
+ let selector = current.tagName.toLowerCase();
16
+ if (current.id) {
17
+ selector = `#${current.id}`;
18
+ path.unshift(selector);
19
+ break;
20
+ }
21
+ const parent = current.parentElement;
22
+ if (parent) {
23
+ const siblings = Array.from(parent.children).filter(
24
+ (c) => c.tagName === current.tagName
25
+ );
26
+ if (siblings.length > 1) {
27
+ const index = siblings.indexOf(current) + 1;
28
+ selector += `:nth-of-type(${index})`;
29
+ }
30
+ }
31
+ path.unshift(selector);
32
+ current = current.parentElement;
33
+ }
34
+ return path.join(" > ");
35
+ }
36
+ function getElementXPath(el) {
37
+ const parts = [];
38
+ let current = el;
39
+ while (current && current !== document.body && current !== document.documentElement) {
40
+ let part = current.tagName.toLowerCase();
41
+ if (current.id) {
42
+ parts.unshift(`//*[@id="${current.id}"]`);
43
+ break;
44
+ }
45
+ const parent = current.parentElement;
46
+ if (parent) {
47
+ const siblings = Array.from(parent.children).filter(
48
+ (c) => c.tagName === current.tagName
49
+ );
50
+ if (siblings.length > 1) {
51
+ const index = siblings.indexOf(current) + 1;
52
+ part += `[${index}]`;
53
+ }
54
+ }
55
+ parts.unshift(part);
56
+ current = current.parentElement;
57
+ }
58
+ if (!parts[0]?.startsWith("//*[@id")) {
59
+ parts.unshift("");
60
+ }
61
+ return parts.join("/") || "//" + el.tagName.toLowerCase();
62
+ }
63
+
64
+ // src/core/useElementSelection.ts
65
+ function useElementSelection(options) {
66
+ const { container, exclude = [], enabled = true } = options;
67
+ const [hoveredElement, setHoveredElement] = useState(
68
+ null
69
+ );
70
+ const [selectedElement, setSelectedElement] = useState(null);
71
+ const lastRectRef = useRef(null);
72
+ const isExcluded = useCallback(
73
+ (el) => {
74
+ if (el.closest("[data-bobbin]")) return true;
75
+ return exclude.some(
76
+ (selector) => el.matches(selector) || el.closest(selector)
77
+ );
78
+ },
79
+ [exclude]
80
+ );
81
+ const createSelectedElement = useCallback(
82
+ (el) => {
83
+ return {
84
+ element: el,
85
+ rect: el.getBoundingClientRect(),
86
+ path: getElementPath(el),
87
+ xpath: getElementXPath(el),
88
+ tagName: el.tagName.toLowerCase(),
89
+ id: el.id || void 0,
90
+ classList: Array.from(el.classList)
91
+ };
92
+ },
93
+ []
94
+ );
95
+ const handleMouseMove = useCallback(
96
+ (e) => {
97
+ if (!enabled) return;
98
+ const target = document.elementFromPoint(
99
+ e.clientX,
100
+ e.clientY
101
+ );
102
+ if (!target || isExcluded(target)) {
103
+ setHoveredElement(null);
104
+ return;
105
+ }
106
+ if (container && !container.contains(target)) {
107
+ setHoveredElement(null);
108
+ return;
109
+ }
110
+ setHoveredElement(createSelectedElement(target));
111
+ },
112
+ [enabled, container, isExcluded, createSelectedElement]
113
+ );
114
+ const handleClick = useCallback(
115
+ (e) => {
116
+ if (!enabled) return;
117
+ const target = e.target;
118
+ if (target.closest("[data-bobbin]")) {
119
+ return;
120
+ }
121
+ if (!hoveredElement) return;
122
+ e.preventDefault();
123
+ e.stopPropagation();
124
+ setSelectedElement(hoveredElement);
125
+ lastRectRef.current = hoveredElement.rect;
126
+ },
127
+ [enabled, hoveredElement]
128
+ );
129
+ const clearSelection = useCallback(() => {
130
+ setSelectedElement(null);
131
+ setHoveredElement(null);
132
+ }, []);
133
+ const selectElement = useCallback(
134
+ (el) => {
135
+ if (!el) {
136
+ clearSelection();
137
+ return;
138
+ }
139
+ setSelectedElement(createSelectedElement(el));
140
+ },
141
+ [createSelectedElement, clearSelection]
142
+ );
143
+ useEffect(() => {
144
+ if (!enabled) return;
145
+ document.addEventListener("mousemove", handleMouseMove, { passive: true });
146
+ document.addEventListener("click", handleClick, { capture: true });
147
+ return () => {
148
+ document.removeEventListener("mousemove", handleMouseMove);
149
+ document.removeEventListener("click", handleClick, { capture: true });
150
+ };
151
+ }, [enabled, handleMouseMove, handleClick]);
152
+ useEffect(() => {
153
+ if (!selectedElement) return;
154
+ const updateRect = () => {
155
+ const newRect = selectedElement.element.getBoundingClientRect();
156
+ setSelectedElement((prev) => prev ? { ...prev, rect: newRect } : null);
157
+ };
158
+ window.addEventListener("scroll", updateRect, { passive: true });
159
+ window.addEventListener("resize", updateRect, { passive: true });
160
+ return () => {
161
+ window.removeEventListener("scroll", updateRect);
162
+ window.removeEventListener("resize", updateRect);
163
+ };
164
+ }, [selectedElement?.element]);
165
+ return {
166
+ hoveredElement,
167
+ selectedElement,
168
+ selectElement,
169
+ clearSelection,
170
+ lastRect: lastRectRef.current
171
+ };
172
+ }
173
+ function useChangeTracker() {
174
+ const [changes, setChanges] = useState([]);
175
+ const historyRef = useRef([]);
176
+ const originalStatesRef = useRef(/* @__PURE__ */ new Map());
177
+ const recordOriginalState = useCallback(
178
+ (path, property, value) => {
179
+ if (!originalStatesRef.current.has(path)) {
180
+ originalStatesRef.current.set(path, /* @__PURE__ */ new Map());
181
+ }
182
+ const elementState = originalStatesRef.current.get(path);
183
+ if (!elementState.has(property)) {
184
+ elementState.set(property, value);
185
+ }
186
+ },
187
+ []
188
+ );
189
+ const addChange = useCallback(
190
+ (change) => {
191
+ const fullChange = {
192
+ ...change,
193
+ id: generateId(),
194
+ timestamp: Date.now()
195
+ };
196
+ setChanges((prev) => [...prev, fullChange]);
197
+ historyRef.current.push(fullChange);
198
+ return fullChange;
199
+ },
200
+ []
201
+ );
202
+ const recordStyleChange = useCallback(
203
+ (path, xpath, tagName, property, value, originalValue) => {
204
+ recordOriginalState(path, property, originalValue);
205
+ return addChange({
206
+ type: "style",
207
+ target: { path, xpath, tagName },
208
+ before: { property, value: originalValue },
209
+ after: { property, value }
210
+ });
211
+ },
212
+ [addChange, recordOriginalState]
213
+ );
214
+ const recordTextChange = useCallback(
215
+ (path, xpath, tagName, originalText, newText) => {
216
+ return addChange({
217
+ type: "text",
218
+ target: { path, xpath, tagName },
219
+ before: originalText,
220
+ after: newText
221
+ });
222
+ },
223
+ [addChange]
224
+ );
225
+ const recordMoveChange = useCallback(
226
+ (path, xpath, tagName, fromParent, fromIndex, toParent, toIndex) => {
227
+ return addChange({
228
+ type: "move",
229
+ target: { path, xpath, tagName },
230
+ before: { parent: fromParent, index: fromIndex },
231
+ after: { parent: toParent, index: toIndex }
232
+ });
233
+ },
234
+ [addChange]
235
+ );
236
+ const recordChange = useCallback(
237
+ (type, path, xpath, tagName, before, after, metadata) => {
238
+ return addChange({
239
+ type,
240
+ target: { path, xpath, tagName },
241
+ before,
242
+ after,
243
+ metadata
244
+ });
245
+ },
246
+ [addChange]
247
+ );
248
+ const undo = useCallback(() => {
249
+ if (changes.length === 0) return null;
250
+ const lastChange = changes[changes.length - 1];
251
+ setChanges((prev) => prev.slice(0, -1));
252
+ return lastChange;
253
+ }, [changes]);
254
+ const clearChanges = useCallback(() => {
255
+ setChanges([]);
256
+ originalStatesRef.current.clear();
257
+ }, []);
258
+ const getChanges = useCallback(() => [...changes], [changes]);
259
+ const deduplicatedChanges = useMemo(() => {
260
+ const uniqueChanges = /* @__PURE__ */ new Map();
261
+ for (const change of changes) {
262
+ let key;
263
+ if (change.type === "style") {
264
+ const styleChange = change;
265
+ key = `${change.target.path}:style:${styleChange.after.property}`;
266
+ } else {
267
+ key = `${change.target.path}:${change.type}:${change.id}`;
268
+ }
269
+ if (change.type === "style") {
270
+ const styleChange = change;
271
+ const originalValue = originalStatesRef.current.get(change.target.path)?.get(styleChange.after.property);
272
+ if (originalValue === styleChange.after.value) {
273
+ uniqueChanges.delete(key);
274
+ continue;
275
+ }
276
+ }
277
+ uniqueChanges.set(key, change);
278
+ }
279
+ return Array.from(uniqueChanges.values());
280
+ }, [changes]);
281
+ return {
282
+ changes,
283
+ deduplicatedChanges,
284
+ changeCount: deduplicatedChanges.length,
285
+ recordStyleChange,
286
+ recordTextChange,
287
+ recordMoveChange,
288
+ recordChange,
289
+ undo,
290
+ clearChanges,
291
+ getChanges,
292
+ originalStates: originalStatesRef.current
293
+ };
294
+ }
295
+ function useClipboard() {
296
+ const [copied, setCopied] = useState(null);
297
+ const copy = useCallback((element) => {
298
+ setCopied(element);
299
+ }, []);
300
+ const clear = useCallback(() => {
301
+ setCopied(null);
302
+ }, []);
303
+ return {
304
+ copied,
305
+ copy,
306
+ clear,
307
+ hasCopied: copied !== null
308
+ };
309
+ }
310
+
311
+ // src/core/changeSerializer.ts
312
+ function serializeChangesToYAML(changes, annotations = []) {
313
+ const changeset = {
314
+ version: "1.0",
315
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
316
+ changeCount: changes.length + annotations.length,
317
+ changes: changes.map((change) => {
318
+ const base = {
319
+ type: change.type,
320
+ target: change.target.path,
321
+ xpath: change.target.xpath
322
+ };
323
+ switch (change.type) {
324
+ case "style":
325
+ return {
326
+ ...base,
327
+ property: change.before.property,
328
+ before: change.before.value,
329
+ after: change.after.value
330
+ };
331
+ case "text":
332
+ return {
333
+ ...base,
334
+ before: change.before,
335
+ after: change.after
336
+ };
337
+ case "move":
338
+ return {
339
+ ...base,
340
+ before: `${change.before.parent}[${change.before.index}]`,
341
+ after: `${change.after.parent}[${change.after.index}]`
342
+ };
343
+ default:
344
+ return {
345
+ ...base,
346
+ before: JSON.stringify(change.before),
347
+ after: JSON.stringify(change.after)
348
+ };
349
+ }
350
+ }),
351
+ annotations: annotations.map((a) => ({
352
+ type: "annotation",
353
+ target: a.elementPath,
354
+ xpath: a.elementXpath,
355
+ note: a.content
356
+ }))
357
+ };
358
+ return toYAML(changeset);
359
+ }
360
+ function toYAML(obj, indent = 0) {
361
+ const spaces = " ".repeat(indent);
362
+ if (obj === null || obj === void 0) return "null";
363
+ if (typeof obj === "boolean") return obj ? "true" : "false";
364
+ if (typeof obj === "number") return String(obj);
365
+ if (typeof obj === "string") {
366
+ if (/[\n:{}[\],&*#?|\-<>=!%@`]/.test(obj) || obj.trim() !== obj) {
367
+ return `"${obj.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
368
+ }
369
+ return obj || '""';
370
+ }
371
+ if (Array.isArray(obj)) {
372
+ if (obj.length === 0) return "[]";
373
+ return obj.map((item) => {
374
+ const itemYaml = toYAML(item, indent + 1);
375
+ if (typeof item === "object" && item !== null) {
376
+ return `${spaces}- ${itemYaml.trimStart()}`;
377
+ }
378
+ return `${spaces}- ${itemYaml}`;
379
+ }).join("\n");
380
+ }
381
+ if (typeof obj === "object") {
382
+ const entries = Object.entries(obj);
383
+ if (entries.length === 0) return "{}";
384
+ return entries.map(([key, value]) => {
385
+ const valueYaml = toYAML(value, indent + 1);
386
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
387
+ return `${spaces}${key}:
388
+ ${valueYaml}`;
389
+ }
390
+ if (Array.isArray(value) && value.length > 0) {
391
+ return `${spaces}${key}:
392
+ ${valueYaml}`;
393
+ }
394
+ return `${spaces}${key}: ${valueYaml}`;
395
+ }).join("\n");
396
+ }
397
+ return String(obj);
398
+ }
399
+ function parseYAMLChangeset(yaml) {
400
+ const lines = yaml.split("\n");
401
+ const result = {
402
+ version: "1.0",
403
+ timestamp: "",
404
+ changeCount: 0,
405
+ changes: [],
406
+ annotations: []
407
+ };
408
+ for (const line of lines) {
409
+ const trimmed = line.trim();
410
+ if (trimmed.startsWith("version:")) {
411
+ const parts = trimmed.split(":");
412
+ result.version = parts[1]?.trim() ?? "1.0";
413
+ } else if (trimmed.startsWith("timestamp:")) {
414
+ result.timestamp = trimmed.split(":").slice(1).join(":").trim();
415
+ } else if (trimmed.startsWith("changeCount:")) {
416
+ const parts = trimmed.split(":");
417
+ result.changeCount = parseInt(parts[1]?.trim() ?? "0", 10);
418
+ }
419
+ }
420
+ return result;
421
+ }
422
+
423
+ // src/tokens/colors.ts
424
+ var colors = {
425
+ slate: {
426
+ 50: "#f8fafc",
427
+ 100: "#f1f5f9",
428
+ 200: "#e2e8f0",
429
+ 300: "#cbd5e1",
430
+ 400: "#94a3b8",
431
+ 500: "#64748b",
432
+ 600: "#475569",
433
+ 700: "#334155",
434
+ 800: "#1e293b",
435
+ 900: "#0f172a",
436
+ 950: "#020617"
437
+ },
438
+ gray: {
439
+ 50: "#f9fafb",
440
+ 100: "#f3f4f6",
441
+ 200: "#e5e7eb",
442
+ 300: "#d1d5db",
443
+ 400: "#9ca3af",
444
+ 500: "#6b7280",
445
+ 600: "#4b5563",
446
+ 700: "#374151",
447
+ 800: "#1f2937",
448
+ 900: "#111827",
449
+ 950: "#030712"
450
+ },
451
+ zinc: {
452
+ 50: "#fafafa",
453
+ 100: "#f4f4f5",
454
+ 200: "#e4e4e7",
455
+ 300: "#d4d4d8",
456
+ 400: "#a1a1aa",
457
+ 500: "#71717a",
458
+ 600: "#52525b",
459
+ 700: "#3f3f46",
460
+ 800: "#27272a",
461
+ 900: "#18181b",
462
+ 950: "#09090b"
463
+ },
464
+ red: {
465
+ 50: "#fef2f2",
466
+ 100: "#fee2e2",
467
+ 200: "#fecaca",
468
+ 300: "#fca5a5",
469
+ 400: "#f87171",
470
+ 500: "#ef4444",
471
+ 600: "#dc2626",
472
+ 700: "#b91c1c",
473
+ 800: "#991b1b",
474
+ 900: "#7f1d1d",
475
+ 950: "#450a0a"
476
+ },
477
+ orange: {
478
+ 50: "#fff7ed",
479
+ 100: "#ffedd5",
480
+ 200: "#fed7aa",
481
+ 300: "#fdba74",
482
+ 400: "#fb923c",
483
+ 500: "#f97316",
484
+ 600: "#ea580c",
485
+ 700: "#c2410c",
486
+ 800: "#9a3412",
487
+ 900: "#7c2d12",
488
+ 950: "#431407"
489
+ },
490
+ yellow: {
491
+ 50: "#fefce8",
492
+ 100: "#fef9c3",
493
+ 200: "#fef08a",
494
+ 300: "#fde047",
495
+ 400: "#facc15",
496
+ 500: "#eab308",
497
+ 600: "#ca8a04",
498
+ 700: "#a16207",
499
+ 800: "#854d0e",
500
+ 900: "#713f12",
501
+ 950: "#422006"
502
+ },
503
+ green: {
504
+ 50: "#f0fdf4",
505
+ 100: "#dcfce7",
506
+ 200: "#bbf7d0",
507
+ 300: "#86efac",
508
+ 400: "#4ade80",
509
+ 500: "#22c55e",
510
+ 600: "#16a34a",
511
+ 700: "#15803d",
512
+ 800: "#166534",
513
+ 900: "#14532d",
514
+ 950: "#052e16"
515
+ },
516
+ blue: {
517
+ 50: "#eff6ff",
518
+ 100: "#dbeafe",
519
+ 200: "#bfdbfe",
520
+ 300: "#93c5fd",
521
+ 400: "#60a5fa",
522
+ 500: "#3b82f6",
523
+ 600: "#2563eb",
524
+ 700: "#1d4ed8",
525
+ 800: "#1e40af",
526
+ 900: "#1e3a8a",
527
+ 950: "#172554"
528
+ },
529
+ indigo: {
530
+ 50: "#eef2ff",
531
+ 100: "#e0e7ff",
532
+ 200: "#c7d2fe",
533
+ 300: "#a5b4fc",
534
+ 400: "#818cf8",
535
+ 500: "#6366f1",
536
+ 600: "#4f46e5",
537
+ 700: "#4338ca",
538
+ 800: "#3730a3",
539
+ 900: "#312e81",
540
+ 950: "#1e1b4b"
541
+ },
542
+ purple: {
543
+ 50: "#faf5ff",
544
+ 100: "#f3e8ff",
545
+ 200: "#e9d5ff",
546
+ 300: "#d8b4fe",
547
+ 400: "#c084fc",
548
+ 500: "#a855f7",
549
+ 600: "#9333ea",
550
+ 700: "#7e22ce",
551
+ 800: "#6b21a8",
552
+ 900: "#581c87",
553
+ 950: "#3b0764"
554
+ },
555
+ pink: {
556
+ 50: "#fdf2f8",
557
+ 100: "#fce7f3",
558
+ 200: "#fbcfe8",
559
+ 300: "#f9a8d4",
560
+ 400: "#f472b6",
561
+ 500: "#ec4899",
562
+ 600: "#db2777",
563
+ 700: "#be185d",
564
+ 800: "#9d174d",
565
+ 900: "#831843",
566
+ 950: "#500724"
567
+ },
568
+ // Semantic
569
+ white: { DEFAULT: "#ffffff" },
570
+ black: { DEFAULT: "#000000" },
571
+ transparent: { DEFAULT: "transparent" },
572
+ current: { DEFAULT: "currentColor" }
573
+ };
574
+
575
+ // src/tokens/spacing.ts
576
+ var spacing = {
577
+ px: "1px",
578
+ "0": "0px",
579
+ "0.5": "0.125rem",
580
+ "1": "0.25rem",
581
+ "1.5": "0.375rem",
582
+ "2": "0.5rem",
583
+ "2.5": "0.625rem",
584
+ "3": "0.75rem",
585
+ "3.5": "0.875rem",
586
+ "4": "1rem",
587
+ "5": "1.25rem",
588
+ "6": "1.5rem",
589
+ "7": "1.75rem",
590
+ "8": "2rem",
591
+ "9": "2.25rem",
592
+ "10": "2.5rem",
593
+ "11": "2.75rem",
594
+ "12": "3rem",
595
+ "14": "3.5rem",
596
+ "16": "4rem",
597
+ "20": "5rem",
598
+ "24": "6rem",
599
+ "28": "7rem",
600
+ "32": "8rem",
601
+ "36": "9rem",
602
+ "40": "10rem",
603
+ "44": "11rem",
604
+ "48": "12rem",
605
+ "52": "13rem",
606
+ "56": "14rem",
607
+ "60": "15rem",
608
+ "64": "16rem",
609
+ "72": "18rem",
610
+ "80": "20rem",
611
+ "96": "24rem"
612
+ };
613
+
614
+ // src/tokens/typography.ts
615
+ var fontSize = {
616
+ xs: "0.75rem",
617
+ sm: "0.875rem",
618
+ base: "1rem",
619
+ lg: "1.125rem",
620
+ xl: "1.25rem",
621
+ "2xl": "1.5rem",
622
+ "3xl": "1.875rem",
623
+ "4xl": "2.25rem",
624
+ "5xl": "3rem",
625
+ "6xl": "3.75rem",
626
+ "7xl": "4.5rem",
627
+ "8xl": "6rem",
628
+ "9xl": "8rem"
629
+ };
630
+ var fontWeight = {
631
+ thin: "100",
632
+ extralight: "200",
633
+ light: "300",
634
+ normal: "400",
635
+ medium: "500",
636
+ semibold: "600",
637
+ bold: "700",
638
+ extrabold: "800",
639
+ black: "900"
640
+ };
641
+ var fontFamily = {
642
+ sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
643
+ serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
644
+ mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace'
645
+ };
646
+ var lineHeight = {
647
+ none: "1",
648
+ tight: "1.25",
649
+ snug: "1.375",
650
+ normal: "1.5",
651
+ relaxed: "1.625",
652
+ loose: "2"
653
+ };
654
+ var letterSpacing = {
655
+ tighter: "-0.05em",
656
+ tight: "-0.025em",
657
+ normal: "0em",
658
+ wide: "0.025em",
659
+ wider: "0.05em",
660
+ widest: "0.1em"
661
+ };
662
+
663
+ // src/tokens/borders.ts
664
+ var borderRadius = {
665
+ none: "0px",
666
+ sm: "0.125rem",
667
+ DEFAULT: "0.25rem",
668
+ md: "0.375rem",
669
+ lg: "0.5rem",
670
+ xl: "0.75rem",
671
+ "2xl": "1rem",
672
+ "3xl": "1.5rem",
673
+ full: "9999px"
674
+ };
675
+ var borderWidth = {
676
+ "0": "0px",
677
+ DEFAULT: "1px",
678
+ "2": "2px",
679
+ "4": "4px",
680
+ "8": "8px"
681
+ };
682
+
683
+ // src/tokens/shadows.ts
684
+ var boxShadow = {
685
+ sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
686
+ DEFAULT: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
687
+ md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
688
+ lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
689
+ xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
690
+ "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
691
+ inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",
692
+ none: "none"
693
+ };
694
+
695
+ // src/tokens/index.ts
696
+ var defaultTokens = {
697
+ colors,
698
+ spacing,
699
+ fontSize,
700
+ fontWeight,
701
+ fontFamily,
702
+ borderRadius,
703
+ borderWidth,
704
+ boxShadow,
705
+ lineHeight,
706
+ letterSpacing
707
+ };
708
+
709
+ // src/utils/dom.ts
710
+ function applyStyleToElement(el, property, value) {
711
+ el.style.setProperty(property, value);
712
+ }
713
+ function enableContentEditable(el, enabled) {
714
+ if (enabled) {
715
+ el.contentEditable = "true";
716
+ const textElements = el.querySelectorAll(
717
+ "p, span, h1, h2, h3, h4, h5, h6, a, li, td, th, label, div"
718
+ );
719
+ textElements.forEach((child) => {
720
+ if (child instanceof HTMLElement && child.childNodes.length > 0) {
721
+ for (const node of child.childNodes) {
722
+ if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {
723
+ child.contentEditable = "true";
724
+ break;
725
+ }
726
+ }
727
+ }
728
+ });
729
+ } else {
730
+ el.contentEditable = "false";
731
+ const textElements = el.querySelectorAll('[contenteditable="true"]');
732
+ textElements.forEach((child) => {
733
+ if (child instanceof HTMLElement) {
734
+ child.contentEditable = "false";
735
+ }
736
+ });
737
+ }
738
+ }
739
+
740
+ // src/core/useBobbin.ts
741
+ function useBobbin(props = {}) {
742
+ const {
743
+ tokens: customTokens,
744
+ container,
745
+ defaultActive = false,
746
+ onChanges,
747
+ onSelect,
748
+ exclude = []
749
+ } = props;
750
+ const [isActive, setIsActive] = useState(defaultActive);
751
+ const [isPillExpanded, setIsPillExpanded] = useState(false);
752
+ const [annotations, setAnnotations] = useState([]);
753
+ const [showMarginPadding, setShowMarginPadding] = useState(false);
754
+ const [activePanel, setActivePanel] = useState(
755
+ "style"
756
+ );
757
+ const [theme, setTheme] = useState("system");
758
+ const tokens = useMemo(
759
+ () => ({
760
+ ...defaultTokens,
761
+ ...customTokens
762
+ }),
763
+ [customTokens]
764
+ );
765
+ const { hoveredElement, selectedElement, selectElement, clearSelection } = useElementSelection({
766
+ container,
767
+ exclude: [...exclude, "[data-bobbin]"],
768
+ enabled: isActive
769
+ });
770
+ const changeTracker = useChangeTracker();
771
+ const clipboard = useClipboard();
772
+ useEffect(() => {
773
+ if (selectedElement) {
774
+ enableContentEditable(selectedElement.element, true);
775
+ return () => enableContentEditable(selectedElement.element, false);
776
+ }
777
+ }, [selectedElement]);
778
+ useEffect(() => {
779
+ onChanges?.(changeTracker.changes);
780
+ }, [changeTracker.changes, onChanges]);
781
+ useEffect(() => {
782
+ onSelect?.(selectedElement);
783
+ }, [selectedElement, onSelect]);
784
+ useEffect(() => {
785
+ const root = document.documentElement;
786
+ if (theme === "dark") {
787
+ root.classList.add("dark");
788
+ root.style.colorScheme = "dark";
789
+ } else if (theme === "light") {
790
+ root.classList.remove("dark");
791
+ root.style.colorScheme = "light";
792
+ } else {
793
+ const prefersDark = window.matchMedia(
794
+ "(prefers-color-scheme: dark)"
795
+ ).matches;
796
+ root.classList.toggle("dark", prefersDark);
797
+ root.style.colorScheme = prefersDark ? "dark" : "light";
798
+ }
799
+ }, [theme]);
800
+ const activate = useCallback(() => setIsActive(true), []);
801
+ const deactivate = useCallback(() => {
802
+ setIsActive(false);
803
+ clearSelection();
804
+ }, [clearSelection]);
805
+ const applyStyle = useCallback(
806
+ (property, value) => {
807
+ if (!selectedElement) return;
808
+ const el = selectedElement.element;
809
+ const originalValue = getComputedStyle(el).getPropertyValue(property);
810
+ applyStyleToElement(el, property, value);
811
+ changeTracker.recordStyleChange(
812
+ selectedElement.path,
813
+ selectedElement.xpath,
814
+ selectedElement.tagName,
815
+ property,
816
+ value,
817
+ originalValue
818
+ );
819
+ },
820
+ [selectedElement, changeTracker]
821
+ );
822
+ const deleteElement = useCallback(() => {
823
+ if (!selectedElement) return;
824
+ const el = selectedElement.element;
825
+ const parent = el.parentElement;
826
+ if (!parent) return;
827
+ changeTracker.recordChange(
828
+ "delete",
829
+ selectedElement.path,
830
+ selectedElement.xpath,
831
+ selectedElement.tagName,
832
+ el.outerHTML,
833
+ null
834
+ );
835
+ el.remove();
836
+ clearSelection();
837
+ }, [selectedElement, changeTracker, clearSelection]);
838
+ const moveElement = useCallback(
839
+ (targetParent, index) => {
840
+ if (!selectedElement) return;
841
+ const el = selectedElement.element;
842
+ const fromParent = el.parentElement;
843
+ if (!fromParent) return;
844
+ const fromIndex = Array.from(fromParent.children).indexOf(el);
845
+ const fromPath = getElementPath(fromParent);
846
+ const toPath = getElementPath(targetParent);
847
+ if (index >= targetParent.children.length) {
848
+ targetParent.appendChild(el);
849
+ } else {
850
+ const referenceNode = targetParent.children[index] ?? null;
851
+ targetParent.insertBefore(el, referenceNode);
852
+ }
853
+ changeTracker.recordMoveChange(
854
+ selectedElement.path,
855
+ selectedElement.xpath,
856
+ selectedElement.tagName,
857
+ fromPath,
858
+ fromIndex,
859
+ toPath,
860
+ index
861
+ );
862
+ },
863
+ [selectedElement, changeTracker]
864
+ );
865
+ const duplicateElement = useCallback(() => {
866
+ if (!selectedElement) return;
867
+ const el = selectedElement.element;
868
+ const clone = el.cloneNode(true);
869
+ const parent = el.parentElement;
870
+ if (parent) {
871
+ parent.insertBefore(clone, el.nextSibling);
872
+ }
873
+ changeTracker.recordChange(
874
+ "duplicate",
875
+ selectedElement.path,
876
+ selectedElement.xpath,
877
+ selectedElement.tagName,
878
+ null,
879
+ clone.outerHTML
880
+ );
881
+ selectElement(clone);
882
+ }, [selectedElement, changeTracker, selectElement]);
883
+ const insertElement = useCallback(
884
+ (direction, content = "") => {
885
+ if (!selectedElement) return;
886
+ const el = selectedElement.element;
887
+ const newEl = document.createElement("span");
888
+ newEl.textContent = content || "\u200B";
889
+ newEl.contentEditable = "true";
890
+ if (direction === "child") {
891
+ el.appendChild(newEl);
892
+ } else if (direction === "before") {
893
+ el.parentElement?.insertBefore(newEl, el);
894
+ } else {
895
+ el.parentElement?.insertBefore(newEl, el.nextSibling);
896
+ }
897
+ changeTracker.recordChange(
898
+ "insert",
899
+ getElementPath(newEl),
900
+ getElementXPath(newEl),
901
+ "span",
902
+ null,
903
+ newEl.outerHTML,
904
+ { direction }
905
+ );
906
+ selectElement(newEl);
907
+ },
908
+ [selectedElement, changeTracker, selectElement]
909
+ );
910
+ const copyElement = useCallback(() => {
911
+ if (!selectedElement) return;
912
+ clipboard.copy(selectedElement);
913
+ }, [selectedElement, clipboard]);
914
+ const pasteElement = useCallback(
915
+ (direction) => {
916
+ if (!selectedElement || !clipboard.copied) return;
917
+ const el = selectedElement.element;
918
+ const clone = clipboard.copied.element.cloneNode(true);
919
+ if (direction === "child") {
920
+ el.appendChild(clone);
921
+ } else if (direction === "before") {
922
+ el.parentElement?.insertBefore(clone, el);
923
+ } else {
924
+ el.parentElement?.insertBefore(clone, el.nextSibling);
925
+ }
926
+ changeTracker.recordChange(
927
+ "insert",
928
+ getElementPath(clone),
929
+ getElementXPath(clone),
930
+ clone.tagName.toLowerCase(),
931
+ null,
932
+ clone.outerHTML,
933
+ { source: "paste", direction }
934
+ );
935
+ selectElement(clone);
936
+ },
937
+ [selectedElement, clipboard, changeTracker, selectElement]
938
+ );
939
+ const annotate = useCallback(
940
+ (content) => {
941
+ if (!selectedElement) return;
942
+ setAnnotations((prev) => {
943
+ const existingIndex = prev.findIndex(
944
+ (a) => a.elementPath === selectedElement.path
945
+ );
946
+ if (existingIndex >= 0) {
947
+ const existing = prev[existingIndex];
948
+ if (!existing) return prev;
949
+ if (!content.trim()) {
950
+ return prev.filter((_, i) => i !== existingIndex);
951
+ }
952
+ const updated = [...prev];
953
+ updated[existingIndex] = {
954
+ id: existing.id,
955
+ elementPath: existing.elementPath,
956
+ elementXpath: existing.elementXpath,
957
+ content,
958
+ createdAt: Date.now()
959
+ };
960
+ return updated;
961
+ } else if (content.trim()) {
962
+ const annotation = {
963
+ id: crypto.randomUUID(),
964
+ elementPath: selectedElement.path,
965
+ elementXpath: selectedElement.xpath,
966
+ content,
967
+ createdAt: Date.now()
968
+ };
969
+ return [...prev, annotation];
970
+ }
971
+ return prev;
972
+ });
973
+ },
974
+ [selectedElement]
975
+ );
976
+ const toggleMarginPadding = useCallback(() => {
977
+ setShowMarginPadding((prev) => !prev);
978
+ }, []);
979
+ const toggleTheme = useCallback(() => {
980
+ setTheme((prev) => {
981
+ if (prev === "light") return "dark";
982
+ if (prev === "dark") return "system";
983
+ return "light";
984
+ });
985
+ }, []);
986
+ const undo = useCallback(() => {
987
+ const lastChange = changeTracker.undo();
988
+ if (!lastChange) return;
989
+ console.log("Undoing:", lastChange);
990
+ }, [changeTracker]);
991
+ const resetChanges = useCallback(() => {
992
+ const originalStates = changeTracker.originalStates;
993
+ for (const [path, properties] of originalStates.entries()) {
994
+ const el = document.querySelector(path);
995
+ if (!el) continue;
996
+ for (const [property, originalValue] of properties.entries()) {
997
+ applyStyleToElement(el, property, originalValue);
998
+ }
999
+ }
1000
+ changeTracker.clearChanges();
1001
+ }, [changeTracker]);
1002
+ const exportChanges = useCallback(() => {
1003
+ return serializeChangesToYAML(
1004
+ changeTracker.deduplicatedChanges,
1005
+ annotations
1006
+ );
1007
+ }, [changeTracker.deduplicatedChanges, annotations]);
1008
+ const state = {
1009
+ isActive,
1010
+ isPillExpanded,
1011
+ hoveredElement,
1012
+ selectedElement,
1013
+ changes: changeTracker.deduplicatedChanges,
1014
+ annotations,
1015
+ clipboard: clipboard.copied,
1016
+ showMarginPadding,
1017
+ activePanel,
1018
+ theme
1019
+ };
1020
+ const actions = {
1021
+ activate,
1022
+ deactivate,
1023
+ selectElement,
1024
+ clearSelection,
1025
+ applyStyle,
1026
+ deleteElement,
1027
+ moveElement,
1028
+ duplicateElement,
1029
+ insertElement,
1030
+ copyElement,
1031
+ pasteElement,
1032
+ annotate,
1033
+ toggleMarginPadding,
1034
+ toggleTheme,
1035
+ undo,
1036
+ exportChanges,
1037
+ getChanges: changeTracker.getChanges,
1038
+ resetChanges
1039
+ };
1040
+ return {
1041
+ ...state,
1042
+ ...actions,
1043
+ tokens,
1044
+ setActivePanel,
1045
+ setIsPillExpanded
1046
+ };
1047
+ }
1048
+ var CopyIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1049
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
1050
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
1051
+ ] });
1052
+ var EditIcon = () => /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1053
+ /* @__PURE__ */ jsx("path", { d: "M12 20h9" }),
1054
+ /* @__PURE__ */ jsx("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })
1055
+ ] });
1056
+ function Pill({ state, actions, position, container, zIndex = 9999 }) {
1057
+ const [copyHovered, setCopyHovered] = useState(false);
1058
+ const [copied, setCopied] = useState(false);
1059
+ const [computedPosition, setComputedPosition] = useState(null);
1060
+ const offset = position ?? { bottom: 16, right: 16 };
1061
+ useLayoutEffect(() => {
1062
+ if (!container) {
1063
+ setComputedPosition(null);
1064
+ return;
1065
+ }
1066
+ const updatePosition = () => {
1067
+ const rect = container.getBoundingClientRect();
1068
+ setComputedPosition({
1069
+ bottom: window.innerHeight - rect.bottom + offset.bottom,
1070
+ right: window.innerWidth - rect.right + offset.right
1071
+ });
1072
+ };
1073
+ updatePosition();
1074
+ const observer = new ResizeObserver(updatePosition);
1075
+ observer.observe(container);
1076
+ window.addEventListener("scroll", updatePosition, true);
1077
+ return () => {
1078
+ observer.disconnect();
1079
+ window.removeEventListener("scroll", updatePosition, true);
1080
+ };
1081
+ }, [container, offset.bottom, offset.right]);
1082
+ const pos = computedPosition ?? offset;
1083
+ const handleClick = () => {
1084
+ if (state.isActive) {
1085
+ actions.deactivate();
1086
+ } else {
1087
+ actions.activate();
1088
+ }
1089
+ };
1090
+ const handleCopyChanges = (e) => {
1091
+ e.stopPropagation();
1092
+ const yaml = actions.exportChanges();
1093
+ navigator.clipboard.writeText(yaml);
1094
+ setCopied(true);
1095
+ setTimeout(() => setCopied(false), 1500);
1096
+ };
1097
+ return /* @__PURE__ */ jsxs(
1098
+ "div",
1099
+ {
1100
+ "data-bobbin": "pill",
1101
+ className: "bobbin-pill",
1102
+ style: {
1103
+ position: "fixed",
1104
+ bottom: pos.bottom,
1105
+ right: pos.right,
1106
+ zIndex,
1107
+ display: "flex",
1108
+ alignItems: "center",
1109
+ gap: "6px",
1110
+ padding: "6px 10px",
1111
+ borderRadius: "9999px",
1112
+ backgroundColor: state.isActive ? "#18181b" : "#fafafa",
1113
+ color: state.isActive ? "#fafafa" : "#18181b",
1114
+ border: "1px solid #e4e4e7",
1115
+ cursor: "pointer",
1116
+ boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
1117
+ transition: "all 0.15s ease",
1118
+ userSelect: "none",
1119
+ fontSize: "13px",
1120
+ fontFamily: "system-ui, -apple-system, sans-serif"
1121
+ },
1122
+ onClick: handleClick,
1123
+ children: [
1124
+ /* @__PURE__ */ jsx(EditIcon, {}),
1125
+ (state.changes.length > 0 || state.annotations.length > 0) && /* @__PURE__ */ jsxs(
1126
+ "button",
1127
+ {
1128
+ onClick: handleCopyChanges,
1129
+ onMouseEnter: () => setCopyHovered(true),
1130
+ onMouseLeave: () => setCopyHovered(false),
1131
+ style: {
1132
+ display: "flex",
1133
+ alignItems: "center",
1134
+ gap: "4px",
1135
+ padding: "2px 6px",
1136
+ borderRadius: "9999px",
1137
+ backgroundColor: state.isActive ? "#fafafa" : "#18181b",
1138
+ color: state.isActive ? "#18181b" : "#fafafa",
1139
+ fontSize: "11px",
1140
+ fontWeight: 500,
1141
+ border: "none",
1142
+ cursor: "pointer",
1143
+ transition: "all 0.1s ease"
1144
+ },
1145
+ title: "Copy changes as YAML",
1146
+ children: [
1147
+ /* @__PURE__ */ jsx("span", { children: state.changes.length + state.annotations.length }),
1148
+ copyHovered && /* @__PURE__ */ jsx(CopyIcon, {}),
1149
+ copied && /* @__PURE__ */ jsx("span", { style: { fontSize: "10px" }, children: "\u2713" })
1150
+ ]
1151
+ }
1152
+ ),
1153
+ state.clipboard && /* @__PURE__ */ jsx(
1154
+ "div",
1155
+ {
1156
+ style: {
1157
+ width: "6px",
1158
+ height: "6px",
1159
+ borderRadius: "50%",
1160
+ backgroundColor: state.isActive ? "#fafafa" : "#18181b",
1161
+ border: "1px solid #a1a1aa"
1162
+ },
1163
+ title: "Element copied"
1164
+ }
1165
+ )
1166
+ ]
1167
+ }
1168
+ );
1169
+ }
1170
+ function SelectionOverlay({
1171
+ hoveredElement,
1172
+ selectedElement,
1173
+ offset = 4,
1174
+ zIndex = 9998
1175
+ }) {
1176
+ const [hoverRect, setHoverRect] = useState(null);
1177
+ const [selectRect, setSelectRect] = useState(null);
1178
+ useLayoutEffect(() => {
1179
+ if (!hoveredElement || hoveredElement === selectedElement) {
1180
+ setHoverRect(null);
1181
+ return;
1182
+ }
1183
+ setHoverRect(hoveredElement.rect);
1184
+ }, [hoveredElement, selectedElement]);
1185
+ useLayoutEffect(() => {
1186
+ if (!selectedElement) {
1187
+ setSelectRect(null);
1188
+ return;
1189
+ }
1190
+ setSelectRect(selectedElement.rect);
1191
+ }, [selectedElement]);
1192
+ const createBoxStyle = (rect, isSelected) => {
1193
+ if (!rect) return { opacity: 0, pointerEvents: "none" };
1194
+ return {
1195
+ position: "fixed",
1196
+ top: rect.top - offset,
1197
+ left: rect.left - offset,
1198
+ width: rect.width + offset * 2,
1199
+ height: rect.height + offset * 2,
1200
+ border: isSelected ? "1.5px solid #18181b" : "1.5px dashed #71717a",
1201
+ borderRadius: "3px",
1202
+ pointerEvents: "none",
1203
+ zIndex,
1204
+ transition: "all 0.12s cubic-bezier(0.4, 0, 0.2, 1)",
1205
+ opacity: 1,
1206
+ boxShadow: isSelected ? "0 0 0 1px rgba(24, 24, 27, 0.1)" : "none"
1207
+ };
1208
+ };
1209
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1210
+ /* @__PURE__ */ jsx(
1211
+ "div",
1212
+ {
1213
+ "data-bobbin": "hover-overlay",
1214
+ style: createBoxStyle(hoverRect, false)
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ jsx(
1218
+ "div",
1219
+ {
1220
+ "data-bobbin": "select-overlay",
1221
+ style: createBoxStyle(selectRect, true)
1222
+ }
1223
+ )
1224
+ ] });
1225
+ }
1226
+ function getLayoutDirection(element) {
1227
+ const parent = element.parentElement;
1228
+ if (!parent) return "unknown";
1229
+ const style = getComputedStyle(parent);
1230
+ const display = style.display;
1231
+ const flexDirection = style.flexDirection;
1232
+ if (display.includes("flex")) {
1233
+ if (flexDirection === "column" || flexDirection === "column-reverse") {
1234
+ return "vertical";
1235
+ }
1236
+ return "horizontal";
1237
+ }
1238
+ if (display.includes("grid")) {
1239
+ return "horizontal";
1240
+ }
1241
+ return "vertical";
1242
+ }
1243
+ var MIN_WIDTH_FOR_CORNER_TOOLBAR = 60;
1244
+ var MIN_SIZE_FOR_EDGE_ICONS = 70;
1245
+ var CORNER_HANDLE_SIZE = 18;
1246
+ var HOVER_ZONE_SIZE = 28;
1247
+ function ControlHandles({
1248
+ selectedElement,
1249
+ actions,
1250
+ clipboard,
1251
+ zIndex = 9999
1252
+ }) {
1253
+ const [hoveredEdge, setHoveredEdge] = useState(null);
1254
+ const [expandedEdge, setExpandedEdge] = useState(null);
1255
+ const [isDragging, setIsDragging] = useState(false);
1256
+ const [dropTarget, setDropTarget] = useState(null);
1257
+ const [cornerToolbarExpanded, setCornerToolbarExpanded] = useState(false);
1258
+ useRef(null);
1259
+ const { rect } = selectedElement;
1260
+ const layoutDirection = useMemo(
1261
+ () => getLayoutDirection(selectedElement.element),
1262
+ [selectedElement.element]
1263
+ );
1264
+ rect.width < MIN_WIDTH_FOR_CORNER_TOOLBAR;
1265
+ rect.height < MIN_SIZE_FOR_EDGE_ICONS;
1266
+ rect.width < MIN_SIZE_FOR_EDGE_ICONS;
1267
+ useEffect(() => {
1268
+ setCornerToolbarExpanded(false);
1269
+ setExpandedEdge(null);
1270
+ setHoveredEdge(null);
1271
+ }, [selectedElement.path]);
1272
+ const TrashIcon = () => /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2" }) });
1273
+ const CopyIcon2 = () => /* @__PURE__ */ jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1274
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
1275
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
1276
+ ] });
1277
+ const MoveIcon = () => /* @__PURE__ */ jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1278
+ /* @__PURE__ */ jsx("polyline", { points: "5 9 2 12 5 15" }),
1279
+ /* @__PURE__ */ jsx("polyline", { points: "9 5 12 2 15 5" }),
1280
+ /* @__PURE__ */ jsx("polyline", { points: "15 19 12 22 9 19" }),
1281
+ /* @__PURE__ */ jsx("polyline", { points: "19 9 22 12 19 15" }),
1282
+ /* @__PURE__ */ jsx("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
1283
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "12", y2: "22" })
1284
+ ] });
1285
+ const PlusIcon = () => /* @__PURE__ */ jsx("svg", { width: "9", height: "9", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }) });
1286
+ const DuplicateIcon = () => /* @__PURE__ */ jsxs("svg", { width: "9", height: "9", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1287
+ /* @__PURE__ */ jsx("rect", { x: "8", y: "8", width: "12", height: "12", rx: "2" }),
1288
+ /* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "12", height: "12", rx: "2" })
1289
+ ] });
1290
+ const PasteIcon = () => /* @__PURE__ */ jsxs("svg", { width: "9", height: "9", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1291
+ /* @__PURE__ */ jsx("path", { d: "M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2" }),
1292
+ /* @__PURE__ */ jsx("rect", { x: "8", y: "2", width: "8", height: "4", rx: "1" })
1293
+ ] });
1294
+ useEffect(() => {
1295
+ if (!cornerToolbarExpanded && !expandedEdge) return;
1296
+ const handleClickOutside = (e) => {
1297
+ const target = e.target;
1298
+ if (target.closest('[data-bobbin="control-handles"]')) return;
1299
+ setCornerToolbarExpanded(false);
1300
+ setExpandedEdge(null);
1301
+ };
1302
+ const timer = setTimeout(() => {
1303
+ document.addEventListener("mousedown", handleClickOutside);
1304
+ }, 0);
1305
+ return () => {
1306
+ clearTimeout(timer);
1307
+ document.removeEventListener("mousedown", handleClickOutside);
1308
+ };
1309
+ }, [cornerToolbarExpanded, expandedEdge]);
1310
+ const handleMouseMove = useCallback((e) => {
1311
+ if (!isDragging) return;
1312
+ const elementsAtPoint = document.elementsFromPoint(e.clientX, e.clientY);
1313
+ const target = elementsAtPoint.find(
1314
+ (el) => !el.hasAttribute("data-bobbin") && el !== selectedElement.element && !selectedElement.element.contains(el) && el instanceof HTMLElement && el.tagName !== "HTML" && el.tagName !== "BODY"
1315
+ );
1316
+ if (target !== dropTarget) {
1317
+ if (dropTarget) {
1318
+ dropTarget.style.outline = "";
1319
+ dropTarget.style.outlineOffset = "";
1320
+ }
1321
+ if (target) {
1322
+ target.style.outline = "2px dashed #3b82f6";
1323
+ target.style.outlineOffset = "2px";
1324
+ }
1325
+ setDropTarget(target || null);
1326
+ }
1327
+ }, [isDragging, dropTarget, selectedElement.element]);
1328
+ const handleMouseUp = useCallback(() => {
1329
+ if (isDragging && dropTarget) {
1330
+ const parent = dropTarget.parentElement;
1331
+ if (parent) {
1332
+ const index = Array.from(parent.children).indexOf(dropTarget) + 1;
1333
+ actions.moveElement(parent, index);
1334
+ }
1335
+ dropTarget.style.outline = "";
1336
+ dropTarget.style.outlineOffset = "";
1337
+ }
1338
+ setIsDragging(false);
1339
+ setDropTarget(null);
1340
+ }, [isDragging, dropTarget, actions]);
1341
+ useEffect(() => {
1342
+ if (isDragging) {
1343
+ document.addEventListener("mousemove", handleMouseMove);
1344
+ document.addEventListener("mouseup", handleMouseUp);
1345
+ document.body.style.cursor = "grabbing";
1346
+ }
1347
+ return () => {
1348
+ document.removeEventListener("mousemove", handleMouseMove);
1349
+ document.removeEventListener("mouseup", handleMouseUp);
1350
+ document.body.style.cursor = "";
1351
+ if (dropTarget) {
1352
+ dropTarget.style.outline = "";
1353
+ dropTarget.style.outlineOffset = "";
1354
+ }
1355
+ };
1356
+ }, [isDragging, handleMouseMove, handleMouseUp, dropTarget]);
1357
+ const getEdgeZoneStyle = (position) => {
1358
+ const isHorizontal = position === "top" || position === "bottom";
1359
+ const base = {
1360
+ position: "fixed",
1361
+ display: "flex",
1362
+ alignItems: "center",
1363
+ justifyContent: "center",
1364
+ gap: "3px",
1365
+ zIndex,
1366
+ transition: "opacity 0.1s ease",
1367
+ pointerEvents: "auto"
1368
+ };
1369
+ if (isHorizontal) {
1370
+ return {
1371
+ ...base,
1372
+ left: rect.left,
1373
+ width: rect.width,
1374
+ height: HOVER_ZONE_SIZE,
1375
+ top: position === "top" ? rect.top - HOVER_ZONE_SIZE : rect.bottom,
1376
+ flexDirection: "row"
1377
+ };
1378
+ } else {
1379
+ return {
1380
+ ...base,
1381
+ top: rect.top,
1382
+ height: rect.height,
1383
+ width: HOVER_ZONE_SIZE,
1384
+ left: position === "left" ? rect.left - HOVER_ZONE_SIZE : rect.right,
1385
+ flexDirection: "column"
1386
+ };
1387
+ }
1388
+ };
1389
+ const EdgeActionButton = ({
1390
+ icon,
1391
+ onClick,
1392
+ title,
1393
+ visible
1394
+ }) => {
1395
+ const [isHovered, setIsHovered] = useState(false);
1396
+ if (!visible) return null;
1397
+ return /* @__PURE__ */ jsx(
1398
+ "button",
1399
+ {
1400
+ style: {
1401
+ width: CORNER_HANDLE_SIZE,
1402
+ height: CORNER_HANDLE_SIZE,
1403
+ borderRadius: "3px",
1404
+ backgroundColor: isHovered ? "#27272a" : "#18181b",
1405
+ color: "#fafafa",
1406
+ border: "none",
1407
+ cursor: "pointer",
1408
+ display: "flex",
1409
+ alignItems: "center",
1410
+ justifyContent: "center",
1411
+ transition: "all 0.1s ease",
1412
+ boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.2)",
1413
+ pointerEvents: "auto",
1414
+ flexShrink: 0
1415
+ },
1416
+ onClick: (e) => {
1417
+ e.stopPropagation();
1418
+ e.preventDefault();
1419
+ onClick();
1420
+ },
1421
+ onMouseEnter: () => setIsHovered(true),
1422
+ onMouseLeave: () => setIsHovered(false),
1423
+ title,
1424
+ children: icon
1425
+ }
1426
+ );
1427
+ };
1428
+ const MoveActionButton = () => {
1429
+ const [isHovered, setIsHovered] = useState(false);
1430
+ return /* @__PURE__ */ jsx(
1431
+ "button",
1432
+ {
1433
+ style: {
1434
+ width: CORNER_HANDLE_SIZE,
1435
+ height: CORNER_HANDLE_SIZE,
1436
+ borderRadius: "3px",
1437
+ backgroundColor: isHovered ? "#27272a" : "#18181b",
1438
+ color: "#fafafa",
1439
+ border: "none",
1440
+ cursor: isDragging ? "grabbing" : "grab",
1441
+ display: "flex",
1442
+ alignItems: "center",
1443
+ justifyContent: "center",
1444
+ transition: "all 0.1s ease",
1445
+ boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.2)",
1446
+ pointerEvents: "auto",
1447
+ flexShrink: 0
1448
+ },
1449
+ onMouseDown: (e) => {
1450
+ e.stopPropagation();
1451
+ e.preventDefault();
1452
+ setIsDragging(true);
1453
+ },
1454
+ onMouseEnter: () => setIsHovered(true),
1455
+ onMouseLeave: () => setIsHovered(false),
1456
+ title: "Move element (drag to new location)",
1457
+ children: /* @__PURE__ */ jsx(MoveIcon, {})
1458
+ }
1459
+ );
1460
+ };
1461
+ const EdgeButtons = ({ position }) => {
1462
+ const insertDir = getInsertDirection(position);
1463
+ const isHorizontal = position === "top" || position === "bottom";
1464
+ const separator = isHorizontal ? /* @__PURE__ */ jsx("div", { style: { width: "1px", height: CORNER_HANDLE_SIZE, backgroundColor: "#3f3f46", margin: "0 2px" } }) : /* @__PURE__ */ jsx("div", { style: { height: "1px", width: CORNER_HANDLE_SIZE, backgroundColor: "#3f3f46", margin: "2px 0" } });
1465
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1466
+ position === "top" && /* @__PURE__ */ jsxs(Fragment, { children: [
1467
+ /* @__PURE__ */ jsx(
1468
+ EdgeActionButton,
1469
+ {
1470
+ icon: /* @__PURE__ */ jsx(TrashIcon, {}),
1471
+ onClick: () => actions.deleteElement(),
1472
+ title: "Delete element",
1473
+ visible: true
1474
+ }
1475
+ ),
1476
+ /* @__PURE__ */ jsx(
1477
+ EdgeActionButton,
1478
+ {
1479
+ icon: /* @__PURE__ */ jsx(CopyIcon2, {}),
1480
+ onClick: () => actions.copyElement(),
1481
+ title: "Copy element",
1482
+ visible: true
1483
+ }
1484
+ ),
1485
+ separator
1486
+ ] }),
1487
+ /* @__PURE__ */ jsx(
1488
+ EdgeActionButton,
1489
+ {
1490
+ icon: /* @__PURE__ */ jsx(PlusIcon, {}),
1491
+ onClick: () => actions.insertElement(insertDir),
1492
+ title: `Add text ${insertDir}`,
1493
+ visible: true
1494
+ }
1495
+ ),
1496
+ /* @__PURE__ */ jsx(
1497
+ EdgeActionButton,
1498
+ {
1499
+ icon: /* @__PURE__ */ jsx(PasteIcon, {}),
1500
+ onClick: () => actions.pasteElement(insertDir),
1501
+ title: `Paste ${insertDir}`,
1502
+ visible: !!clipboard
1503
+ }
1504
+ ),
1505
+ /* @__PURE__ */ jsx(
1506
+ EdgeActionButton,
1507
+ {
1508
+ icon: /* @__PURE__ */ jsx(DuplicateIcon, {}),
1509
+ onClick: actions.duplicateElement,
1510
+ title: "Duplicate element",
1511
+ visible: true
1512
+ }
1513
+ ),
1514
+ position === "top" && /* @__PURE__ */ jsxs(Fragment, { children: [
1515
+ separator,
1516
+ /* @__PURE__ */ jsx(MoveActionButton, {})
1517
+ ] })
1518
+ ] });
1519
+ };
1520
+ const getInsertDirection = (position) => {
1521
+ if (layoutDirection === "horizontal") {
1522
+ return position === "left" ? "before" : "after";
1523
+ } else {
1524
+ return position === "top" ? "before" : "after";
1525
+ }
1526
+ };
1527
+ const [cornerHover, setCornerHover] = useState(null);
1528
+ return /* @__PURE__ */ jsx("div", { "data-bobbin": "control-handles", style: { pointerEvents: "none" }, children: ["top", "bottom", "left", "right"].map((position) => {
1529
+ const isHovered = hoveredEdge === position;
1530
+ return /* @__PURE__ */ jsx(
1531
+ "div",
1532
+ {
1533
+ style: getEdgeZoneStyle(position),
1534
+ onMouseEnter: () => setHoveredEdge(position),
1535
+ onMouseLeave: () => setHoveredEdge(null),
1536
+ children: isHovered && /* @__PURE__ */ jsx(EdgeButtons, { position })
1537
+ },
1538
+ position
1539
+ );
1540
+ }) });
1541
+ }
1542
+ function MarginPaddingOverlay({
1543
+ selectedElement,
1544
+ zIndex = 9997
1545
+ }) {
1546
+ const { element, rect } = selectedElement;
1547
+ const computed = window.getComputedStyle(element);
1548
+ const margin = {
1549
+ top: parseFloat(computed.marginTop) || 0,
1550
+ right: parseFloat(computed.marginRight) || 0,
1551
+ bottom: parseFloat(computed.marginBottom) || 0,
1552
+ left: parseFloat(computed.marginLeft) || 0
1553
+ };
1554
+ const padding = {
1555
+ top: parseFloat(computed.paddingTop) || 0,
1556
+ right: parseFloat(computed.paddingRight) || 0,
1557
+ bottom: parseFloat(computed.paddingBottom) || 0,
1558
+ left: parseFloat(computed.paddingLeft) || 0
1559
+ };
1560
+ const gap = parseFloat(computed.gap) || 0;
1561
+ const rowGap = parseFloat(computed.rowGap) || gap;
1562
+ const columnGap = parseFloat(computed.columnGap) || gap;
1563
+ const display = computed.display;
1564
+ const flexDirection = computed.flexDirection;
1565
+ const isFlexOrGrid = display.includes("flex") || display.includes("grid");
1566
+ const isColumn = flexDirection === "column" || flexDirection === "column-reverse";
1567
+ const marginColor = "rgba(251, 146, 60, 0.3)";
1568
+ const paddingColor = "rgba(74, 222, 128, 0.3)";
1569
+ const gapColor = "rgba(168, 85, 247, 0.35)";
1570
+ const getGapOverlays = () => {
1571
+ if (!isFlexOrGrid || rowGap === 0 && columnGap === 0) return [];
1572
+ const children = Array.from(element.children).filter(
1573
+ (child) => child instanceof HTMLElement && getComputedStyle(child).display !== "none" && getComputedStyle(child).visibility !== "hidden"
1574
+ );
1575
+ if (children.length < 2) return [];
1576
+ const overlays = [];
1577
+ for (let i = 0; i < children.length - 1; i++) {
1578
+ const currentChild = children[i];
1579
+ const nextChild = children[i + 1];
1580
+ if (!currentChild || !nextChild) continue;
1581
+ const currentRect = currentChild.getBoundingClientRect();
1582
+ const nextRect = nextChild.getBoundingClientRect();
1583
+ if (isColumn || display.includes("grid")) {
1584
+ if (rowGap > 0 && nextRect.top > currentRect.bottom) {
1585
+ const gapTop = currentRect.bottom;
1586
+ const gapHeight = Math.min(rowGap, nextRect.top - currentRect.bottom);
1587
+ if (gapHeight > 0) {
1588
+ overlays.push({
1589
+ top: gapTop,
1590
+ left: rect.left + padding.left,
1591
+ width: rect.width - padding.left - padding.right,
1592
+ height: gapHeight
1593
+ });
1594
+ }
1595
+ }
1596
+ }
1597
+ if (!isColumn || display.includes("grid")) {
1598
+ if (columnGap > 0 && nextRect.left > currentRect.right) {
1599
+ const gapLeft = currentRect.right;
1600
+ const gapWidth = Math.min(columnGap, nextRect.left - currentRect.right);
1601
+ if (gapWidth > 0) {
1602
+ overlays.push({
1603
+ top: rect.top + padding.top,
1604
+ left: gapLeft,
1605
+ width: gapWidth,
1606
+ height: rect.height - padding.top - padding.bottom
1607
+ });
1608
+ }
1609
+ }
1610
+ }
1611
+ }
1612
+ return overlays;
1613
+ };
1614
+ const gapOverlays = getGapOverlays();
1615
+ return /* @__PURE__ */ jsxs("div", { "data-bobbin": "margin-padding-overlay", style: { position: "fixed", top: 0, left: 0, zIndex, pointerEvents: "none" }, children: [
1616
+ margin.top > 0 && /* @__PURE__ */ jsx(
1617
+ "div",
1618
+ {
1619
+ style: {
1620
+ position: "fixed",
1621
+ top: rect.top - margin.top,
1622
+ left: rect.left,
1623
+ width: rect.width,
1624
+ height: margin.top,
1625
+ backgroundColor: marginColor
1626
+ }
1627
+ }
1628
+ ),
1629
+ margin.bottom > 0 && /* @__PURE__ */ jsx(
1630
+ "div",
1631
+ {
1632
+ style: {
1633
+ position: "fixed",
1634
+ top: rect.bottom,
1635
+ left: rect.left,
1636
+ width: rect.width,
1637
+ height: margin.bottom,
1638
+ backgroundColor: marginColor
1639
+ }
1640
+ }
1641
+ ),
1642
+ margin.left > 0 && /* @__PURE__ */ jsx(
1643
+ "div",
1644
+ {
1645
+ style: {
1646
+ position: "fixed",
1647
+ top: rect.top,
1648
+ left: rect.left - margin.left,
1649
+ width: margin.left,
1650
+ height: rect.height,
1651
+ backgroundColor: marginColor
1652
+ }
1653
+ }
1654
+ ),
1655
+ margin.right > 0 && /* @__PURE__ */ jsx(
1656
+ "div",
1657
+ {
1658
+ style: {
1659
+ position: "fixed",
1660
+ top: rect.top,
1661
+ left: rect.right,
1662
+ width: margin.right,
1663
+ height: rect.height,
1664
+ backgroundColor: marginColor
1665
+ }
1666
+ }
1667
+ ),
1668
+ padding.top > 0 && /* @__PURE__ */ jsx(
1669
+ "div",
1670
+ {
1671
+ style: {
1672
+ position: "fixed",
1673
+ top: rect.top,
1674
+ left: rect.left,
1675
+ width: rect.width,
1676
+ height: padding.top,
1677
+ backgroundColor: paddingColor
1678
+ }
1679
+ }
1680
+ ),
1681
+ padding.bottom > 0 && /* @__PURE__ */ jsx(
1682
+ "div",
1683
+ {
1684
+ style: {
1685
+ position: "fixed",
1686
+ top: rect.bottom - padding.bottom,
1687
+ left: rect.left,
1688
+ width: rect.width,
1689
+ height: padding.bottom,
1690
+ backgroundColor: paddingColor
1691
+ }
1692
+ }
1693
+ ),
1694
+ padding.left > 0 && /* @__PURE__ */ jsx(
1695
+ "div",
1696
+ {
1697
+ style: {
1698
+ position: "fixed",
1699
+ top: rect.top + padding.top,
1700
+ left: rect.left,
1701
+ width: padding.left,
1702
+ height: rect.height - padding.top - padding.bottom,
1703
+ backgroundColor: paddingColor
1704
+ }
1705
+ }
1706
+ ),
1707
+ padding.right > 0 && /* @__PURE__ */ jsx(
1708
+ "div",
1709
+ {
1710
+ style: {
1711
+ position: "fixed",
1712
+ top: rect.top + padding.top,
1713
+ left: rect.right - padding.right,
1714
+ width: padding.right,
1715
+ height: rect.height - padding.top - padding.bottom,
1716
+ backgroundColor: paddingColor
1717
+ }
1718
+ }
1719
+ ),
1720
+ gapOverlays.map((overlay, index) => /* @__PURE__ */ jsx(
1721
+ "div",
1722
+ {
1723
+ style: {
1724
+ position: "fixed",
1725
+ top: overlay.top,
1726
+ left: overlay.left,
1727
+ width: overlay.width,
1728
+ height: overlay.height,
1729
+ backgroundColor: gapColor
1730
+ }
1731
+ },
1732
+ `gap-${index}`
1733
+ ))
1734
+ ] });
1735
+ }
1736
+ function SectionWrapper({ title, expanded, onToggle, children, hasChanges = false }) {
1737
+ return /* @__PURE__ */ jsxs("div", { style: { borderBottom: "1px solid #f4f4f5" }, children: [
1738
+ /* @__PURE__ */ jsxs(
1739
+ "button",
1740
+ {
1741
+ onClick: onToggle,
1742
+ style: {
1743
+ width: "100%",
1744
+ padding: "8px 12px",
1745
+ display: "flex",
1746
+ alignItems: "center",
1747
+ justifyContent: "space-between",
1748
+ backgroundColor: hasChanges ? "#eff6ff" : "transparent",
1749
+ border: "none",
1750
+ cursor: "pointer",
1751
+ color: "#18181b",
1752
+ fontSize: "11px",
1753
+ fontWeight: 500,
1754
+ textTransform: "uppercase",
1755
+ letterSpacing: "0.05em"
1756
+ },
1757
+ children: [
1758
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
1759
+ title,
1760
+ hasChanges && /* @__PURE__ */ jsx(
1761
+ "span",
1762
+ {
1763
+ style: {
1764
+ width: "6px",
1765
+ height: "6px",
1766
+ borderRadius: "50%",
1767
+ backgroundColor: "#3b82f6"
1768
+ },
1769
+ title: "Has unsaved changes"
1770
+ }
1771
+ )
1772
+ ] }),
1773
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: "#a1a1aa" }, children: expanded ? "\u25B2" : "\u25BC" })
1774
+ ]
1775
+ }
1776
+ ),
1777
+ expanded && /* @__PURE__ */ jsx("div", { style: { padding: "0 12px 12px" }, children })
1778
+ ] });
1779
+ }
1780
+ function ToggleGroup({ value, options, onChange }) {
1781
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "2px" }, children: options.map((option) => /* @__PURE__ */ jsx(
1782
+ "button",
1783
+ {
1784
+ onClick: () => onChange(option.value),
1785
+ style: {
1786
+ flex: 1,
1787
+ padding: "4px 6px",
1788
+ borderRadius: "4px",
1789
+ border: "1px solid #e4e4e7",
1790
+ backgroundColor: value === option.value ? "#18181b" : "#ffffff",
1791
+ color: value === option.value ? "#fafafa" : "#71717a",
1792
+ cursor: "pointer",
1793
+ fontSize: "10px",
1794
+ fontWeight: 500,
1795
+ transition: "all 0.1s ease",
1796
+ display: "flex",
1797
+ alignItems: "center",
1798
+ justifyContent: "center"
1799
+ },
1800
+ children: option.label
1801
+ },
1802
+ option.value
1803
+ )) });
1804
+ }
1805
+ function TokenDropdown({ value, tokens, onChange, placeholder = "Select..." }) {
1806
+ return /* @__PURE__ */ jsxs(
1807
+ "select",
1808
+ {
1809
+ value,
1810
+ onChange: (e) => onChange(e.target.value),
1811
+ style: {
1812
+ width: "100%",
1813
+ padding: "4px 8px",
1814
+ borderRadius: "4px",
1815
+ border: "1px solid #e4e4e7",
1816
+ backgroundColor: "#ffffff",
1817
+ color: "#18181b",
1818
+ fontSize: "11px",
1819
+ cursor: "pointer",
1820
+ outline: "none"
1821
+ },
1822
+ children: [
1823
+ /* @__PURE__ */ jsx("option", { value: "", children: placeholder }),
1824
+ Object.entries(tokens).map(([key, tokenValue]) => /* @__PURE__ */ jsxs("option", { value: tokenValue, children: [
1825
+ key,
1826
+ ": ",
1827
+ tokenValue
1828
+ ] }, key))
1829
+ ]
1830
+ }
1831
+ );
1832
+ }
1833
+ var ArrowRightIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1834
+ /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
1835
+ /* @__PURE__ */ jsx("polyline", { points: "12 5 19 12 12 19" })
1836
+ ] });
1837
+ var ArrowLeftIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1838
+ /* @__PURE__ */ jsx("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
1839
+ /* @__PURE__ */ jsx("polyline", { points: "12 19 5 12 12 5" })
1840
+ ] });
1841
+ var ArrowDownIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1842
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1843
+ /* @__PURE__ */ jsx("polyline", { points: "19 12 12 19 5 12" })
1844
+ ] });
1845
+ var ArrowUpIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1846
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
1847
+ /* @__PURE__ */ jsx("polyline", { points: "5 12 12 5 19 12" })
1848
+ ] });
1849
+ var JustifyStartIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1850
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "4", x2: "3", y2: "20" }),
1851
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "8", width: "4", height: "8", rx: "1" }),
1852
+ /* @__PURE__ */ jsx("rect", { x: "13", y: "8", width: "4", height: "8", rx: "1" })
1853
+ ] });
1854
+ var JustifyCenterIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1855
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "8", width: "4", height: "8", rx: "1" }),
1856
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "8", width: "4", height: "8", rx: "1" })
1857
+ ] });
1858
+ var JustifyEndIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1859
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "4", x2: "21", y2: "20" }),
1860
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "8", width: "4", height: "8", rx: "1" }),
1861
+ /* @__PURE__ */ jsx("rect", { x: "13", y: "8", width: "4", height: "8", rx: "1" })
1862
+ ] });
1863
+ var JustifyBetweenIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1864
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "4", x2: "3", y2: "20" }),
1865
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "4", x2: "21", y2: "20" }),
1866
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "8", width: "4", height: "8", rx: "1" }),
1867
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "8", width: "4", height: "8", rx: "1" })
1868
+ ] });
1869
+ var JustifyAroundIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1870
+ /* @__PURE__ */ jsx("rect", { x: "4", y: "8", width: "4", height: "8", rx: "1" }),
1871
+ /* @__PURE__ */ jsx("rect", { x: "10", y: "8", width: "4", height: "8", rx: "1" }),
1872
+ /* @__PURE__ */ jsx("rect", { x: "16", y: "8", width: "4", height: "8", rx: "1" })
1873
+ ] });
1874
+ var AlignStartIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1875
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "3", x2: "20", y2: "3" }),
1876
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "4", height: "10", rx: "1" }),
1877
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "6", width: "4", height: "6", rx: "1" })
1878
+ ] });
1879
+ var AlignCenterVIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1880
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "5", width: "4", height: "14", rx: "1" }),
1881
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "7", width: "4", height: "10", rx: "1" })
1882
+ ] });
1883
+ var AlignEndIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1884
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "21", x2: "20", y2: "21" }),
1885
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "8", width: "4", height: "10", rx: "1" }),
1886
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "12", width: "4", height: "6", rx: "1" })
1887
+ ] });
1888
+ var AlignStretchIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1889
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "3", x2: "20", y2: "3" }),
1890
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "21", x2: "20", y2: "21" }),
1891
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "4", height: "12", rx: "1" }),
1892
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "6", width: "4", height: "12", rx: "1" })
1893
+ ] });
1894
+ function LayoutSection({
1895
+ expanded,
1896
+ onToggle,
1897
+ computedStyle,
1898
+ onApplyStyle,
1899
+ tokens,
1900
+ hasChanges = false
1901
+ }) {
1902
+ const display = computedStyle.display;
1903
+ const flexDirection = computedStyle.flexDirection;
1904
+ const justifyContent = computedStyle.justifyContent;
1905
+ const alignItems = computedStyle.alignItems;
1906
+ const gap = computedStyle.gap;
1907
+ const isFlex = display === "flex" || display === "inline-flex";
1908
+ const isGrid = display === "grid" || display === "inline-grid";
1909
+ return /* @__PURE__ */ jsxs(SectionWrapper, { title: "Layout", expanded, onToggle, hasChanges, children: [
1910
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
1911
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Display" }),
1912
+ /* @__PURE__ */ jsx(
1913
+ ToggleGroup,
1914
+ {
1915
+ value: display,
1916
+ options: [
1917
+ { value: "block", label: "Block" },
1918
+ { value: "flex", label: "Flex" },
1919
+ { value: "grid", label: "Grid" },
1920
+ { value: "inline", label: "Inline" },
1921
+ { value: "none", label: "None" }
1922
+ ],
1923
+ onChange: (value) => onApplyStyle("display", value)
1924
+ }
1925
+ )
1926
+ ] }),
1927
+ isFlex && /* @__PURE__ */ jsxs(Fragment, { children: [
1928
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
1929
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Direction" }),
1930
+ /* @__PURE__ */ jsx(
1931
+ ToggleGroup,
1932
+ {
1933
+ value: flexDirection,
1934
+ options: [
1935
+ { value: "row", label: /* @__PURE__ */ jsx(ArrowRightIcon, {}) },
1936
+ { value: "row-reverse", label: /* @__PURE__ */ jsx(ArrowLeftIcon, {}) },
1937
+ { value: "column", label: /* @__PURE__ */ jsx(ArrowDownIcon, {}) },
1938
+ { value: "column-reverse", label: /* @__PURE__ */ jsx(ArrowUpIcon, {}) }
1939
+ ],
1940
+ onChange: (value) => onApplyStyle("flex-direction", value)
1941
+ }
1942
+ )
1943
+ ] }),
1944
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
1945
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Justify" }),
1946
+ /* @__PURE__ */ jsx(
1947
+ ToggleGroup,
1948
+ {
1949
+ value: justifyContent,
1950
+ options: [
1951
+ { value: "flex-start", label: /* @__PURE__ */ jsx(JustifyStartIcon, {}) },
1952
+ { value: "center", label: /* @__PURE__ */ jsx(JustifyCenterIcon, {}) },
1953
+ { value: "flex-end", label: /* @__PURE__ */ jsx(JustifyEndIcon, {}) },
1954
+ { value: "space-between", label: /* @__PURE__ */ jsx(JustifyBetweenIcon, {}) },
1955
+ { value: "space-around", label: /* @__PURE__ */ jsx(JustifyAroundIcon, {}) }
1956
+ ],
1957
+ onChange: (value) => onApplyStyle("justify-content", value)
1958
+ }
1959
+ )
1960
+ ] }),
1961
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
1962
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Align" }),
1963
+ /* @__PURE__ */ jsx(
1964
+ ToggleGroup,
1965
+ {
1966
+ value: alignItems,
1967
+ options: [
1968
+ { value: "flex-start", label: /* @__PURE__ */ jsx(AlignStartIcon, {}) },
1969
+ { value: "center", label: /* @__PURE__ */ jsx(AlignCenterVIcon, {}) },
1970
+ { value: "flex-end", label: /* @__PURE__ */ jsx(AlignEndIcon, {}) },
1971
+ { value: "stretch", label: /* @__PURE__ */ jsx(AlignStretchIcon, {}) }
1972
+ ],
1973
+ onChange: (value) => onApplyStyle("align-items", value)
1974
+ }
1975
+ )
1976
+ ] })
1977
+ ] }),
1978
+ (isFlex || isGrid) && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
1979
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Gap" }),
1980
+ /* @__PURE__ */ jsx(
1981
+ TokenDropdown,
1982
+ {
1983
+ value: gap,
1984
+ tokens: tokens.spacing,
1985
+ onChange: (value) => onApplyStyle("gap", value)
1986
+ }
1987
+ )
1988
+ ] })
1989
+ ] });
1990
+ }
1991
+ var ChainIcon = ({ linked }) => /* @__PURE__ */ jsx(
1992
+ "svg",
1993
+ {
1994
+ width: "12",
1995
+ height: "12",
1996
+ viewBox: "0 0 24 24",
1997
+ fill: "none",
1998
+ stroke: "currentColor",
1999
+ strokeWidth: "2",
2000
+ strokeLinecap: "round",
2001
+ strokeLinejoin: "round",
2002
+ style: { opacity: linked ? 1 : 0.4 },
2003
+ children: linked ? (
2004
+ // Linked chain
2005
+ /* @__PURE__ */ jsxs(Fragment, { children: [
2006
+ /* @__PURE__ */ jsx("path", { d: "M9 17H7A5 5 0 0 1 7 7h2" }),
2007
+ /* @__PURE__ */ jsx("path", { d: "M15 7h2a5 5 0 1 1 0 10h-2" }),
2008
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "12", x2: "16", y2: "12" })
2009
+ ] })
2010
+ ) : (
2011
+ // Broken chain
2012
+ /* @__PURE__ */ jsxs(Fragment, { children: [
2013
+ /* @__PURE__ */ jsx("path", { d: "M9 17H7A5 5 0 0 1 7 7h2" }),
2014
+ /* @__PURE__ */ jsx("path", { d: "M15 7h2a5 5 0 1 1 0 10h-2" }),
2015
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "12", x2: "10", y2: "12" }),
2016
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "12", x2: "16", y2: "12" })
2017
+ ] })
2018
+ )
2019
+ }
2020
+ );
2021
+ function isValidCSSValue(value) {
2022
+ if (!value || value.trim() === "") return true;
2023
+ const validPatterns = [
2024
+ /^-?\d+(\.\d+)?(px|em|rem|%|vh|vw|pt|cm|mm|in|pc)?$/i,
2025
+ /^auto$/i,
2026
+ /^inherit$/i,
2027
+ /^initial$/i,
2028
+ /^unset$/i,
2029
+ /^0$/
2030
+ ];
2031
+ return validPatterns.some((pattern) => pattern.test(value.trim()));
2032
+ }
2033
+ function SpacingControl({ values, onChange, label, hasChanges = {} }) {
2034
+ const [editingSide, setEditingSide] = useState(null);
2035
+ const [editValue, setEditValue] = useState("");
2036
+ const [linkVertical, setLinkVertical] = useState(false);
2037
+ const [linkHorizontal, setLinkHorizontal] = useState(false);
2038
+ const [highlightLinked, setHighlightLinked] = useState(null);
2039
+ const inputRef = useRef(null);
2040
+ useEffect(() => {
2041
+ if (editingSide && inputRef.current) {
2042
+ inputRef.current.focus();
2043
+ inputRef.current.select();
2044
+ }
2045
+ }, [editingSide]);
2046
+ const handleChange = useCallback((side, value) => {
2047
+ onChange(side, value);
2048
+ if (linkVertical && (side === "top" || side === "bottom")) {
2049
+ onChange(side === "top" ? "bottom" : "top", value);
2050
+ }
2051
+ if (linkHorizontal && (side === "left" || side === "right")) {
2052
+ onChange(side === "left" ? "right" : "left", value);
2053
+ }
2054
+ }, [onChange, linkVertical, linkHorizontal]);
2055
+ const handleBoxClick = (side, e) => {
2056
+ e.stopPropagation();
2057
+ setEditingSide(side);
2058
+ setEditValue(values[side]);
2059
+ if (linkVertical && (side === "top" || side === "bottom")) {
2060
+ setHighlightLinked("vertical");
2061
+ } else if (linkHorizontal && (side === "left" || side === "right")) {
2062
+ setHighlightLinked("horizontal");
2063
+ }
2064
+ };
2065
+ const handleInputChange = (value) => {
2066
+ setEditValue(value);
2067
+ if (editingSide) {
2068
+ handleChange(editingSide, value);
2069
+ }
2070
+ };
2071
+ const handleInputBlur = () => {
2072
+ setEditingSide(null);
2073
+ setHighlightLinked(null);
2074
+ };
2075
+ const handleKeyDown = (e) => {
2076
+ if (e.key === "Enter" || e.key === "Escape") {
2077
+ setEditingSide(null);
2078
+ setHighlightLinked(null);
2079
+ }
2080
+ };
2081
+ const formatValue = (value) => {
2082
+ const match = value.match(/^([\d.]+)/);
2083
+ return match ? match[1] : value || "0";
2084
+ };
2085
+ const isLinkedHighlighted = (side) => {
2086
+ if (!highlightLinked) return false;
2087
+ if (highlightLinked === "vertical" && (side === "top" || side === "bottom")) return true;
2088
+ if (highlightLinked === "horizontal" && (side === "left" || side === "right")) return true;
2089
+ return false;
2090
+ };
2091
+ const getBoxStyle = (side) => {
2092
+ const isEditing = editingSide === side;
2093
+ const isLinked = isLinkedHighlighted(side);
2094
+ const isValid = isEditing ? isValidCSSValue(editValue) : true;
2095
+ const hasChange = hasChanges[side];
2096
+ return {
2097
+ padding: isEditing ? "0" : "2px 4px",
2098
+ borderRadius: "3px",
2099
+ border: `1px solid ${!isValid ? "#ef4444" : hasChange ? "#3b82f6" : isLinked ? "#8b5cf6" : isEditing ? "#18181b" : "#e4e4e7"}`,
2100
+ backgroundColor: !isValid ? "#fef2f2" : hasChange ? "#eff6ff" : isLinked ? "#f5f3ff" : "#ffffff",
2101
+ color: "#18181b",
2102
+ fontSize: "9px",
2103
+ textAlign: "center",
2104
+ cursor: "text",
2105
+ minWidth: "28px",
2106
+ transition: "all 0.1s ease",
2107
+ outline: "none"
2108
+ };
2109
+ };
2110
+ const linkButtonStyle = (active) => ({
2111
+ padding: "2px 4px",
2112
+ borderRadius: "3px",
2113
+ border: `1px solid ${active ? "#8b5cf6" : "#e4e4e7"}`,
2114
+ backgroundColor: active ? "#f5f3ff" : "#ffffff",
2115
+ color: active ? "#8b5cf6" : "#71717a",
2116
+ cursor: "pointer",
2117
+ display: "flex",
2118
+ alignItems: "center",
2119
+ justifyContent: "center",
2120
+ transition: "all 0.1s ease"
2121
+ });
2122
+ const renderValueBox = (side, position) => {
2123
+ const isEditing = editingSide === side;
2124
+ return /* @__PURE__ */ jsx("div", { style: { position: "absolute", ...position }, children: isEditing ? /* @__PURE__ */ jsx(
2125
+ "input",
2126
+ {
2127
+ ref: inputRef,
2128
+ type: "text",
2129
+ value: editValue,
2130
+ onChange: (e) => handleInputChange(e.target.value),
2131
+ onBlur: handleInputBlur,
2132
+ onKeyDown: handleKeyDown,
2133
+ style: {
2134
+ ...getBoxStyle(side),
2135
+ width: "36px",
2136
+ fontFamily: "inherit"
2137
+ }
2138
+ }
2139
+ ) : /* @__PURE__ */ jsx(
2140
+ "div",
2141
+ {
2142
+ onClick: (e) => handleBoxClick(side, e),
2143
+ style: getBoxStyle(side),
2144
+ title: `${side}: ${values[side]}`,
2145
+ children: formatValue(values[side])
2146
+ }
2147
+ ) });
2148
+ };
2149
+ return /* @__PURE__ */ jsxs("div", { children: [
2150
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "6px" }, children: [
2151
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a" }, children: label }),
2152
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "2px" }, children: [
2153
+ /* @__PURE__ */ jsx(
2154
+ "button",
2155
+ {
2156
+ style: linkButtonStyle(linkVertical),
2157
+ onClick: () => setLinkVertical(!linkVertical),
2158
+ title: linkVertical ? "Unlink top/bottom" : "Link top/bottom",
2159
+ children: /* @__PURE__ */ jsx("span", { style: { transform: "rotate(90deg)", display: "flex" }, children: /* @__PURE__ */ jsx(ChainIcon, { linked: linkVertical }) })
2160
+ }
2161
+ ),
2162
+ /* @__PURE__ */ jsx(
2163
+ "button",
2164
+ {
2165
+ style: linkButtonStyle(linkHorizontal),
2166
+ onClick: () => setLinkHorizontal(!linkHorizontal),
2167
+ title: linkHorizontal ? "Unlink left/right" : "Link left/right",
2168
+ children: /* @__PURE__ */ jsx(ChainIcon, { linked: linkHorizontal })
2169
+ }
2170
+ )
2171
+ ] })
2172
+ ] }),
2173
+ /* @__PURE__ */ jsxs(
2174
+ "div",
2175
+ {
2176
+ style: {
2177
+ position: "relative",
2178
+ width: "100px",
2179
+ height: "40px",
2180
+ margin: "0 auto"
2181
+ },
2182
+ children: [
2183
+ /* @__PURE__ */ jsxs(
2184
+ "svg",
2185
+ {
2186
+ style: {
2187
+ position: "absolute",
2188
+ top: 0,
2189
+ left: 0,
2190
+ width: "100%",
2191
+ height: "100%",
2192
+ pointerEvents: "none"
2193
+ },
2194
+ viewBox: "0 0 100 70",
2195
+ children: [
2196
+ /* @__PURE__ */ jsx(
2197
+ "rect",
2198
+ {
2199
+ x: "10",
2200
+ y: "10",
2201
+ width: "80",
2202
+ height: "50",
2203
+ fill: "none",
2204
+ stroke: "lightgray",
2205
+ strokeWidth: "1",
2206
+ strokeDasharray: "3,2",
2207
+ rx: "2"
2208
+ }
2209
+ ),
2210
+ /* @__PURE__ */ jsx("line", { x1: "50", y1: "10", x2: "50", y2: "2", stroke: "lightgray", strokeWidth: "1" }),
2211
+ /* @__PURE__ */ jsx("line", { x1: "50", y1: "60", x2: "50", y2: "68", stroke: "lightgray", strokeWidth: "1" }),
2212
+ /* @__PURE__ */ jsx("line", { x1: "10", y1: "35", x2: "2", y2: "35", stroke: "lightgray", strokeWidth: "1" }),
2213
+ /* @__PURE__ */ jsx("line", { x1: "90", y1: "35", x2: "98", y2: "35", stroke: "lightgray", strokeWidth: "1" })
2214
+ ]
2215
+ }
2216
+ ),
2217
+ renderValueBox("top", { top: "-8px", left: "50%", transform: "translateX(-50%)" }),
2218
+ renderValueBox("bottom", { bottom: "-8px", left: "50%", transform: "translateX(-50%)" }),
2219
+ renderValueBox("left", { left: "-12px", top: "50%", transform: "translateY(-50%)" }),
2220
+ renderValueBox("right", { right: "-12px", top: "50%", transform: "translateY(-50%)" })
2221
+ ]
2222
+ }
2223
+ )
2224
+ ] });
2225
+ }
2226
+ function SpacingSection({
2227
+ expanded,
2228
+ onToggle,
2229
+ computedStyle,
2230
+ onApplyStyle,
2231
+ hasChanges = false,
2232
+ changedProps = {}
2233
+ }) {
2234
+ const margin = {
2235
+ top: computedStyle.marginTop,
2236
+ right: computedStyle.marginRight,
2237
+ bottom: computedStyle.marginBottom,
2238
+ left: computedStyle.marginLeft
2239
+ };
2240
+ const padding = {
2241
+ top: computedStyle.paddingTop,
2242
+ right: computedStyle.paddingRight,
2243
+ bottom: computedStyle.paddingBottom,
2244
+ left: computedStyle.paddingLeft
2245
+ };
2246
+ const marginChanges = {
2247
+ top: changedProps["margin-top"],
2248
+ right: changedProps["margin-right"],
2249
+ bottom: changedProps["margin-bottom"],
2250
+ left: changedProps["margin-left"]
2251
+ };
2252
+ const paddingChanges = {
2253
+ top: changedProps["padding-top"],
2254
+ right: changedProps["padding-right"],
2255
+ bottom: changedProps["padding-bottom"],
2256
+ left: changedProps["padding-left"]
2257
+ };
2258
+ return /* @__PURE__ */ jsxs(SectionWrapper, { title: "Spacing", expanded, onToggle, hasChanges, children: [
2259
+ /* @__PURE__ */ jsx("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ jsx(
2260
+ SpacingControl,
2261
+ {
2262
+ label: "Margin",
2263
+ values: margin,
2264
+ onChange: (side, value) => onApplyStyle(`margin-${side}`, value),
2265
+ hasChanges: marginChanges
2266
+ }
2267
+ ) }),
2268
+ /* @__PURE__ */ jsx(
2269
+ SpacingControl,
2270
+ {
2271
+ label: "Padding",
2272
+ values: padding,
2273
+ onChange: (side, value) => onApplyStyle(`padding-${side}`, value),
2274
+ hasChanges: paddingChanges
2275
+ }
2276
+ )
2277
+ ] });
2278
+ }
2279
+ function SliderInput({
2280
+ value,
2281
+ min = 0,
2282
+ max = 100,
2283
+ step = 1,
2284
+ unit = "px",
2285
+ onChange,
2286
+ label
2287
+ }) {
2288
+ const [isDragging, setIsDragging] = useState(false);
2289
+ const [localValue, setLocalValue] = useState(value);
2290
+ const handleMouseDown = useCallback((e) => {
2291
+ e.preventDefault();
2292
+ setIsDragging(true);
2293
+ const startX = e.clientX;
2294
+ const startValue = localValue;
2295
+ const handleMouseMove = (e2) => {
2296
+ const delta = e2.clientX - startX;
2297
+ const sensitivity = e2.shiftKey ? 0.1 : 1;
2298
+ const newValue = Math.min(max, Math.max(min, startValue + delta * sensitivity));
2299
+ const steppedValue = Math.round(newValue / step) * step;
2300
+ setLocalValue(steppedValue);
2301
+ onChange(steppedValue);
2302
+ };
2303
+ const handleMouseUp = () => {
2304
+ setIsDragging(false);
2305
+ document.removeEventListener("mousemove", handleMouseMove);
2306
+ document.removeEventListener("mouseup", handleMouseUp);
2307
+ };
2308
+ document.addEventListener("mousemove", handleMouseMove);
2309
+ document.addEventListener("mouseup", handleMouseUp);
2310
+ }, [localValue, min, max, step, onChange]);
2311
+ const handleInputChange = (e) => {
2312
+ const newValue = parseFloat(e.target.value) || 0;
2313
+ setLocalValue(newValue);
2314
+ onChange(newValue);
2315
+ };
2316
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
2317
+ label && /* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: "#71717a", width: "18px" }, children: label }),
2318
+ /* @__PURE__ */ jsxs(
2319
+ "div",
2320
+ {
2321
+ style: {
2322
+ flex: 1,
2323
+ display: "flex",
2324
+ alignItems: "center",
2325
+ backgroundColor: "#ffffff",
2326
+ border: "1px solid #e4e4e7",
2327
+ borderRadius: "4px",
2328
+ padding: "3px 6px",
2329
+ cursor: "ew-resize"
2330
+ },
2331
+ onMouseDown: handleMouseDown,
2332
+ children: [
2333
+ /* @__PURE__ */ jsx(
2334
+ "input",
2335
+ {
2336
+ type: "number",
2337
+ value: localValue,
2338
+ onChange: handleInputChange,
2339
+ style: {
2340
+ width: "100%",
2341
+ backgroundColor: "transparent",
2342
+ border: "none",
2343
+ color: "#18181b",
2344
+ fontSize: "11px",
2345
+ fontFamily: "ui-monospace, monospace",
2346
+ outline: "none",
2347
+ cursor: isDragging ? "ew-resize" : "text"
2348
+ },
2349
+ onClick: (e) => e.stopPropagation()
2350
+ }
2351
+ ),
2352
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: "#a1a1aa", marginLeft: "4px" }, children: unit })
2353
+ ]
2354
+ }
2355
+ )
2356
+ ] });
2357
+ }
2358
+ var sizePresets = {
2359
+ "auto": "auto",
2360
+ "full": "100%",
2361
+ "fit": "fit-content",
2362
+ "min": "min-content",
2363
+ "max": "max-content",
2364
+ "screen": "100vh"
2365
+ };
2366
+ function SizeInput({
2367
+ label,
2368
+ value,
2369
+ property,
2370
+ onApplyStyle
2371
+ }) {
2372
+ const [showSlider, setShowSlider] = useState(false);
2373
+ const numericValue = parseFloat(value) || 0;
2374
+ const isPresetSelected = (presetValue) => {
2375
+ return value.toLowerCase() === presetValue.toLowerCase();
2376
+ };
2377
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "10px" }, children: [
2378
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "4px" }, children: [
2379
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a" }, children: label }),
2380
+ /* @__PURE__ */ jsx(
2381
+ "button",
2382
+ {
2383
+ onClick: () => setShowSlider(!showSlider),
2384
+ style: {
2385
+ padding: "1px 4px",
2386
+ borderRadius: "2px",
2387
+ border: "1px solid #e4e4e7",
2388
+ backgroundColor: showSlider ? "#18181b" : "#ffffff",
2389
+ color: showSlider ? "#fafafa" : "#71717a",
2390
+ fontSize: "8px",
2391
+ cursor: "pointer"
2392
+ },
2393
+ title: "Toggle custom size slider",
2394
+ children: "px"
2395
+ }
2396
+ )
2397
+ ] }),
2398
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "2px", flexWrap: "wrap", marginBottom: showSlider ? "6px" : 0 }, children: Object.entries(sizePresets).slice(0, 5).map(([key, presetValue]) => /* @__PURE__ */ jsx(
2399
+ "button",
2400
+ {
2401
+ onClick: () => onApplyStyle(property, presetValue),
2402
+ style: {
2403
+ padding: "2px 5px",
2404
+ borderRadius: "3px",
2405
+ border: "1px solid",
2406
+ borderColor: isPresetSelected(presetValue) ? "#18181b" : "#e4e4e7",
2407
+ backgroundColor: isPresetSelected(presetValue) ? "#18181b" : "#ffffff",
2408
+ color: isPresetSelected(presetValue) ? "#fafafa" : "#18181b",
2409
+ fontSize: "9px",
2410
+ fontFamily: "ui-monospace, monospace",
2411
+ cursor: "pointer",
2412
+ transition: "all 0.1s ease"
2413
+ },
2414
+ title: presetValue,
2415
+ children: key
2416
+ },
2417
+ key
2418
+ )) }),
2419
+ showSlider && /* @__PURE__ */ jsx(
2420
+ SliderInput,
2421
+ {
2422
+ value: numericValue,
2423
+ min: 0,
2424
+ max: 1e3,
2425
+ onChange: (v) => onApplyStyle(property, `${v}px`)
2426
+ }
2427
+ )
2428
+ ] });
2429
+ }
2430
+ function SizeSection({
2431
+ expanded,
2432
+ onToggle,
2433
+ computedStyle,
2434
+ onApplyStyle,
2435
+ hasChanges = false
2436
+ }) {
2437
+ const width = computedStyle.width;
2438
+ const height = computedStyle.height;
2439
+ const minWidth = computedStyle.minWidth;
2440
+ const maxWidth = computedStyle.maxWidth;
2441
+ const minHeight = computedStyle.minHeight;
2442
+ const maxHeight = computedStyle.maxHeight;
2443
+ return /* @__PURE__ */ jsxs(SectionWrapper, { title: "Size", expanded, onToggle, hasChanges, children: [
2444
+ /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "12px" }, children: [
2445
+ /* @__PURE__ */ jsx(
2446
+ SizeInput,
2447
+ {
2448
+ label: "Width",
2449
+ value: width,
2450
+ property: "width",
2451
+ onApplyStyle
2452
+ }
2453
+ ),
2454
+ /* @__PURE__ */ jsx(
2455
+ SizeInput,
2456
+ {
2457
+ label: "Height",
2458
+ value: height,
2459
+ property: "height",
2460
+ onApplyStyle
2461
+ }
2462
+ )
2463
+ ] }),
2464
+ /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "12px" }, children: [
2465
+ /* @__PURE__ */ jsx(
2466
+ SizeInput,
2467
+ {
2468
+ label: "Min W",
2469
+ value: minWidth,
2470
+ property: "min-width",
2471
+ onApplyStyle
2472
+ }
2473
+ ),
2474
+ /* @__PURE__ */ jsx(
2475
+ SizeInput,
2476
+ {
2477
+ label: "Max W",
2478
+ value: maxWidth,
2479
+ property: "max-width",
2480
+ onApplyStyle
2481
+ }
2482
+ )
2483
+ ] }),
2484
+ /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "12px" }, children: [
2485
+ /* @__PURE__ */ jsx(
2486
+ SizeInput,
2487
+ {
2488
+ label: "Min H",
2489
+ value: minHeight,
2490
+ property: "min-height",
2491
+ onApplyStyle
2492
+ }
2493
+ ),
2494
+ /* @__PURE__ */ jsx(
2495
+ SizeInput,
2496
+ {
2497
+ label: "Max H",
2498
+ value: maxHeight,
2499
+ property: "max-height",
2500
+ onApplyStyle
2501
+ }
2502
+ )
2503
+ ] })
2504
+ ] });
2505
+ }
2506
+ function QuickSelectDropdown({
2507
+ value,
2508
+ tokens,
2509
+ quickKeys,
2510
+ onChange,
2511
+ placeholder = "More..."
2512
+ }) {
2513
+ const [showDropdown, setShowDropdown] = useState(false);
2514
+ const { quickItems, dropdownItems } = useMemo(() => {
2515
+ const quick = [];
2516
+ const dropdown = [];
2517
+ for (const [key, tokenValue] of Object.entries(tokens)) {
2518
+ if (quickKeys.includes(key)) {
2519
+ quick.push({ key, value: tokenValue });
2520
+ } else {
2521
+ dropdown.push({ key, value: tokenValue });
2522
+ }
2523
+ }
2524
+ quick.sort((a, b) => quickKeys.indexOf(a.key) - quickKeys.indexOf(b.key));
2525
+ return { quickItems: quick, dropdownItems: dropdown };
2526
+ }, [tokens, quickKeys]);
2527
+ const isSelected = (tokenValue) => {
2528
+ const normalizeValue = (v) => v.replace(/\s+/g, "").toLowerCase();
2529
+ return normalizeValue(value) === normalizeValue(tokenValue);
2530
+ };
2531
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "3px", flexWrap: "wrap" }, children: [
2532
+ quickItems.map(({ key, value: tokenValue }) => /* @__PURE__ */ jsx(
2533
+ "button",
2534
+ {
2535
+ onClick: () => onChange(tokenValue),
2536
+ style: {
2537
+ padding: "3px 6px",
2538
+ borderRadius: "3px",
2539
+ border: "1px solid",
2540
+ borderColor: isSelected(tokenValue) ? "#18181b" : "#e4e4e7",
2541
+ backgroundColor: isSelected(tokenValue) ? "#18181b" : "#ffffff",
2542
+ color: isSelected(tokenValue) ? "#fafafa" : "#18181b",
2543
+ fontSize: "10px",
2544
+ fontFamily: "ui-monospace, monospace",
2545
+ cursor: "pointer",
2546
+ transition: "all 0.1s ease",
2547
+ minWidth: "28px",
2548
+ textAlign: "center"
2549
+ },
2550
+ title: `${key}: ${tokenValue}`,
2551
+ children: key
2552
+ },
2553
+ key
2554
+ )),
2555
+ dropdownItems.length > 0 && /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
2556
+ /* @__PURE__ */ jsx(
2557
+ "button",
2558
+ {
2559
+ onClick: () => setShowDropdown(!showDropdown),
2560
+ onBlur: () => setTimeout(() => setShowDropdown(false), 150),
2561
+ style: {
2562
+ padding: "3px 6px",
2563
+ borderRadius: "3px",
2564
+ border: "1px solid #e4e4e7",
2565
+ backgroundColor: "#ffffff",
2566
+ color: "#71717a",
2567
+ fontSize: "10px",
2568
+ cursor: "pointer",
2569
+ display: "flex",
2570
+ alignItems: "center",
2571
+ gap: "2px"
2572
+ },
2573
+ children: /* @__PURE__ */ jsx("span", { children: "..." })
2574
+ }
2575
+ ),
2576
+ showDropdown && /* @__PURE__ */ jsx(
2577
+ "div",
2578
+ {
2579
+ style: {
2580
+ position: "absolute",
2581
+ top: "100%",
2582
+ left: 0,
2583
+ marginTop: "2px",
2584
+ backgroundColor: "#ffffff",
2585
+ border: "1px solid #e4e4e7",
2586
+ borderRadius: "4px",
2587
+ boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)",
2588
+ zIndex: 10,
2589
+ maxHeight: "150px",
2590
+ overflow: "auto",
2591
+ minWidth: "100px"
2592
+ },
2593
+ children: dropdownItems.map(({ key, value: tokenValue }) => /* @__PURE__ */ jsxs(
2594
+ "button",
2595
+ {
2596
+ onClick: () => {
2597
+ onChange(tokenValue);
2598
+ setShowDropdown(false);
2599
+ },
2600
+ style: {
2601
+ display: "block",
2602
+ width: "100%",
2603
+ padding: "4px 8px",
2604
+ border: "none",
2605
+ backgroundColor: isSelected(tokenValue) ? "#f4f4f5" : "transparent",
2606
+ color: "#18181b",
2607
+ fontSize: "10px",
2608
+ fontFamily: "ui-monospace, monospace",
2609
+ cursor: "pointer",
2610
+ textAlign: "left"
2611
+ },
2612
+ children: [
2613
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: key }),
2614
+ /* @__PURE__ */ jsx("span", { style: { color: "#71717a", marginLeft: "4px" }, children: tokenValue })
2615
+ ]
2616
+ },
2617
+ key
2618
+ ))
2619
+ }
2620
+ )
2621
+ ] })
2622
+ ] });
2623
+ }
2624
+ function ColorPicker({ value, colors: colors2, onChange }) {
2625
+ const [isExpanded, setIsExpanded] = useState(false);
2626
+ const colorGrid = useMemo(() => {
2627
+ const grid = [];
2628
+ for (const [name, shades] of Object.entries(colors2)) {
2629
+ if (typeof shades === "object") {
2630
+ for (const [shade, colorValue] of Object.entries(shades)) {
2631
+ grid.push({ name, shade, value: colorValue });
2632
+ }
2633
+ }
2634
+ }
2635
+ return grid;
2636
+ }, [colors2]);
2637
+ const commonShades = ["500", "600", "700"];
2638
+ const compactColors = useMemo(() => {
2639
+ return colorGrid.filter((c) => commonShades.includes(c.shade));
2640
+ }, [colorGrid]);
2641
+ return /* @__PURE__ */ jsxs("div", { children: [
2642
+ /* @__PURE__ */ jsxs(
2643
+ "div",
2644
+ {
2645
+ style: {
2646
+ display: "flex",
2647
+ alignItems: "center",
2648
+ gap: "6px",
2649
+ marginBottom: "6px"
2650
+ },
2651
+ children: [
2652
+ /* @__PURE__ */ jsx(
2653
+ "div",
2654
+ {
2655
+ style: {
2656
+ width: "22px",
2657
+ height: "22px",
2658
+ borderRadius: "4px",
2659
+ backgroundColor: value,
2660
+ border: "1px solid #e4e4e7",
2661
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06)"
2662
+ }
2663
+ }
2664
+ ),
2665
+ /* @__PURE__ */ jsx(
2666
+ "input",
2667
+ {
2668
+ type: "text",
2669
+ value,
2670
+ onChange: (e) => onChange(e.target.value),
2671
+ style: {
2672
+ flex: 1,
2673
+ backgroundColor: "#ffffff",
2674
+ border: "1px solid #e4e4e7",
2675
+ borderRadius: "4px",
2676
+ padding: "4px 8px",
2677
+ color: "#18181b",
2678
+ fontSize: "11px",
2679
+ fontFamily: "ui-monospace, monospace"
2680
+ }
2681
+ }
2682
+ ),
2683
+ /* @__PURE__ */ jsx(
2684
+ "button",
2685
+ {
2686
+ onClick: () => setIsExpanded(!isExpanded),
2687
+ style: {
2688
+ padding: "4px 6px",
2689
+ borderRadius: "4px",
2690
+ border: "1px solid #e4e4e7",
2691
+ backgroundColor: "#ffffff",
2692
+ color: "#71717a",
2693
+ fontSize: "10px",
2694
+ cursor: "pointer"
2695
+ },
2696
+ children: isExpanded ? "\u25B2" : "\u25BC"
2697
+ }
2698
+ )
2699
+ ]
2700
+ }
2701
+ ),
2702
+ isExpanded && /* @__PURE__ */ jsx(
2703
+ "div",
2704
+ {
2705
+ style: {
2706
+ display: "grid",
2707
+ gridTemplateColumns: "repeat(11, 1fr)",
2708
+ gap: "2px",
2709
+ padding: "6px",
2710
+ backgroundColor: "#fafafa",
2711
+ border: "1px solid #e4e4e7",
2712
+ borderRadius: "8px",
2713
+ maxHeight: "200px",
2714
+ overflow: "auto"
2715
+ },
2716
+ children: colorGrid.map((color, i) => /* @__PURE__ */ jsx(
2717
+ "button",
2718
+ {
2719
+ onClick: () => onChange(color.value),
2720
+ style: {
2721
+ width: "18px",
2722
+ height: "18px",
2723
+ borderRadius: "3px",
2724
+ backgroundColor: color.value,
2725
+ border: value === color.value ? "2px solid #18181b" : "1px solid #e4e4e7",
2726
+ cursor: "pointer"
2727
+ },
2728
+ title: `${color.name}-${color.shade}`
2729
+ },
2730
+ i
2731
+ ))
2732
+ }
2733
+ ),
2734
+ !isExpanded && /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "3px", flexWrap: "wrap" }, children: compactColors.map((color, i) => /* @__PURE__ */ jsx(
2735
+ "button",
2736
+ {
2737
+ onClick: () => onChange(color.value),
2738
+ style: {
2739
+ width: "14px",
2740
+ height: "14px",
2741
+ borderRadius: "3px",
2742
+ backgroundColor: color.value,
2743
+ border: value === color.value ? "2px solid #18181b" : "1px solid #e4e4e7",
2744
+ cursor: "pointer"
2745
+ },
2746
+ title: `${color.name}-${color.shade}`
2747
+ },
2748
+ i
2749
+ )) })
2750
+ ] });
2751
+ }
2752
+ var AlignLeftIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2753
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
2754
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "15", y2: "12" }),
2755
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "18", y2: "18" })
2756
+ ] });
2757
+ var AlignCenterIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2758
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
2759
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "12", x2: "18", y2: "12" }),
2760
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "18", x2: "20", y2: "18" })
2761
+ ] });
2762
+ var AlignRightIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2763
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
2764
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "12", x2: "21", y2: "12" }),
2765
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "18", x2: "21", y2: "18" })
2766
+ ] });
2767
+ var AlignJustifyIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2768
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
2769
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }),
2770
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
2771
+ ] });
2772
+ function TypographySection({
2773
+ expanded,
2774
+ onToggle,
2775
+ computedStyle,
2776
+ onApplyStyle,
2777
+ tokens,
2778
+ hasChanges = false
2779
+ }) {
2780
+ const color = computedStyle.color;
2781
+ const fontSize2 = computedStyle.fontSize;
2782
+ const fontWeight2 = computedStyle.fontWeight;
2783
+ const fontFamily2 = computedStyle.fontFamily;
2784
+ const textAlign = computedStyle.textAlign;
2785
+ const lineHeight2 = computedStyle.lineHeight;
2786
+ return /* @__PURE__ */ jsxs(SectionWrapper, { title: "Typography", expanded, onToggle, hasChanges, children: [
2787
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2788
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Color" }),
2789
+ /* @__PURE__ */ jsx(
2790
+ ColorPicker,
2791
+ {
2792
+ value: color,
2793
+ colors: tokens.colors,
2794
+ onChange: (value) => onApplyStyle("color", value)
2795
+ }
2796
+ )
2797
+ ] }),
2798
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2799
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Font Size" }),
2800
+ /* @__PURE__ */ jsx(
2801
+ QuickSelectDropdown,
2802
+ {
2803
+ value: fontSize2,
2804
+ tokens: tokens.fontSize,
2805
+ quickKeys: ["xs", "sm", "base", "lg", "xl", "2xl"],
2806
+ onChange: (value) => onApplyStyle("font-size", value)
2807
+ }
2808
+ )
2809
+ ] }),
2810
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2811
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Font Weight" }),
2812
+ /* @__PURE__ */ jsx(
2813
+ QuickSelectDropdown,
2814
+ {
2815
+ value: fontWeight2,
2816
+ tokens: tokens.fontWeight,
2817
+ quickKeys: ["light", "normal", "medium", "semibold", "bold"],
2818
+ onChange: (value) => onApplyStyle("font-weight", value)
2819
+ }
2820
+ )
2821
+ ] }),
2822
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2823
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Font Family" }),
2824
+ /* @__PURE__ */ jsx(
2825
+ TokenDropdown,
2826
+ {
2827
+ value: fontFamily2,
2828
+ tokens: tokens.fontFamily,
2829
+ onChange: (value) => onApplyStyle("font-family", value)
2830
+ }
2831
+ )
2832
+ ] }),
2833
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2834
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Text Align" }),
2835
+ /* @__PURE__ */ jsx(
2836
+ ToggleGroup,
2837
+ {
2838
+ value: textAlign,
2839
+ options: [
2840
+ { value: "left", label: /* @__PURE__ */ jsx(AlignLeftIcon, {}) },
2841
+ { value: "center", label: /* @__PURE__ */ jsx(AlignCenterIcon, {}) },
2842
+ { value: "right", label: /* @__PURE__ */ jsx(AlignRightIcon, {}) },
2843
+ { value: "justify", label: /* @__PURE__ */ jsx(AlignJustifyIcon, {}) }
2844
+ ],
2845
+ onChange: (value) => onApplyStyle("text-align", value)
2846
+ }
2847
+ )
2848
+ ] }),
2849
+ /* @__PURE__ */ jsxs("div", { children: [
2850
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Line Height" }),
2851
+ /* @__PURE__ */ jsx(
2852
+ QuickSelectDropdown,
2853
+ {
2854
+ value: lineHeight2,
2855
+ tokens: tokens.lineHeight,
2856
+ quickKeys: ["tight", "snug", "normal", "relaxed"],
2857
+ onChange: (value) => onApplyStyle("line-height", value)
2858
+ }
2859
+ )
2860
+ ] })
2861
+ ] });
2862
+ }
2863
+ function BackgroundSection({
2864
+ expanded,
2865
+ onToggle,
2866
+ computedStyle,
2867
+ onApplyStyle,
2868
+ tokens,
2869
+ hasChanges = false
2870
+ }) {
2871
+ const backgroundColor = computedStyle.backgroundColor;
2872
+ const borderColor = computedStyle.borderColor;
2873
+ const borderWidth2 = computedStyle.borderWidth;
2874
+ const borderRadius2 = computedStyle.borderRadius;
2875
+ return /* @__PURE__ */ jsxs(SectionWrapper, { title: "Background & Border", expanded, onToggle, hasChanges, children: [
2876
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2877
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Background" }),
2878
+ /* @__PURE__ */ jsx(
2879
+ ColorPicker,
2880
+ {
2881
+ value: backgroundColor,
2882
+ colors: tokens.colors,
2883
+ onChange: (value) => onApplyStyle("background-color", value)
2884
+ }
2885
+ )
2886
+ ] }),
2887
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2888
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Border Color" }),
2889
+ /* @__PURE__ */ jsx(
2890
+ ColorPicker,
2891
+ {
2892
+ value: borderColor,
2893
+ colors: tokens.colors,
2894
+ onChange: (value) => onApplyStyle("border-color", value)
2895
+ }
2896
+ )
2897
+ ] }),
2898
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2899
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Border Width" }),
2900
+ /* @__PURE__ */ jsx(
2901
+ TokenDropdown,
2902
+ {
2903
+ value: borderWidth2,
2904
+ tokens: tokens.borderWidth,
2905
+ onChange: (value) => onApplyStyle("border-width", value)
2906
+ }
2907
+ )
2908
+ ] }),
2909
+ /* @__PURE__ */ jsxs("div", { children: [
2910
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Border Radius" }),
2911
+ /* @__PURE__ */ jsx(
2912
+ TokenDropdown,
2913
+ {
2914
+ value: borderRadius2,
2915
+ tokens: tokens.borderRadius,
2916
+ onChange: (value) => onApplyStyle("border-radius", value)
2917
+ }
2918
+ )
2919
+ ] })
2920
+ ] });
2921
+ }
2922
+ function EffectsSection({
2923
+ expanded,
2924
+ onToggle,
2925
+ computedStyle,
2926
+ onApplyStyle,
2927
+ tokens,
2928
+ hasChanges = false
2929
+ }) {
2930
+ const boxShadow2 = computedStyle.boxShadow;
2931
+ const borderRadius2 = computedStyle.borderRadius;
2932
+ const borderWidth2 = computedStyle.borderWidth;
2933
+ const opacity = parseFloat(computedStyle.opacity) * 100;
2934
+ return /* @__PURE__ */ jsxs(SectionWrapper, { title: "Effects", expanded, onToggle, hasChanges, children: [
2935
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2936
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Border Radius" }),
2937
+ /* @__PURE__ */ jsx(
2938
+ QuickSelectDropdown,
2939
+ {
2940
+ value: borderRadius2,
2941
+ tokens: tokens.borderRadius,
2942
+ quickKeys: ["none", "sm", "md", "lg", "full"],
2943
+ onChange: (value) => onApplyStyle("border-radius", value)
2944
+ }
2945
+ )
2946
+ ] }),
2947
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2948
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Border Width" }),
2949
+ /* @__PURE__ */ jsx(
2950
+ QuickSelectDropdown,
2951
+ {
2952
+ value: borderWidth2,
2953
+ tokens: tokens.borderWidth,
2954
+ quickKeys: ["0", "DEFAULT", "2", "4"],
2955
+ onChange: (value) => onApplyStyle("border-width", value)
2956
+ }
2957
+ )
2958
+ ] }),
2959
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px" }, children: [
2960
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Shadow" }),
2961
+ /* @__PURE__ */ jsx(
2962
+ QuickSelectDropdown,
2963
+ {
2964
+ value: boxShadow2,
2965
+ tokens: tokens.boxShadow,
2966
+ quickKeys: ["none", "sm", "md", "lg"],
2967
+ onChange: (value) => onApplyStyle("box-shadow", value)
2968
+ }
2969
+ )
2970
+ ] }),
2971
+ /* @__PURE__ */ jsxs("div", { children: [
2972
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "10px", color: "#71717a", marginBottom: "4px", display: "block" }, children: "Opacity" }),
2973
+ /* @__PURE__ */ jsx(
2974
+ SliderInput,
2975
+ {
2976
+ value: opacity,
2977
+ min: 0,
2978
+ max: 100,
2979
+ step: 1,
2980
+ unit: "%",
2981
+ onChange: (value) => onApplyStyle("opacity", String(value / 100))
2982
+ }
2983
+ )
2984
+ ] })
2985
+ ] });
2986
+ }
2987
+ function AnnotationSection({
2988
+ expanded,
2989
+ onToggle,
2990
+ onAnnotate,
2991
+ existingAnnotation = "",
2992
+ hasChanges = false
2993
+ }) {
2994
+ const [note, setNote] = useState(existingAnnotation);
2995
+ const [isSaved, setIsSaved] = useState(!!existingAnnotation);
2996
+ const debounceRef = useRef(null);
2997
+ const textareaRef = useRef(null);
2998
+ useEffect(() => {
2999
+ setNote(existingAnnotation);
3000
+ setIsSaved(!!existingAnnotation);
3001
+ }, [existingAnnotation]);
3002
+ const handleChange = useCallback((value) => {
3003
+ setNote(value);
3004
+ setIsSaved(false);
3005
+ if (debounceRef.current) {
3006
+ clearTimeout(debounceRef.current);
3007
+ }
3008
+ if (value.trim()) {
3009
+ debounceRef.current = setTimeout(() => {
3010
+ onAnnotate(value.trim());
3011
+ setIsSaved(true);
3012
+ }, 500);
3013
+ }
3014
+ }, [onAnnotate]);
3015
+ useEffect(() => {
3016
+ return () => {
3017
+ if (debounceRef.current) {
3018
+ clearTimeout(debounceRef.current);
3019
+ }
3020
+ };
3021
+ }, []);
3022
+ const handleBlur = () => {
3023
+ if (debounceRef.current) {
3024
+ clearTimeout(debounceRef.current);
3025
+ }
3026
+ if (note.trim() && !isSaved) {
3027
+ onAnnotate(note.trim());
3028
+ setIsSaved(true);
3029
+ }
3030
+ };
3031
+ const showActiveState = !!(note.trim() && isSaved);
3032
+ return /* @__PURE__ */ jsx(
3033
+ SectionWrapper,
3034
+ {
3035
+ title: "Annotation",
3036
+ expanded,
3037
+ onToggle,
3038
+ hasChanges: hasChanges || showActiveState,
3039
+ children: /* @__PURE__ */ jsxs(
3040
+ "div",
3041
+ {
3042
+ style: {
3043
+ position: "relative",
3044
+ borderRadius: "4px",
3045
+ border: `1px solid ${showActiveState ? "#3b82f6" : "#e4e4e7"}`,
3046
+ backgroundColor: showActiveState ? "#eff6ff" : "#ffffff",
3047
+ transition: "all 0.15s ease"
3048
+ },
3049
+ children: [
3050
+ /* @__PURE__ */ jsx(
3051
+ "textarea",
3052
+ {
3053
+ ref: textareaRef,
3054
+ value: note,
3055
+ onChange: (e) => handleChange(e.target.value),
3056
+ onBlur: handleBlur,
3057
+ placeholder: "Add a note about this element...",
3058
+ style: {
3059
+ width: "100%",
3060
+ minHeight: "60px",
3061
+ padding: "8px",
3062
+ borderRadius: "4px",
3063
+ border: "none",
3064
+ backgroundColor: "transparent",
3065
+ color: "#18181b",
3066
+ fontSize: "11px",
3067
+ resize: "vertical",
3068
+ fontFamily: "system-ui, -apple-system, sans-serif",
3069
+ outline: "none"
3070
+ }
3071
+ }
3072
+ ),
3073
+ note.trim() && /* @__PURE__ */ jsx(
3074
+ "div",
3075
+ {
3076
+ style: {
3077
+ position: "absolute",
3078
+ bottom: "4px",
3079
+ right: "4px",
3080
+ fontSize: "9px",
3081
+ color: isSaved ? "#3b82f6" : "#a1a1aa",
3082
+ display: "flex",
3083
+ alignItems: "center",
3084
+ gap: "2px"
3085
+ },
3086
+ children: isSaved ? /* @__PURE__ */ jsxs(Fragment, { children: [
3087
+ /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }),
3088
+ "Saved"
3089
+ ] }) : "Saving..."
3090
+ }
3091
+ )
3092
+ ]
3093
+ }
3094
+ )
3095
+ }
3096
+ );
3097
+ }
3098
+ var SunIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
3099
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "5" }),
3100
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }),
3101
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }),
3102
+ /* @__PURE__ */ jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }),
3103
+ /* @__PURE__ */ jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }),
3104
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }),
3105
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }),
3106
+ /* @__PURE__ */ jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }),
3107
+ /* @__PURE__ */ jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })
3108
+ ] });
3109
+ var MoonIcon = () => /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) });
3110
+ var MonitorIcon = () => /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
3111
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
3112
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
3113
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
3114
+ ] });
3115
+ var SpacingIcon = () => /* @__PURE__ */ jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
3116
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
3117
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "7", width: "10", height: "10", rx: "1" })
3118
+ ] });
3119
+ var ResetIcon = () => /* @__PURE__ */ jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
3120
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
3121
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" })
3122
+ ] });
3123
+ var propertySectionMap = {
3124
+ "display": "layout",
3125
+ "flex-direction": "layout",
3126
+ "justify-content": "layout",
3127
+ "align-items": "layout",
3128
+ "flex-wrap": "layout",
3129
+ "gap": "layout",
3130
+ "margin-top": "spacing",
3131
+ "margin-right": "spacing",
3132
+ "margin-bottom": "spacing",
3133
+ "margin-left": "spacing",
3134
+ "padding-top": "spacing",
3135
+ "padding-right": "spacing",
3136
+ "padding-bottom": "spacing",
3137
+ "padding-left": "spacing",
3138
+ "width": "size",
3139
+ "height": "size",
3140
+ "min-width": "size",
3141
+ "min-height": "size",
3142
+ "max-width": "size",
3143
+ "max-height": "size",
3144
+ "font-size": "typography",
3145
+ "font-weight": "typography",
3146
+ "font-family": "typography",
3147
+ "line-height": "typography",
3148
+ "letter-spacing": "typography",
3149
+ "text-align": "typography",
3150
+ "color": "typography",
3151
+ "background-color": "background",
3152
+ "background": "background",
3153
+ "border-radius": "effects",
3154
+ "box-shadow": "effects",
3155
+ "border": "effects",
3156
+ "border-width": "effects",
3157
+ "opacity": "effects"
3158
+ };
3159
+ function EditPanel({
3160
+ selectedElement,
3161
+ actions,
3162
+ tokens,
3163
+ onClose,
3164
+ showMarginPadding,
3165
+ zIndex = 9999,
3166
+ theme,
3167
+ onThemeToggle,
3168
+ changes,
3169
+ annotations,
3170
+ onReset
3171
+ }) {
3172
+ const [expandedSections, setExpandedSections] = useState(
3173
+ /* @__PURE__ */ new Set(["annotation", "layout", "spacing", "typography"])
3174
+ );
3175
+ const changedSections = useMemo(() => {
3176
+ const sections = /* @__PURE__ */ new Set();
3177
+ const elementChanges = changes.filter(
3178
+ (c) => c.target.path === selectedElement.path && c.type === "style"
3179
+ );
3180
+ for (const change of elementChanges) {
3181
+ const property = change.after.property;
3182
+ const section = propertySectionMap[property];
3183
+ if (section) {
3184
+ sections.add(section);
3185
+ }
3186
+ }
3187
+ return sections;
3188
+ }, [changes, selectedElement.path]);
3189
+ const changedSpacingProps = useMemo(() => {
3190
+ const props = {};
3191
+ const elementChanges = changes.filter(
3192
+ (c) => c.target.path === selectedElement.path && c.type === "style"
3193
+ );
3194
+ for (const change of elementChanges) {
3195
+ const prop = change.after.property;
3196
+ if (prop.startsWith("margin-") || prop.startsWith("padding-")) {
3197
+ props[prop] = true;
3198
+ }
3199
+ }
3200
+ return props;
3201
+ }, [changes, selectedElement.path]);
3202
+ const toggleSection = (section) => {
3203
+ setExpandedSections((prev) => {
3204
+ const next = new Set(prev);
3205
+ if (next.has(section)) {
3206
+ next.delete(section);
3207
+ } else {
3208
+ next.add(section);
3209
+ }
3210
+ return next;
3211
+ });
3212
+ };
3213
+ const computedStyle = window.getComputedStyle(selectedElement.element);
3214
+ const themeIcons = {
3215
+ light: /* @__PURE__ */ jsx(SunIcon, {}),
3216
+ dark: /* @__PURE__ */ jsx(MoonIcon, {}),
3217
+ system: /* @__PURE__ */ jsx(MonitorIcon, {})
3218
+ };
3219
+ const hasAnyChanges = changes.some((c) => c.target.path === selectedElement.path);
3220
+ return /* @__PURE__ */ jsxs(
3221
+ "div",
3222
+ {
3223
+ "data-bobbin": "edit-panel",
3224
+ style: {
3225
+ position: "fixed",
3226
+ top: "16px",
3227
+ right: "16px",
3228
+ width: "280px",
3229
+ maxHeight: "calc(100vh - 32px)",
3230
+ backgroundColor: "#fafafa",
3231
+ borderRadius: "8px",
3232
+ border: "1px solid #e4e4e7",
3233
+ boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
3234
+ zIndex,
3235
+ overflow: "hidden",
3236
+ display: "flex",
3237
+ flexDirection: "column",
3238
+ color: "#18181b",
3239
+ fontFamily: "system-ui, -apple-system, sans-serif",
3240
+ fontSize: "13px"
3241
+ },
3242
+ children: [
3243
+ /* @__PURE__ */ jsxs(
3244
+ "div",
3245
+ {
3246
+ style: {
3247
+ padding: "10px 12px",
3248
+ borderBottom: "1px solid #e4e4e7",
3249
+ display: "flex",
3250
+ alignItems: "center",
3251
+ justifyContent: "space-between",
3252
+ backgroundColor: "#ffffff"
3253
+ },
3254
+ children: [
3255
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
3256
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500, fontSize: "12px" }, children: selectedElement.tagName }),
3257
+ selectedElement.id && /* @__PURE__ */ jsxs("span", { style: { color: "#71717a", fontSize: "11px" }, children: [
3258
+ "#",
3259
+ selectedElement.id
3260
+ ] }),
3261
+ selectedElement.classList.length > 0 && /* @__PURE__ */ jsxs("span", { style: { color: "#a1a1aa", fontSize: "10px" }, children: [
3262
+ ".",
3263
+ selectedElement.classList.slice(0, 2).join("."),
3264
+ selectedElement.classList.length > 2 && "..."
3265
+ ] })
3266
+ ] }),
3267
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
3268
+ /* @__PURE__ */ jsx(
3269
+ "button",
3270
+ {
3271
+ onClick: onThemeToggle,
3272
+ style: {
3273
+ padding: "4px 6px",
3274
+ borderRadius: "4px",
3275
+ border: "1px solid #e4e4e7",
3276
+ backgroundColor: "#ffffff",
3277
+ cursor: "pointer",
3278
+ fontSize: "12px"
3279
+ },
3280
+ title: `Theme: ${theme}`,
3281
+ children: themeIcons[theme]
3282
+ }
3283
+ ),
3284
+ /* @__PURE__ */ jsx(
3285
+ "button",
3286
+ {
3287
+ onClick: actions.toggleMarginPadding,
3288
+ style: {
3289
+ padding: "4px 6px",
3290
+ borderRadius: "4px",
3291
+ border: "1px solid #e4e4e7",
3292
+ backgroundColor: showMarginPadding ? "#18181b" : "#ffffff",
3293
+ color: showMarginPadding ? "#fafafa" : "#71717a",
3294
+ cursor: "pointer",
3295
+ fontSize: "10px",
3296
+ display: "flex",
3297
+ alignItems: "center",
3298
+ justifyContent: "center"
3299
+ },
3300
+ title: "Toggle spacing visualization",
3301
+ children: /* @__PURE__ */ jsx(SpacingIcon, {})
3302
+ }
3303
+ ),
3304
+ hasAnyChanges && /* @__PURE__ */ jsx(
3305
+ "button",
3306
+ {
3307
+ onClick: onReset,
3308
+ style: {
3309
+ padding: "4px 6px",
3310
+ borderRadius: "4px",
3311
+ border: "1px solid #e4e4e7",
3312
+ backgroundColor: "#ffffff",
3313
+ color: "#71717a",
3314
+ cursor: "pointer",
3315
+ fontSize: "10px",
3316
+ display: "flex",
3317
+ alignItems: "center",
3318
+ justifyContent: "center"
3319
+ },
3320
+ title: "Reset all changes",
3321
+ children: /* @__PURE__ */ jsx(ResetIcon, {})
3322
+ }
3323
+ ),
3324
+ /* @__PURE__ */ jsx(
3325
+ "button",
3326
+ {
3327
+ onClick: onClose,
3328
+ style: {
3329
+ width: "22px",
3330
+ height: "22px",
3331
+ borderRadius: "4px",
3332
+ border: "1px solid #e4e4e7",
3333
+ backgroundColor: "#ffffff",
3334
+ color: "#71717a",
3335
+ cursor: "pointer",
3336
+ display: "flex",
3337
+ alignItems: "center",
3338
+ justifyContent: "center",
3339
+ fontSize: "12px"
3340
+ },
3341
+ children: "\xD7"
3342
+ }
3343
+ )
3344
+ ] })
3345
+ ]
3346
+ }
3347
+ ),
3348
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, overflow: "auto", padding: "8px 0" }, children: [
3349
+ /* @__PURE__ */ jsx(
3350
+ AnnotationSection,
3351
+ {
3352
+ expanded: expandedSections.has("annotation"),
3353
+ onToggle: () => toggleSection("annotation"),
3354
+ onAnnotate: actions.annotate,
3355
+ existingAnnotation: annotations.find((a) => a.elementPath === selectedElement.path)?.content,
3356
+ hasChanges: annotations.some((a) => a.elementPath === selectedElement.path)
3357
+ }
3358
+ ),
3359
+ /* @__PURE__ */ jsx(
3360
+ LayoutSection,
3361
+ {
3362
+ expanded: expandedSections.has("layout"),
3363
+ onToggle: () => toggleSection("layout"),
3364
+ computedStyle,
3365
+ onApplyStyle: actions.applyStyle,
3366
+ tokens,
3367
+ hasChanges: changedSections.has("layout")
3368
+ }
3369
+ ),
3370
+ /* @__PURE__ */ jsx(
3371
+ SpacingSection,
3372
+ {
3373
+ expanded: expandedSections.has("spacing"),
3374
+ onToggle: () => toggleSection("spacing"),
3375
+ computedStyle,
3376
+ onApplyStyle: actions.applyStyle,
3377
+ tokens,
3378
+ hasChanges: changedSections.has("spacing"),
3379
+ changedProps: changedSpacingProps
3380
+ }
3381
+ ),
3382
+ /* @__PURE__ */ jsx(
3383
+ SizeSection,
3384
+ {
3385
+ expanded: expandedSections.has("size"),
3386
+ onToggle: () => toggleSection("size"),
3387
+ computedStyle,
3388
+ onApplyStyle: actions.applyStyle,
3389
+ tokens,
3390
+ hasChanges: changedSections.has("size")
3391
+ }
3392
+ ),
3393
+ /* @__PURE__ */ jsx(
3394
+ TypographySection,
3395
+ {
3396
+ expanded: expandedSections.has("typography"),
3397
+ onToggle: () => toggleSection("typography"),
3398
+ computedStyle,
3399
+ onApplyStyle: actions.applyStyle,
3400
+ tokens,
3401
+ hasChanges: changedSections.has("typography")
3402
+ }
3403
+ ),
3404
+ /* @__PURE__ */ jsx(
3405
+ BackgroundSection,
3406
+ {
3407
+ expanded: expandedSections.has("background"),
3408
+ onToggle: () => toggleSection("background"),
3409
+ computedStyle,
3410
+ onApplyStyle: actions.applyStyle,
3411
+ tokens,
3412
+ hasChanges: changedSections.has("background")
3413
+ }
3414
+ ),
3415
+ /* @__PURE__ */ jsx(
3416
+ EffectsSection,
3417
+ {
3418
+ expanded: expandedSections.has("effects"),
3419
+ onToggle: () => toggleSection("effects"),
3420
+ computedStyle,
3421
+ onApplyStyle: actions.applyStyle,
3422
+ tokens,
3423
+ hasChanges: changedSections.has("effects")
3424
+ }
3425
+ )
3426
+ ] })
3427
+ ]
3428
+ }
3429
+ );
3430
+ }
3431
+ function Inspector({
3432
+ selectedElement,
3433
+ onSelectElement,
3434
+ zIndex = 9999
3435
+ }) {
3436
+ const [activeTab, setActiveTab] = useState("attributes");
3437
+ const [isMinimized, setIsMinimized] = useState(false);
3438
+ const computedStyles = useMemo(() => {
3439
+ const computed = window.getComputedStyle(selectedElement.element);
3440
+ const styles = {};
3441
+ const properties = [
3442
+ "display",
3443
+ "position",
3444
+ "width",
3445
+ "height",
3446
+ "margin",
3447
+ "padding",
3448
+ "border",
3449
+ "background",
3450
+ "color",
3451
+ "font-family",
3452
+ "font-size",
3453
+ "font-weight",
3454
+ "line-height",
3455
+ "text-align",
3456
+ "flex",
3457
+ "grid"
3458
+ ];
3459
+ for (const prop of properties) {
3460
+ styles[prop] = computed.getPropertyValue(prop);
3461
+ }
3462
+ return styles;
3463
+ }, [selectedElement.element]);
3464
+ const attributes = useMemo(() => {
3465
+ const attrs = {};
3466
+ const el = selectedElement.element;
3467
+ for (const attr of el.attributes) {
3468
+ if (attr.name.toLowerCase() === "contenteditable") {
3469
+ continue;
3470
+ }
3471
+ attrs[attr.name] = attr.value;
3472
+ }
3473
+ return attrs;
3474
+ }, [selectedElement.element]);
3475
+ const domPath = useMemo(() => {
3476
+ const path = [];
3477
+ let el = selectedElement.element;
3478
+ while (el && el !== document.body) {
3479
+ path.unshift(el);
3480
+ el = el.parentElement;
3481
+ }
3482
+ return path;
3483
+ }, [selectedElement.element]);
3484
+ return /* @__PURE__ */ jsx(
3485
+ "div",
3486
+ {
3487
+ "data-bobbin": "inspector",
3488
+ style: {
3489
+ position: "fixed",
3490
+ bottom: "16px",
3491
+ left: "16px",
3492
+ width: isMinimized ? "auto" : "320px",
3493
+ maxHeight: isMinimized ? "auto" : "260px",
3494
+ backgroundColor: "#fafafa",
3495
+ borderRadius: "8px",
3496
+ border: "1px solid #e4e4e7",
3497
+ boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)",
3498
+ zIndex,
3499
+ overflow: "hidden",
3500
+ display: "flex",
3501
+ flexDirection: "column",
3502
+ color: "#18181b",
3503
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
3504
+ fontSize: "11px"
3505
+ },
3506
+ children: isMinimized ? /* @__PURE__ */ jsx(
3507
+ "div",
3508
+ {
3509
+ style: {
3510
+ padding: "6px 10px",
3511
+ display: "flex",
3512
+ alignItems: "center",
3513
+ gap: "8px",
3514
+ cursor: "pointer",
3515
+ backgroundColor: "#ffffff"
3516
+ },
3517
+ onClick: () => setIsMinimized(false),
3518
+ children: /* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: "#71717a" }, children: "Inspector" })
3519
+ }
3520
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
3521
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", borderBottom: "1px solid #e4e4e7", backgroundColor: "#ffffff" }, children: [
3522
+ ["styles", "attributes"].map((tab) => /* @__PURE__ */ jsx(
3523
+ "button",
3524
+ {
3525
+ onClick: () => setActiveTab(tab),
3526
+ style: {
3527
+ flex: 1,
3528
+ padding: "6px 8px",
3529
+ border: "none",
3530
+ borderBottom: activeTab === tab ? "2px solid #18181b" : "2px solid transparent",
3531
+ backgroundColor: "transparent",
3532
+ color: activeTab === tab ? "#18181b" : "#71717a",
3533
+ cursor: "pointer",
3534
+ textTransform: "capitalize",
3535
+ fontSize: "11px",
3536
+ fontWeight: activeTab === tab ? 500 : 400
3537
+ },
3538
+ children: tab
3539
+ },
3540
+ tab
3541
+ )),
3542
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "2px", padding: "0 4px" }, children: /* @__PURE__ */ jsx(
3543
+ "button",
3544
+ {
3545
+ onClick: () => setIsMinimized(true),
3546
+ style: {
3547
+ width: "18px",
3548
+ height: "18px",
3549
+ borderRadius: "3px",
3550
+ border: "none",
3551
+ backgroundColor: "transparent",
3552
+ color: "#71717a",
3553
+ cursor: "pointer",
3554
+ display: "flex",
3555
+ alignItems: "center",
3556
+ justifyContent: "center",
3557
+ fontSize: "14px"
3558
+ },
3559
+ title: "Minimize",
3560
+ children: "\u2212"
3561
+ }
3562
+ ) })
3563
+ ] }),
3564
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, overflow: "auto", padding: "6px" }, children: [
3565
+ activeTab === "tree" && /* @__PURE__ */ jsx("div", { children: domPath.map((el, i) => /* @__PURE__ */ jsxs(
3566
+ "div",
3567
+ {
3568
+ onClick: () => onSelectElement(el),
3569
+ style: {
3570
+ padding: "3px 6px",
3571
+ paddingLeft: `${i * 10 + 6}px`,
3572
+ cursor: "pointer",
3573
+ backgroundColor: el === selectedElement.element ? "#18181b" : "transparent",
3574
+ color: el === selectedElement.element ? "#fafafa" : "#18181b",
3575
+ borderRadius: "3px",
3576
+ marginBottom: "1px"
3577
+ },
3578
+ children: [
3579
+ /* @__PURE__ */ jsx("span", { style: { color: el === selectedElement.element ? "#a1a1aa" : "#52525b" }, children: el.tagName.toLowerCase() }),
3580
+ el.id && /* @__PURE__ */ jsxs("span", { style: { color: el === selectedElement.element ? "#d4d4d8" : "#71717a" }, children: [
3581
+ "#",
3582
+ el.id
3583
+ ] }),
3584
+ el.classList.length > 0 && /* @__PURE__ */ jsxs("span", { style: { color: el === selectedElement.element ? "#a1a1aa" : "#a1a1aa" }, children: [
3585
+ ".",
3586
+ Array.from(el.classList).slice(0, 2).join(".")
3587
+ ] })
3588
+ ]
3589
+ },
3590
+ i
3591
+ )) }),
3592
+ activeTab === "styles" && /* @__PURE__ */ jsx("div", { children: Object.entries(computedStyles).map(([prop, value]) => /* @__PURE__ */ jsxs(
3593
+ "div",
3594
+ {
3595
+ style: {
3596
+ display: "flex",
3597
+ padding: "2px 0",
3598
+ borderBottom: "1px solid #f4f4f5"
3599
+ },
3600
+ children: [
3601
+ /* @__PURE__ */ jsxs("span", { style: { color: "#52525b", width: "100px" }, children: [
3602
+ prop,
3603
+ ":"
3604
+ ] }),
3605
+ /* @__PURE__ */ jsx("span", { style: { color: "#18181b", flex: 1 }, children: value })
3606
+ ]
3607
+ },
3608
+ prop
3609
+ )) }),
3610
+ activeTab === "attributes" && /* @__PURE__ */ jsx("div", { children: Object.entries(attributes).map(([name, value]) => /* @__PURE__ */ jsxs(
3611
+ "div",
3612
+ {
3613
+ style: {
3614
+ display: "flex",
3615
+ padding: "2px 0",
3616
+ borderBottom: "1px solid #f4f4f5"
3617
+ },
3618
+ children: [
3619
+ /* @__PURE__ */ jsx("span", { style: { color: "#71717a", width: "80px" }, children: name }),
3620
+ /* @__PURE__ */ jsxs("span", { style: { color: "#18181b" }, children: [
3621
+ '"',
3622
+ value,
3623
+ '"'
3624
+ ] })
3625
+ ]
3626
+ },
3627
+ name
3628
+ )) })
3629
+ ] })
3630
+ ] })
3631
+ }
3632
+ );
3633
+ }
3634
+ function Bobbin(props) {
3635
+ const { showInspector = false, ...bobbinProps } = props;
3636
+ const bobbin = useBobbin(bobbinProps);
3637
+ const { zIndex = 9999, pillContainer } = bobbinProps;
3638
+ return createPortal(
3639
+ /* @__PURE__ */ jsxs("div", { "data-bobbin": "root", children: [
3640
+ /* @__PURE__ */ jsx(
3641
+ Pill,
3642
+ {
3643
+ state: bobbin,
3644
+ actions: bobbin,
3645
+ position: bobbinProps.position,
3646
+ container: pillContainer ?? bobbinProps.container,
3647
+ zIndex
3648
+ }
3649
+ ),
3650
+ bobbin.isActive && /* @__PURE__ */ jsx(
3651
+ SelectionOverlay,
3652
+ {
3653
+ hoveredElement: bobbin.hoveredElement,
3654
+ selectedElement: bobbin.selectedElement,
3655
+ zIndex: zIndex - 10
3656
+ }
3657
+ ),
3658
+ bobbin.selectedElement && /* @__PURE__ */ jsx(
3659
+ ControlHandles,
3660
+ {
3661
+ selectedElement: bobbin.selectedElement,
3662
+ actions: bobbin,
3663
+ clipboard: bobbin.clipboard,
3664
+ zIndex
3665
+ }
3666
+ ),
3667
+ bobbin.selectedElement && bobbin.showMarginPadding && /* @__PURE__ */ jsx(
3668
+ MarginPaddingOverlay,
3669
+ {
3670
+ selectedElement: bobbin.selectedElement,
3671
+ zIndex: zIndex - 5
3672
+ }
3673
+ ),
3674
+ bobbin.selectedElement && bobbin.activePanel === "style" && /* @__PURE__ */ jsx(
3675
+ EditPanel,
3676
+ {
3677
+ selectedElement: bobbin.selectedElement,
3678
+ actions: bobbin,
3679
+ tokens: bobbin.tokens,
3680
+ onClose: bobbin.clearSelection,
3681
+ showMarginPadding: bobbin.showMarginPadding,
3682
+ zIndex,
3683
+ theme: bobbin.theme,
3684
+ onThemeToggle: bobbin.toggleTheme,
3685
+ changes: bobbin.changes,
3686
+ annotations: bobbin.annotations,
3687
+ onReset: bobbin.resetChanges
3688
+ }
3689
+ ),
3690
+ bobbin.selectedElement && (showInspector || bobbin.activePanel === "inspector") && /* @__PURE__ */ jsx(
3691
+ Inspector,
3692
+ {
3693
+ selectedElement: bobbin.selectedElement,
3694
+ onSelectElement: (el) => bobbin.selectElement(el),
3695
+ onClose: () => bobbin.setActivePanel(null),
3696
+ zIndex
3697
+ }
3698
+ )
3699
+ ] }),
3700
+ document.body
3701
+ );
3702
+ }
3703
+
3704
+ export { Bobbin, borderRadius, borderWidth, boxShadow, colors, defaultTokens, fontFamily, fontSize, fontWeight, generateId, getElementPath, getElementXPath, letterSpacing, lineHeight, parseYAMLChangeset, serializeChangesToYAML, spacing, useBobbin, useChangeTracker, useClipboard, useElementSelection };