dialkit 1.0.0 → 1.2.0

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 (97) hide show
  1. package/README.md +293 -2
  2. package/dist/index.cjs +905 -363
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +46 -9
  5. package/dist/index.d.ts +46 -9
  6. package/dist/index.js +910 -370
  7. package/dist/index.js.map +1 -1
  8. package/dist/solid/index.cjs +1042 -532
  9. package/dist/solid/index.cjs.map +1 -1
  10. package/dist/solid/index.d.cts +36 -3
  11. package/dist/solid/index.d.ts +36 -3
  12. package/dist/solid/index.js +911 -402
  13. package/dist/solid/index.js.map +1 -1
  14. package/dist/store/index.cjs +490 -0
  15. package/dist/store/index.cjs.map +1 -0
  16. package/dist/store/index.d.cts +146 -0
  17. package/dist/store/index.d.ts +146 -0
  18. package/dist/store/index.js +465 -0
  19. package/dist/store/index.js.map +1 -0
  20. package/dist/styles.css +287 -25
  21. package/dist/svelte/Portal.svelte +30 -0
  22. package/dist/svelte/Portal.svelte.d.ts +9 -0
  23. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  24. package/dist/svelte/components/ButtonGroup.svelte +16 -0
  25. package/dist/svelte/components/ButtonGroup.svelte.d.ts +11 -0
  26. package/dist/svelte/components/ButtonGroup.svelte.d.ts.map +1 -0
  27. package/dist/svelte/components/ColorControl.svelte +81 -0
  28. package/dist/svelte/components/ColorControl.svelte.d.ts +9 -0
  29. package/dist/svelte/components/ColorControl.svelte.d.ts.map +1 -0
  30. package/dist/svelte/components/ControlRenderer.svelte +96 -0
  31. package/dist/svelte/components/ControlRenderer.svelte.d.ts +11 -0
  32. package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -0
  33. package/dist/svelte/components/DialRoot.svelte +79 -0
  34. package/dist/svelte/components/DialRoot.svelte.d.ts +14 -0
  35. package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -0
  36. package/dist/svelte/components/EasingVisualization.svelte +50 -0
  37. package/dist/svelte/components/EasingVisualization.svelte.d.ts +10 -0
  38. package/dist/svelte/components/EasingVisualization.svelte.d.ts.map +1 -0
  39. package/dist/svelte/components/Folder.svelte +213 -0
  40. package/dist/svelte/components/Folder.svelte.d.ts +14 -0
  41. package/dist/svelte/components/Folder.svelte.d.ts.map +1 -0
  42. package/dist/svelte/components/Panel.svelte +149 -0
  43. package/dist/svelte/components/Panel.svelte.d.ts +10 -0
  44. package/dist/svelte/components/Panel.svelte.d.ts.map +1 -0
  45. package/dist/svelte/components/PresetManager.svelte +157 -0
  46. package/dist/svelte/components/PresetManager.svelte.d.ts +10 -0
  47. package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -0
  48. package/dist/svelte/components/SegmentedControl.svelte +62 -0
  49. package/dist/svelte/components/SegmentedControl.svelte.d.ts +13 -0
  50. package/dist/svelte/components/SegmentedControl.svelte.d.ts.map +1 -0
  51. package/dist/svelte/components/SelectControl.svelte +152 -0
  52. package/dist/svelte/components/SelectControl.svelte.d.ts +14 -0
  53. package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -0
  54. package/dist/svelte/components/ShortcutListener.svelte +265 -0
  55. package/dist/svelte/components/ShortcutListener.svelte.d.ts +13 -0
  56. package/dist/svelte/components/ShortcutListener.svelte.d.ts.map +1 -0
  57. package/dist/svelte/components/ShortcutsMenu.svelte +128 -0
  58. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts +7 -0
  59. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts.map +1 -0
  60. package/dist/svelte/components/Slider.svelte +332 -0
  61. package/dist/svelte/components/Slider.svelte.d.ts +16 -0
  62. package/dist/svelte/components/Slider.svelte.d.ts.map +1 -0
  63. package/dist/svelte/components/SpringControl.svelte +126 -0
  64. package/dist/svelte/components/SpringControl.svelte.d.ts +12 -0
  65. package/dist/svelte/components/SpringControl.svelte.d.ts.map +1 -0
  66. package/dist/svelte/components/SpringVisualization.svelte +115 -0
  67. package/dist/svelte/components/SpringVisualization.svelte.d.ts +9 -0
  68. package/dist/svelte/components/SpringVisualization.svelte.d.ts.map +1 -0
  69. package/dist/svelte/components/TextControl.svelte +19 -0
  70. package/dist/svelte/components/TextControl.svelte.d.ts +10 -0
  71. package/dist/svelte/components/TextControl.svelte.d.ts.map +1 -0
  72. package/dist/svelte/components/Toggle.svelte +32 -0
  73. package/dist/svelte/components/Toggle.svelte.d.ts +12 -0
  74. package/dist/svelte/components/Toggle.svelte.d.ts.map +1 -0
  75. package/dist/svelte/components/TransitionControl.svelte +203 -0
  76. package/dist/svelte/components/TransitionControl.svelte.d.ts +12 -0
  77. package/dist/svelte/components/TransitionControl.svelte.d.ts.map +1 -0
  78. package/dist/svelte/components/transitions.d.ts +6 -0
  79. package/dist/svelte/components/transitions.d.ts.map +1 -0
  80. package/dist/svelte/components/transitions.js +14 -0
  81. package/dist/svelte/createDialKit.svelte.d.ts +8 -0
  82. package/dist/svelte/createDialKit.svelte.d.ts.map +1 -0
  83. package/dist/svelte/createDialKit.svelte.js +82 -0
  84. package/dist/svelte/index.d.ts +23 -0
  85. package/dist/svelte/index.d.ts.map +1 -0
  86. package/dist/svelte/index.js +23 -0
  87. package/dist/svelte/shims.d.ts +9 -0
  88. package/dist/svelte/theme-css.d.ts +2 -0
  89. package/dist/svelte/theme-css.d.ts.map +1 -0
  90. package/dist/svelte/theme-css.js +1328 -0
  91. package/dist/vue/index.cjs +3056 -0
  92. package/dist/vue/index.cjs.map +1 -0
  93. package/dist/vue/index.d.cts +675 -0
  94. package/dist/vue/index.d.ts +675 -0
  95. package/dist/vue/index.js +3014 -0
  96. package/dist/vue/index.js.map +1 -0
  97. package/package.json +50 -6
package/dist/index.js CHANGED
@@ -16,22 +16,22 @@ var DialStoreClass = class {
16
16
  this.activePreset = /* @__PURE__ */ new Map();
17
17
  this.baseValues = /* @__PURE__ */ new Map();
18
18
  }
19
- registerPanel(id, name, config) {
20
- const controls = this.parseConfig(config, "");
19
+ registerPanel(id, name, config, shortcuts) {
20
+ const controls = this.parseConfig(config, "", shortcuts);
21
21
  const values = this.flattenValues(config, "");
22
22
  this.initTransitionModes(config, "", values);
23
- this.panels.set(id, { id, name, controls, values });
23
+ this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {} });
24
24
  this.snapshots.set(id, { ...values });
25
25
  this.baseValues.set(id, { ...values });
26
26
  this.notifyGlobal();
27
27
  }
28
- updatePanel(id, name, config) {
28
+ updatePanel(id, name, config, shortcuts) {
29
29
  const existing = this.panels.get(id);
30
30
  if (!existing) {
31
- this.registerPanel(id, name, config);
31
+ this.registerPanel(id, name, config, shortcuts);
32
32
  return;
33
33
  }
34
- const controls = this.parseConfig(config, "");
34
+ const controls = this.parseConfig(config, "", shortcuts);
35
35
  const controlsByPath = this.mapControlsByPath(controls);
36
36
  const defaultValues = this.flattenValues(config, "");
37
37
  const nextValues = {};
@@ -53,7 +53,7 @@ var DialStoreClass = class {
53
53
  nextValues[path] = mode;
54
54
  }
55
55
  }
56
- const nextPanel = { id, name, controls, values: nextValues };
56
+ const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts };
57
57
  this.panels.set(id, nextPanel);
58
58
  this.snapshots.set(id, { ...nextValues });
59
59
  const previousBaseValues = this.baseValues.get(id) ?? {};
@@ -211,6 +211,44 @@ var DialStoreClass = class {
211
211
  this.activePreset.set(panelId, null);
212
212
  this.notify(panelId);
213
213
  }
214
+ resolveShortcutTarget(key, modifier) {
215
+ for (const panel of this.panels.values()) {
216
+ for (const [path, shortcut] of Object.entries(panel.shortcuts)) {
217
+ if (!shortcut.key) continue;
218
+ if (shortcut.key.toLowerCase() !== key.toLowerCase()) continue;
219
+ const scMod = shortcut.modifier ?? void 0;
220
+ if (scMod !== modifier) continue;
221
+ const control = this.findControlByPath(panel.controls, path);
222
+ if (control) {
223
+ return { panelId: panel.id, path, control };
224
+ }
225
+ }
226
+ }
227
+ return null;
228
+ }
229
+ resolveScrollOnlyTargets() {
230
+ const results = [];
231
+ for (const panel of this.panels.values()) {
232
+ for (const [path, shortcut] of Object.entries(panel.shortcuts)) {
233
+ if ((shortcut.interaction ?? "scroll") !== "scroll-only") continue;
234
+ const control = this.findControlByPath(panel.controls, path);
235
+ if (control) {
236
+ results.push({ panelId: panel.id, path, control, shortcut });
237
+ }
238
+ }
239
+ }
240
+ return results;
241
+ }
242
+ findControlByPath(controls, path) {
243
+ for (const control of controls) {
244
+ if (control.path === path) return control;
245
+ if (control.type === "folder" && control.children) {
246
+ const found = this.findControlByPath(control.children, path);
247
+ if (found) return found;
248
+ }
249
+ }
250
+ return null;
251
+ }
214
252
  notify(panelId) {
215
253
  this.listeners.get(panelId)?.forEach((fn) => fn());
216
254
  }
@@ -232,12 +270,13 @@ var DialStoreClass = class {
232
270
  }
233
271
  }
234
272
  }
235
- parseConfig(config, prefix) {
273
+ parseConfig(config, prefix, shortcuts) {
236
274
  const controls = [];
237
275
  for (const [key, value] of Object.entries(config)) {
238
276
  if (key === "_collapsed") continue;
239
277
  const path = prefix ? `${prefix}.${key}` : key;
240
278
  const label = this.formatLabel(key);
279
+ const shortcut = shortcuts?.[path];
241
280
  if (Array.isArray(value) && value.length <= 4 && typeof value[0] === "number") {
242
281
  controls.push({
243
282
  type: "slider",
@@ -245,13 +284,14 @@ var DialStoreClass = class {
245
284
  label,
246
285
  min: value[1],
247
286
  max: value[2],
248
- step: value[3] ?? this.inferStep(value[1], value[2])
287
+ step: value[3] ?? this.inferStep(value[1], value[2]),
288
+ shortcut
249
289
  });
250
290
  } else if (typeof value === "number") {
251
291
  const { min, max, step } = this.inferRange(value);
252
- controls.push({ type: "slider", path, label, min, max, step });
292
+ controls.push({ type: "slider", path, label, min, max, step, shortcut });
253
293
  } else if (typeof value === "boolean") {
254
- controls.push({ type: "toggle", path, label });
294
+ controls.push({ type: "toggle", path, label, shortcut });
255
295
  } else if (this.isSpringConfig(value) || this.isEasingConfig(value)) {
256
296
  controls.push({ type: "transition", path, label });
257
297
  } else if (this.isActionConfig(value)) {
@@ -276,7 +316,7 @@ var DialStoreClass = class {
276
316
  path,
277
317
  label,
278
318
  defaultOpen,
279
- children: this.parseConfig(folderConfig, path)
319
+ children: this.parseConfig(folderConfig, path, shortcuts)
280
320
  });
281
321
  }
282
322
  }
@@ -434,8 +474,11 @@ function useDialKit(name, config, options) {
434
474
  configRef.current = config;
435
475
  const onActionRef = useRef(options?.onAction);
436
476
  onActionRef.current = options?.onAction;
477
+ const shortcutsRef = useRef(options?.shortcuts);
478
+ shortcutsRef.current = options?.shortcuts;
479
+ const serializedShortcuts = JSON.stringify(options?.shortcuts);
437
480
  useEffect(() => {
438
- DialStore.registerPanel(panelId, name, configRef.current);
481
+ DialStore.registerPanel(panelId, name, configRef.current, shortcutsRef.current);
439
482
  return () => DialStore.unregisterPanel(panelId);
440
483
  }, [panelId, name]);
441
484
  const mountedRef = useRef(false);
@@ -444,8 +487,8 @@ function useDialKit(name, config, options) {
444
487
  mountedRef.current = true;
445
488
  return;
446
489
  }
447
- DialStore.updatePanel(panelId, name, configRef.current);
448
- }, [panelId, name, serializedConfig]);
490
+ DialStore.updatePanel(panelId, name, configRef.current, shortcutsRef.current);
491
+ }, [panelId, name, serializedConfig, serializedShortcuts]);
449
492
  useEffect(() => {
450
493
  return DialStore.subscribeActions(panelId, (action) => {
451
494
  onActionRef.current?.(action);
@@ -511,23 +554,336 @@ function getFirstOptionValue(options) {
511
554
  }
512
555
 
513
556
  // src/components/DialRoot.tsx
514
- import { useEffect as useEffect7, useState as useState9 } from "react";
557
+ import { useEffect as useEffect8, useState as useState10, useRef as useRef11, useCallback as useCallback6 } from "react";
515
558
  import { createPortal as createPortal3 } from "react-dom";
516
559
 
517
560
  // src/components/Panel.tsx
518
- import { useState as useState8, useSyncExternalStore as useSyncExternalStore4 } from "react";
519
- import { motion as motion6, AnimatePresence as AnimatePresence4 } from "motion/react";
561
+ import { useState as useState9, useContext, useSyncExternalStore as useSyncExternalStore4 } from "react";
562
+ import { motion as motion5, AnimatePresence as AnimatePresence4 } from "motion/react";
563
+
564
+ // src/components/ShortcutListener.tsx
565
+ import { createContext, useEffect as useEffect2, useRef as useRef2, useState, useCallback } from "react";
566
+
567
+ // src/shortcut-utils.ts
568
+ function decimalsForStep(step) {
569
+ const s = step.toString();
570
+ const dot = s.indexOf(".");
571
+ return dot === -1 ? 0 : s.length - dot - 1;
572
+ }
573
+ function roundValue(val, step) {
574
+ const raw = Math.round(val / step) * step;
575
+ return parseFloat(raw.toFixed(decimalsForStep(step)));
576
+ }
577
+ function getEffectiveStep(control, shortcut) {
578
+ const min = control.min ?? 0;
579
+ const max = control.max ?? 1;
580
+ const range = max - min;
581
+ const mode = shortcut.mode ?? "normal";
582
+ return mode === "fine" ? range * 0.01 : mode === "coarse" ? range * 0.1 : control.step ?? 1;
583
+ }
584
+ function applySliderDelta(panelId, path, control, effectiveStep, direction) {
585
+ const currentValue = DialStore.getValue(panelId, path);
586
+ const min = control.min ?? 0;
587
+ const max = control.max ?? 1;
588
+ const newValue = Math.max(min, Math.min(max, currentValue + direction * effectiveStep));
589
+ DialStore.updateValue(panelId, path, roundValue(newValue, effectiveStep));
590
+ }
591
+ function snapToDecile(rawValue, min, max) {
592
+ const normalized = (rawValue - min) / (max - min);
593
+ const nearest = Math.round(normalized * 10) / 10;
594
+ if (Math.abs(normalized - nearest) <= 0.03125) {
595
+ return min + nearest * (max - min);
596
+ }
597
+ return rawValue;
598
+ }
599
+ function isInputFocused() {
600
+ const el = document.activeElement;
601
+ if (!el) return false;
602
+ const tag = el.tagName;
603
+ if (tag === "INPUT" || tag === "TEXTAREA") return true;
604
+ if (el.contentEditable === "true") return true;
605
+ return false;
606
+ }
607
+ function getActiveModifier(e) {
608
+ if (e.altKey) return "alt";
609
+ if (e.shiftKey) return "shift";
610
+ if (e.metaKey) return "meta";
611
+ return void 0;
612
+ }
613
+ function findControl(controls, path) {
614
+ for (const control of controls) {
615
+ if (control.path === path) return control;
616
+ if (control.type === "folder" && control.children) {
617
+ const found = findControl(control.children, path);
618
+ if (found) return found;
619
+ }
620
+ }
621
+ return null;
622
+ }
623
+ var DRAG_SENSITIVITY = 4;
624
+ function formatInteractionLabel(interaction) {
625
+ switch (interaction) {
626
+ case "drag":
627
+ return "Drag";
628
+ case "move":
629
+ return "Move";
630
+ case "scroll-only":
631
+ return "Scroll";
632
+ default:
633
+ return "Scroll";
634
+ }
635
+ }
636
+ function formatSliderShortcut(sc) {
637
+ const interaction = sc.interaction ?? "scroll";
638
+ const actionLabel = formatInteractionLabel(interaction);
639
+ if (!sc.key) return actionLabel;
640
+ const mod = formatModifier(sc.modifier);
641
+ return `${mod}${sc.key.toUpperCase()}+${actionLabel}`;
642
+ }
643
+ function formatToggleShortcut(sc) {
644
+ if (!sc.key) return "Press";
645
+ const mod = formatModifier(sc.modifier);
646
+ return `${mod}${sc.key.toUpperCase()}`;
647
+ }
648
+ function formatModifier(modifier) {
649
+ return modifier === "alt" ? "\u2325" : modifier === "shift" ? "\u21E7" : modifier === "meta" ? "\u2318" : "";
650
+ }
651
+
652
+ // src/components/ShortcutListener.tsx
653
+ import { jsx } from "react/jsx-runtime";
654
+ var ShortcutContext = createContext({ activePanelId: null, activePath: null });
655
+ function ShortcutListener({ children }) {
656
+ const [activeShortcut, setActiveShortcut] = useState({ activePanelId: null, activePath: null });
657
+ const activeKeysRef = useRef2(/* @__PURE__ */ new Set());
658
+ const isDraggingRef = useRef2(false);
659
+ const lastMouseXRef = useRef2(null);
660
+ const dragAccumulatorRef = useRef2(0);
661
+ const resolveActiveTarget = useCallback((interaction) => {
662
+ for (const key of activeKeysRef.current) {
663
+ const panels = DialStore.getPanels();
664
+ for (const panel of panels) {
665
+ for (const [path, shortcut] of Object.entries(panel.shortcuts)) {
666
+ if (!shortcut.key) continue;
667
+ if (shortcut.key.toLowerCase() !== key) continue;
668
+ if ((shortcut.interaction ?? "scroll") !== interaction) continue;
669
+ const control = findControl(panel.controls, path);
670
+ if (control && control.type === "slider") {
671
+ return { panelId: panel.id, path, control, shortcut };
672
+ }
673
+ }
674
+ }
675
+ }
676
+ return null;
677
+ }, []);
678
+ useEffect2(() => {
679
+ const handleKeyDown = (e) => {
680
+ if (isInputFocused()) return;
681
+ const key = e.key.toLowerCase();
682
+ if (key === "arrowleft" || key === "arrowright" || key === "arrowup" || key === "arrowdown") {
683
+ if (activeKeysRef.current.size > 0) {
684
+ const target2 = resolveActiveTarget("scroll") || resolveActiveTarget("drag") || resolveActiveTarget("move");
685
+ if (target2 && target2.control.type === "slider") {
686
+ e.preventDefault();
687
+ const direction = key === "arrowright" || key === "arrowup" ? 1 : -1;
688
+ const effectiveStep = getEffectiveStep(target2.control, target2.shortcut);
689
+ applySliderDelta(target2.panelId, target2.path, target2.control, effectiveStep, direction);
690
+ return;
691
+ }
692
+ }
693
+ }
694
+ const wasAlreadyHeld = activeKeysRef.current.has(key);
695
+ activeKeysRef.current.add(key);
696
+ const modifier = getActiveModifier(e);
697
+ const target = DialStore.resolveShortcutTarget(key, modifier);
698
+ if (target) {
699
+ setActiveShortcut({ activePanelId: target.panelId, activePath: target.path });
700
+ if (!wasAlreadyHeld && target.control.type === "toggle") {
701
+ const currentValue = DialStore.getValue(target.panelId, target.path);
702
+ DialStore.updateValue(target.panelId, target.path, !currentValue);
703
+ }
704
+ }
705
+ if (!wasAlreadyHeld) {
706
+ lastMouseXRef.current = null;
707
+ dragAccumulatorRef.current = 0;
708
+ }
709
+ };
710
+ const handleKeyUp = (e) => {
711
+ const key = e.key.toLowerCase();
712
+ activeKeysRef.current.delete(key);
713
+ isDraggingRef.current = false;
714
+ lastMouseXRef.current = null;
715
+ dragAccumulatorRef.current = 0;
716
+ if (activeKeysRef.current.size === 0) {
717
+ setActiveShortcut({ activePanelId: null, activePath: null });
718
+ } else {
719
+ let found = false;
720
+ for (const remainingKey of activeKeysRef.current) {
721
+ const modifier = getActiveModifier(e);
722
+ const target = DialStore.resolveShortcutTarget(remainingKey, modifier);
723
+ if (target) {
724
+ setActiveShortcut({ activePanelId: target.panelId, activePath: target.path });
725
+ found = true;
726
+ break;
727
+ }
728
+ }
729
+ if (!found) {
730
+ setActiveShortcut({ activePanelId: null, activePath: null });
731
+ }
732
+ }
733
+ };
734
+ const handleWheel = (e) => {
735
+ if (isInputFocused()) return;
736
+ const modifier = getActiveModifier(e);
737
+ if (activeKeysRef.current.size > 0) {
738
+ for (const key of activeKeysRef.current) {
739
+ const target = DialStore.resolveShortcutTarget(key, modifier);
740
+ if (!target) continue;
741
+ const { panelId, path, control } = target;
742
+ const interaction = control.shortcut?.interaction ?? "scroll";
743
+ if (interaction !== "scroll" || control.type !== "slider") continue;
744
+ e.preventDefault();
745
+ const effectiveStep = getEffectiveStep(control, control.shortcut);
746
+ const direction = e.deltaY > 0 ? -1 : 1;
747
+ applySliderDelta(panelId, path, control, effectiveStep, direction);
748
+ return;
749
+ }
750
+ }
751
+ const scrollOnlyTargets = DialStore.resolveScrollOnlyTargets();
752
+ for (const { panelId, path, control, shortcut } of scrollOnlyTargets) {
753
+ if (control.type !== "slider") continue;
754
+ e.preventDefault();
755
+ const effectiveStep = getEffectiveStep(control, shortcut);
756
+ const direction = e.deltaY > 0 ? -1 : 1;
757
+ applySliderDelta(panelId, path, control, effectiveStep, direction);
758
+ return;
759
+ }
760
+ };
761
+ const handleMouseDown = (e) => {
762
+ if (isInputFocused()) return;
763
+ if (activeKeysRef.current.size === 0) return;
764
+ const target = resolveActiveTarget("drag");
765
+ if (target) {
766
+ isDraggingRef.current = true;
767
+ lastMouseXRef.current = e.clientX;
768
+ dragAccumulatorRef.current = 0;
769
+ e.preventDefault();
770
+ }
771
+ };
772
+ const handleMouseUp = () => {
773
+ isDraggingRef.current = false;
774
+ lastMouseXRef.current = null;
775
+ dragAccumulatorRef.current = 0;
776
+ };
777
+ const handleMouseMove = (e) => {
778
+ if (isInputFocused()) return;
779
+ if (activeKeysRef.current.size === 0) return;
780
+ if (isDraggingRef.current) {
781
+ const target = resolveActiveTarget("drag");
782
+ if (target && lastMouseXRef.current !== null) {
783
+ const deltaX = e.clientX - lastMouseXRef.current;
784
+ lastMouseXRef.current = e.clientX;
785
+ dragAccumulatorRef.current += deltaX;
786
+ const effectiveStep = getEffectiveStep(target.control, target.shortcut);
787
+ const steps = Math.trunc(dragAccumulatorRef.current / DRAG_SENSITIVITY);
788
+ if (steps !== 0) {
789
+ dragAccumulatorRef.current -= steps * DRAG_SENSITIVITY;
790
+ applySliderDelta(target.panelId, target.path, target.control, effectiveStep, steps);
791
+ }
792
+ }
793
+ return;
794
+ }
795
+ const moveTarget = resolveActiveTarget("move");
796
+ if (moveTarget) {
797
+ if (lastMouseXRef.current === null) {
798
+ lastMouseXRef.current = e.clientX;
799
+ return;
800
+ }
801
+ const deltaX = e.clientX - lastMouseXRef.current;
802
+ lastMouseXRef.current = e.clientX;
803
+ dragAccumulatorRef.current += deltaX;
804
+ const effectiveStep = getEffectiveStep(moveTarget.control, moveTarget.shortcut);
805
+ const steps = Math.trunc(dragAccumulatorRef.current / DRAG_SENSITIVITY);
806
+ if (steps !== 0) {
807
+ dragAccumulatorRef.current -= steps * DRAG_SENSITIVITY;
808
+ applySliderDelta(moveTarget.panelId, moveTarget.path, moveTarget.control, effectiveStep, steps);
809
+ }
810
+ }
811
+ };
812
+ const handleWindowBlur = () => {
813
+ activeKeysRef.current.clear();
814
+ isDraggingRef.current = false;
815
+ lastMouseXRef.current = null;
816
+ dragAccumulatorRef.current = 0;
817
+ setActiveShortcut({ activePanelId: null, activePath: null });
818
+ };
819
+ window.addEventListener("keydown", handleKeyDown);
820
+ window.addEventListener("keyup", handleKeyUp);
821
+ window.addEventListener("wheel", handleWheel, { passive: false });
822
+ window.addEventListener("mousedown", handleMouseDown);
823
+ window.addEventListener("mouseup", handleMouseUp);
824
+ window.addEventListener("mousemove", handleMouseMove);
825
+ window.addEventListener("blur", handleWindowBlur);
826
+ return () => {
827
+ window.removeEventListener("keydown", handleKeyDown);
828
+ window.removeEventListener("keyup", handleKeyUp);
829
+ window.removeEventListener("wheel", handleWheel);
830
+ window.removeEventListener("mousedown", handleMouseDown);
831
+ window.removeEventListener("mouseup", handleMouseUp);
832
+ window.removeEventListener("mousemove", handleMouseMove);
833
+ window.removeEventListener("blur", handleWindowBlur);
834
+ };
835
+ }, [resolveActiveTarget]);
836
+ return /* @__PURE__ */ jsx(ShortcutContext.Provider, { value: activeShortcut, children });
837
+ }
838
+
839
+ // src/icons.ts
840
+ var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
841
+ var ICON_CHECK = "M5 12.75L10 19L19 5";
842
+ var ICON_CLIPBOARD = {
843
+ board: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z",
844
+ sparkle: "M19.2405 16.1852L18.5436 14.3733C18.4571 14.1484 18.241 14 18 14C17.759 14 17.5429 14.1484 17.4564 14.3733L16.7595 16.1852C16.658 16.4493 16.4493 16.658 16.1852 16.7595L14.3733 17.4564C14.1484 17.5429 14 17.759 14 18C14 18.241 14.1484 18.4571 14.3733 18.5436L16.1852 19.2405C16.4493 19.342 16.658 19.5507 16.7595 19.8148L17.4564 21.6267C17.5429 21.8516 17.759 22 18 22C18.241 22 18.4571 21.8516 18.5436 21.6267L19.2405 19.8148C19.342 19.5507 19.5507 19.342 19.8148 19.2405L21.6267 18.5436C21.8516 18.4571 22 18.241 22 18C22 17.759 21.8516 17.5429 21.6267 17.4564L19.8148 16.7595C19.5507 16.658 19.342 16.4493 19.2405 16.1852Z",
845
+ body: "M16 5H17C18.6569 5 20 6.34315 20 8V11M8 5H7C5.34315 5 4 6.34315 4 8V18C4 19.6569 5.34315 21 7 21H12"
846
+ };
847
+ var ICON_ADD_PRESET = [
848
+ "M4 6H20",
849
+ "M4 12H10",
850
+ "M15 15L21 15",
851
+ "M18 12V18",
852
+ "M4 18H10"
853
+ ];
854
+ var ICON_TRASH = [
855
+ "M5 6.5L5.80734 18.2064C5.91582 19.7794 7.22348 21 8.80023 21H15.1998C16.7765 21 18.0842 19.7794 18.1927 18.2064L19 6.5",
856
+ "M10 11V16",
857
+ "M14 11V16",
858
+ "M3.5 6H20.5",
859
+ "M8.07092 5.74621C8.42348 3.89745 10.0485 2.5 12 2.5C13.9515 2.5 15.5765 3.89745 15.9291 5.74621"
860
+ ];
861
+ var ICON_PANEL = {
862
+ path: "M6.84766 11.75C6.78583 11.9899 6.75 12.2408 6.75 12.5C6.75 12.7592 6.78583 13.0101 6.84766 13.25H2C1.58579 13.25 1.25 12.9142 1.25 12.5C1.25 12.0858 1.58579 11.75 2 11.75H6.84766ZM14 11.75C14.4142 11.75 14.75 12.0858 14.75 12.5C14.75 12.9142 14.4142 13.25 14 13.25H12.6523C12.7142 13.0101 12.75 12.7592 12.75 12.5C12.75 12.2408 12.7142 11.9899 12.6523 11.75H14ZM3.09766 7.25C3.03583 7.48994 3 7.74075 3 8C3 8.25925 3.03583 8.51006 3.09766 8.75H2C1.58579 8.75 1.25 8.41421 1.25 8C1.25 7.58579 1.58579 7.25 2 7.25H3.09766ZM14 7.25C14.4142 7.25 14.75 7.58579 14.75 8C14.75 8.41421 14.4142 8.75 14 8.75H8.90234C8.96417 8.51006 9 8.25925 9 8C9 7.74075 8.96417 7.48994 8.90234 7.25H14ZM7.59766 2.75C7.53583 2.98994 7.5 3.24075 7.5 3.5C7.5 3.75925 7.53583 4.01006 7.59766 4.25H2C1.58579 4.25 1.25 3.91421 1.25 3.5C1.25 3.08579 1.58579 2.75 2 2.75H7.59766ZM14 2.75C14.4142 2.75 14.75 3.08579 14.75 3.5C14.75 3.91421 14.4142 4.25 14 4.25H13.4023C13.4642 4.01006 13.5 3.75925 13.5 3.5C13.5 3.24075 13.4642 2.98994 13.4023 2.75H14Z",
863
+ circles: [
864
+ { cx: "6", cy: "8", r: "0.998596" },
865
+ { cx: "10.4999", cy: "3.5", r: "0.998657" },
866
+ { cx: "9.75015", cy: "12.5", r: "0.997986" }
867
+ ]
868
+ };
520
869
 
521
870
  // src/components/Folder.tsx
522
- import { useState, useRef as useRef2, useEffect as useEffect2 } from "react";
871
+ import { useState as useState2, useRef as useRef3, useEffect as useEffect3 } from "react";
523
872
  import { motion, AnimatePresence } from "motion/react";
524
- import { jsx, jsxs } from "react/jsx-runtime";
525
- function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenChange, toolbar }) {
526
- const [isOpen, setIsOpen] = useState(defaultOpen);
527
- const [isCollapsed, setIsCollapsed] = useState(!defaultOpen);
528
- const contentRef = useRef2(null);
529
- const [contentHeight, setContentHeight] = useState(void 0);
530
- useEffect2(() => {
873
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
874
+ function Folder({ title, children, defaultOpen = true, isRoot = false, inline = false, onOpenChange, toolbar }) {
875
+ const [isOpen, setIsOpen] = useState2(defaultOpen);
876
+ const [isCollapsed, setIsCollapsed] = useState2(!defaultOpen);
877
+ const contentRef = useRef3(null);
878
+ const [contentHeight, setContentHeight] = useState2(void 0);
879
+ const [windowHeight, setWindowHeight] = useState2(typeof window !== "undefined" ? window.innerHeight : 800);
880
+ useEffect3(() => {
881
+ if (!isRoot) return;
882
+ const onResize = () => setWindowHeight(window.innerHeight);
883
+ window.addEventListener("resize", onResize);
884
+ return () => window.removeEventListener("resize", onResize);
885
+ }, [isRoot]);
886
+ useEffect3(() => {
531
887
  const el = contentRef.current;
532
888
  if (!el) return;
533
889
  const ro = new ResizeObserver(() => {
@@ -540,6 +896,7 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
540
896
  return () => ro.disconnect();
541
897
  }, [isOpen]);
542
898
  const handleToggle = () => {
899
+ if (inline && isRoot) return;
543
900
  const next = !isOpen;
544
901
  setIsOpen(next);
545
902
  if (next) {
@@ -552,46 +909,39 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
552
909
  const folderContent = /* @__PURE__ */ jsxs("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
553
910
  /* @__PURE__ */ jsxs("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: handleToggle, children: [
554
911
  /* @__PURE__ */ jsxs("div", { className: "dialkit-folder-header-top", children: [
555
- isRoot ? isOpen && /* @__PURE__ */ jsx("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ jsx("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title }) }) : /* @__PURE__ */ jsx("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ jsx("span", { className: "dialkit-folder-title", children: title }) }),
556
- isRoot ? (
557
- // Root panel icon — fixed position, container morphs around it
558
- /* @__PURE__ */ jsxs(
559
- "svg",
560
- {
561
- className: "dialkit-panel-icon",
562
- viewBox: "0 0 16 16",
563
- fill: "none",
564
- children: [
565
- /* @__PURE__ */ jsx("path", { opacity: "0.5", d: "M6.84766 11.75C6.78583 11.9899 6.75 12.2408 6.75 12.5C6.75 12.7592 6.78583 13.0101 6.84766 13.25H2C1.58579 13.25 1.25 12.9142 1.25 12.5C1.25 12.0858 1.58579 11.75 2 11.75H6.84766ZM14 11.75C14.4142 11.75 14.75 12.0858 14.75 12.5C14.75 12.9142 14.4142 13.25 14 13.25H12.6523C12.7142 13.0101 12.75 12.7592 12.75 12.5C12.75 12.2408 12.7142 11.9899 12.6523 11.75H14ZM3.09766 7.25C3.03583 7.48994 3 7.74075 3 8C3 8.25925 3.03583 8.51006 3.09766 8.75H2C1.58579 8.75 1.25 8.41421 1.25 8C1.25 7.58579 1.58579 7.25 2 7.25H3.09766ZM14 7.25C14.4142 7.25 14.75 7.58579 14.75 8C14.75 8.41421 14.4142 8.75 14 8.75H8.90234C8.96417 8.51006 9 8.25925 9 8C9 7.74075 8.96417 7.48994 8.90234 7.25H14ZM7.59766 2.75C7.53583 2.98994 7.5 3.24075 7.5 3.5C7.5 3.75925 7.53583 4.01006 7.59766 4.25H2C1.58579 4.25 1.25 3.91421 1.25 3.5C1.25 3.08579 1.58579 2.75 2 2.75H7.59766ZM14 2.75C14.4142 2.75 14.75 3.08579 14.75 3.5C14.75 3.91421 14.4142 4.25 14 4.25H13.4023C13.4642 4.01006 13.5 3.75925 13.5 3.5C13.5 3.24075 13.4642 2.98994 13.4023 2.75H14Z", fill: "currentColor" }),
566
- /* @__PURE__ */ jsx("circle", { cx: "6", cy: "8", r: "0.998596", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
567
- /* @__PURE__ */ jsx("circle", { cx: "10.4999", cy: "3.5", r: "0.998657", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
568
- /* @__PURE__ */ jsx("circle", { cx: "9.75015", cy: "12.5", r: "0.997986", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })
569
- ]
570
- }
571
- )
572
- ) : (
573
- // Section folders use rotating chevron with gentle spring
574
- /* @__PURE__ */ jsx(
575
- motion.svg,
576
- {
577
- className: "dialkit-folder-icon",
578
- viewBox: "0 0 24 24",
579
- fill: "none",
580
- stroke: "currentColor",
581
- strokeWidth: "2.5",
582
- strokeLinecap: "round",
583
- strokeLinejoin: "round",
584
- initial: false,
585
- animate: { rotate: isOpen ? 0 : 180 },
586
- transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
587
- children: /* @__PURE__ */ jsx("path", { d: "M6 9.5L12 15.5L18 9.5" })
588
- }
589
- )
912
+ isRoot ? isOpen && /* @__PURE__ */ jsx2("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ jsx2("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title }) }) : /* @__PURE__ */ jsx2("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ jsx2("span", { className: "dialkit-folder-title", children: title }) }),
913
+ isRoot && !inline && /* @__PURE__ */ jsxs(
914
+ "svg",
915
+ {
916
+ className: "dialkit-panel-icon",
917
+ viewBox: "0 0 16 16",
918
+ fill: "none",
919
+ children: [
920
+ /* @__PURE__ */ jsx2("path", { opacity: "0.5", d: ICON_PANEL.path, fill: "currentColor" }),
921
+ ICON_PANEL.circles.map((c, i) => /* @__PURE__ */ jsx2("circle", { cx: c.cx, cy: c.cy, r: c.r, fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }, i))
922
+ ]
923
+ }
924
+ ),
925
+ !isRoot && /* @__PURE__ */ jsx2(
926
+ motion.svg,
927
+ {
928
+ className: "dialkit-folder-icon",
929
+ viewBox: "0 0 24 24",
930
+ fill: "none",
931
+ stroke: "currentColor",
932
+ strokeWidth: "2.5",
933
+ strokeLinecap: "round",
934
+ strokeLinejoin: "round",
935
+ initial: false,
936
+ animate: { rotate: isOpen ? 0 : 180 },
937
+ transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
938
+ children: /* @__PURE__ */ jsx2("path", { d: ICON_CHEVRON })
939
+ }
590
940
  )
591
941
  ] }),
592
- isRoot && toolbar && isOpen && /* @__PURE__ */ jsx("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
942
+ isRoot && toolbar && isOpen && /* @__PURE__ */ jsx2("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
593
943
  ] }),
594
- /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
944
+ /* @__PURE__ */ jsx2(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx2(
595
945
  motion.div,
596
946
  {
597
947
  className: "dialkit-folder-content",
@@ -600,13 +950,16 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
600
950
  exit: isRoot ? void 0 : { height: 0, opacity: 0 },
601
951
  transition: isRoot ? void 0 : { type: "spring", visualDuration: 0.35, bounce: 0.1 },
602
952
  style: isRoot ? void 0 : { clipPath: "inset(0 -20px)" },
603
- children: /* @__PURE__ */ jsx("div", { className: "dialkit-folder-inner", children })
953
+ children: /* @__PURE__ */ jsx2("div", { className: "dialkit-folder-inner", children })
604
954
  }
605
955
  ) })
606
956
  ] });
607
957
  if (isRoot) {
608
- const panelStyle = isOpen ? { width: 280, height: contentHeight !== void 0 ? contentHeight + 24 : "auto", borderRadius: 14, boxShadow: "0 8px 32px rgba(0, 0, 0, 0.5)", cursor: void 0 } : { width: 42, height: 42, borderRadius: 21, boxShadow: "0 4px 16px rgba(0, 0, 0, 0.25)", overflow: "hidden", cursor: "pointer" };
609
- return /* @__PURE__ */ jsx(
958
+ if (inline) {
959
+ return /* @__PURE__ */ jsx2("div", { className: "dialkit-panel-inner dialkit-panel-inline", children: folderContent });
960
+ }
961
+ const panelStyle = isOpen ? { width: 280, height: contentHeight !== void 0 ? Math.min(contentHeight + 10, windowHeight - 32) : "auto", borderRadius: 14, boxShadow: "var(--dial-shadow)", cursor: void 0, overflowY: "auto" } : { width: 42, height: 42, borderRadius: "50%", boxSizing: "border-box", boxShadow: "var(--dial-shadow-collapsed)", overflow: "hidden", cursor: "pointer" };
962
+ return /* @__PURE__ */ jsx2(
610
963
  motion.div,
611
964
  {
612
965
  className: "dialkit-panel-inner",
@@ -623,30 +976,13 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
623
976
  }
624
977
 
625
978
  // src/components/Slider.tsx
626
- import { useRef as useRef3, useState as useState2, useCallback, useEffect as useEffect3 } from "react";
979
+ import { useRef as useRef4, useState as useState3, useCallback as useCallback2, useEffect as useEffect4 } from "react";
627
980
  import { motion as motion2, useMotionValue, useTransform, animate } from "motion/react";
628
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
981
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
629
982
  var CLICK_THRESHOLD = 3;
630
983
  var DEAD_ZONE = 32;
631
984
  var MAX_CURSOR_RANGE = 200;
632
985
  var MAX_STRETCH = 8;
633
- function decimalsForStep(step) {
634
- const s = step.toString();
635
- const dot = s.indexOf(".");
636
- return dot === -1 ? 0 : s.length - dot - 1;
637
- }
638
- function roundValue(val, step) {
639
- const raw = Math.round(val / step) * step;
640
- return parseFloat(raw.toFixed(decimalsForStep(step)));
641
- }
642
- function snapToDecile(rawValue, min, max) {
643
- const normalized = (rawValue - min) / (max - min);
644
- const nearest = Math.round(normalized * 10) / 10;
645
- if (Math.abs(normalized - nearest) <= 0.03125) {
646
- return min + nearest * (max - min);
647
- }
648
- return rawValue;
649
- }
650
986
  function Slider({
651
987
  label,
652
988
  value,
@@ -654,26 +990,28 @@ function Slider({
654
990
  min = 0,
655
991
  max = 1,
656
992
  step = 0.01,
657
- unit
993
+ unit,
994
+ shortcut,
995
+ shortcutActive
658
996
  }) {
659
- const wrapperRef = useRef3(null);
660
- const trackRef = useRef3(null);
661
- const inputRef = useRef3(null);
662
- const labelRef = useRef3(null);
663
- const valueSpanRef = useRef3(null);
664
- const [isInteracting, setIsInteracting] = useState2(false);
665
- const [isDragging, setIsDragging] = useState2(false);
666
- const [isHovered, setIsHovered] = useState2(false);
667
- const [isValueHovered, setIsValueHovered] = useState2(false);
668
- const [isValueEditable, setIsValueEditable] = useState2(false);
669
- const [showInput, setShowInput] = useState2(false);
670
- const [inputValue, setInputValue] = useState2("");
671
- const hoverTimeoutRef = useRef3(null);
672
- const pointerDownPos = useRef3(null);
673
- const isClickRef = useRef3(true);
674
- const animRef = useRef3(null);
675
- const wrapperRectRef = useRef3(null);
676
- const scaleRef = useRef3(1);
997
+ const wrapperRef = useRef4(null);
998
+ const trackRef = useRef4(null);
999
+ const inputRef = useRef4(null);
1000
+ const labelRef = useRef4(null);
1001
+ const valueSpanRef = useRef4(null);
1002
+ const [isInteracting, setIsInteracting] = useState3(false);
1003
+ const [isDragging, setIsDragging] = useState3(false);
1004
+ const [isHovered, setIsHovered] = useState3(false);
1005
+ const [isValueHovered, setIsValueHovered] = useState3(false);
1006
+ const [isValueEditable, setIsValueEditable] = useState3(false);
1007
+ const [showInput, setShowInput] = useState3(false);
1008
+ const [inputValue, setInputValue] = useState3("");
1009
+ const hoverTimeoutRef = useRef4(null);
1010
+ const pointerDownPos = useRef4(null);
1011
+ const isClickRef = useRef4(true);
1012
+ const animRef = useRef4(null);
1013
+ const wrapperRectRef = useRef4(null);
1014
+ const scaleRef = useRef4(1);
677
1015
  const percentage = (value - min) / (max - min) * 100;
678
1016
  const isActive = isInteracting || isHovered;
679
1017
  const fillPercent = useMotionValue(percentage);
@@ -691,12 +1029,12 @@ function Slider({
691
1029
  rubberStretchPx,
692
1030
  (stretch) => stretch < 0 ? stretch : 0
693
1031
  );
694
- useEffect3(() => {
1032
+ useEffect4(() => {
695
1033
  if (!isInteracting && !animRef.current) {
696
1034
  fillPercent.jump(percentage);
697
1035
  }
698
1036
  }, [percentage, isInteracting, fillPercent]);
699
- const positionToValue = useCallback(
1037
+ const positionToValue = useCallback2(
700
1038
  (clientX) => {
701
1039
  const rect = wrapperRectRef.current;
702
1040
  if (!rect) return value;
@@ -709,11 +1047,11 @@ function Slider({
709
1047
  },
710
1048
  [min, max, value]
711
1049
  );
712
- const percentFromValue = useCallback(
1050
+ const percentFromValue = useCallback2(
713
1051
  (v) => (v - min) / (max - min) * 100,
714
1052
  [min, max]
715
1053
  );
716
- const computeRubberStretch = useCallback(
1054
+ const computeRubberStretch = useCallback2(
717
1055
  (clientX, sign) => {
718
1056
  const rect = wrapperRectRef.current;
719
1057
  if (!rect) return 0;
@@ -723,7 +1061,7 @@ function Slider({
723
1061
  },
724
1062
  []
725
1063
  );
726
- const handlePointerDown = useCallback(
1064
+ const handlePointerDown = useCallback2(
727
1065
  (e) => {
728
1066
  if (showInput) return;
729
1067
  e.preventDefault();
@@ -739,7 +1077,7 @@ function Slider({
739
1077
  },
740
1078
  [showInput]
741
1079
  );
742
- const handlePointerMove = useCallback(
1080
+ const handlePointerMove = useCallback2(
743
1081
  (e) => {
744
1082
  if (!isInteracting || !pointerDownPos.current) return;
745
1083
  const dx = e.clientX - pointerDownPos.current.x;
@@ -780,7 +1118,7 @@ function Slider({
780
1118
  computeRubberStretch
781
1119
  ]
782
1120
  );
783
- const handlePointerUp = useCallback(
1121
+ const handlePointerUp = useCallback2(
784
1122
  (e) => {
785
1123
  if (!isInteracting) return;
786
1124
  if (isClickRef.current) {
@@ -824,7 +1162,7 @@ function Slider({
824
1162
  rubberStretchPx
825
1163
  ]
826
1164
  );
827
- useEffect3(() => {
1165
+ useEffect4(() => {
828
1166
  if (isValueHovered && !showInput && !isValueEditable) {
829
1167
  hoverTimeoutRef.current = setTimeout(() => {
830
1168
  setIsValueEditable(true);
@@ -842,7 +1180,7 @@ function Slider({
842
1180
  }
843
1181
  };
844
1182
  }, [isValueHovered, showInput, isValueEditable]);
845
- useEffect3(() => {
1183
+ useEffect4(() => {
846
1184
  if (showInput && inputRef.current) {
847
1185
  inputRef.current.focus();
848
1186
  inputRef.current.select();
@@ -897,11 +1235,10 @@ function Slider({
897
1235
  }
898
1236
  const valueDodge = percentage < leftThreshold || percentage > rightThreshold;
899
1237
  const handleOpacity = !isActive ? 0 : valueDodge ? 0.1 : isDragging ? 0.9 : 0.5;
900
- const fillBackground = isActive ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.11)";
901
1238
  const discreteSteps = (max - min) / step;
902
1239
  const hashMarks = discreteSteps <= 10 ? Array.from({ length: discreteSteps - 1 }, (_, i) => {
903
1240
  const pct = (i + 1) * step / (max - min) * 100;
904
- return /* @__PURE__ */ jsx2(
1241
+ return /* @__PURE__ */ jsx3(
905
1242
  "div",
906
1243
  {
907
1244
  className: "dialkit-slider-hashmark",
@@ -911,7 +1248,7 @@ function Slider({
911
1248
  );
912
1249
  }) : Array.from({ length: 9 }, (_, i) => {
913
1250
  const pct = (i + 1) * 10;
914
- return /* @__PURE__ */ jsx2(
1251
+ return /* @__PURE__ */ jsx3(
915
1252
  "div",
916
1253
  {
917
1254
  className: "dialkit-slider-hashmark",
@@ -920,7 +1257,7 @@ function Slider({
920
1257
  i
921
1258
  );
922
1259
  });
923
- return /* @__PURE__ */ jsx2("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ jsxs2(
1260
+ return /* @__PURE__ */ jsx3("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ jsxs2(
924
1261
  motion2.div,
925
1262
  {
926
1263
  ref: trackRef,
@@ -932,26 +1269,23 @@ function Slider({
932
1269
  onMouseLeave: () => setIsHovered(false),
933
1270
  style: { width: rubberBandWidth, x: rubberBandX },
934
1271
  children: [
935
- /* @__PURE__ */ jsx2("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
936
- /* @__PURE__ */ jsx2(
1272
+ /* @__PURE__ */ jsx3("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
1273
+ /* @__PURE__ */ jsx3(
937
1274
  motion2.div,
938
1275
  {
939
1276
  className: "dialkit-slider-fill",
940
1277
  style: {
941
- background: fillBackground,
942
- width: fillWidth,
943
- transition: "background 0.15s"
1278
+ width: fillWidth
944
1279
  }
945
1280
  }
946
1281
  ),
947
- /* @__PURE__ */ jsx2(
1282
+ /* @__PURE__ */ jsx3(
948
1283
  motion2.div,
949
1284
  {
950
1285
  className: "dialkit-slider-handle",
951
1286
  style: {
952
1287
  left: handleLeft,
953
- y: "-50%",
954
- background: "rgba(255, 255, 255, 0.9)"
1288
+ y: "-50%"
955
1289
  },
956
1290
  animate: {
957
1291
  opacity: handleOpacity,
@@ -965,8 +1299,11 @@ function Slider({
965
1299
  }
966
1300
  }
967
1301
  ),
968
- /* @__PURE__ */ jsx2("span", { ref: labelRef, className: "dialkit-slider-label", children: label }),
969
- showInput ? /* @__PURE__ */ jsx2(
1302
+ /* @__PURE__ */ jsxs2("span", { ref: labelRef, className: "dialkit-slider-label", children: [
1303
+ label,
1304
+ shortcut && /* @__PURE__ */ jsx3("span", { className: `dialkit-shortcut-pill${shortcutActive ? " dialkit-shortcut-pill-active" : ""}`, children: formatSliderShortcut(shortcut) })
1305
+ ] }),
1306
+ showInput ? /* @__PURE__ */ jsx3(
970
1307
  "input",
971
1308
  {
972
1309
  ref: inputRef,
@@ -979,7 +1316,7 @@ function Slider({
979
1316
  onClick: (e) => e.stopPropagation(),
980
1317
  onMouseDown: (e) => e.stopPropagation()
981
1318
  }
982
- ) : /* @__PURE__ */ jsx2(
1319
+ ) : /* @__PURE__ */ jsx3(
983
1320
  "span",
984
1321
  {
985
1322
  ref: valueSpanRef,
@@ -998,51 +1335,48 @@ function Slider({
998
1335
  }
999
1336
 
1000
1337
  // src/components/SegmentedControl.tsx
1001
- import { useRef as useRef4, useState as useState3, useLayoutEffect } from "react";
1002
- import { motion as motion3 } from "motion/react";
1003
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1338
+ import { useRef as useRef5, useState as useState4, useLayoutEffect, useCallback as useCallback3 } from "react";
1339
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1004
1340
  function SegmentedControl({
1005
1341
  options,
1006
1342
  value,
1007
1343
  onChange
1008
1344
  }) {
1009
- const containerRef = useRef4(null);
1010
- const buttonRefs = useRef4(/* @__PURE__ */ new Map());
1011
- const [pillStyle, setPillStyle] = useState3(null);
1012
- const hasAnimated = useRef4(false);
1013
- useLayoutEffect(() => {
1014
- const button = buttonRefs.current.get(value);
1345
+ const containerRef = useRef5(null);
1346
+ const hasAnimated = useRef5(false);
1347
+ const [pillStyle, setPillStyle] = useState4(null);
1348
+ const measure = useCallback3(() => {
1015
1349
  const container = containerRef.current;
1016
- if (button && container) {
1017
- const containerRect = container.getBoundingClientRect();
1018
- const buttonRect = button.getBoundingClientRect();
1019
- setPillStyle({
1020
- left: buttonRect.left - containerRect.left,
1021
- width: buttonRect.width
1022
- });
1023
- }
1024
- }, [value]);
1025
- return /* @__PURE__ */ jsxs3("div", { ref: containerRef, className: "dialkit-segmented", children: [
1026
- pillStyle && /* @__PURE__ */ jsx3(
1027
- motion3.div,
1350
+ if (!container) return;
1351
+ const activeButton = container.querySelector('[data-active="true"]');
1352
+ if (!activeButton) return;
1353
+ setPillStyle({
1354
+ left: activeButton.offsetLeft,
1355
+ width: activeButton.offsetWidth
1356
+ });
1357
+ }, []);
1358
+ useLayoutEffect(() => {
1359
+ measure();
1360
+ }, [value, options.length, measure]);
1361
+ const shouldAnimate = hasAnimated.current;
1362
+ hasAnimated.current = true;
1363
+ return /* @__PURE__ */ jsxs3("div", { className: "dialkit-segmented", ref: containerRef, children: [
1364
+ pillStyle && /* @__PURE__ */ jsx4(
1365
+ "div",
1028
1366
  {
1029
1367
  className: "dialkit-segmented-pill",
1030
- style: { left: pillStyle.left, width: pillStyle.width },
1031
- animate: { left: pillStyle.left, width: pillStyle.width },
1032
- transition: hasAnimated.current ? { type: "spring", visualDuration: 0.2, bounce: 0.15 } : { duration: 0 },
1033
- onAnimationComplete: () => {
1034
- hasAnimated.current = true;
1368
+ style: {
1369
+ left: pillStyle.left,
1370
+ width: pillStyle.width,
1371
+ transition: shouldAnimate ? "left 0.2s cubic-bezier(0.25, 1, 0.5, 1), width 0.2s cubic-bezier(0.25, 1, 0.5, 1)" : "none"
1035
1372
  }
1036
1373
  }
1037
1374
  ),
1038
1375
  options.map((option) => {
1039
1376
  const isActive = value === option.value;
1040
- return /* @__PURE__ */ jsx3(
1377
+ return /* @__PURE__ */ jsx4(
1041
1378
  "button",
1042
1379
  {
1043
- ref: (el) => {
1044
- if (el) buttonRefs.current.set(option.value, el);
1045
- },
1046
1380
  onClick: () => onChange(option.value),
1047
1381
  className: "dialkit-segmented-button",
1048
1382
  "data-active": String(isActive),
@@ -1055,11 +1389,14 @@ function SegmentedControl({
1055
1389
  }
1056
1390
 
1057
1391
  // src/components/Toggle.tsx
1058
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1059
- function Toggle({ label, checked, onChange }) {
1392
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1393
+ function Toggle({ label, checked, onChange, shortcut, shortcutActive }) {
1060
1394
  return /* @__PURE__ */ jsxs4("div", { className: "dialkit-labeled-control", children: [
1061
- /* @__PURE__ */ jsx4("span", { className: "dialkit-labeled-control-label", children: label }),
1062
- /* @__PURE__ */ jsx4(
1395
+ /* @__PURE__ */ jsxs4("span", { className: "dialkit-labeled-control-label", children: [
1396
+ label,
1397
+ shortcut && /* @__PURE__ */ jsx5("span", { className: `dialkit-shortcut-pill${shortcutActive ? " dialkit-shortcut-pill-active" : ""}`, children: formatToggleShortcut(shortcut) })
1398
+ ] }),
1399
+ /* @__PURE__ */ jsx5(
1063
1400
  SegmentedControl,
1064
1401
  {
1065
1402
  options: [
@@ -1074,7 +1411,7 @@ function Toggle({ label, checked, onChange }) {
1074
1411
  }
1075
1412
 
1076
1413
  // src/components/SpringVisualization.tsx
1077
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1414
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1078
1415
  function generateSpringCurve(stiffness, damping, mass, duration) {
1079
1416
  const points = [];
1080
1417
  const steps = 100;
@@ -1129,13 +1466,13 @@ function SpringVisualization({ spring, isSimpleMode }) {
1129
1466
  const x = width / 4 * i;
1130
1467
  const y = height / 4 * i;
1131
1468
  gridLines.push(
1132
- /* @__PURE__ */ jsx5("line", { x1: x, y1: 0, x2: x, y2: height, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `v-${i}`),
1133
- /* @__PURE__ */ jsx5("line", { x1: 0, y1: y, x2: width, y2: y, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `h-${i}`)
1469
+ /* @__PURE__ */ jsx6("line", { x1: x, y1: 0, x2: x, y2: height, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `v-${i}`),
1470
+ /* @__PURE__ */ jsx6("line", { x1: 0, y1: y, x2: width, y2: y, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `h-${i}`)
1134
1471
  );
1135
1472
  }
1136
1473
  return /* @__PURE__ */ jsxs5("svg", { viewBox: `0 0 ${width} ${height}`, className: "dialkit-spring-viz", children: [
1137
1474
  gridLines,
1138
- /* @__PURE__ */ jsx5(
1475
+ /* @__PURE__ */ jsx6(
1139
1476
  "line",
1140
1477
  {
1141
1478
  x1: 0,
@@ -1147,7 +1484,7 @@ function SpringVisualization({ spring, isSimpleMode }) {
1147
1484
  strokeDasharray: "4,4"
1148
1485
  }
1149
1486
  ),
1150
- /* @__PURE__ */ jsx5(
1487
+ /* @__PURE__ */ jsx6(
1151
1488
  "path",
1152
1489
  {
1153
1490
  d: pathData,
@@ -1162,8 +1499,8 @@ function SpringVisualization({ spring, isSimpleMode }) {
1162
1499
  }
1163
1500
 
1164
1501
  // src/components/SpringControl.tsx
1165
- import { useSyncExternalStore as useSyncExternalStore2 } from "react";
1166
- import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1502
+ import { useSyncExternalStore as useSyncExternalStore2, useRef as useRef6 } from "react";
1503
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1167
1504
  function SpringControl({ panelId, path, label, spring, onChange }) {
1168
1505
  const mode = useSyncExternalStore2(
1169
1506
  (cb) => DialStore.subscribe(panelId, cb),
@@ -1171,25 +1508,21 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1171
1508
  () => DialStore.getSpringMode(panelId, path)
1172
1509
  );
1173
1510
  const isSimpleMode = mode === "simple";
1511
+ const cache = useRef6({
1512
+ simple: spring.visualDuration !== void 0 ? spring : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1513
+ advanced: spring.stiffness !== void 0 ? spring : { type: "spring", stiffness: 200, damping: 25, mass: 1 }
1514
+ });
1515
+ if (isSimpleMode) {
1516
+ cache.current.simple = spring;
1517
+ } else {
1518
+ cache.current.advanced = spring;
1519
+ }
1174
1520
  const handleModeChange = (newMode) => {
1175
1521
  DialStore.updateSpringMode(panelId, path, newMode);
1176
1522
  if (newMode === "simple") {
1177
- const { stiffness, damping, mass, ...rest } = spring;
1178
- onChange({
1179
- ...rest,
1180
- type: "spring",
1181
- visualDuration: spring.visualDuration ?? 0.3,
1182
- bounce: spring.bounce ?? 0.2
1183
- });
1523
+ onChange(cache.current.simple);
1184
1524
  } else {
1185
- const { visualDuration, bounce, ...rest } = spring;
1186
- onChange({
1187
- ...rest,
1188
- type: "spring",
1189
- stiffness: spring.stiffness ?? 200,
1190
- damping: spring.damping ?? 25,
1191
- mass: spring.mass ?? 1
1192
- });
1525
+ onChange(cache.current.advanced);
1193
1526
  }
1194
1527
  };
1195
1528
  const handleUpdate = (key, value) => {
@@ -1201,11 +1534,11 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1201
1534
  onChange({ ...rest, [key]: value });
1202
1535
  }
1203
1536
  };
1204
- return /* @__PURE__ */ jsx6(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1205
- /* @__PURE__ */ jsx6(SpringVisualization, { spring, isSimpleMode }),
1537
+ return /* @__PURE__ */ jsx7(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1538
+ /* @__PURE__ */ jsx7(SpringVisualization, { spring, isSimpleMode }),
1206
1539
  /* @__PURE__ */ jsxs6("div", { className: "dialkit-labeled-control", children: [
1207
- /* @__PURE__ */ jsx6("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1208
- /* @__PURE__ */ jsx6(
1540
+ /* @__PURE__ */ jsx7("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1541
+ /* @__PURE__ */ jsx7(
1209
1542
  SegmentedControl,
1210
1543
  {
1211
1544
  options: [
@@ -1218,7 +1551,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1218
1551
  )
1219
1552
  ] }),
1220
1553
  isSimpleMode ? /* @__PURE__ */ jsxs6(Fragment, { children: [
1221
- /* @__PURE__ */ jsx6(
1554
+ /* @__PURE__ */ jsx7(
1222
1555
  Slider,
1223
1556
  {
1224
1557
  label: "Duration",
@@ -1230,7 +1563,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1230
1563
  unit: "s"
1231
1564
  }
1232
1565
  ),
1233
- /* @__PURE__ */ jsx6(
1566
+ /* @__PURE__ */ jsx7(
1234
1567
  Slider,
1235
1568
  {
1236
1569
  label: "Bounce",
@@ -1242,7 +1575,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1242
1575
  }
1243
1576
  )
1244
1577
  ] }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
1245
- /* @__PURE__ */ jsx6(
1578
+ /* @__PURE__ */ jsx7(
1246
1579
  Slider,
1247
1580
  {
1248
1581
  label: "Stiffness",
@@ -1253,7 +1586,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1253
1586
  step: 10
1254
1587
  }
1255
1588
  ),
1256
- /* @__PURE__ */ jsx6(
1589
+ /* @__PURE__ */ jsx7(
1257
1590
  Slider,
1258
1591
  {
1259
1592
  label: "Damping",
@@ -1264,7 +1597,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1264
1597
  step: 1
1265
1598
  }
1266
1599
  ),
1267
- /* @__PURE__ */ jsx6(
1600
+ /* @__PURE__ */ jsx7(
1268
1601
  Slider,
1269
1602
  {
1270
1603
  label: "Mass",
@@ -1280,7 +1613,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1280
1613
  }
1281
1614
 
1282
1615
  // src/components/EasingVisualization.tsx
1283
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1616
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1284
1617
  function EasingVisualization({ easing }) {
1285
1618
  const ease = easing.ease;
1286
1619
  const s = 200;
@@ -1303,7 +1636,7 @@ function EasingVisualization({ easing }) {
1303
1636
  preserveAspectRatio: "xMidYMid slice",
1304
1637
  className: "dialkit-spring-viz dialkit-easing-viz",
1305
1638
  children: [
1306
- /* @__PURE__ */ jsx7(
1639
+ /* @__PURE__ */ jsx8(
1307
1640
  "line",
1308
1641
  {
1309
1642
  x1: start.x,
@@ -1315,15 +1648,15 @@ function EasingVisualization({ easing }) {
1315
1648
  strokeDasharray: "4,4"
1316
1649
  }
1317
1650
  ),
1318
- /* @__PURE__ */ jsx7("path", { d: curvePath, fill: "none", stroke: "rgba(255, 255, 255, 0.6)", strokeWidth: "2", strokeLinecap: "round" })
1651
+ /* @__PURE__ */ jsx8("path", { d: curvePath, fill: "none", stroke: "rgba(255, 255, 255, 0.6)", strokeWidth: "2", strokeLinecap: "round" })
1319
1652
  ]
1320
1653
  }
1321
1654
  );
1322
1655
  }
1323
1656
 
1324
1657
  // src/components/TransitionControl.tsx
1325
- import { useSyncExternalStore as useSyncExternalStore3, useState as useState4 } from "react";
1326
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1658
+ import { useSyncExternalStore as useSyncExternalStore3, useRef as useRef7, useState as useState5 } from "react";
1659
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1327
1660
  function TransitionControl({ panelId, path, label, value, onChange }) {
1328
1661
  const mode = useSyncExternalStore3(
1329
1662
  (cb) => DialStore.subscribe(panelId, cb),
@@ -1332,26 +1665,28 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1332
1665
  );
1333
1666
  const isEasing = mode === "easing";
1334
1667
  const isSimpleSpring = mode === "simple";
1335
- const spring = value.type === "spring" ? value : { type: "spring", visualDuration: 0.3, bounce: 0.2 };
1336
- const easing = value.type === "easing" ? value : { type: "easing", duration: 0.3, ease: [1, -0.4, 0.5, 1] };
1668
+ const cache = useRef7({
1669
+ easing: value.type === "easing" ? value : { type: "easing", duration: 0.3, ease: [1, -0.4, 0.5, 1] },
1670
+ simple: value.type === "spring" && value.visualDuration !== void 0 ? value : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1671
+ advanced: value.type === "spring" && value.stiffness !== void 0 ? value : { type: "spring", stiffness: 200, damping: 25, mass: 1 }
1672
+ });
1673
+ if (isEasing && value.type === "easing") {
1674
+ cache.current.easing = value;
1675
+ } else if (isSimpleSpring && value.type === "spring") {
1676
+ cache.current.simple = value;
1677
+ } else if (mode === "advanced" && value.type === "spring") {
1678
+ cache.current.advanced = value;
1679
+ }
1680
+ const spring = value.type === "spring" ? value : cache.current.simple;
1681
+ const easing = value.type === "easing" ? value : cache.current.easing;
1337
1682
  const handleModeChange = (newMode) => {
1338
1683
  DialStore.updateTransitionMode(panelId, path, newMode);
1339
1684
  if (newMode === "easing") {
1340
- const duration = value.type === "spring" ? value.visualDuration ?? 0.3 : value.duration;
1341
- onChange({ type: "easing", duration, ease: easing.ease });
1685
+ onChange(cache.current.easing);
1342
1686
  } else if (newMode === "simple") {
1343
- onChange({
1344
- type: "spring",
1345
- visualDuration: spring.visualDuration ?? (value.type === "easing" ? value.duration : 0.3),
1346
- bounce: spring.bounce ?? 0.2
1347
- });
1687
+ onChange(cache.current.simple);
1348
1688
  } else {
1349
- onChange({
1350
- type: "spring",
1351
- stiffness: spring.stiffness ?? 200,
1352
- damping: spring.damping ?? 25,
1353
- mass: spring.mass ?? 1
1354
- });
1689
+ onChange(cache.current.advanced);
1355
1690
  }
1356
1691
  };
1357
1692
  const handleSpringUpdate = (key, val) => {
@@ -1368,11 +1703,11 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1368
1703
  newEase[index] = val;
1369
1704
  onChange({ ...easing, ease: newEase });
1370
1705
  };
1371
- return /* @__PURE__ */ jsx8(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1372
- isEasing ? /* @__PURE__ */ jsx8(EasingVisualization, { easing }) : /* @__PURE__ */ jsx8(SpringVisualization, { spring, isSimpleMode: isSimpleSpring }),
1706
+ return /* @__PURE__ */ jsx9(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1707
+ isEasing ? /* @__PURE__ */ jsx9(EasingVisualization, { easing }) : /* @__PURE__ */ jsx9(SpringVisualization, { spring, isSimpleMode: isSimpleSpring }),
1373
1708
  /* @__PURE__ */ jsxs8("div", { className: "dialkit-labeled-control", children: [
1374
- /* @__PURE__ */ jsx8("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1375
- /* @__PURE__ */ jsx8(
1709
+ /* @__PURE__ */ jsx9("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1710
+ /* @__PURE__ */ jsx9(
1376
1711
  SegmentedControl,
1377
1712
  {
1378
1713
  options: [
@@ -1386,19 +1721,19 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1386
1721
  )
1387
1722
  ] }),
1388
1723
  isEasing ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
1389
- /* @__PURE__ */ jsx8(Slider, { label: "x1", value: easing.ease[0], onChange: (v) => updateEase(0, v), min: 0, max: 1, step: 0.01 }),
1390
- /* @__PURE__ */ jsx8(Slider, { label: "y1", value: easing.ease[1], onChange: (v) => updateEase(1, v), min: -1, max: 2, step: 0.01 }),
1391
- /* @__PURE__ */ jsx8(Slider, { label: "x2", value: easing.ease[2], onChange: (v) => updateEase(2, v), min: 0, max: 1, step: 0.01 }),
1392
- /* @__PURE__ */ jsx8(Slider, { label: "y2", value: easing.ease[3], onChange: (v) => updateEase(3, v), min: -1, max: 2, step: 0.01 }),
1393
- /* @__PURE__ */ jsx8(Slider, { label: "Duration", value: easing.duration, onChange: (v) => onChange({ ...easing, duration: v }), min: 0.1, max: 2, step: 0.05, unit: "s" }),
1394
- /* @__PURE__ */ jsx8(EaseTextInput, { ease: easing.ease, onChange: (newEase) => onChange({ ...easing, ease: newEase }) })
1724
+ /* @__PURE__ */ jsx9(Slider, { label: "x1", value: easing.ease[0], onChange: (v) => updateEase(0, v), min: 0, max: 1, step: 0.01 }),
1725
+ /* @__PURE__ */ jsx9(Slider, { label: "y1", value: easing.ease[1], onChange: (v) => updateEase(1, v), min: -1, max: 2, step: 0.01 }),
1726
+ /* @__PURE__ */ jsx9(Slider, { label: "x2", value: easing.ease[2], onChange: (v) => updateEase(2, v), min: 0, max: 1, step: 0.01 }),
1727
+ /* @__PURE__ */ jsx9(Slider, { label: "y2", value: easing.ease[3], onChange: (v) => updateEase(3, v), min: -1, max: 2, step: 0.01 }),
1728
+ /* @__PURE__ */ jsx9(Slider, { label: "Duration", value: easing.duration, onChange: (v) => onChange({ ...easing, duration: v }), min: 0.1, max: 2, step: 0.05, unit: "s" }),
1729
+ /* @__PURE__ */ jsx9(EaseTextInput, { ease: easing.ease, onChange: (newEase) => onChange({ ...easing, ease: newEase }) })
1395
1730
  ] }) : isSimpleSpring ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
1396
- /* @__PURE__ */ jsx8(Slider, { label: "Duration", value: spring.visualDuration ?? 0.3, onChange: (v) => handleSpringUpdate("visualDuration", v), min: 0.1, max: 1, step: 0.05, unit: "s" }),
1397
- /* @__PURE__ */ jsx8(Slider, { label: "Bounce", value: spring.bounce ?? 0.2, onChange: (v) => handleSpringUpdate("bounce", v), min: 0, max: 1, step: 0.05 })
1731
+ /* @__PURE__ */ jsx9(Slider, { label: "Duration", value: spring.visualDuration ?? 0.3, onChange: (v) => handleSpringUpdate("visualDuration", v), min: 0.1, max: 1, step: 0.05, unit: "s" }),
1732
+ /* @__PURE__ */ jsx9(Slider, { label: "Bounce", value: spring.bounce ?? 0.2, onChange: (v) => handleSpringUpdate("bounce", v), min: 0, max: 1, step: 0.05 })
1398
1733
  ] }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
1399
- /* @__PURE__ */ jsx8(Slider, { label: "Stiffness", value: spring.stiffness ?? 400, onChange: (v) => handleSpringUpdate("stiffness", v), min: 1, max: 1e3, step: 10 }),
1400
- /* @__PURE__ */ jsx8(Slider, { label: "Damping", value: spring.damping ?? 17, onChange: (v) => handleSpringUpdate("damping", v), min: 1, max: 100, step: 1 }),
1401
- /* @__PURE__ */ jsx8(Slider, { label: "Mass", value: spring.mass ?? 1, onChange: (v) => handleSpringUpdate("mass", v), min: 0.1, max: 10, step: 0.1 })
1734
+ /* @__PURE__ */ jsx9(Slider, { label: "Stiffness", value: spring.stiffness ?? 400, onChange: (v) => handleSpringUpdate("stiffness", v), min: 1, max: 1e3, step: 10 }),
1735
+ /* @__PURE__ */ jsx9(Slider, { label: "Damping", value: spring.damping ?? 17, onChange: (v) => handleSpringUpdate("damping", v), min: 1, max: 100, step: 1 }),
1736
+ /* @__PURE__ */ jsx9(Slider, { label: "Mass", value: spring.mass ?? 1, onChange: (v) => handleSpringUpdate("mass", v), min: 0.1, max: 10, step: 0.1 })
1402
1737
  ] })
1403
1738
  ] }) });
1404
1739
  }
@@ -1413,8 +1748,8 @@ function parseEase(str) {
1413
1748
  return null;
1414
1749
  }
1415
1750
  function EaseTextInput({ ease, onChange }) {
1416
- const [editing, setEditing] = useState4(false);
1417
- const [draft, setDraft] = useState4("");
1751
+ const [editing, setEditing] = useState5(false);
1752
+ const [draft, setDraft] = useState5("");
1418
1753
  const handleFocus = () => {
1419
1754
  setDraft(formatEase(ease));
1420
1755
  setEditing(true);
@@ -1430,8 +1765,8 @@ function EaseTextInput({ ease, onChange }) {
1430
1765
  }
1431
1766
  };
1432
1767
  return /* @__PURE__ */ jsxs8("div", { className: "dialkit-labeled-control", children: [
1433
- /* @__PURE__ */ jsx8("span", { className: "dialkit-labeled-control-label", children: "Ease" }),
1434
- /* @__PURE__ */ jsx8(
1768
+ /* @__PURE__ */ jsx9("span", { className: "dialkit-labeled-control-label", children: "Ease" }),
1769
+ /* @__PURE__ */ jsx9(
1435
1770
  "input",
1436
1771
  {
1437
1772
  type: "text",
@@ -1448,11 +1783,11 @@ function EaseTextInput({ ease, onChange }) {
1448
1783
  }
1449
1784
 
1450
1785
  // src/components/TextControl.tsx
1451
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1786
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1452
1787
  function TextControl({ label, value, onChange, placeholder }) {
1453
1788
  return /* @__PURE__ */ jsxs9("div", { className: "dialkit-text-control", children: [
1454
- /* @__PURE__ */ jsx9("label", { className: "dialkit-text-label", children: label }),
1455
- /* @__PURE__ */ jsx9(
1789
+ /* @__PURE__ */ jsx10("label", { className: "dialkit-text-label", children: label }),
1790
+ /* @__PURE__ */ jsx10(
1456
1791
  "input",
1457
1792
  {
1458
1793
  type: "text",
@@ -1466,10 +1801,10 @@ function TextControl({ label, value, onChange, placeholder }) {
1466
1801
  }
1467
1802
 
1468
1803
  // src/components/SelectControl.tsx
1469
- import { useState as useState5, useRef as useRef5, useEffect as useEffect4, useCallback as useCallback2 } from "react";
1804
+ import { useState as useState6, useRef as useRef8, useEffect as useEffect5, useCallback as useCallback4 } from "react";
1470
1805
  import { createPortal } from "react-dom";
1471
- import { motion as motion4, AnimatePresence as AnimatePresence2 } from "motion/react";
1472
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1806
+ import { motion as motion3, AnimatePresence as AnimatePresence2 } from "motion/react";
1807
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1473
1808
  function toTitleCase(s) {
1474
1809
  return s.replace(/\b\w/g, (c) => c.toUpperCase());
1475
1810
  }
@@ -1479,14 +1814,14 @@ function normalizeOptions(options) {
1479
1814
  );
1480
1815
  }
1481
1816
  function SelectControl({ label, value, options, onChange }) {
1482
- const [isOpen, setIsOpen] = useState5(false);
1483
- const triggerRef = useRef5(null);
1484
- const dropdownRef = useRef5(null);
1485
- const [portalTarget, setPortalTarget] = useState5(null);
1486
- const [pos, setPos] = useState5(null);
1817
+ const [isOpen, setIsOpen] = useState6(false);
1818
+ const triggerRef = useRef8(null);
1819
+ const dropdownRef = useRef8(null);
1820
+ const [portalTarget, setPortalTarget] = useState6(null);
1821
+ const [pos, setPos] = useState6(null);
1487
1822
  const normalized = normalizeOptions(options);
1488
1823
  const selectedOption = normalized.find((o) => o.value === value);
1489
- const updatePos = useCallback2(() => {
1824
+ const updatePos = useCallback4(() => {
1490
1825
  const el = triggerRef.current;
1491
1826
  if (!el) return;
1492
1827
  const rect = el.getBoundingClientRect();
@@ -1500,15 +1835,15 @@ function SelectControl({ label, value, options, onChange }) {
1500
1835
  above
1501
1836
  });
1502
1837
  }, [normalized.length]);
1503
- useEffect4(() => {
1838
+ useEffect5(() => {
1504
1839
  const root = triggerRef.current?.closest(".dialkit-root");
1505
1840
  setPortalTarget(root ?? document.body);
1506
1841
  }, []);
1507
- useEffect4(() => {
1842
+ useEffect5(() => {
1508
1843
  if (!isOpen) return;
1509
1844
  updatePos();
1510
1845
  }, [isOpen, updatePos]);
1511
- useEffect4(() => {
1846
+ useEffect5(() => {
1512
1847
  if (!isOpen) return;
1513
1848
  const handleClick = (e) => {
1514
1849
  const target = e.target;
@@ -1528,11 +1863,11 @@ function SelectControl({ label, value, options, onChange }) {
1528
1863
  onClick: () => setIsOpen(!isOpen),
1529
1864
  "data-open": String(isOpen),
1530
1865
  children: [
1531
- /* @__PURE__ */ jsx10("span", { className: "dialkit-select-label", children: label }),
1866
+ /* @__PURE__ */ jsx11("span", { className: "dialkit-select-label", children: label }),
1532
1867
  /* @__PURE__ */ jsxs10("div", { className: "dialkit-select-right", children: [
1533
- /* @__PURE__ */ jsx10("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
1534
- /* @__PURE__ */ jsx10(
1535
- motion4.svg,
1868
+ /* @__PURE__ */ jsx11("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
1869
+ /* @__PURE__ */ jsx11(
1870
+ motion3.svg,
1536
1871
  {
1537
1872
  className: "dialkit-select-chevron",
1538
1873
  viewBox: "0 0 24 24",
@@ -1543,7 +1878,7 @@ function SelectControl({ label, value, options, onChange }) {
1543
1878
  strokeLinejoin: "round",
1544
1879
  animate: { rotate: isOpen ? 180 : 0 },
1545
1880
  transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1546
- children: /* @__PURE__ */ jsx10("path", { d: "M6 9.5L12 15.5L18 9.5" })
1881
+ children: /* @__PURE__ */ jsx11("path", { d: ICON_CHEVRON })
1547
1882
  }
1548
1883
  )
1549
1884
  ] })
@@ -1551,8 +1886,8 @@ function SelectControl({ label, value, options, onChange }) {
1551
1886
  }
1552
1887
  ),
1553
1888
  portalTarget && createPortal(
1554
- /* @__PURE__ */ jsx10(AnimatePresence2, { children: isOpen && pos && /* @__PURE__ */ jsx10(
1555
- motion4.div,
1889
+ /* @__PURE__ */ jsx11(AnimatePresence2, { children: isOpen && pos && /* @__PURE__ */ jsx11(
1890
+ motion3.div,
1556
1891
  {
1557
1892
  ref: dropdownRef,
1558
1893
  className: "dialkit-select-dropdown",
@@ -1566,7 +1901,7 @@ function SelectControl({ label, value, options, onChange }) {
1566
1901
  width: pos.width,
1567
1902
  ...pos.above ? { bottom: window.innerHeight - pos.top, transformOrigin: "bottom" } : { top: pos.top, transformOrigin: "top" }
1568
1903
  },
1569
- children: normalized.map((option) => /* @__PURE__ */ jsx10(
1904
+ children: normalized.map((option) => /* @__PURE__ */ jsx11(
1570
1905
  "button",
1571
1906
  {
1572
1907
  className: "dialkit-select-option",
@@ -1587,14 +1922,14 @@ function SelectControl({ label, value, options, onChange }) {
1587
1922
  }
1588
1923
 
1589
1924
  // src/components/ColorControl.tsx
1590
- import { useState as useState6, useRef as useRef6, useEffect as useEffect5 } from "react";
1591
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1925
+ import { useState as useState7, useRef as useRef9, useEffect as useEffect6 } from "react";
1926
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1592
1927
  var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
1593
1928
  function ColorControl({ label, value, onChange }) {
1594
- const [isEditing, setIsEditing] = useState6(false);
1595
- const [editValue, setEditValue] = useState6(value);
1596
- const colorInputRef = useRef6(null);
1597
- useEffect5(() => {
1929
+ const [isEditing, setIsEditing] = useState7(false);
1930
+ const [editValue, setEditValue] = useState7(value);
1931
+ const colorInputRef = useRef9(null);
1932
+ useEffect6(() => {
1598
1933
  if (!isEditing) {
1599
1934
  setEditValue(value);
1600
1935
  }
@@ -1616,9 +1951,9 @@ function ColorControl({ label, value, onChange }) {
1616
1951
  }
1617
1952
  }
1618
1953
  return /* @__PURE__ */ jsxs11("div", { className: "dialkit-color-control", children: [
1619
- /* @__PURE__ */ jsx11("span", { className: "dialkit-color-label", children: label }),
1954
+ /* @__PURE__ */ jsx12("span", { className: "dialkit-color-label", children: label }),
1620
1955
  /* @__PURE__ */ jsxs11("div", { className: "dialkit-color-inputs", children: [
1621
- isEditing ? /* @__PURE__ */ jsx11(
1956
+ isEditing ? /* @__PURE__ */ jsx12(
1622
1957
  "input",
1623
1958
  {
1624
1959
  type: "text",
@@ -1629,7 +1964,7 @@ function ColorControl({ label, value, onChange }) {
1629
1964
  onKeyDown: handleKeyDown,
1630
1965
  autoFocus: true
1631
1966
  }
1632
- ) : /* @__PURE__ */ jsx11(
1967
+ ) : /* @__PURE__ */ jsx12(
1633
1968
  "span",
1634
1969
  {
1635
1970
  className: "dialkit-color-hex",
@@ -1637,7 +1972,7 @@ function ColorControl({ label, value, onChange }) {
1637
1972
  children: (value ?? "").toUpperCase()
1638
1973
  }
1639
1974
  ),
1640
- /* @__PURE__ */ jsx11(
1975
+ /* @__PURE__ */ jsx12(
1641
1976
  "button",
1642
1977
  {
1643
1978
  className: "dialkit-color-swatch",
@@ -1646,7 +1981,7 @@ function ColorControl({ label, value, onChange }) {
1646
1981
  title: "Pick color"
1647
1982
  }
1648
1983
  ),
1649
- /* @__PURE__ */ jsx11(
1984
+ /* @__PURE__ */ jsx12(
1650
1985
  "input",
1651
1986
  {
1652
1987
  ref: colorInputRef,
@@ -1665,18 +2000,18 @@ function expandShorthandHex(hex) {
1665
2000
  }
1666
2001
 
1667
2002
  // src/components/PresetManager.tsx
1668
- import { useState as useState7, useRef as useRef7, useEffect as useEffect6, useCallback as useCallback3 } from "react";
2003
+ import { useState as useState8, useRef as useRef10, useEffect as useEffect7, useCallback as useCallback5 } from "react";
1669
2004
  import { createPortal as createPortal2 } from "react-dom";
1670
- import { motion as motion5, AnimatePresence as AnimatePresence3 } from "motion/react";
1671
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2005
+ import { motion as motion4, AnimatePresence as AnimatePresence3 } from "motion/react";
2006
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1672
2007
  function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1673
- const [isOpen, setIsOpen] = useState7(false);
1674
- const triggerRef = useRef7(null);
1675
- const dropdownRef = useRef7(null);
1676
- const [pos, setPos] = useState7({ top: 0, left: 0, width: 0 });
2008
+ const [isOpen, setIsOpen] = useState8(false);
2009
+ const triggerRef = useRef10(null);
2010
+ const dropdownRef = useRef10(null);
2011
+ const [pos, setPos] = useState8({ top: 0, left: 0, width: 0 });
1677
2012
  const hasPresets = presets.length > 0;
1678
2013
  const activePreset = presets.find((p) => p.id === activePresetId);
1679
- const open = useCallback3(() => {
2014
+ const open = useCallback5(() => {
1680
2015
  if (!hasPresets) return;
1681
2016
  const rect = triggerRef.current?.getBoundingClientRect();
1682
2017
  if (rect) {
@@ -1684,12 +2019,12 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1684
2019
  }
1685
2020
  setIsOpen(true);
1686
2021
  }, [hasPresets]);
1687
- const close = useCallback3(() => setIsOpen(false), []);
1688
- const toggle = useCallback3(() => {
2022
+ const close = useCallback5(() => setIsOpen(false), []);
2023
+ const toggle = useCallback5(() => {
1689
2024
  if (isOpen) close();
1690
2025
  else open();
1691
2026
  }, [isOpen, open, close]);
1692
- useEffect6(() => {
2027
+ useEffect7(() => {
1693
2028
  if (!isOpen) return;
1694
2029
  const handler = (e) => {
1695
2030
  const target = e.target;
@@ -1722,9 +2057,9 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1722
2057
  "data-has-preset": String(!!activePreset),
1723
2058
  "data-disabled": String(!hasPresets),
1724
2059
  children: [
1725
- /* @__PURE__ */ jsx12("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
1726
- /* @__PURE__ */ jsx12(
1727
- motion5.svg,
2060
+ /* @__PURE__ */ jsx13("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
2061
+ /* @__PURE__ */ jsx13(
2062
+ motion4.svg,
1728
2063
  {
1729
2064
  className: "dialkit-select-chevron",
1730
2065
  viewBox: "0 0 24 24",
@@ -1735,15 +2070,15 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1735
2070
  strokeLinejoin: "round",
1736
2071
  animate: { rotate: isOpen ? 180 : 0, opacity: hasPresets ? 0.6 : 0.25 },
1737
2072
  transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1738
- children: /* @__PURE__ */ jsx12("path", { d: "M6 9.5L12 15.5L18 9.5" })
2073
+ children: /* @__PURE__ */ jsx13("path", { d: ICON_CHEVRON })
1739
2074
  }
1740
2075
  )
1741
2076
  ]
1742
2077
  }
1743
2078
  ),
1744
2079
  createPortal2(
1745
- /* @__PURE__ */ jsx12(AnimatePresence3, { children: isOpen && /* @__PURE__ */ jsxs12(
1746
- motion5.div,
2080
+ /* @__PURE__ */ jsx13(AnimatePresence3, { children: isOpen && /* @__PURE__ */ jsxs12(
2081
+ motion4.div,
1747
2082
  {
1748
2083
  ref: dropdownRef,
1749
2084
  className: "dialkit-root dialkit-preset-dropdown",
@@ -1753,13 +2088,13 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1753
2088
  exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
1754
2089
  transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
1755
2090
  children: [
1756
- /* @__PURE__ */ jsx12(
2091
+ /* @__PURE__ */ jsx13(
1757
2092
  "div",
1758
2093
  {
1759
2094
  className: "dialkit-preset-item",
1760
2095
  "data-active": String(!activePresetId),
1761
2096
  onClick: () => handleSelect(null),
1762
- children: /* @__PURE__ */ jsx12("span", { className: "dialkit-preset-name", children: "Version 1" })
2097
+ children: /* @__PURE__ */ jsx13("span", { className: "dialkit-preset-name", children: "Version 1" })
1763
2098
  }
1764
2099
  ),
1765
2100
  presets.map((preset) => /* @__PURE__ */ jsxs12(
@@ -1769,20 +2104,14 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1769
2104
  "data-active": String(preset.id === activePresetId),
1770
2105
  onClick: () => handleSelect(preset.id),
1771
2106
  children: [
1772
- /* @__PURE__ */ jsx12("span", { className: "dialkit-preset-name", children: preset.name }),
1773
- /* @__PURE__ */ jsx12(
2107
+ /* @__PURE__ */ jsx13("span", { className: "dialkit-preset-name", children: preset.name }),
2108
+ /* @__PURE__ */ jsx13(
1774
2109
  "button",
1775
2110
  {
1776
2111
  className: "dialkit-preset-delete",
1777
2112
  onClick: (e) => handleDelete(e, preset.id),
1778
2113
  title: "Delete preset",
1779
- children: /* @__PURE__ */ jsxs12("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1780
- /* @__PURE__ */ jsx12("path", { d: "M5 6.5L5.80734 18.2064C5.91582 19.7794 7.22348 21 8.80023 21H15.1998C16.7765 21 18.0842 19.7794 18.1927 18.2064L19 6.5" }),
1781
- /* @__PURE__ */ jsx12("path", { d: "M10 11V16" }),
1782
- /* @__PURE__ */ jsx12("path", { d: "M14 11V16" }),
1783
- /* @__PURE__ */ jsx12("path", { d: "M3.5 6H20.5" }),
1784
- /* @__PURE__ */ jsx12("path", { d: "M8.07092 5.74621C8.42348 3.89745 10.0485 2.5 12 2.5C13.9515 2.5 15.5765 3.89745 15.9291 5.74621" })
1785
- ] })
2114
+ children: /* @__PURE__ */ jsx13("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: ICON_TRASH.map((d, i) => /* @__PURE__ */ jsx13("path", { d }, i)) })
1786
2115
  }
1787
2116
  )
1788
2117
  ]
@@ -1798,10 +2127,12 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1798
2127
  }
1799
2128
 
1800
2129
  // src/components/Panel.tsx
1801
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1802
- function Panel({ panel, defaultOpen = true }) {
1803
- const [copied, setCopied] = useState8(false);
1804
- const [isPanelOpen, setIsPanelOpen] = useState8(defaultOpen);
2130
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
2131
+ function Panel({ panel, defaultOpen = true, inline = false }) {
2132
+ const [copied, setCopied] = useState9(false);
2133
+ const [isPanelOpen, setIsPanelOpen] = useState9(defaultOpen);
2134
+ const shortcutCtx = useContext(ShortcutContext);
2135
+ const hasShortcuts = Object.keys(panel.shortcuts).length > 0;
1805
2136
  const values = useSyncExternalStore4(
1806
2137
  (cb) => DialStore.subscribe(panel.id, cb),
1807
2138
  () => DialStore.getValues(panel.id),
@@ -1830,7 +2161,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1830
2161
  const value = values[control.path];
1831
2162
  switch (control.type) {
1832
2163
  case "slider":
1833
- return /* @__PURE__ */ jsx13(
2164
+ return /* @__PURE__ */ jsx14(
1834
2165
  Slider,
1835
2166
  {
1836
2167
  label: control.label,
@@ -1838,22 +2169,26 @@ Apply these values as the new defaults in the useDialKit call.`;
1838
2169
  onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
1839
2170
  min: control.min,
1840
2171
  max: control.max,
1841
- step: control.step
2172
+ step: control.step,
2173
+ shortcut: control.shortcut,
2174
+ shortcutActive: shortcutCtx.activePanelId === panel.id && shortcutCtx.activePath === control.path
1842
2175
  },
1843
2176
  control.path
1844
2177
  );
1845
2178
  case "toggle":
1846
- return /* @__PURE__ */ jsx13(
2179
+ return /* @__PURE__ */ jsx14(
1847
2180
  Toggle,
1848
2181
  {
1849
2182
  label: control.label,
1850
2183
  checked: value,
1851
- onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
2184
+ onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
2185
+ shortcut: control.shortcut,
2186
+ shortcutActive: shortcutCtx.activePanelId === panel.id && shortcutCtx.activePath === control.path
1852
2187
  },
1853
2188
  control.path
1854
2189
  );
1855
2190
  case "spring":
1856
- return /* @__PURE__ */ jsx13(
2191
+ return /* @__PURE__ */ jsx14(
1857
2192
  SpringControl,
1858
2193
  {
1859
2194
  panelId: panel.id,
@@ -1865,7 +2200,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1865
2200
  control.path
1866
2201
  );
1867
2202
  case "transition":
1868
- return /* @__PURE__ */ jsx13(
2203
+ return /* @__PURE__ */ jsx14(
1869
2204
  TransitionControl,
1870
2205
  {
1871
2206
  panelId: panel.id,
@@ -1877,9 +2212,9 @@ Apply these values as the new defaults in the useDialKit call.`;
1877
2212
  control.path
1878
2213
  );
1879
2214
  case "folder":
1880
- return /* @__PURE__ */ jsx13(Folder, { title: control.label, defaultOpen: control.defaultOpen ?? true, children: control.children?.map(renderControl) }, control.path);
2215
+ return /* @__PURE__ */ jsx14(Folder, { title: control.label, defaultOpen: control.defaultOpen ?? true, children: control.children?.map(renderControl) }, control.path);
1881
2216
  case "text":
1882
- return /* @__PURE__ */ jsx13(
2217
+ return /* @__PURE__ */ jsx14(
1883
2218
  TextControl,
1884
2219
  {
1885
2220
  label: control.label,
@@ -1890,7 +2225,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1890
2225
  control.path
1891
2226
  );
1892
2227
  case "select":
1893
- return /* @__PURE__ */ jsx13(
2228
+ return /* @__PURE__ */ jsx14(
1894
2229
  SelectControl,
1895
2230
  {
1896
2231
  label: control.label,
@@ -1901,7 +2236,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1901
2236
  control.path
1902
2237
  );
1903
2238
  case "color":
1904
- return /* @__PURE__ */ jsx13(
2239
+ return /* @__PURE__ */ jsx14(
1905
2240
  ColorControl,
1906
2241
  {
1907
2242
  label: control.label,
@@ -1911,7 +2246,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1911
2246
  control.path
1912
2247
  );
1913
2248
  case "action":
1914
- return /* @__PURE__ */ jsx13(
2249
+ return /* @__PURE__ */ jsx14(
1915
2250
  "button",
1916
2251
  {
1917
2252
  className: "dialkit-button",
@@ -1929,24 +2264,18 @@ Apply these values as the new defaults in the useDialKit call.`;
1929
2264
  };
1930
2265
  const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
1931
2266
  const toolbar = /* @__PURE__ */ jsxs13(Fragment3, { children: [
1932
- /* @__PURE__ */ jsx13(
1933
- motion6.button,
2267
+ /* @__PURE__ */ jsx14(
2268
+ motion5.button,
1934
2269
  {
1935
2270
  className: "dialkit-toolbar-add",
1936
2271
  onClick: handleAddPreset,
1937
2272
  title: "Add preset",
1938
2273
  whileTap: { scale: 0.9 },
1939
2274
  transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
1940
- children: /* @__PURE__ */ jsxs13("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1941
- /* @__PURE__ */ jsx13("path", { d: "M4 6H20" }),
1942
- /* @__PURE__ */ jsx13("path", { d: "M4 12H10" }),
1943
- /* @__PURE__ */ jsx13("path", { d: "M15 15L21 15" }),
1944
- /* @__PURE__ */ jsx13("path", { d: "M18 12V18" }),
1945
- /* @__PURE__ */ jsx13("path", { d: "M4 18H10" })
1946
- ] })
2275
+ children: /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: ICON_ADD_PRESET.map((d, i) => /* @__PURE__ */ jsx14("path", { d }, i)) })
1947
2276
  }
1948
2277
  ),
1949
- /* @__PURE__ */ jsx13(
2278
+ /* @__PURE__ */ jsx14(
1950
2279
  PresetManager,
1951
2280
  {
1952
2281
  panelId: panel.id,
@@ -1955,64 +2284,71 @@ Apply these values as the new defaults in the useDialKit call.`;
1955
2284
  onAdd: handleAddPreset
1956
2285
  }
1957
2286
  ),
1958
- /* @__PURE__ */ jsxs13(
1959
- motion6.button,
2287
+ /* @__PURE__ */ jsx14(
2288
+ motion5.button,
1960
2289
  {
1961
- className: "dialkit-toolbar-copy",
2290
+ className: "dialkit-toolbar-add",
1962
2291
  onClick: handleCopy,
1963
2292
  title: "Copy parameters",
1964
- whileTap: { scale: 0.95 },
2293
+ whileTap: { scale: 0.9 },
1965
2294
  transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
1966
- children: [
1967
- /* @__PURE__ */ jsx13("span", { className: "dialkit-toolbar-copy-icon-wrap", children: /* @__PURE__ */ jsx13(AnimatePresence4, { initial: false, mode: "popLayout", children: copied ? /* @__PURE__ */ jsx13(
1968
- motion6.svg,
1969
- {
1970
- className: "dialkit-toolbar-copy-icon",
1971
- viewBox: "0 0 24 24",
1972
- fill: "none",
1973
- stroke: "currentColor",
1974
- strokeWidth: "2",
1975
- strokeLinecap: "round",
1976
- strokeLinejoin: "round",
1977
- initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1978
- animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
1979
- exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1980
- transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1981
- children: /* @__PURE__ */ jsx13("path", { d: "M5 12.75L10 19L19 5" })
1982
- },
1983
- "check"
1984
- ) : /* @__PURE__ */ jsxs13(
1985
- motion6.svg,
1986
- {
1987
- className: "dialkit-toolbar-copy-icon",
1988
- viewBox: "0 0 24 24",
1989
- fill: "none",
1990
- initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1991
- animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
1992
- exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
1993
- transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1994
- children: [
1995
- /* @__PURE__ */ jsx13("path", { d: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
1996
- /* @__PURE__ */ jsx13("path", { d: "M19.2405 16.1852L18.5436 14.3733C18.4571 14.1484 18.241 14 18 14C17.759 14 17.5429 14.1484 17.4564 14.3733L16.7595 16.1852C16.658 16.4493 16.4493 16.658 16.1852 16.7595L14.3733 17.4564C14.1484 17.5429 14 17.759 14 18C14 18.241 14.1484 18.4571 14.3733 18.5436L16.1852 19.2405C16.4493 19.342 16.658 19.5507 16.7595 19.8148L17.4564 21.6267C17.5429 21.8516 17.759 22 18 22C18.241 22 18.4571 21.8516 18.5436 21.6267L19.2405 19.8148C19.342 19.5507 19.5507 19.342 19.8148 19.2405L21.6267 18.5436C21.8516 18.4571 22 18.241 22 18C22 17.759 21.8516 17.5429 21.6267 17.4564L19.8148 16.7595C19.5507 16.658 19.342 16.4493 19.2405 16.1852Z", fill: "currentColor" }),
1997
- /* @__PURE__ */ jsx13("path", { d: "M16 5H17C18.6569 5 20 6.34315 20 8V11M8 5H7C5.34315 5 4 6.34315 4 8V18C4 19.6569 5.34315 21 7 21H12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
1998
- ]
1999
- },
2000
- "clipboard"
2001
- ) }) }),
2002
- "Copy"
2003
- ]
2295
+ children: /* @__PURE__ */ jsx14("span", { style: { position: "relative", width: 16, height: 16 }, children: /* @__PURE__ */ jsx14(AnimatePresence4, { initial: false, mode: "wait", children: copied ? /* @__PURE__ */ jsx14(
2296
+ motion5.svg,
2297
+ {
2298
+ viewBox: "0 0 24 24",
2299
+ fill: "none",
2300
+ stroke: "currentColor",
2301
+ strokeWidth: "2",
2302
+ strokeLinecap: "round",
2303
+ strokeLinejoin: "round",
2304
+ style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
2305
+ initial: { scale: 0.8, opacity: 0 },
2306
+ animate: { scale: 1, opacity: 1 },
2307
+ exit: { scale: 0.8, opacity: 0 },
2308
+ transition: { duration: 0.08 },
2309
+ children: /* @__PURE__ */ jsx14("path", { d: ICON_CHECK })
2310
+ },
2311
+ "check"
2312
+ ) : /* @__PURE__ */ jsxs13(
2313
+ motion5.svg,
2314
+ {
2315
+ viewBox: "0 0 24 24",
2316
+ fill: "none",
2317
+ style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
2318
+ initial: { scale: 0.8, opacity: 0 },
2319
+ animate: { scale: 1, opacity: 1 },
2320
+ exit: { scale: 0.8, opacity: 0 },
2321
+ transition: { duration: 0.08 },
2322
+ children: [
2323
+ /* @__PURE__ */ jsx14("path", { d: ICON_CLIPBOARD.board, stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
2324
+ /* @__PURE__ */ jsx14("path", { d: ICON_CLIPBOARD.sparkle, fill: "currentColor" }),
2325
+ /* @__PURE__ */ jsx14("path", { d: ICON_CLIPBOARD.body, stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
2326
+ ]
2327
+ },
2328
+ "clipboard"
2329
+ ) }) })
2004
2330
  }
2005
2331
  )
2006
2332
  ] });
2007
- return /* @__PURE__ */ jsx13("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ jsx13(Folder, { title: panel.name, defaultOpen, isRoot: true, onOpenChange: setIsPanelOpen, toolbar, children: renderControls() }) });
2333
+ return /* @__PURE__ */ jsx14("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ jsx14(Folder, { title: panel.name, defaultOpen, isRoot: true, inline, onOpenChange: setIsPanelOpen, toolbar, children: renderControls() }) });
2008
2334
  }
2009
2335
 
2010
2336
  // src/components/DialRoot.tsx
2011
- import { jsx as jsx14 } from "react/jsx-runtime";
2012
- function DialRoot({ position = "top-right", defaultOpen = true }) {
2013
- const [panels, setPanels] = useState9([]);
2014
- const [mounted, setMounted] = useState9(false);
2015
- useEffect7(() => {
2337
+ import { jsx as jsx15 } from "react/jsx-runtime";
2338
+ var isDevDefault = typeof process !== "undefined" && process?.env?.NODE_ENV ? process.env.NODE_ENV !== "production" : typeof import.meta !== "undefined" && import.meta.env?.MODE ? import.meta.env.MODE !== "production" : true;
2339
+ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover", theme = "system", productionEnabled = isDevDefault }) {
2340
+ if (!productionEnabled) return null;
2341
+ const [panels, setPanels] = useState10([]);
2342
+ const [mounted, setMounted] = useState10(false);
2343
+ const inline = mode === "inline";
2344
+ const panelRef = useRef11(null);
2345
+ const [dragOffset, setDragOffset] = useState10(null);
2346
+ const [activePosition, setActivePosition] = useState10(position);
2347
+ const lastDragOffset = useRef11(null);
2348
+ const draggingRef = useRef11(false);
2349
+ const dragStartRef = useRef11(null);
2350
+ const didDragRef = useRef11(false);
2351
+ useEffect8(() => {
2016
2352
  setMounted(true);
2017
2353
  setPanels(DialStore.getPanels());
2018
2354
  const unsubscribe = DialStore.subscribeGlobal(() => {
@@ -2020,20 +2356,105 @@ function DialRoot({ position = "top-right", defaultOpen = true }) {
2020
2356
  });
2021
2357
  return unsubscribe;
2022
2358
  }, []);
2359
+ useEffect8(() => {
2360
+ if (!panelRef.current || inline) return;
2361
+ const observer = new MutationObserver(() => {
2362
+ const inner = panelRef.current?.querySelector(".dialkit-panel-inner");
2363
+ if (!inner) return;
2364
+ const collapsed = inner.getAttribute("data-collapsed") === "true";
2365
+ if (!collapsed) {
2366
+ if (dragOffset) {
2367
+ lastDragOffset.current = dragOffset;
2368
+ const bubbleCenterX = dragOffset.x + 21;
2369
+ const midX = window.innerWidth / 2;
2370
+ setActivePosition(bubbleCenterX < midX ? "top-left" : "top-right");
2371
+ } else {
2372
+ setActivePosition(position);
2373
+ }
2374
+ setDragOffset(null);
2375
+ } else if (lastDragOffset.current) {
2376
+ setDragOffset(lastDragOffset.current);
2377
+ }
2378
+ });
2379
+ observer.observe(panelRef.current, { subtree: true, attributes: true, attributeFilter: ["data-collapsed"] });
2380
+ return () => observer.disconnect();
2381
+ }, [inline, dragOffset, position]);
2382
+ const handlePointerDown = useCallback6((e) => {
2383
+ const inner = panelRef.current?.querySelector(".dialkit-panel-inner");
2384
+ if (!inner || inner.getAttribute("data-collapsed") !== "true") return;
2385
+ const rect = panelRef.current.getBoundingClientRect();
2386
+ dragStartRef.current = {
2387
+ pointerX: e.clientX,
2388
+ pointerY: e.clientY,
2389
+ elX: rect.left,
2390
+ elY: rect.top
2391
+ };
2392
+ didDragRef.current = false;
2393
+ draggingRef.current = true;
2394
+ e.target.setPointerCapture(e.pointerId);
2395
+ }, []);
2396
+ const handlePointerMove = useCallback6((e) => {
2397
+ if (!draggingRef.current || !dragStartRef.current) return;
2398
+ const dx = e.clientX - dragStartRef.current.pointerX;
2399
+ const dy = e.clientY - dragStartRef.current.pointerY;
2400
+ if (!didDragRef.current && Math.abs(dx) + Math.abs(dy) < 4) return;
2401
+ didDragRef.current = true;
2402
+ setDragOffset({
2403
+ x: dragStartRef.current.elX + dx,
2404
+ y: dragStartRef.current.elY + dy
2405
+ });
2406
+ }, []);
2407
+ const handlePointerUp = useCallback6((e) => {
2408
+ if (!draggingRef.current) return;
2409
+ draggingRef.current = false;
2410
+ dragStartRef.current = null;
2411
+ if (didDragRef.current) {
2412
+ e.stopPropagation();
2413
+ const inner = panelRef.current?.querySelector(".dialkit-panel-inner");
2414
+ if (inner) {
2415
+ const blocker = (ev) => {
2416
+ ev.stopPropagation();
2417
+ };
2418
+ inner.addEventListener("click", blocker, { capture: true, once: true });
2419
+ }
2420
+ }
2421
+ }, []);
2023
2422
  if (!mounted || typeof window === "undefined") {
2024
2423
  return null;
2025
2424
  }
2026
2425
  if (panels.length === 0) {
2027
2426
  return null;
2028
2427
  }
2029
- const content = /* @__PURE__ */ jsx14("div", { className: "dialkit-root", children: /* @__PURE__ */ jsx14("div", { className: "dialkit-panel", "data-position": position, children: panels.map((panel) => /* @__PURE__ */ jsx14(Panel, { panel, defaultOpen }, panel.id)) }) });
2428
+ const dragStyle = dragOffset ? {
2429
+ top: dragOffset.y,
2430
+ left: dragOffset.x,
2431
+ right: "auto",
2432
+ bottom: "auto"
2433
+ } : void 0;
2434
+ const content = /* @__PURE__ */ jsx15(ShortcutListener, { children: /* @__PURE__ */ jsx15("div", { className: "dialkit-root", "data-mode": mode, "data-theme": theme, children: /* @__PURE__ */ jsx15(
2435
+ "div",
2436
+ {
2437
+ ref: panelRef,
2438
+ className: "dialkit-panel",
2439
+ "data-position": inline ? void 0 : dragOffset ? void 0 : activePosition,
2440
+ "data-mode": mode,
2441
+ style: dragStyle,
2442
+ onPointerDown: !inline ? handlePointerDown : void 0,
2443
+ onPointerMove: !inline ? handlePointerMove : void 0,
2444
+ onPointerUp: !inline ? handlePointerUp : void 0,
2445
+ children: panels.map((panel) => /* @__PURE__ */ jsx15(Panel, { panel, defaultOpen: inline || defaultOpen, inline }, panel.id))
2446
+ }
2447
+ ) }) });
2448
+ if (inline) {
2449
+ return content;
2450
+ }
2030
2451
  return createPortal3(content, document.body);
2031
2452
  }
2032
2453
 
2033
2454
  // src/components/ButtonGroup.tsx
2034
- import { jsx as jsx15 } from "react/jsx-runtime";
2455
+ import { jsx as jsx16 } from "react/jsx-runtime";
2035
2456
  function ButtonGroup({ buttons }) {
2036
- return /* @__PURE__ */ jsx15("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ jsx15(
2457
+ return /* @__PURE__ */ jsx16("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ jsx16(
2037
2458
  "button",
2038
2459
  {
2039
2460
  className: "dialkit-button",
@@ -2043,6 +2464,124 @@ function ButtonGroup({ buttons }) {
2043
2464
  index
2044
2465
  )) });
2045
2466
  }
2467
+
2468
+ // src/components/ShortcutsMenu.tsx
2469
+ import { useState as useState11, useRef as useRef12, useEffect as useEffect9, useCallback as useCallback7 } from "react";
2470
+ import { createPortal as createPortal4 } from "react-dom";
2471
+ import { motion as motion6, AnimatePresence as AnimatePresence5 } from "motion/react";
2472
+ import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
2473
+ function formatShortcutKey(sc) {
2474
+ if (!sc.key) return "\u2014";
2475
+ const mod = sc.modifier === "alt" ? "\u2325" : sc.modifier === "shift" ? "\u21E7" : sc.modifier === "meta" ? "\u2318" : "";
2476
+ return `${mod}${sc.key.toUpperCase()}`;
2477
+ }
2478
+ function formatInteraction(sc) {
2479
+ const interaction = sc.interaction ?? "scroll";
2480
+ switch (interaction) {
2481
+ case "scroll":
2482
+ return sc.key ? "key+scroll" : "scroll";
2483
+ case "drag":
2484
+ return "key+drag";
2485
+ case "move":
2486
+ return "key+move";
2487
+ case "scroll-only":
2488
+ return "scroll";
2489
+ }
2490
+ }
2491
+ function ShortcutsMenu({ panelId }) {
2492
+ const [isOpen, setIsOpen] = useState11(false);
2493
+ const triggerRef = useRef12(null);
2494
+ const dropdownRef = useRef12(null);
2495
+ const [pos, setPos] = useState11({ top: 0, right: 0 });
2496
+ const open = useCallback7(() => {
2497
+ const rect = triggerRef.current?.getBoundingClientRect();
2498
+ if (rect) {
2499
+ setPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
2500
+ }
2501
+ setIsOpen(true);
2502
+ }, []);
2503
+ const close = useCallback7(() => setIsOpen(false), []);
2504
+ const toggle = useCallback7(() => {
2505
+ if (isOpen) close();
2506
+ else open();
2507
+ }, [isOpen, open, close]);
2508
+ useEffect9(() => {
2509
+ if (!isOpen) return;
2510
+ const handler = (e) => {
2511
+ const target = e.target;
2512
+ if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
2513
+ close();
2514
+ };
2515
+ document.addEventListener("mousedown", handler);
2516
+ return () => document.removeEventListener("mousedown", handler);
2517
+ }, [isOpen, close]);
2518
+ const panel = DialStore.getPanel(panelId);
2519
+ if (!panel) return null;
2520
+ const shortcuts = Object.entries(panel.shortcuts);
2521
+ if (shortcuts.length === 0) return null;
2522
+ const rows = shortcuts.map(([path, shortcut]) => {
2523
+ const findLabel = (controls) => {
2524
+ for (const c of controls) {
2525
+ if (c.path === path) return c.label;
2526
+ if (c.type === "folder" && c.children) {
2527
+ const found = findLabel(c.children);
2528
+ if (found) return found;
2529
+ }
2530
+ }
2531
+ return path;
2532
+ };
2533
+ return {
2534
+ path,
2535
+ shortcut,
2536
+ label: findLabel(panel.controls)
2537
+ };
2538
+ });
2539
+ return /* @__PURE__ */ jsxs14(Fragment4, { children: [
2540
+ /* @__PURE__ */ jsx17(
2541
+ motion6.button,
2542
+ {
2543
+ ref: triggerRef,
2544
+ className: "dialkit-shortcuts-trigger",
2545
+ onClick: toggle,
2546
+ title: "Keyboard shortcuts",
2547
+ whileTap: { scale: 0.9 },
2548
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
2549
+ children: /* @__PURE__ */ jsxs14("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2550
+ /* @__PURE__ */ jsx17("rect", { x: "2", y: "6", width: "20", height: "12", rx: "2" }),
2551
+ /* @__PURE__ */ jsx17("path", { d: "M6 10H6.01" }),
2552
+ /* @__PURE__ */ jsx17("path", { d: "M10 10H10.01" }),
2553
+ /* @__PURE__ */ jsx17("path", { d: "M14 10H14.01" }),
2554
+ /* @__PURE__ */ jsx17("path", { d: "M18 10H18.01" }),
2555
+ /* @__PURE__ */ jsx17("path", { d: "M8 14H16" })
2556
+ ] })
2557
+ }
2558
+ ),
2559
+ createPortal4(
2560
+ /* @__PURE__ */ jsx17(AnimatePresence5, { children: isOpen && /* @__PURE__ */ jsxs14(
2561
+ motion6.div,
2562
+ {
2563
+ ref: dropdownRef,
2564
+ className: "dialkit-root dialkit-shortcuts-dropdown",
2565
+ style: { position: "fixed", top: pos.top, right: pos.right },
2566
+ initial: { opacity: 0, y: 4, scale: 0.97 },
2567
+ animate: { opacity: 1, y: 0, scale: 1 },
2568
+ exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
2569
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
2570
+ children: [
2571
+ /* @__PURE__ */ jsx17("div", { className: "dialkit-shortcuts-title", children: "Keyboard Shortcuts" }),
2572
+ /* @__PURE__ */ jsx17("div", { className: "dialkit-shortcuts-list", children: rows.map((row) => /* @__PURE__ */ jsxs14("div", { className: "dialkit-shortcuts-row", children: [
2573
+ /* @__PURE__ */ jsx17("span", { className: "dialkit-shortcuts-row-key", children: formatShortcutKey(row.shortcut) }),
2574
+ /* @__PURE__ */ jsx17("span", { className: "dialkit-shortcuts-row-label", children: row.label }),
2575
+ /* @__PURE__ */ jsx17("span", { className: "dialkit-shortcuts-row-mode", children: formatInteraction(row.shortcut) })
2576
+ ] }, row.path)) }),
2577
+ /* @__PURE__ */ jsx17("div", { className: "dialkit-shortcuts-hint", children: "See pill badges on controls for keys" })
2578
+ ]
2579
+ }
2580
+ ) }),
2581
+ document.body
2582
+ )
2583
+ ] });
2584
+ }
2046
2585
  export {
2047
2586
  ButtonGroup,
2048
2587
  ColorControl,
@@ -2052,6 +2591,7 @@ export {
2052
2591
  Folder,
2053
2592
  PresetManager,
2054
2593
  SelectControl,
2594
+ ShortcutsMenu,
2055
2595
  Slider,
2056
2596
  SpringControl,
2057
2597
  SpringVisualization,