dialkit 1.1.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 (67) hide show
  1. package/README.md +171 -1
  2. package/dist/index.cjs +876 -337
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +41 -6
  5. package/dist/index.d.ts +41 -6
  6. package/dist/index.js +878 -341
  7. package/dist/index.js.map +1 -1
  8. package/dist/solid/index.cjs +1011 -544
  9. package/dist/solid/index.cjs.map +1 -1
  10. package/dist/solid/index.d.cts +33 -3
  11. package/dist/solid/index.d.ts +33 -3
  12. package/dist/solid/index.js +864 -398
  13. package/dist/solid/index.js.map +1 -1
  14. package/dist/store/index.cjs +52 -12
  15. package/dist/store/index.cjs.map +1 -1
  16. package/dist/store/index.d.cts +25 -3
  17. package/dist/store/index.d.ts +25 -3
  18. package/dist/store/index.js +52 -12
  19. package/dist/store/index.js.map +1 -1
  20. package/dist/styles.css +256 -25
  21. package/dist/svelte/components/ControlRenderer.svelte +12 -0
  22. package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -1
  23. package/dist/svelte/components/DialRoot.svelte +22 -8
  24. package/dist/svelte/components/DialRoot.svelte.d.ts +3 -0
  25. package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -1
  26. package/dist/svelte/components/Folder.svelte +17 -8
  27. package/dist/svelte/components/Folder.svelte.d.ts.map +1 -1
  28. package/dist/svelte/components/Panel.svelte +14 -9
  29. package/dist/svelte/components/Panel.svelte.d.ts.map +1 -1
  30. package/dist/svelte/components/PresetManager.svelte +7 -6
  31. package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -1
  32. package/dist/svelte/components/SegmentedControl.svelte +30 -74
  33. package/dist/svelte/components/SegmentedControl.svelte.d.ts.map +1 -1
  34. package/dist/svelte/components/SelectControl.svelte +2 -1
  35. package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -1
  36. package/dist/svelte/components/ShortcutListener.svelte +265 -0
  37. package/dist/svelte/components/ShortcutListener.svelte.d.ts +13 -0
  38. package/dist/svelte/components/ShortcutListener.svelte.d.ts.map +1 -0
  39. package/dist/svelte/components/ShortcutsMenu.svelte +128 -0
  40. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts +7 -0
  41. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts.map +1 -0
  42. package/dist/svelte/components/Slider.svelte +16 -29
  43. package/dist/svelte/components/Slider.svelte.d.ts +3 -0
  44. package/dist/svelte/components/Slider.svelte.d.ts.map +1 -1
  45. package/dist/svelte/components/SpringControl.svelte +17 -15
  46. package/dist/svelte/components/SpringControl.svelte.d.ts.map +1 -1
  47. package/dist/svelte/components/Toggle.svelte +13 -2
  48. package/dist/svelte/components/Toggle.svelte.d.ts +3 -0
  49. package/dist/svelte/components/Toggle.svelte.d.ts.map +1 -1
  50. package/dist/svelte/components/TransitionControl.svelte +28 -25
  51. package/dist/svelte/components/TransitionControl.svelte.d.ts.map +1 -1
  52. package/dist/svelte/createDialKit.svelte.d.ts +2 -1
  53. package/dist/svelte/createDialKit.svelte.d.ts.map +1 -1
  54. package/dist/svelte/createDialKit.svelte.js +1 -1
  55. package/dist/svelte/index.d.ts +6 -2
  56. package/dist/svelte/index.d.ts.map +1 -1
  57. package/dist/svelte/index.js +4 -0
  58. package/dist/svelte/theme-css.d.ts +1 -1
  59. package/dist/svelte/theme-css.d.ts.map +1 -1
  60. package/dist/svelte/theme-css.js +257 -26
  61. package/dist/vue/index.cjs +3056 -0
  62. package/dist/vue/index.cjs.map +1 -0
  63. package/dist/vue/index.d.cts +675 -0
  64. package/dist/vue/index.d.ts +675 -0
  65. package/dist/vue/index.js +3014 -0
  66. package/dist/vue/index.js.map +1 -0
  67. package/package.json +24 -4
package/dist/index.cjs CHANGED
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  Folder: () => Folder,
30
30
  PresetManager: () => PresetManager,
31
31
  SelectControl: () => SelectControl,
32
+ ShortcutsMenu: () => ShortcutsMenu,
32
33
  Slider: () => Slider,
33
34
  SpringControl: () => SpringControl,
34
35
  SpringVisualization: () => SpringVisualization,
@@ -55,22 +56,22 @@ var DialStoreClass = class {
55
56
  this.activePreset = /* @__PURE__ */ new Map();
56
57
  this.baseValues = /* @__PURE__ */ new Map();
57
58
  }
58
- registerPanel(id, name, config) {
59
- const controls = this.parseConfig(config, "");
59
+ registerPanel(id, name, config, shortcuts) {
60
+ const controls = this.parseConfig(config, "", shortcuts);
60
61
  const values = this.flattenValues(config, "");
61
62
  this.initTransitionModes(config, "", values);
62
- this.panels.set(id, { id, name, controls, values });
63
+ this.panels.set(id, { id, name, controls, values, shortcuts: shortcuts ?? {} });
63
64
  this.snapshots.set(id, { ...values });
64
65
  this.baseValues.set(id, { ...values });
65
66
  this.notifyGlobal();
66
67
  }
67
- updatePanel(id, name, config) {
68
+ updatePanel(id, name, config, shortcuts) {
68
69
  const existing = this.panels.get(id);
69
70
  if (!existing) {
70
- this.registerPanel(id, name, config);
71
+ this.registerPanel(id, name, config, shortcuts);
71
72
  return;
72
73
  }
73
- const controls = this.parseConfig(config, "");
74
+ const controls = this.parseConfig(config, "", shortcuts);
74
75
  const controlsByPath = this.mapControlsByPath(controls);
75
76
  const defaultValues = this.flattenValues(config, "");
76
77
  const nextValues = {};
@@ -92,7 +93,7 @@ var DialStoreClass = class {
92
93
  nextValues[path] = mode;
93
94
  }
94
95
  }
95
- const nextPanel = { id, name, controls, values: nextValues };
96
+ const nextPanel = { id, name, controls, values: nextValues, shortcuts: shortcuts ?? existing.shortcuts };
96
97
  this.panels.set(id, nextPanel);
97
98
  this.snapshots.set(id, { ...nextValues });
98
99
  const previousBaseValues = this.baseValues.get(id) ?? {};
@@ -250,6 +251,44 @@ var DialStoreClass = class {
250
251
  this.activePreset.set(panelId, null);
251
252
  this.notify(panelId);
252
253
  }
254
+ resolveShortcutTarget(key, modifier) {
255
+ for (const panel of this.panels.values()) {
256
+ for (const [path, shortcut] of Object.entries(panel.shortcuts)) {
257
+ if (!shortcut.key) continue;
258
+ if (shortcut.key.toLowerCase() !== key.toLowerCase()) continue;
259
+ const scMod = shortcut.modifier ?? void 0;
260
+ if (scMod !== modifier) continue;
261
+ const control = this.findControlByPath(panel.controls, path);
262
+ if (control) {
263
+ return { panelId: panel.id, path, control };
264
+ }
265
+ }
266
+ }
267
+ return null;
268
+ }
269
+ resolveScrollOnlyTargets() {
270
+ const results = [];
271
+ for (const panel of this.panels.values()) {
272
+ for (const [path, shortcut] of Object.entries(panel.shortcuts)) {
273
+ if ((shortcut.interaction ?? "scroll") !== "scroll-only") continue;
274
+ const control = this.findControlByPath(panel.controls, path);
275
+ if (control) {
276
+ results.push({ panelId: panel.id, path, control, shortcut });
277
+ }
278
+ }
279
+ }
280
+ return results;
281
+ }
282
+ findControlByPath(controls, path) {
283
+ for (const control of controls) {
284
+ if (control.path === path) return control;
285
+ if (control.type === "folder" && control.children) {
286
+ const found = this.findControlByPath(control.children, path);
287
+ if (found) return found;
288
+ }
289
+ }
290
+ return null;
291
+ }
253
292
  notify(panelId) {
254
293
  this.listeners.get(panelId)?.forEach((fn) => fn());
255
294
  }
@@ -271,12 +310,13 @@ var DialStoreClass = class {
271
310
  }
272
311
  }
273
312
  }
274
- parseConfig(config, prefix) {
313
+ parseConfig(config, prefix, shortcuts) {
275
314
  const controls = [];
276
315
  for (const [key, value] of Object.entries(config)) {
277
316
  if (key === "_collapsed") continue;
278
317
  const path = prefix ? `${prefix}.${key}` : key;
279
318
  const label = this.formatLabel(key);
319
+ const shortcut = shortcuts?.[path];
280
320
  if (Array.isArray(value) && value.length <= 4 && typeof value[0] === "number") {
281
321
  controls.push({
282
322
  type: "slider",
@@ -284,13 +324,14 @@ var DialStoreClass = class {
284
324
  label,
285
325
  min: value[1],
286
326
  max: value[2],
287
- step: value[3] ?? this.inferStep(value[1], value[2])
327
+ step: value[3] ?? this.inferStep(value[1], value[2]),
328
+ shortcut
288
329
  });
289
330
  } else if (typeof value === "number") {
290
331
  const { min, max, step } = this.inferRange(value);
291
- controls.push({ type: "slider", path, label, min, max, step });
332
+ controls.push({ type: "slider", path, label, min, max, step, shortcut });
292
333
  } else if (typeof value === "boolean") {
293
- controls.push({ type: "toggle", path, label });
334
+ controls.push({ type: "toggle", path, label, shortcut });
294
335
  } else if (this.isSpringConfig(value) || this.isEasingConfig(value)) {
295
336
  controls.push({ type: "transition", path, label });
296
337
  } else if (this.isActionConfig(value)) {
@@ -315,7 +356,7 @@ var DialStoreClass = class {
315
356
  path,
316
357
  label,
317
358
  defaultOpen,
318
- children: this.parseConfig(folderConfig, path)
359
+ children: this.parseConfig(folderConfig, path, shortcuts)
319
360
  });
320
361
  }
321
362
  }
@@ -473,8 +514,11 @@ function useDialKit(name, config, options) {
473
514
  configRef.current = config;
474
515
  const onActionRef = (0, import_react.useRef)(options?.onAction);
475
516
  onActionRef.current = options?.onAction;
517
+ const shortcutsRef = (0, import_react.useRef)(options?.shortcuts);
518
+ shortcutsRef.current = options?.shortcuts;
519
+ const serializedShortcuts = JSON.stringify(options?.shortcuts);
476
520
  (0, import_react.useEffect)(() => {
477
- DialStore.registerPanel(panelId, name, configRef.current);
521
+ DialStore.registerPanel(panelId, name, configRef.current, shortcutsRef.current);
478
522
  return () => DialStore.unregisterPanel(panelId);
479
523
  }, [panelId, name]);
480
524
  const mountedRef = (0, import_react.useRef)(false);
@@ -483,8 +527,8 @@ function useDialKit(name, config, options) {
483
527
  mountedRef.current = true;
484
528
  return;
485
529
  }
486
- DialStore.updatePanel(panelId, name, configRef.current);
487
- }, [panelId, name, serializedConfig]);
530
+ DialStore.updatePanel(panelId, name, configRef.current, shortcutsRef.current);
531
+ }, [panelId, name, serializedConfig, serializedShortcuts]);
488
532
  (0, import_react.useEffect)(() => {
489
533
  return DialStore.subscribeActions(panelId, (action) => {
490
534
  onActionRef.current?.(action);
@@ -557,16 +601,329 @@ var import_react_dom3 = require("react-dom");
557
601
  var import_react15 = require("react");
558
602
  var import_react16 = require("motion/react");
559
603
 
560
- // src/components/Folder.tsx
604
+ // src/components/ShortcutListener.tsx
561
605
  var import_react2 = require("react");
562
- var import_react3 = require("motion/react");
606
+
607
+ // src/shortcut-utils.ts
608
+ function decimalsForStep(step) {
609
+ const s = step.toString();
610
+ const dot = s.indexOf(".");
611
+ return dot === -1 ? 0 : s.length - dot - 1;
612
+ }
613
+ function roundValue(val, step) {
614
+ const raw = Math.round(val / step) * step;
615
+ return parseFloat(raw.toFixed(decimalsForStep(step)));
616
+ }
617
+ function getEffectiveStep(control, shortcut) {
618
+ const min = control.min ?? 0;
619
+ const max = control.max ?? 1;
620
+ const range = max - min;
621
+ const mode = shortcut.mode ?? "normal";
622
+ return mode === "fine" ? range * 0.01 : mode === "coarse" ? range * 0.1 : control.step ?? 1;
623
+ }
624
+ function applySliderDelta(panelId, path, control, effectiveStep, direction) {
625
+ const currentValue = DialStore.getValue(panelId, path);
626
+ const min = control.min ?? 0;
627
+ const max = control.max ?? 1;
628
+ const newValue = Math.max(min, Math.min(max, currentValue + direction * effectiveStep));
629
+ DialStore.updateValue(panelId, path, roundValue(newValue, effectiveStep));
630
+ }
631
+ function snapToDecile(rawValue, min, max) {
632
+ const normalized = (rawValue - min) / (max - min);
633
+ const nearest = Math.round(normalized * 10) / 10;
634
+ if (Math.abs(normalized - nearest) <= 0.03125) {
635
+ return min + nearest * (max - min);
636
+ }
637
+ return rawValue;
638
+ }
639
+ function isInputFocused() {
640
+ const el = document.activeElement;
641
+ if (!el) return false;
642
+ const tag = el.tagName;
643
+ if (tag === "INPUT" || tag === "TEXTAREA") return true;
644
+ if (el.contentEditable === "true") return true;
645
+ return false;
646
+ }
647
+ function getActiveModifier(e) {
648
+ if (e.altKey) return "alt";
649
+ if (e.shiftKey) return "shift";
650
+ if (e.metaKey) return "meta";
651
+ return void 0;
652
+ }
653
+ function findControl(controls, path) {
654
+ for (const control of controls) {
655
+ if (control.path === path) return control;
656
+ if (control.type === "folder" && control.children) {
657
+ const found = findControl(control.children, path);
658
+ if (found) return found;
659
+ }
660
+ }
661
+ return null;
662
+ }
663
+ var DRAG_SENSITIVITY = 4;
664
+ function formatInteractionLabel(interaction) {
665
+ switch (interaction) {
666
+ case "drag":
667
+ return "Drag";
668
+ case "move":
669
+ return "Move";
670
+ case "scroll-only":
671
+ return "Scroll";
672
+ default:
673
+ return "Scroll";
674
+ }
675
+ }
676
+ function formatSliderShortcut(sc) {
677
+ const interaction = sc.interaction ?? "scroll";
678
+ const actionLabel = formatInteractionLabel(interaction);
679
+ if (!sc.key) return actionLabel;
680
+ const mod = formatModifier(sc.modifier);
681
+ return `${mod}${sc.key.toUpperCase()}+${actionLabel}`;
682
+ }
683
+ function formatToggleShortcut(sc) {
684
+ if (!sc.key) return "Press";
685
+ const mod = formatModifier(sc.modifier);
686
+ return `${mod}${sc.key.toUpperCase()}`;
687
+ }
688
+ function formatModifier(modifier) {
689
+ return modifier === "alt" ? "\u2325" : modifier === "shift" ? "\u21E7" : modifier === "meta" ? "\u2318" : "";
690
+ }
691
+
692
+ // src/components/ShortcutListener.tsx
563
693
  var import_jsx_runtime = require("react/jsx-runtime");
564
- function Folder({ title, children, defaultOpen = true, isRoot = false, inline = false, onOpenChange, toolbar }) {
565
- const [isOpen, setIsOpen] = (0, import_react2.useState)(defaultOpen);
566
- const [isCollapsed, setIsCollapsed] = (0, import_react2.useState)(!defaultOpen);
567
- const contentRef = (0, import_react2.useRef)(null);
568
- const [contentHeight, setContentHeight] = (0, import_react2.useState)(void 0);
694
+ var ShortcutContext = (0, import_react2.createContext)({ activePanelId: null, activePath: null });
695
+ function ShortcutListener({ children }) {
696
+ const [activeShortcut, setActiveShortcut] = (0, import_react2.useState)({ activePanelId: null, activePath: null });
697
+ const activeKeysRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
698
+ const isDraggingRef = (0, import_react2.useRef)(false);
699
+ const lastMouseXRef = (0, import_react2.useRef)(null);
700
+ const dragAccumulatorRef = (0, import_react2.useRef)(0);
701
+ const resolveActiveTarget = (0, import_react2.useCallback)((interaction) => {
702
+ for (const key of activeKeysRef.current) {
703
+ const panels = DialStore.getPanels();
704
+ for (const panel of panels) {
705
+ for (const [path, shortcut] of Object.entries(panel.shortcuts)) {
706
+ if (!shortcut.key) continue;
707
+ if (shortcut.key.toLowerCase() !== key) continue;
708
+ if ((shortcut.interaction ?? "scroll") !== interaction) continue;
709
+ const control = findControl(panel.controls, path);
710
+ if (control && control.type === "slider") {
711
+ return { panelId: panel.id, path, control, shortcut };
712
+ }
713
+ }
714
+ }
715
+ }
716
+ return null;
717
+ }, []);
569
718
  (0, import_react2.useEffect)(() => {
719
+ const handleKeyDown = (e) => {
720
+ if (isInputFocused()) return;
721
+ const key = e.key.toLowerCase();
722
+ if (key === "arrowleft" || key === "arrowright" || key === "arrowup" || key === "arrowdown") {
723
+ if (activeKeysRef.current.size > 0) {
724
+ const target2 = resolveActiveTarget("scroll") || resolveActiveTarget("drag") || resolveActiveTarget("move");
725
+ if (target2 && target2.control.type === "slider") {
726
+ e.preventDefault();
727
+ const direction = key === "arrowright" || key === "arrowup" ? 1 : -1;
728
+ const effectiveStep = getEffectiveStep(target2.control, target2.shortcut);
729
+ applySliderDelta(target2.panelId, target2.path, target2.control, effectiveStep, direction);
730
+ return;
731
+ }
732
+ }
733
+ }
734
+ const wasAlreadyHeld = activeKeysRef.current.has(key);
735
+ activeKeysRef.current.add(key);
736
+ const modifier = getActiveModifier(e);
737
+ const target = DialStore.resolveShortcutTarget(key, modifier);
738
+ if (target) {
739
+ setActiveShortcut({ activePanelId: target.panelId, activePath: target.path });
740
+ if (!wasAlreadyHeld && target.control.type === "toggle") {
741
+ const currentValue = DialStore.getValue(target.panelId, target.path);
742
+ DialStore.updateValue(target.panelId, target.path, !currentValue);
743
+ }
744
+ }
745
+ if (!wasAlreadyHeld) {
746
+ lastMouseXRef.current = null;
747
+ dragAccumulatorRef.current = 0;
748
+ }
749
+ };
750
+ const handleKeyUp = (e) => {
751
+ const key = e.key.toLowerCase();
752
+ activeKeysRef.current.delete(key);
753
+ isDraggingRef.current = false;
754
+ lastMouseXRef.current = null;
755
+ dragAccumulatorRef.current = 0;
756
+ if (activeKeysRef.current.size === 0) {
757
+ setActiveShortcut({ activePanelId: null, activePath: null });
758
+ } else {
759
+ let found = false;
760
+ for (const remainingKey of activeKeysRef.current) {
761
+ const modifier = getActiveModifier(e);
762
+ const target = DialStore.resolveShortcutTarget(remainingKey, modifier);
763
+ if (target) {
764
+ setActiveShortcut({ activePanelId: target.panelId, activePath: target.path });
765
+ found = true;
766
+ break;
767
+ }
768
+ }
769
+ if (!found) {
770
+ setActiveShortcut({ activePanelId: null, activePath: null });
771
+ }
772
+ }
773
+ };
774
+ const handleWheel = (e) => {
775
+ if (isInputFocused()) return;
776
+ const modifier = getActiveModifier(e);
777
+ if (activeKeysRef.current.size > 0) {
778
+ for (const key of activeKeysRef.current) {
779
+ const target = DialStore.resolveShortcutTarget(key, modifier);
780
+ if (!target) continue;
781
+ const { panelId, path, control } = target;
782
+ const interaction = control.shortcut?.interaction ?? "scroll";
783
+ if (interaction !== "scroll" || control.type !== "slider") continue;
784
+ e.preventDefault();
785
+ const effectiveStep = getEffectiveStep(control, control.shortcut);
786
+ const direction = e.deltaY > 0 ? -1 : 1;
787
+ applySliderDelta(panelId, path, control, effectiveStep, direction);
788
+ return;
789
+ }
790
+ }
791
+ const scrollOnlyTargets = DialStore.resolveScrollOnlyTargets();
792
+ for (const { panelId, path, control, shortcut } of scrollOnlyTargets) {
793
+ if (control.type !== "slider") continue;
794
+ e.preventDefault();
795
+ const effectiveStep = getEffectiveStep(control, shortcut);
796
+ const direction = e.deltaY > 0 ? -1 : 1;
797
+ applySliderDelta(panelId, path, control, effectiveStep, direction);
798
+ return;
799
+ }
800
+ };
801
+ const handleMouseDown = (e) => {
802
+ if (isInputFocused()) return;
803
+ if (activeKeysRef.current.size === 0) return;
804
+ const target = resolveActiveTarget("drag");
805
+ if (target) {
806
+ isDraggingRef.current = true;
807
+ lastMouseXRef.current = e.clientX;
808
+ dragAccumulatorRef.current = 0;
809
+ e.preventDefault();
810
+ }
811
+ };
812
+ const handleMouseUp = () => {
813
+ isDraggingRef.current = false;
814
+ lastMouseXRef.current = null;
815
+ dragAccumulatorRef.current = 0;
816
+ };
817
+ const handleMouseMove = (e) => {
818
+ if (isInputFocused()) return;
819
+ if (activeKeysRef.current.size === 0) return;
820
+ if (isDraggingRef.current) {
821
+ const target = resolveActiveTarget("drag");
822
+ if (target && lastMouseXRef.current !== null) {
823
+ const deltaX = e.clientX - lastMouseXRef.current;
824
+ lastMouseXRef.current = e.clientX;
825
+ dragAccumulatorRef.current += deltaX;
826
+ const effectiveStep = getEffectiveStep(target.control, target.shortcut);
827
+ const steps = Math.trunc(dragAccumulatorRef.current / DRAG_SENSITIVITY);
828
+ if (steps !== 0) {
829
+ dragAccumulatorRef.current -= steps * DRAG_SENSITIVITY;
830
+ applySliderDelta(target.panelId, target.path, target.control, effectiveStep, steps);
831
+ }
832
+ }
833
+ return;
834
+ }
835
+ const moveTarget = resolveActiveTarget("move");
836
+ if (moveTarget) {
837
+ if (lastMouseXRef.current === null) {
838
+ lastMouseXRef.current = e.clientX;
839
+ return;
840
+ }
841
+ const deltaX = e.clientX - lastMouseXRef.current;
842
+ lastMouseXRef.current = e.clientX;
843
+ dragAccumulatorRef.current += deltaX;
844
+ const effectiveStep = getEffectiveStep(moveTarget.control, moveTarget.shortcut);
845
+ const steps = Math.trunc(dragAccumulatorRef.current / DRAG_SENSITIVITY);
846
+ if (steps !== 0) {
847
+ dragAccumulatorRef.current -= steps * DRAG_SENSITIVITY;
848
+ applySliderDelta(moveTarget.panelId, moveTarget.path, moveTarget.control, effectiveStep, steps);
849
+ }
850
+ }
851
+ };
852
+ const handleWindowBlur = () => {
853
+ activeKeysRef.current.clear();
854
+ isDraggingRef.current = false;
855
+ lastMouseXRef.current = null;
856
+ dragAccumulatorRef.current = 0;
857
+ setActiveShortcut({ activePanelId: null, activePath: null });
858
+ };
859
+ window.addEventListener("keydown", handleKeyDown);
860
+ window.addEventListener("keyup", handleKeyUp);
861
+ window.addEventListener("wheel", handleWheel, { passive: false });
862
+ window.addEventListener("mousedown", handleMouseDown);
863
+ window.addEventListener("mouseup", handleMouseUp);
864
+ window.addEventListener("mousemove", handleMouseMove);
865
+ window.addEventListener("blur", handleWindowBlur);
866
+ return () => {
867
+ window.removeEventListener("keydown", handleKeyDown);
868
+ window.removeEventListener("keyup", handleKeyUp);
869
+ window.removeEventListener("wheel", handleWheel);
870
+ window.removeEventListener("mousedown", handleMouseDown);
871
+ window.removeEventListener("mouseup", handleMouseUp);
872
+ window.removeEventListener("mousemove", handleMouseMove);
873
+ window.removeEventListener("blur", handleWindowBlur);
874
+ };
875
+ }, [resolveActiveTarget]);
876
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShortcutContext.Provider, { value: activeShortcut, children });
877
+ }
878
+
879
+ // src/icons.ts
880
+ var ICON_CHEVRON = "M6 9.5L12 15.5L18 9.5";
881
+ var ICON_CHECK = "M5 12.75L10 19L19 5";
882
+ var ICON_CLIPBOARD = {
883
+ board: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z",
884
+ 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",
885
+ 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"
886
+ };
887
+ var ICON_ADD_PRESET = [
888
+ "M4 6H20",
889
+ "M4 12H10",
890
+ "M15 15L21 15",
891
+ "M18 12V18",
892
+ "M4 18H10"
893
+ ];
894
+ var ICON_TRASH = [
895
+ "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",
896
+ "M10 11V16",
897
+ "M14 11V16",
898
+ "M3.5 6H20.5",
899
+ "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"
900
+ ];
901
+ var ICON_PANEL = {
902
+ 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",
903
+ circles: [
904
+ { cx: "6", cy: "8", r: "0.998596" },
905
+ { cx: "10.4999", cy: "3.5", r: "0.998657" },
906
+ { cx: "9.75015", cy: "12.5", r: "0.997986" }
907
+ ]
908
+ };
909
+
910
+ // src/components/Folder.tsx
911
+ var import_react3 = require("react");
912
+ var import_react4 = require("motion/react");
913
+ var import_jsx_runtime2 = require("react/jsx-runtime");
914
+ function Folder({ title, children, defaultOpen = true, isRoot = false, inline = false, onOpenChange, toolbar }) {
915
+ const [isOpen, setIsOpen] = (0, import_react3.useState)(defaultOpen);
916
+ const [isCollapsed, setIsCollapsed] = (0, import_react3.useState)(!defaultOpen);
917
+ const contentRef = (0, import_react3.useRef)(null);
918
+ const [contentHeight, setContentHeight] = (0, import_react3.useState)(void 0);
919
+ const [windowHeight, setWindowHeight] = (0, import_react3.useState)(typeof window !== "undefined" ? window.innerHeight : 800);
920
+ (0, import_react3.useEffect)(() => {
921
+ if (!isRoot) return;
922
+ const onResize = () => setWindowHeight(window.innerHeight);
923
+ window.addEventListener("resize", onResize);
924
+ return () => window.removeEventListener("resize", onResize);
925
+ }, [isRoot]);
926
+ (0, import_react3.useEffect)(() => {
570
927
  const el = contentRef.current;
571
928
  if (!el) return;
572
929
  const ro = new ResizeObserver(() => {
@@ -589,26 +946,24 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, inline =
589
946
  }
590
947
  onOpenChange?.(next);
591
948
  };
592
- const folderContent = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
593
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: handleToggle, children: [
594
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-header-top", children: [
595
- isRoot ? isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dialkit-folder-title", children: title }) }),
596
- isRoot && !inline && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
949
+ const folderContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
950
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: handleToggle, children: [
951
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dialkit-folder-header-top", children: [
952
+ isRoot ? isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dialkit-folder-title dialkit-folder-title-root", children: title }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-folder-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dialkit-folder-title", children: title }) }),
953
+ isRoot && !inline && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
597
954
  "svg",
598
955
  {
599
956
  className: "dialkit-panel-icon",
600
957
  viewBox: "0 0 16 16",
601
958
  fill: "none",
602
959
  children: [
603
- /* @__PURE__ */ (0, import_jsx_runtime.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" }),
604
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "8", r: "0.998596", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
605
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.4999", cy: "3.5", r: "0.998657", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
606
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9.75015", cy: "12.5", r: "0.997986", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })
960
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { opacity: "0.5", d: ICON_PANEL.path, fill: "currentColor" }),
961
+ ICON_PANEL.circles.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: c.cx, cy: c.cy, r: c.r, fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }, i))
607
962
  ]
608
963
  }
609
964
  ),
610
- !isRoot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
611
- import_react3.motion.svg,
965
+ !isRoot && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
966
+ import_react4.motion.svg,
612
967
  {
613
968
  className: "dialkit-folder-icon",
614
969
  viewBox: "0 0 24 24",
@@ -620,14 +975,14 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, inline =
620
975
  initial: false,
621
976
  animate: { rotate: isOpen ? 0 : 180 },
622
977
  transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
623
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
978
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: ICON_CHEVRON })
624
979
  }
625
980
  )
626
981
  ] }),
627
- isRoot && toolbar && isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
982
+ isRoot && toolbar && isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-panel-toolbar", onClick: (e) => e.stopPropagation(), children: toolbar })
628
983
  ] }),
629
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
630
- import_react3.motion.div,
984
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
985
+ import_react4.motion.div,
631
986
  {
632
987
  className: "dialkit-folder-content",
633
988
  initial: isRoot ? void 0 : { height: 0, opacity: 0 },
@@ -635,17 +990,17 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, inline =
635
990
  exit: isRoot ? void 0 : { height: 0, opacity: 0 },
636
991
  transition: isRoot ? void 0 : { type: "spring", visualDuration: 0.35, bounce: 0.1 },
637
992
  style: isRoot ? void 0 : { clipPath: "inset(0 -20px)" },
638
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-folder-inner", children })
993
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-folder-inner", children })
639
994
  }
640
995
  ) })
641
996
  ] });
642
997
  if (isRoot) {
643
998
  if (inline) {
644
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dialkit-panel-inner dialkit-panel-inline", children: folderContent });
999
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-panel-inner dialkit-panel-inline", children: folderContent });
645
1000
  }
646
- 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" };
647
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
648
- import_react3.motion.div,
1001
+ 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" };
1002
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1003
+ import_react4.motion.div,
649
1004
  {
650
1005
  className: "dialkit-panel-inner",
651
1006
  style: panelStyle,
@@ -661,30 +1016,13 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, inline =
661
1016
  }
662
1017
 
663
1018
  // src/components/Slider.tsx
664
- var import_react4 = require("react");
665
- var import_react5 = require("motion/react");
666
- var import_jsx_runtime2 = require("react/jsx-runtime");
1019
+ var import_react5 = require("react");
1020
+ var import_react6 = require("motion/react");
1021
+ var import_jsx_runtime3 = require("react/jsx-runtime");
667
1022
  var CLICK_THRESHOLD = 3;
668
1023
  var DEAD_ZONE = 32;
669
1024
  var MAX_CURSOR_RANGE = 200;
670
1025
  var MAX_STRETCH = 8;
671
- function decimalsForStep(step) {
672
- const s = step.toString();
673
- const dot = s.indexOf(".");
674
- return dot === -1 ? 0 : s.length - dot - 1;
675
- }
676
- function roundValue(val, step) {
677
- const raw = Math.round(val / step) * step;
678
- return parseFloat(raw.toFixed(decimalsForStep(step)));
679
- }
680
- function snapToDecile(rawValue, min, max) {
681
- const normalized = (rawValue - min) / (max - min);
682
- const nearest = Math.round(normalized * 10) / 10;
683
- if (Math.abs(normalized - nearest) <= 0.03125) {
684
- return min + nearest * (max - min);
685
- }
686
- return rawValue;
687
- }
688
1026
  function Slider({
689
1027
  label,
690
1028
  value,
@@ -692,49 +1030,51 @@ function Slider({
692
1030
  min = 0,
693
1031
  max = 1,
694
1032
  step = 0.01,
695
- unit
1033
+ unit,
1034
+ shortcut,
1035
+ shortcutActive
696
1036
  }) {
697
- const wrapperRef = (0, import_react4.useRef)(null);
698
- const trackRef = (0, import_react4.useRef)(null);
699
- const inputRef = (0, import_react4.useRef)(null);
700
- const labelRef = (0, import_react4.useRef)(null);
701
- const valueSpanRef = (0, import_react4.useRef)(null);
702
- const [isInteracting, setIsInteracting] = (0, import_react4.useState)(false);
703
- const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
704
- const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
705
- const [isValueHovered, setIsValueHovered] = (0, import_react4.useState)(false);
706
- const [isValueEditable, setIsValueEditable] = (0, import_react4.useState)(false);
707
- const [showInput, setShowInput] = (0, import_react4.useState)(false);
708
- const [inputValue, setInputValue] = (0, import_react4.useState)("");
709
- const hoverTimeoutRef = (0, import_react4.useRef)(null);
710
- const pointerDownPos = (0, import_react4.useRef)(null);
711
- const isClickRef = (0, import_react4.useRef)(true);
712
- const animRef = (0, import_react4.useRef)(null);
713
- const wrapperRectRef = (0, import_react4.useRef)(null);
714
- const scaleRef = (0, import_react4.useRef)(1);
1037
+ const wrapperRef = (0, import_react5.useRef)(null);
1038
+ const trackRef = (0, import_react5.useRef)(null);
1039
+ const inputRef = (0, import_react5.useRef)(null);
1040
+ const labelRef = (0, import_react5.useRef)(null);
1041
+ const valueSpanRef = (0, import_react5.useRef)(null);
1042
+ const [isInteracting, setIsInteracting] = (0, import_react5.useState)(false);
1043
+ const [isDragging, setIsDragging] = (0, import_react5.useState)(false);
1044
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
1045
+ const [isValueHovered, setIsValueHovered] = (0, import_react5.useState)(false);
1046
+ const [isValueEditable, setIsValueEditable] = (0, import_react5.useState)(false);
1047
+ const [showInput, setShowInput] = (0, import_react5.useState)(false);
1048
+ const [inputValue, setInputValue] = (0, import_react5.useState)("");
1049
+ const hoverTimeoutRef = (0, import_react5.useRef)(null);
1050
+ const pointerDownPos = (0, import_react5.useRef)(null);
1051
+ const isClickRef = (0, import_react5.useRef)(true);
1052
+ const animRef = (0, import_react5.useRef)(null);
1053
+ const wrapperRectRef = (0, import_react5.useRef)(null);
1054
+ const scaleRef = (0, import_react5.useRef)(1);
715
1055
  const percentage = (value - min) / (max - min) * 100;
716
1056
  const isActive = isInteracting || isHovered;
717
- const fillPercent = (0, import_react5.useMotionValue)(percentage);
718
- const fillWidth = (0, import_react5.useTransform)(fillPercent, (pct) => `${pct}%`);
719
- const handleLeft = (0, import_react5.useTransform)(
1057
+ const fillPercent = (0, import_react6.useMotionValue)(percentage);
1058
+ const fillWidth = (0, import_react6.useTransform)(fillPercent, (pct) => `${pct}%`);
1059
+ const handleLeft = (0, import_react6.useTransform)(
720
1060
  fillPercent,
721
1061
  (pct) => `max(5px, calc(${pct}% - 9px))`
722
1062
  );
723
- const rubberStretchPx = (0, import_react5.useMotionValue)(0);
724
- const rubberBandWidth = (0, import_react5.useTransform)(
1063
+ const rubberStretchPx = (0, import_react6.useMotionValue)(0);
1064
+ const rubberBandWidth = (0, import_react6.useTransform)(
725
1065
  rubberStretchPx,
726
1066
  (stretch) => `calc(100% + ${Math.abs(stretch)}px)`
727
1067
  );
728
- const rubberBandX = (0, import_react5.useTransform)(
1068
+ const rubberBandX = (0, import_react6.useTransform)(
729
1069
  rubberStretchPx,
730
1070
  (stretch) => stretch < 0 ? stretch : 0
731
1071
  );
732
- (0, import_react4.useEffect)(() => {
1072
+ (0, import_react5.useEffect)(() => {
733
1073
  if (!isInteracting && !animRef.current) {
734
1074
  fillPercent.jump(percentage);
735
1075
  }
736
1076
  }, [percentage, isInteracting, fillPercent]);
737
- const positionToValue = (0, import_react4.useCallback)(
1077
+ const positionToValue = (0, import_react5.useCallback)(
738
1078
  (clientX) => {
739
1079
  const rect = wrapperRectRef.current;
740
1080
  if (!rect) return value;
@@ -747,11 +1087,11 @@ function Slider({
747
1087
  },
748
1088
  [min, max, value]
749
1089
  );
750
- const percentFromValue = (0, import_react4.useCallback)(
1090
+ const percentFromValue = (0, import_react5.useCallback)(
751
1091
  (v) => (v - min) / (max - min) * 100,
752
1092
  [min, max]
753
1093
  );
754
- const computeRubberStretch = (0, import_react4.useCallback)(
1094
+ const computeRubberStretch = (0, import_react5.useCallback)(
755
1095
  (clientX, sign) => {
756
1096
  const rect = wrapperRectRef.current;
757
1097
  if (!rect) return 0;
@@ -761,7 +1101,7 @@ function Slider({
761
1101
  },
762
1102
  []
763
1103
  );
764
- const handlePointerDown = (0, import_react4.useCallback)(
1104
+ const handlePointerDown = (0, import_react5.useCallback)(
765
1105
  (e) => {
766
1106
  if (showInput) return;
767
1107
  e.preventDefault();
@@ -777,7 +1117,7 @@ function Slider({
777
1117
  },
778
1118
  [showInput]
779
1119
  );
780
- const handlePointerMove = (0, import_react4.useCallback)(
1120
+ const handlePointerMove = (0, import_react5.useCallback)(
781
1121
  (e) => {
782
1122
  if (!isInteracting || !pointerDownPos.current) return;
783
1123
  const dx = e.clientX - pointerDownPos.current.x;
@@ -818,7 +1158,7 @@ function Slider({
818
1158
  computeRubberStretch
819
1159
  ]
820
1160
  );
821
- const handlePointerUp = (0, import_react4.useCallback)(
1161
+ const handlePointerUp = (0, import_react5.useCallback)(
822
1162
  (e) => {
823
1163
  if (!isInteracting) return;
824
1164
  if (isClickRef.current) {
@@ -829,7 +1169,7 @@ function Slider({
829
1169
  if (animRef.current) {
830
1170
  animRef.current.stop();
831
1171
  }
832
- animRef.current = (0, import_react5.animate)(fillPercent, newPct, {
1172
+ animRef.current = (0, import_react6.animate)(fillPercent, newPct, {
833
1173
  type: "spring",
834
1174
  stiffness: 300,
835
1175
  damping: 25,
@@ -841,7 +1181,7 @@ function Slider({
841
1181
  onChange(roundValue(snappedValue, step));
842
1182
  }
843
1183
  if (rubberStretchPx.get() !== 0) {
844
- (0, import_react5.animate)(rubberStretchPx, 0, {
1184
+ (0, import_react6.animate)(rubberStretchPx, 0, {
845
1185
  type: "spring",
846
1186
  visualDuration: 0.35,
847
1187
  bounce: 0.15
@@ -862,7 +1202,7 @@ function Slider({
862
1202
  rubberStretchPx
863
1203
  ]
864
1204
  );
865
- (0, import_react4.useEffect)(() => {
1205
+ (0, import_react5.useEffect)(() => {
866
1206
  if (isValueHovered && !showInput && !isValueEditable) {
867
1207
  hoverTimeoutRef.current = setTimeout(() => {
868
1208
  setIsValueEditable(true);
@@ -880,7 +1220,7 @@ function Slider({
880
1220
  }
881
1221
  };
882
1222
  }, [isValueHovered, showInput, isValueEditable]);
883
- (0, import_react4.useEffect)(() => {
1223
+ (0, import_react5.useEffect)(() => {
884
1224
  if (showInput && inputRef.current) {
885
1225
  inputRef.current.focus();
886
1226
  inputRef.current.select();
@@ -935,11 +1275,10 @@ function Slider({
935
1275
  }
936
1276
  const valueDodge = percentage < leftThreshold || percentage > rightThreshold;
937
1277
  const handleOpacity = !isActive ? 0 : valueDodge ? 0.1 : isDragging ? 0.9 : 0.5;
938
- const fillBackground = isActive ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.11)";
939
1278
  const discreteSteps = (max - min) / step;
940
1279
  const hashMarks = discreteSteps <= 10 ? Array.from({ length: discreteSteps - 1 }, (_, i) => {
941
1280
  const pct = (i + 1) * step / (max - min) * 100;
942
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1281
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
943
1282
  "div",
944
1283
  {
945
1284
  className: "dialkit-slider-hashmark",
@@ -949,7 +1288,7 @@ function Slider({
949
1288
  );
950
1289
  }) : Array.from({ length: 9 }, (_, i) => {
951
1290
  const pct = (i + 1) * 10;
952
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1291
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
953
1292
  "div",
954
1293
  {
955
1294
  className: "dialkit-slider-hashmark",
@@ -958,8 +1297,8 @@ function Slider({
958
1297
  i
959
1298
  );
960
1299
  });
961
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
962
- import_react5.motion.div,
1300
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1301
+ import_react6.motion.div,
963
1302
  {
964
1303
  ref: trackRef,
965
1304
  className: `dialkit-slider ${isActive ? "dialkit-slider-active" : ""}`,
@@ -970,26 +1309,23 @@ function Slider({
970
1309
  onMouseLeave: () => setIsHovered(false),
971
1310
  style: { width: rubberBandWidth, x: rubberBandX },
972
1311
  children: [
973
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
974
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
975
- import_react5.motion.div,
1312
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1314
+ import_react6.motion.div,
976
1315
  {
977
1316
  className: "dialkit-slider-fill",
978
1317
  style: {
979
- background: fillBackground,
980
- width: fillWidth,
981
- transition: "background 0.15s"
1318
+ width: fillWidth
982
1319
  }
983
1320
  }
984
1321
  ),
985
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
986
- import_react5.motion.div,
1322
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1323
+ import_react6.motion.div,
987
1324
  {
988
1325
  className: "dialkit-slider-handle",
989
1326
  style: {
990
1327
  left: handleLeft,
991
- y: "-50%",
992
- background: "rgba(255, 255, 255, 0.9)"
1328
+ y: "-50%"
993
1329
  },
994
1330
  animate: {
995
1331
  opacity: handleOpacity,
@@ -1003,8 +1339,11 @@ function Slider({
1003
1339
  }
1004
1340
  }
1005
1341
  ),
1006
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { ref: labelRef, className: "dialkit-slider-label", children: label }),
1007
- showInput ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1342
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { ref: labelRef, className: "dialkit-slider-label", children: [
1343
+ label,
1344
+ shortcut && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `dialkit-shortcut-pill${shortcutActive ? " dialkit-shortcut-pill-active" : ""}`, children: formatSliderShortcut(shortcut) })
1345
+ ] }),
1346
+ showInput ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1008
1347
  "input",
1009
1348
  {
1010
1349
  ref: inputRef,
@@ -1017,7 +1356,7 @@ function Slider({
1017
1356
  onClick: (e) => e.stopPropagation(),
1018
1357
  onMouseDown: (e) => e.stopPropagation()
1019
1358
  }
1020
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1359
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1021
1360
  "span",
1022
1361
  {
1023
1362
  ref: valueSpanRef,
@@ -1036,51 +1375,48 @@ function Slider({
1036
1375
  }
1037
1376
 
1038
1377
  // src/components/SegmentedControl.tsx
1039
- var import_react6 = require("react");
1040
- var import_react7 = require("motion/react");
1041
- var import_jsx_runtime3 = require("react/jsx-runtime");
1378
+ var import_react7 = require("react");
1379
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1042
1380
  function SegmentedControl({
1043
1381
  options,
1044
1382
  value,
1045
1383
  onChange
1046
1384
  }) {
1047
- const containerRef = (0, import_react6.useRef)(null);
1048
- const buttonRefs = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
1049
- const [pillStyle, setPillStyle] = (0, import_react6.useState)(null);
1050
- const hasAnimated = (0, import_react6.useRef)(false);
1051
- (0, import_react6.useLayoutEffect)(() => {
1052
- const button = buttonRefs.current.get(value);
1385
+ const containerRef = (0, import_react7.useRef)(null);
1386
+ const hasAnimated = (0, import_react7.useRef)(false);
1387
+ const [pillStyle, setPillStyle] = (0, import_react7.useState)(null);
1388
+ const measure = (0, import_react7.useCallback)(() => {
1053
1389
  const container = containerRef.current;
1054
- if (button && container) {
1055
- const containerRect = container.getBoundingClientRect();
1056
- const buttonRect = button.getBoundingClientRect();
1057
- setPillStyle({
1058
- left: buttonRect.left - containerRect.left,
1059
- width: buttonRect.width
1060
- });
1061
- }
1062
- }, [value]);
1063
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: containerRef, className: "dialkit-segmented", children: [
1064
- pillStyle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1065
- import_react7.motion.div,
1390
+ if (!container) return;
1391
+ const activeButton = container.querySelector('[data-active="true"]');
1392
+ if (!activeButton) return;
1393
+ setPillStyle({
1394
+ left: activeButton.offsetLeft,
1395
+ width: activeButton.offsetWidth
1396
+ });
1397
+ }, []);
1398
+ (0, import_react7.useLayoutEffect)(() => {
1399
+ measure();
1400
+ }, [value, options.length, measure]);
1401
+ const shouldAnimate = hasAnimated.current;
1402
+ hasAnimated.current = true;
1403
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dialkit-segmented", ref: containerRef, children: [
1404
+ pillStyle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1405
+ "div",
1066
1406
  {
1067
1407
  className: "dialkit-segmented-pill",
1068
- style: { left: pillStyle.left, width: pillStyle.width },
1069
- animate: { left: pillStyle.left, width: pillStyle.width },
1070
- transition: hasAnimated.current ? { type: "spring", visualDuration: 0.2, bounce: 0.15 } : { duration: 0 },
1071
- onAnimationComplete: () => {
1072
- hasAnimated.current = true;
1408
+ style: {
1409
+ left: pillStyle.left,
1410
+ width: pillStyle.width,
1411
+ 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"
1073
1412
  }
1074
1413
  }
1075
1414
  ),
1076
1415
  options.map((option) => {
1077
1416
  const isActive = value === option.value;
1078
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1417
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1079
1418
  "button",
1080
1419
  {
1081
- ref: (el) => {
1082
- if (el) buttonRefs.current.set(option.value, el);
1083
- },
1084
1420
  onClick: () => onChange(option.value),
1085
1421
  className: "dialkit-segmented-button",
1086
1422
  "data-active": String(isActive),
@@ -1093,11 +1429,14 @@ function SegmentedControl({
1093
1429
  }
1094
1430
 
1095
1431
  // src/components/Toggle.tsx
1096
- var import_jsx_runtime4 = require("react/jsx-runtime");
1097
- function Toggle({ label, checked, onChange }) {
1098
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dialkit-labeled-control", children: [
1099
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dialkit-labeled-control-label", children: label }),
1100
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1432
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1433
+ function Toggle({ label, checked, onChange, shortcut, shortcutActive }) {
1434
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dialkit-labeled-control", children: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dialkit-labeled-control-label", children: [
1436
+ label,
1437
+ shortcut && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: `dialkit-shortcut-pill${shortcutActive ? " dialkit-shortcut-pill-active" : ""}`, children: formatToggleShortcut(shortcut) })
1438
+ ] }),
1439
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1101
1440
  SegmentedControl,
1102
1441
  {
1103
1442
  options: [
@@ -1112,7 +1451,7 @@ function Toggle({ label, checked, onChange }) {
1112
1451
  }
1113
1452
 
1114
1453
  // src/components/SpringVisualization.tsx
1115
- var import_jsx_runtime5 = require("react/jsx-runtime");
1454
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1116
1455
  function generateSpringCurve(stiffness, damping, mass, duration) {
1117
1456
  const points = [];
1118
1457
  const steps = 100;
@@ -1167,13 +1506,13 @@ function SpringVisualization({ spring, isSimpleMode }) {
1167
1506
  const x = width / 4 * i;
1168
1507
  const y = height / 4 * i;
1169
1508
  gridLines.push(
1170
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: x, y1: 0, x2: x, y2: height, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `v-${i}`),
1171
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: y, x2: width, y2: y, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `h-${i}`)
1509
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: x, y1: 0, x2: x, y2: height, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `v-${i}`),
1510
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: y, x2: width, y2: y, stroke: "rgba(255, 255, 255, 0.08)", strokeWidth: "1" }, `h-${i}`)
1172
1511
  );
1173
1512
  }
1174
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "dialkit-spring-viz", children: [
1513
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "dialkit-spring-viz", children: [
1175
1514
  gridLines,
1176
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1515
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1177
1516
  "line",
1178
1517
  {
1179
1518
  x1: 0,
@@ -1185,7 +1524,7 @@ function SpringVisualization({ spring, isSimpleMode }) {
1185
1524
  strokeDasharray: "4,4"
1186
1525
  }
1187
1526
  ),
1188
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1189
1528
  "path",
1190
1529
  {
1191
1530
  d: pathData,
@@ -1201,7 +1540,7 @@ function SpringVisualization({ spring, isSimpleMode }) {
1201
1540
 
1202
1541
  // src/components/SpringControl.tsx
1203
1542
  var import_react8 = require("react");
1204
- var import_jsx_runtime6 = require("react/jsx-runtime");
1543
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1205
1544
  function SpringControl({ panelId, path, label, spring, onChange }) {
1206
1545
  const mode = (0, import_react8.useSyncExternalStore)(
1207
1546
  (cb) => DialStore.subscribe(panelId, cb),
@@ -1209,25 +1548,21 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1209
1548
  () => DialStore.getSpringMode(panelId, path)
1210
1549
  );
1211
1550
  const isSimpleMode = mode === "simple";
1551
+ const cache = (0, import_react8.useRef)({
1552
+ simple: spring.visualDuration !== void 0 ? spring : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1553
+ advanced: spring.stiffness !== void 0 ? spring : { type: "spring", stiffness: 200, damping: 25, mass: 1 }
1554
+ });
1555
+ if (isSimpleMode) {
1556
+ cache.current.simple = spring;
1557
+ } else {
1558
+ cache.current.advanced = spring;
1559
+ }
1212
1560
  const handleModeChange = (newMode) => {
1213
1561
  DialStore.updateSpringMode(panelId, path, newMode);
1214
1562
  if (newMode === "simple") {
1215
- const { stiffness, damping, mass, ...rest } = spring;
1216
- onChange({
1217
- ...rest,
1218
- type: "spring",
1219
- visualDuration: spring.visualDuration ?? 0.3,
1220
- bounce: spring.bounce ?? 0.2
1221
- });
1563
+ onChange(cache.current.simple);
1222
1564
  } else {
1223
- const { visualDuration, bounce, ...rest } = spring;
1224
- onChange({
1225
- ...rest,
1226
- type: "spring",
1227
- stiffness: spring.stiffness ?? 200,
1228
- damping: spring.damping ?? 25,
1229
- mass: spring.mass ?? 1
1230
- });
1565
+ onChange(cache.current.advanced);
1231
1566
  }
1232
1567
  };
1233
1568
  const handleUpdate = (key, value) => {
@@ -1239,11 +1574,11 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1239
1574
  onChange({ ...rest, [key]: value });
1240
1575
  }
1241
1576
  };
1242
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1243
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpringVisualization, { spring, isSimpleMode }),
1244
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dialkit-labeled-control", children: [
1245
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1246
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1577
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1578
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SpringVisualization, { spring, isSimpleMode }),
1579
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dialkit-labeled-control", children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1581
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1247
1582
  SegmentedControl,
1248
1583
  {
1249
1584
  options: [
@@ -1255,8 +1590,8 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1255
1590
  }
1256
1591
  )
1257
1592
  ] }),
1258
- isSimpleMode ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1259
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1593
+ isSimpleMode ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1594
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1260
1595
  Slider,
1261
1596
  {
1262
1597
  label: "Duration",
@@ -1268,7 +1603,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1268
1603
  unit: "s"
1269
1604
  }
1270
1605
  ),
1271
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1606
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1272
1607
  Slider,
1273
1608
  {
1274
1609
  label: "Bounce",
@@ -1279,8 +1614,8 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1279
1614
  step: 0.05
1280
1615
  }
1281
1616
  )
1282
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1283
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1617
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1618
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1284
1619
  Slider,
1285
1620
  {
1286
1621
  label: "Stiffness",
@@ -1291,7 +1626,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1291
1626
  step: 10
1292
1627
  }
1293
1628
  ),
1294
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1629
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1295
1630
  Slider,
1296
1631
  {
1297
1632
  label: "Damping",
@@ -1302,7 +1637,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1302
1637
  step: 1
1303
1638
  }
1304
1639
  ),
1305
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1640
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1306
1641
  Slider,
1307
1642
  {
1308
1643
  label: "Mass",
@@ -1318,7 +1653,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1318
1653
  }
1319
1654
 
1320
1655
  // src/components/EasingVisualization.tsx
1321
- var import_jsx_runtime7 = require("react/jsx-runtime");
1656
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1322
1657
  function EasingVisualization({ easing }) {
1323
1658
  const ease = easing.ease;
1324
1659
  const s = 200;
@@ -1334,14 +1669,14 @@ function EasingVisualization({ easing }) {
1334
1669
  const p1 = toSvg(ease[0], ease[1]);
1335
1670
  const p2 = toSvg(ease[2], ease[3]);
1336
1671
  const curvePath = `M ${start.x} ${start.y} C ${p1.x} ${p1.y}, ${p2.x} ${p2.y}, ${end.x} ${end.y}`;
1337
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1672
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1338
1673
  "svg",
1339
1674
  {
1340
1675
  viewBox: `0 0 ${s} ${s}`,
1341
1676
  preserveAspectRatio: "xMidYMid slice",
1342
1677
  className: "dialkit-spring-viz dialkit-easing-viz",
1343
1678
  children: [
1344
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1679
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1345
1680
  "line",
1346
1681
  {
1347
1682
  x1: start.x,
@@ -1353,7 +1688,7 @@ function EasingVisualization({ easing }) {
1353
1688
  strokeDasharray: "4,4"
1354
1689
  }
1355
1690
  ),
1356
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: curvePath, fill: "none", stroke: "rgba(255, 255, 255, 0.6)", strokeWidth: "2", strokeLinecap: "round" })
1691
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: curvePath, fill: "none", stroke: "rgba(255, 255, 255, 0.6)", strokeWidth: "2", strokeLinecap: "round" })
1357
1692
  ]
1358
1693
  }
1359
1694
  );
@@ -1361,7 +1696,7 @@ function EasingVisualization({ easing }) {
1361
1696
 
1362
1697
  // src/components/TransitionControl.tsx
1363
1698
  var import_react9 = require("react");
1364
- var import_jsx_runtime8 = require("react/jsx-runtime");
1699
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1365
1700
  function TransitionControl({ panelId, path, label, value, onChange }) {
1366
1701
  const mode = (0, import_react9.useSyncExternalStore)(
1367
1702
  (cb) => DialStore.subscribe(panelId, cb),
@@ -1370,26 +1705,28 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1370
1705
  );
1371
1706
  const isEasing = mode === "easing";
1372
1707
  const isSimpleSpring = mode === "simple";
1373
- const spring = value.type === "spring" ? value : { type: "spring", visualDuration: 0.3, bounce: 0.2 };
1374
- const easing = value.type === "easing" ? value : { type: "easing", duration: 0.3, ease: [1, -0.4, 0.5, 1] };
1708
+ const cache = (0, import_react9.useRef)({
1709
+ easing: value.type === "easing" ? value : { type: "easing", duration: 0.3, ease: [1, -0.4, 0.5, 1] },
1710
+ simple: value.type === "spring" && value.visualDuration !== void 0 ? value : { type: "spring", visualDuration: 0.3, bounce: 0.2 },
1711
+ advanced: value.type === "spring" && value.stiffness !== void 0 ? value : { type: "spring", stiffness: 200, damping: 25, mass: 1 }
1712
+ });
1713
+ if (isEasing && value.type === "easing") {
1714
+ cache.current.easing = value;
1715
+ } else if (isSimpleSpring && value.type === "spring") {
1716
+ cache.current.simple = value;
1717
+ } else if (mode === "advanced" && value.type === "spring") {
1718
+ cache.current.advanced = value;
1719
+ }
1720
+ const spring = value.type === "spring" ? value : cache.current.simple;
1721
+ const easing = value.type === "easing" ? value : cache.current.easing;
1375
1722
  const handleModeChange = (newMode) => {
1376
1723
  DialStore.updateTransitionMode(panelId, path, newMode);
1377
1724
  if (newMode === "easing") {
1378
- const duration = value.type === "spring" ? value.visualDuration ?? 0.3 : value.duration;
1379
- onChange({ type: "easing", duration, ease: easing.ease });
1725
+ onChange(cache.current.easing);
1380
1726
  } else if (newMode === "simple") {
1381
- onChange({
1382
- type: "spring",
1383
- visualDuration: spring.visualDuration ?? (value.type === "easing" ? value.duration : 0.3),
1384
- bounce: spring.bounce ?? 0.2
1385
- });
1727
+ onChange(cache.current.simple);
1386
1728
  } else {
1387
- onChange({
1388
- type: "spring",
1389
- stiffness: spring.stiffness ?? 200,
1390
- damping: spring.damping ?? 25,
1391
- mass: spring.mass ?? 1
1392
- });
1729
+ onChange(cache.current.advanced);
1393
1730
  }
1394
1731
  };
1395
1732
  const handleSpringUpdate = (key, val) => {
@@ -1406,11 +1743,11 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1406
1743
  newEase[index] = val;
1407
1744
  onChange({ ...easing, ease: newEase });
1408
1745
  };
1409
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1410
- isEasing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EasingVisualization, { easing }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpringVisualization, { spring, isSimpleMode: isSimpleSpring }),
1411
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dialkit-labeled-control", children: [
1412
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1413
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1746
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Folder, { title: label, defaultOpen: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
1747
+ isEasing ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EasingVisualization, { easing }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SpringVisualization, { spring, isSimpleMode: isSimpleSpring }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-labeled-control", children: [
1749
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1414
1751
  SegmentedControl,
1415
1752
  {
1416
1753
  options: [
@@ -1423,20 +1760,20 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1423
1760
  }
1424
1761
  )
1425
1762
  ] }),
1426
- isEasing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1427
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "x1", value: easing.ease[0], onChange: (v) => updateEase(0, v), min: 0, max: 1, step: 0.01 }),
1428
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "y1", value: easing.ease[1], onChange: (v) => updateEase(1, v), min: -1, max: 2, step: 0.01 }),
1429
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "x2", value: easing.ease[2], onChange: (v) => updateEase(2, v), min: 0, max: 1, step: 0.01 }),
1430
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "y2", value: easing.ease[3], onChange: (v) => updateEase(3, v), min: -1, max: 2, step: 0.01 }),
1431
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Duration", value: easing.duration, onChange: (v) => onChange({ ...easing, duration: v }), min: 0.1, max: 2, step: 0.05, unit: "s" }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EaseTextInput, { ease: easing.ease, onChange: (newEase) => onChange({ ...easing, ease: newEase }) })
1433
- ] }) : isSimpleSpring ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1434
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Duration", value: spring.visualDuration ?? 0.3, onChange: (v) => handleSpringUpdate("visualDuration", v), min: 0.1, max: 1, step: 0.05, unit: "s" }),
1435
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Bounce", value: spring.bounce ?? 0.2, onChange: (v) => handleSpringUpdate("bounce", v), min: 0, max: 1, step: 0.05 })
1436
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1437
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Stiffness", value: spring.stiffness ?? 400, onChange: (v) => handleSpringUpdate("stiffness", v), min: 1, max: 1e3, step: 10 }),
1438
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Damping", value: spring.damping ?? 17, onChange: (v) => handleSpringUpdate("damping", v), min: 1, max: 100, step: 1 }),
1439
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Mass", value: spring.mass ?? 1, onChange: (v) => handleSpringUpdate("mass", v), min: 0.1, max: 10, step: 0.1 })
1763
+ isEasing ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1764
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "x1", value: easing.ease[0], onChange: (v) => updateEase(0, v), min: 0, max: 1, step: 0.01 }),
1765
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "y1", value: easing.ease[1], onChange: (v) => updateEase(1, v), min: -1, max: 2, step: 0.01 }),
1766
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "x2", value: easing.ease[2], onChange: (v) => updateEase(2, v), min: 0, max: 1, step: 0.01 }),
1767
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "y2", value: easing.ease[3], onChange: (v) => updateEase(3, v), min: -1, max: 2, step: 0.01 }),
1768
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "Duration", value: easing.duration, onChange: (v) => onChange({ ...easing, duration: v }), min: 0.1, max: 2, step: 0.05, unit: "s" }),
1769
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EaseTextInput, { ease: easing.ease, onChange: (newEase) => onChange({ ...easing, ease: newEase }) })
1770
+ ] }) : isSimpleSpring ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1771
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "Duration", value: spring.visualDuration ?? 0.3, onChange: (v) => handleSpringUpdate("visualDuration", v), min: 0.1, max: 1, step: 0.05, unit: "s" }),
1772
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "Bounce", value: spring.bounce ?? 0.2, onChange: (v) => handleSpringUpdate("bounce", v), min: 0, max: 1, step: 0.05 })
1773
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1774
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "Stiffness", value: spring.stiffness ?? 400, onChange: (v) => handleSpringUpdate("stiffness", v), min: 1, max: 1e3, step: 10 }),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "Damping", value: spring.damping ?? 17, onChange: (v) => handleSpringUpdate("damping", v), min: 1, max: 100, step: 1 }),
1776
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Slider, { label: "Mass", value: spring.mass ?? 1, onChange: (v) => handleSpringUpdate("mass", v), min: 0.1, max: 10, step: 0.1 })
1440
1777
  ] })
1441
1778
  ] }) });
1442
1779
  }
@@ -1467,9 +1804,9 @@ function EaseTextInput({ ease, onChange }) {
1467
1804
  e.target.blur();
1468
1805
  }
1469
1806
  };
1470
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dialkit-labeled-control", children: [
1471
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-labeled-control-label", children: "Ease" }),
1472
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1807
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-labeled-control", children: [
1808
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dialkit-labeled-control-label", children: "Ease" }),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1473
1810
  "input",
1474
1811
  {
1475
1812
  type: "text",
@@ -1486,11 +1823,11 @@ function EaseTextInput({ ease, onChange }) {
1486
1823
  }
1487
1824
 
1488
1825
  // src/components/TextControl.tsx
1489
- var import_jsx_runtime9 = require("react/jsx-runtime");
1826
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1490
1827
  function TextControl({ label, value, onChange, placeholder }) {
1491
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-text-control", children: [
1492
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "dialkit-text-label", children: label }),
1493
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1828
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-text-control", children: [
1829
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "dialkit-text-label", children: label }),
1830
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1494
1831
  "input",
1495
1832
  {
1496
1833
  type: "text",
@@ -1507,7 +1844,7 @@ function TextControl({ label, value, onChange, placeholder }) {
1507
1844
  var import_react10 = require("react");
1508
1845
  var import_react_dom = require("react-dom");
1509
1846
  var import_react11 = require("motion/react");
1510
- var import_jsx_runtime10 = require("react/jsx-runtime");
1847
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1511
1848
  function toTitleCase(s) {
1512
1849
  return s.replace(/\b\w/g, (c) => c.toUpperCase());
1513
1850
  }
@@ -1557,8 +1894,8 @@ function SelectControl({ label, value, options, onChange }) {
1557
1894
  document.addEventListener("mousedown", handleClick);
1558
1895
  return () => document.removeEventListener("mousedown", handleClick);
1559
1896
  }, [isOpen]);
1560
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-select-row", children: [
1561
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1897
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dialkit-select-row", children: [
1898
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1562
1899
  "button",
1563
1900
  {
1564
1901
  ref: triggerRef,
@@ -1566,10 +1903,10 @@ function SelectControl({ label, value, options, onChange }) {
1566
1903
  onClick: () => setIsOpen(!isOpen),
1567
1904
  "data-open": String(isOpen),
1568
1905
  children: [
1569
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-select-label", children: label }),
1570
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-select-right", children: [
1571
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
1572
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1906
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dialkit-select-label", children: label }),
1907
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dialkit-select-right", children: [
1908
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
1909
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1573
1910
  import_react11.motion.svg,
1574
1911
  {
1575
1912
  className: "dialkit-select-chevron",
@@ -1581,7 +1918,7 @@ function SelectControl({ label, value, options, onChange }) {
1581
1918
  strokeLinejoin: "round",
1582
1919
  animate: { rotate: isOpen ? 180 : 0 },
1583
1920
  transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1584
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
1921
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: ICON_CHEVRON })
1585
1922
  }
1586
1923
  )
1587
1924
  ] })
@@ -1589,7 +1926,7 @@ function SelectControl({ label, value, options, onChange }) {
1589
1926
  }
1590
1927
  ),
1591
1928
  portalTarget && (0, import_react_dom.createPortal)(
1592
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.AnimatePresence, { children: isOpen && pos && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1929
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react11.AnimatePresence, { children: isOpen && pos && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1593
1930
  import_react11.motion.div,
1594
1931
  {
1595
1932
  ref: dropdownRef,
@@ -1604,7 +1941,7 @@ function SelectControl({ label, value, options, onChange }) {
1604
1941
  width: pos.width,
1605
1942
  ...pos.above ? { bottom: window.innerHeight - pos.top, transformOrigin: "bottom" } : { top: pos.top, transformOrigin: "top" }
1606
1943
  },
1607
- children: normalized.map((option) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1944
+ children: normalized.map((option) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1608
1945
  "button",
1609
1946
  {
1610
1947
  className: "dialkit-select-option",
@@ -1626,7 +1963,7 @@ function SelectControl({ label, value, options, onChange }) {
1626
1963
 
1627
1964
  // src/components/ColorControl.tsx
1628
1965
  var import_react12 = require("react");
1629
- var import_jsx_runtime11 = require("react/jsx-runtime");
1966
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1630
1967
  var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
1631
1968
  function ColorControl({ label, value, onChange }) {
1632
1969
  const [isEditing, setIsEditing] = (0, import_react12.useState)(false);
@@ -1653,10 +1990,10 @@ function ColorControl({ label, value, onChange }) {
1653
1990
  setEditValue(value);
1654
1991
  }
1655
1992
  }
1656
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dialkit-color-control", children: [
1657
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dialkit-color-label", children: label }),
1658
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dialkit-color-inputs", children: [
1659
- isEditing ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1993
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dialkit-color-control", children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dialkit-color-label", children: label }),
1995
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dialkit-color-inputs", children: [
1996
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1660
1997
  "input",
1661
1998
  {
1662
1999
  type: "text",
@@ -1667,7 +2004,7 @@ function ColorControl({ label, value, onChange }) {
1667
2004
  onKeyDown: handleKeyDown,
1668
2005
  autoFocus: true
1669
2006
  }
1670
- ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2007
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1671
2008
  "span",
1672
2009
  {
1673
2010
  className: "dialkit-color-hex",
@@ -1675,7 +2012,7 @@ function ColorControl({ label, value, onChange }) {
1675
2012
  children: (value ?? "").toUpperCase()
1676
2013
  }
1677
2014
  ),
1678
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2015
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1679
2016
  "button",
1680
2017
  {
1681
2018
  className: "dialkit-color-swatch",
@@ -1684,7 +2021,7 @@ function ColorControl({ label, value, onChange }) {
1684
2021
  title: "Pick color"
1685
2022
  }
1686
2023
  ),
1687
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2024
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1688
2025
  "input",
1689
2026
  {
1690
2027
  ref: colorInputRef,
@@ -1706,7 +2043,7 @@ function expandShorthandHex(hex) {
1706
2043
  var import_react13 = require("react");
1707
2044
  var import_react_dom2 = require("react-dom");
1708
2045
  var import_react14 = require("motion/react");
1709
- var import_jsx_runtime12 = require("react/jsx-runtime");
2046
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1710
2047
  function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1711
2048
  const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
1712
2049
  const triggerRef = (0, import_react13.useRef)(null);
@@ -1749,8 +2086,8 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1749
2086
  e.stopPropagation();
1750
2087
  DialStore.deletePreset(panelId, presetId);
1751
2088
  };
1752
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dialkit-preset-manager", children: [
1753
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2089
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "dialkit-preset-manager", children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1754
2091
  "button",
1755
2092
  {
1756
2093
  ref: triggerRef,
@@ -1760,8 +2097,8 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1760
2097
  "data-has-preset": String(!!activePreset),
1761
2098
  "data-disabled": String(!hasPresets),
1762
2099
  children: [
1763
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
1764
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2100
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
2101
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1765
2102
  import_react14.motion.svg,
1766
2103
  {
1767
2104
  className: "dialkit-select-chevron",
@@ -1773,14 +2110,14 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1773
2110
  strokeLinejoin: "round",
1774
2111
  animate: { rotate: isOpen ? 180 : 0, opacity: hasPresets ? 0.6 : 0.25 },
1775
2112
  transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1776
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
2113
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: ICON_CHEVRON })
1777
2114
  }
1778
2115
  )
1779
2116
  ]
1780
2117
  }
1781
2118
  ),
1782
2119
  (0, import_react_dom2.createPortal)(
1783
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react14.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2120
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react14.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1784
2121
  import_react14.motion.div,
1785
2122
  {
1786
2123
  ref: dropdownRef,
@@ -1791,36 +2128,30 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1791
2128
  exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
1792
2129
  transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
1793
2130
  children: [
1794
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2131
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1795
2132
  "div",
1796
2133
  {
1797
2134
  className: "dialkit-preset-item",
1798
2135
  "data-active": String(!activePresetId),
1799
2136
  onClick: () => handleSelect(null),
1800
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dialkit-preset-name", children: "Version 1" })
2137
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "dialkit-preset-name", children: "Version 1" })
1801
2138
  }
1802
2139
  ),
1803
- presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2140
+ presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1804
2141
  "div",
1805
2142
  {
1806
2143
  className: "dialkit-preset-item",
1807
2144
  "data-active": String(preset.id === activePresetId),
1808
2145
  onClick: () => handleSelect(preset.id),
1809
2146
  children: [
1810
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dialkit-preset-name", children: preset.name }),
1811
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2147
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "dialkit-preset-name", children: preset.name }),
2148
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1812
2149
  "button",
1813
2150
  {
1814
2151
  className: "dialkit-preset-delete",
1815
2152
  onClick: (e) => handleDelete(e, preset.id),
1816
2153
  title: "Delete preset",
1817
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1818
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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" }),
1819
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M10 11V16" }),
1820
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 11V16" }),
1821
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3.5 6H20.5" }),
1822
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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" })
1823
- ] })
2154
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: ICON_TRASH.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d }, i)) })
1824
2155
  }
1825
2156
  )
1826
2157
  ]
@@ -1836,10 +2167,12 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1836
2167
  }
1837
2168
 
1838
2169
  // src/components/Panel.tsx
1839
- var import_jsx_runtime13 = require("react/jsx-runtime");
2170
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1840
2171
  function Panel({ panel, defaultOpen = true, inline = false }) {
1841
2172
  const [copied, setCopied] = (0, import_react15.useState)(false);
1842
2173
  const [isPanelOpen, setIsPanelOpen] = (0, import_react15.useState)(defaultOpen);
2174
+ const shortcutCtx = (0, import_react15.useContext)(ShortcutContext);
2175
+ const hasShortcuts = Object.keys(panel.shortcuts).length > 0;
1843
2176
  const values = (0, import_react15.useSyncExternalStore)(
1844
2177
  (cb) => DialStore.subscribe(panel.id, cb),
1845
2178
  () => DialStore.getValues(panel.id),
@@ -1868,7 +2201,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1868
2201
  const value = values[control.path];
1869
2202
  switch (control.type) {
1870
2203
  case "slider":
1871
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2204
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1872
2205
  Slider,
1873
2206
  {
1874
2207
  label: control.label,
@@ -1876,22 +2209,26 @@ Apply these values as the new defaults in the useDialKit call.`;
1876
2209
  onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
1877
2210
  min: control.min,
1878
2211
  max: control.max,
1879
- step: control.step
2212
+ step: control.step,
2213
+ shortcut: control.shortcut,
2214
+ shortcutActive: shortcutCtx.activePanelId === panel.id && shortcutCtx.activePath === control.path
1880
2215
  },
1881
2216
  control.path
1882
2217
  );
1883
2218
  case "toggle":
1884
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2219
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1885
2220
  Toggle,
1886
2221
  {
1887
2222
  label: control.label,
1888
2223
  checked: value,
1889
- onChange: (v) => DialStore.updateValue(panel.id, control.path, v)
2224
+ onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
2225
+ shortcut: control.shortcut,
2226
+ shortcutActive: shortcutCtx.activePanelId === panel.id && shortcutCtx.activePath === control.path
1890
2227
  },
1891
2228
  control.path
1892
2229
  );
1893
2230
  case "spring":
1894
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2231
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1895
2232
  SpringControl,
1896
2233
  {
1897
2234
  panelId: panel.id,
@@ -1903,7 +2240,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1903
2240
  control.path
1904
2241
  );
1905
2242
  case "transition":
1906
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2243
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1907
2244
  TransitionControl,
1908
2245
  {
1909
2246
  panelId: panel.id,
@@ -1915,9 +2252,9 @@ Apply these values as the new defaults in the useDialKit call.`;
1915
2252
  control.path
1916
2253
  );
1917
2254
  case "folder":
1918
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Folder, { title: control.label, defaultOpen: control.defaultOpen ?? true, children: control.children?.map(renderControl) }, control.path);
2255
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Folder, { title: control.label, defaultOpen: control.defaultOpen ?? true, children: control.children?.map(renderControl) }, control.path);
1919
2256
  case "text":
1920
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2257
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1921
2258
  TextControl,
1922
2259
  {
1923
2260
  label: control.label,
@@ -1928,7 +2265,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1928
2265
  control.path
1929
2266
  );
1930
2267
  case "select":
1931
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2268
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1932
2269
  SelectControl,
1933
2270
  {
1934
2271
  label: control.label,
@@ -1939,7 +2276,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1939
2276
  control.path
1940
2277
  );
1941
2278
  case "color":
1942
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2279
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1943
2280
  ColorControl,
1944
2281
  {
1945
2282
  label: control.label,
@@ -1949,7 +2286,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1949
2286
  control.path
1950
2287
  );
1951
2288
  case "action":
1952
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2289
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1953
2290
  "button",
1954
2291
  {
1955
2292
  className: "dialkit-button",
@@ -1966,8 +2303,8 @@ Apply these values as the new defaults in the useDialKit call.`;
1966
2303
  return panel.controls.map(renderControl);
1967
2304
  };
1968
2305
  const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
1969
- const toolbar = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1970
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2306
+ const toolbar = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2307
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1971
2308
  import_react16.motion.button,
1972
2309
  {
1973
2310
  className: "dialkit-toolbar-add",
@@ -1975,16 +2312,10 @@ Apply these values as the new defaults in the useDialKit call.`;
1975
2312
  title: "Add preset",
1976
2313
  whileTap: { scale: 0.9 },
1977
2314
  transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
1978
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1979
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 6H20" }),
1980
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 12H10" }),
1981
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M15 15L21 15" }),
1982
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M18 12V18" }),
1983
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 18H10" })
1984
- ] })
2315
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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__ */ (0, import_jsx_runtime14.jsx)("path", { d }, i)) })
1985
2316
  }
1986
2317
  ),
1987
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2318
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1988
2319
  PresetManager,
1989
2320
  {
1990
2321
  panelId: panel.id,
@@ -1993,64 +2324,71 @@ Apply these values as the new defaults in the useDialKit call.`;
1993
2324
  onAdd: handleAddPreset
1994
2325
  }
1995
2326
  ),
1996
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2327
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1997
2328
  import_react16.motion.button,
1998
2329
  {
1999
- className: "dialkit-toolbar-copy",
2330
+ className: "dialkit-toolbar-add",
2000
2331
  onClick: handleCopy,
2001
2332
  title: "Copy parameters",
2002
- whileTap: { scale: 0.95 },
2333
+ whileTap: { scale: 0.9 },
2003
2334
  transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
2004
- children: [
2005
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "dialkit-toolbar-copy-icon-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react16.AnimatePresence, { initial: false, mode: "popLayout", children: copied ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2006
- import_react16.motion.svg,
2007
- {
2008
- className: "dialkit-toolbar-copy-icon",
2009
- viewBox: "0 0 24 24",
2010
- fill: "none",
2011
- stroke: "currentColor",
2012
- strokeWidth: "2",
2013
- strokeLinecap: "round",
2014
- strokeLinejoin: "round",
2015
- initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2016
- animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
2017
- exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2018
- transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
2019
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M5 12.75L10 19L19 5" })
2020
- },
2021
- "check"
2022
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2023
- import_react16.motion.svg,
2024
- {
2025
- className: "dialkit-toolbar-copy-icon",
2026
- viewBox: "0 0 24 24",
2027
- fill: "none",
2028
- initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2029
- animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
2030
- exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2031
- transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
2032
- children: [
2033
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M8 6C8 4.34315 9.34315 3 11 3H13C14.6569 3 16 4.34315 16 6V7H8V6Z", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
2034
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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" }),
2035
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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" })
2036
- ]
2037
- },
2038
- "clipboard"
2039
- ) }) }),
2040
- "Copy"
2041
- ]
2335
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { position: "relative", width: 16, height: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react16.AnimatePresence, { initial: false, mode: "wait", children: copied ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2336
+ import_react16.motion.svg,
2337
+ {
2338
+ viewBox: "0 0 24 24",
2339
+ fill: "none",
2340
+ stroke: "currentColor",
2341
+ strokeWidth: "2",
2342
+ strokeLinecap: "round",
2343
+ strokeLinejoin: "round",
2344
+ style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
2345
+ initial: { scale: 0.8, opacity: 0 },
2346
+ animate: { scale: 1, opacity: 1 },
2347
+ exit: { scale: 0.8, opacity: 0 },
2348
+ transition: { duration: 0.08 },
2349
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: ICON_CHECK })
2350
+ },
2351
+ "check"
2352
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2353
+ import_react16.motion.svg,
2354
+ {
2355
+ viewBox: "0 0 24 24",
2356
+ fill: "none",
2357
+ style: { position: "absolute", inset: 0, width: 16, height: 16, color: "var(--dial-text-label)" },
2358
+ initial: { scale: 0.8, opacity: 0 },
2359
+ animate: { scale: 1, opacity: 1 },
2360
+ exit: { scale: 0.8, opacity: 0 },
2361
+ transition: { duration: 0.08 },
2362
+ children: [
2363
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: ICON_CLIPBOARD.board, stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round" }),
2364
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: ICON_CLIPBOARD.sparkle, fill: "currentColor" }),
2365
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: ICON_CLIPBOARD.body, stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
2366
+ ]
2367
+ },
2368
+ "clipboard"
2369
+ ) }) })
2042
2370
  }
2043
2371
  )
2044
2372
  ] });
2045
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Folder, { title: panel.name, defaultOpen, isRoot: true, inline, onOpenChange: setIsPanelOpen, toolbar, children: renderControls() }) });
2373
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dialkit-panel-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Folder, { title: panel.name, defaultOpen, isRoot: true, inline, onOpenChange: setIsPanelOpen, toolbar, children: renderControls() }) });
2046
2374
  }
2047
2375
 
2048
2376
  // src/components/DialRoot.tsx
2049
- var import_jsx_runtime14 = require("react/jsx-runtime");
2050
- function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover" }) {
2377
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2378
+ var import_meta = {};
2379
+ 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;
2380
+ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover", theme = "system", productionEnabled = isDevDefault }) {
2381
+ if (!productionEnabled) return null;
2051
2382
  const [panels, setPanels] = (0, import_react17.useState)([]);
2052
2383
  const [mounted, setMounted] = (0, import_react17.useState)(false);
2053
2384
  const inline = mode === "inline";
2385
+ const panelRef = (0, import_react17.useRef)(null);
2386
+ const [dragOffset, setDragOffset] = (0, import_react17.useState)(null);
2387
+ const [activePosition, setActivePosition] = (0, import_react17.useState)(position);
2388
+ const lastDragOffset = (0, import_react17.useRef)(null);
2389
+ const draggingRef = (0, import_react17.useRef)(false);
2390
+ const dragStartRef = (0, import_react17.useRef)(null);
2391
+ const didDragRef = (0, import_react17.useRef)(false);
2054
2392
  (0, import_react17.useEffect)(() => {
2055
2393
  setMounted(true);
2056
2394
  setPanels(DialStore.getPanels());
@@ -2059,13 +2397,95 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
2059
2397
  });
2060
2398
  return unsubscribe;
2061
2399
  }, []);
2400
+ (0, import_react17.useEffect)(() => {
2401
+ if (!panelRef.current || inline) return;
2402
+ const observer = new MutationObserver(() => {
2403
+ const inner = panelRef.current?.querySelector(".dialkit-panel-inner");
2404
+ if (!inner) return;
2405
+ const collapsed = inner.getAttribute("data-collapsed") === "true";
2406
+ if (!collapsed) {
2407
+ if (dragOffset) {
2408
+ lastDragOffset.current = dragOffset;
2409
+ const bubbleCenterX = dragOffset.x + 21;
2410
+ const midX = window.innerWidth / 2;
2411
+ setActivePosition(bubbleCenterX < midX ? "top-left" : "top-right");
2412
+ } else {
2413
+ setActivePosition(position);
2414
+ }
2415
+ setDragOffset(null);
2416
+ } else if (lastDragOffset.current) {
2417
+ setDragOffset(lastDragOffset.current);
2418
+ }
2419
+ });
2420
+ observer.observe(panelRef.current, { subtree: true, attributes: true, attributeFilter: ["data-collapsed"] });
2421
+ return () => observer.disconnect();
2422
+ }, [inline, dragOffset, position]);
2423
+ const handlePointerDown = (0, import_react17.useCallback)((e) => {
2424
+ const inner = panelRef.current?.querySelector(".dialkit-panel-inner");
2425
+ if (!inner || inner.getAttribute("data-collapsed") !== "true") return;
2426
+ const rect = panelRef.current.getBoundingClientRect();
2427
+ dragStartRef.current = {
2428
+ pointerX: e.clientX,
2429
+ pointerY: e.clientY,
2430
+ elX: rect.left,
2431
+ elY: rect.top
2432
+ };
2433
+ didDragRef.current = false;
2434
+ draggingRef.current = true;
2435
+ e.target.setPointerCapture(e.pointerId);
2436
+ }, []);
2437
+ const handlePointerMove = (0, import_react17.useCallback)((e) => {
2438
+ if (!draggingRef.current || !dragStartRef.current) return;
2439
+ const dx = e.clientX - dragStartRef.current.pointerX;
2440
+ const dy = e.clientY - dragStartRef.current.pointerY;
2441
+ if (!didDragRef.current && Math.abs(dx) + Math.abs(dy) < 4) return;
2442
+ didDragRef.current = true;
2443
+ setDragOffset({
2444
+ x: dragStartRef.current.elX + dx,
2445
+ y: dragStartRef.current.elY + dy
2446
+ });
2447
+ }, []);
2448
+ const handlePointerUp = (0, import_react17.useCallback)((e) => {
2449
+ if (!draggingRef.current) return;
2450
+ draggingRef.current = false;
2451
+ dragStartRef.current = null;
2452
+ if (didDragRef.current) {
2453
+ e.stopPropagation();
2454
+ const inner = panelRef.current?.querySelector(".dialkit-panel-inner");
2455
+ if (inner) {
2456
+ const blocker = (ev) => {
2457
+ ev.stopPropagation();
2458
+ };
2459
+ inner.addEventListener("click", blocker, { capture: true, once: true });
2460
+ }
2461
+ }
2462
+ }, []);
2062
2463
  if (!mounted || typeof window === "undefined") {
2063
2464
  return null;
2064
2465
  }
2065
2466
  if (panels.length === 0) {
2066
2467
  return null;
2067
2468
  }
2068
- const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dialkit-root", "data-mode": mode, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dialkit-panel", "data-position": inline ? void 0 : position, "data-mode": mode, children: panels.map((panel) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Panel, { panel, defaultOpen: inline || defaultOpen, inline }, panel.id)) }) });
2469
+ const dragStyle = dragOffset ? {
2470
+ top: dragOffset.y,
2471
+ left: dragOffset.x,
2472
+ right: "auto",
2473
+ bottom: "auto"
2474
+ } : void 0;
2475
+ const content = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ShortcutListener, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dialkit-root", "data-mode": mode, "data-theme": theme, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2476
+ "div",
2477
+ {
2478
+ ref: panelRef,
2479
+ className: "dialkit-panel",
2480
+ "data-position": inline ? void 0 : dragOffset ? void 0 : activePosition,
2481
+ "data-mode": mode,
2482
+ style: dragStyle,
2483
+ onPointerDown: !inline ? handlePointerDown : void 0,
2484
+ onPointerMove: !inline ? handlePointerMove : void 0,
2485
+ onPointerUp: !inline ? handlePointerUp : void 0,
2486
+ children: panels.map((panel) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Panel, { panel, defaultOpen: inline || defaultOpen, inline }, panel.id))
2487
+ }
2488
+ ) }) });
2069
2489
  if (inline) {
2070
2490
  return content;
2071
2491
  }
@@ -2073,9 +2493,9 @@ function DialRoot({ position = "top-right", defaultOpen = true, mode = "popover"
2073
2493
  }
2074
2494
 
2075
2495
  // src/components/ButtonGroup.tsx
2076
- var import_jsx_runtime15 = require("react/jsx-runtime");
2496
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2077
2497
  function ButtonGroup({ buttons }) {
2078
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2498
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "dialkit-button-group", children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2079
2499
  "button",
2080
2500
  {
2081
2501
  className: "dialkit-button",
@@ -2085,6 +2505,124 @@ function ButtonGroup({ buttons }) {
2085
2505
  index
2086
2506
  )) });
2087
2507
  }
2508
+
2509
+ // src/components/ShortcutsMenu.tsx
2510
+ var import_react18 = require("react");
2511
+ var import_react_dom4 = require("react-dom");
2512
+ var import_react19 = require("motion/react");
2513
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2514
+ function formatShortcutKey(sc) {
2515
+ if (!sc.key) return "\u2014";
2516
+ const mod = sc.modifier === "alt" ? "\u2325" : sc.modifier === "shift" ? "\u21E7" : sc.modifier === "meta" ? "\u2318" : "";
2517
+ return `${mod}${sc.key.toUpperCase()}`;
2518
+ }
2519
+ function formatInteraction(sc) {
2520
+ const interaction = sc.interaction ?? "scroll";
2521
+ switch (interaction) {
2522
+ case "scroll":
2523
+ return sc.key ? "key+scroll" : "scroll";
2524
+ case "drag":
2525
+ return "key+drag";
2526
+ case "move":
2527
+ return "key+move";
2528
+ case "scroll-only":
2529
+ return "scroll";
2530
+ }
2531
+ }
2532
+ function ShortcutsMenu({ panelId }) {
2533
+ const [isOpen, setIsOpen] = (0, import_react18.useState)(false);
2534
+ const triggerRef = (0, import_react18.useRef)(null);
2535
+ const dropdownRef = (0, import_react18.useRef)(null);
2536
+ const [pos, setPos] = (0, import_react18.useState)({ top: 0, right: 0 });
2537
+ const open = (0, import_react18.useCallback)(() => {
2538
+ const rect = triggerRef.current?.getBoundingClientRect();
2539
+ if (rect) {
2540
+ setPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
2541
+ }
2542
+ setIsOpen(true);
2543
+ }, []);
2544
+ const close = (0, import_react18.useCallback)(() => setIsOpen(false), []);
2545
+ const toggle = (0, import_react18.useCallback)(() => {
2546
+ if (isOpen) close();
2547
+ else open();
2548
+ }, [isOpen, open, close]);
2549
+ (0, import_react18.useEffect)(() => {
2550
+ if (!isOpen) return;
2551
+ const handler = (e) => {
2552
+ const target = e.target;
2553
+ if (triggerRef.current?.contains(target) || dropdownRef.current?.contains(target)) return;
2554
+ close();
2555
+ };
2556
+ document.addEventListener("mousedown", handler);
2557
+ return () => document.removeEventListener("mousedown", handler);
2558
+ }, [isOpen, close]);
2559
+ const panel = DialStore.getPanel(panelId);
2560
+ if (!panel) return null;
2561
+ const shortcuts = Object.entries(panel.shortcuts);
2562
+ if (shortcuts.length === 0) return null;
2563
+ const rows = shortcuts.map(([path, shortcut]) => {
2564
+ const findLabel = (controls) => {
2565
+ for (const c of controls) {
2566
+ if (c.path === path) return c.label;
2567
+ if (c.type === "folder" && c.children) {
2568
+ const found = findLabel(c.children);
2569
+ if (found) return found;
2570
+ }
2571
+ }
2572
+ return path;
2573
+ };
2574
+ return {
2575
+ path,
2576
+ shortcut,
2577
+ label: findLabel(panel.controls)
2578
+ };
2579
+ });
2580
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2581
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2582
+ import_react19.motion.button,
2583
+ {
2584
+ ref: triggerRef,
2585
+ className: "dialkit-shortcuts-trigger",
2586
+ onClick: toggle,
2587
+ title: "Keyboard shortcuts",
2588
+ whileTap: { scale: 0.9 },
2589
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
2590
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2591
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("rect", { x: "2", y: "6", width: "20", height: "12", rx: "2" }),
2592
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M6 10H6.01" }),
2593
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M10 10H10.01" }),
2594
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M14 10H14.01" }),
2595
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M18 10H18.01" }),
2596
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M8 14H16" })
2597
+ ] })
2598
+ }
2599
+ ),
2600
+ (0, import_react_dom4.createPortal)(
2601
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react19.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2602
+ import_react19.motion.div,
2603
+ {
2604
+ ref: dropdownRef,
2605
+ className: "dialkit-root dialkit-shortcuts-dropdown",
2606
+ style: { position: "fixed", top: pos.top, right: pos.right },
2607
+ initial: { opacity: 0, y: 4, scale: 0.97 },
2608
+ animate: { opacity: 1, y: 0, scale: 1 },
2609
+ exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
2610
+ transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
2611
+ children: [
2612
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "dialkit-shortcuts-title", children: "Keyboard Shortcuts" }),
2613
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "dialkit-shortcuts-list", children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "dialkit-shortcuts-row", children: [
2614
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dialkit-shortcuts-row-key", children: formatShortcutKey(row.shortcut) }),
2615
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dialkit-shortcuts-row-label", children: row.label }),
2616
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dialkit-shortcuts-row-mode", children: formatInteraction(row.shortcut) })
2617
+ ] }, row.path)) }),
2618
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "dialkit-shortcuts-hint", children: "See pill badges on controls for keys" })
2619
+ ]
2620
+ }
2621
+ ) }),
2622
+ document.body
2623
+ )
2624
+ ] });
2625
+ }
2088
2626
  // Annotate the CommonJS export names for ESM import in node:
2089
2627
  0 && (module.exports = {
2090
2628
  ButtonGroup,
@@ -2095,6 +2633,7 @@ function ButtonGroup({ buttons }) {
2095
2633
  Folder,
2096
2634
  PresetManager,
2097
2635
  SelectControl,
2636
+ ShortcutsMenu,
2098
2637
  Slider,
2099
2638
  SpringControl,
2100
2639
  SpringVisualization,