dialkit 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +293 -2
  2. package/dist/index.cjs +905 -363
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +46 -9
  5. package/dist/index.d.ts +46 -9
  6. package/dist/index.js +910 -370
  7. package/dist/index.js.map +1 -1
  8. package/dist/solid/index.cjs +1042 -532
  9. package/dist/solid/index.cjs.map +1 -1
  10. package/dist/solid/index.d.cts +36 -3
  11. package/dist/solid/index.d.ts +36 -3
  12. package/dist/solid/index.js +911 -402
  13. package/dist/solid/index.js.map +1 -1
  14. package/dist/store/index.cjs +490 -0
  15. package/dist/store/index.cjs.map +1 -0
  16. package/dist/store/index.d.cts +146 -0
  17. package/dist/store/index.d.ts +146 -0
  18. package/dist/store/index.js +465 -0
  19. package/dist/store/index.js.map +1 -0
  20. package/dist/styles.css +287 -25
  21. package/dist/svelte/Portal.svelte +30 -0
  22. package/dist/svelte/Portal.svelte.d.ts +9 -0
  23. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  24. package/dist/svelte/components/ButtonGroup.svelte +16 -0
  25. package/dist/svelte/components/ButtonGroup.svelte.d.ts +11 -0
  26. package/dist/svelte/components/ButtonGroup.svelte.d.ts.map +1 -0
  27. package/dist/svelte/components/ColorControl.svelte +81 -0
  28. package/dist/svelte/components/ColorControl.svelte.d.ts +9 -0
  29. package/dist/svelte/components/ColorControl.svelte.d.ts.map +1 -0
  30. package/dist/svelte/components/ControlRenderer.svelte +96 -0
  31. package/dist/svelte/components/ControlRenderer.svelte.d.ts +11 -0
  32. package/dist/svelte/components/ControlRenderer.svelte.d.ts.map +1 -0
  33. package/dist/svelte/components/DialRoot.svelte +79 -0
  34. package/dist/svelte/components/DialRoot.svelte.d.ts +14 -0
  35. package/dist/svelte/components/DialRoot.svelte.d.ts.map +1 -0
  36. package/dist/svelte/components/EasingVisualization.svelte +50 -0
  37. package/dist/svelte/components/EasingVisualization.svelte.d.ts +10 -0
  38. package/dist/svelte/components/EasingVisualization.svelte.d.ts.map +1 -0
  39. package/dist/svelte/components/Folder.svelte +213 -0
  40. package/dist/svelte/components/Folder.svelte.d.ts +14 -0
  41. package/dist/svelte/components/Folder.svelte.d.ts.map +1 -0
  42. package/dist/svelte/components/Panel.svelte +149 -0
  43. package/dist/svelte/components/Panel.svelte.d.ts +10 -0
  44. package/dist/svelte/components/Panel.svelte.d.ts.map +1 -0
  45. package/dist/svelte/components/PresetManager.svelte +157 -0
  46. package/dist/svelte/components/PresetManager.svelte.d.ts +10 -0
  47. package/dist/svelte/components/PresetManager.svelte.d.ts.map +1 -0
  48. package/dist/svelte/components/SegmentedControl.svelte +62 -0
  49. package/dist/svelte/components/SegmentedControl.svelte.d.ts +13 -0
  50. package/dist/svelte/components/SegmentedControl.svelte.d.ts.map +1 -0
  51. package/dist/svelte/components/SelectControl.svelte +152 -0
  52. package/dist/svelte/components/SelectControl.svelte.d.ts +14 -0
  53. package/dist/svelte/components/SelectControl.svelte.d.ts.map +1 -0
  54. package/dist/svelte/components/ShortcutListener.svelte +265 -0
  55. package/dist/svelte/components/ShortcutListener.svelte.d.ts +13 -0
  56. package/dist/svelte/components/ShortcutListener.svelte.d.ts.map +1 -0
  57. package/dist/svelte/components/ShortcutsMenu.svelte +128 -0
  58. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts +7 -0
  59. package/dist/svelte/components/ShortcutsMenu.svelte.d.ts.map +1 -0
  60. package/dist/svelte/components/Slider.svelte +332 -0
  61. package/dist/svelte/components/Slider.svelte.d.ts +16 -0
  62. package/dist/svelte/components/Slider.svelte.d.ts.map +1 -0
  63. package/dist/svelte/components/SpringControl.svelte +126 -0
  64. package/dist/svelte/components/SpringControl.svelte.d.ts +12 -0
  65. package/dist/svelte/components/SpringControl.svelte.d.ts.map +1 -0
  66. package/dist/svelte/components/SpringVisualization.svelte +115 -0
  67. package/dist/svelte/components/SpringVisualization.svelte.d.ts +9 -0
  68. package/dist/svelte/components/SpringVisualization.svelte.d.ts.map +1 -0
  69. package/dist/svelte/components/TextControl.svelte +19 -0
  70. package/dist/svelte/components/TextControl.svelte.d.ts +10 -0
  71. package/dist/svelte/components/TextControl.svelte.d.ts.map +1 -0
  72. package/dist/svelte/components/Toggle.svelte +32 -0
  73. package/dist/svelte/components/Toggle.svelte.d.ts +12 -0
  74. package/dist/svelte/components/Toggle.svelte.d.ts.map +1 -0
  75. package/dist/svelte/components/TransitionControl.svelte +203 -0
  76. package/dist/svelte/components/TransitionControl.svelte.d.ts +12 -0
  77. package/dist/svelte/components/TransitionControl.svelte.d.ts.map +1 -0
  78. package/dist/svelte/components/transitions.d.ts +6 -0
  79. package/dist/svelte/components/transitions.d.ts.map +1 -0
  80. package/dist/svelte/components/transitions.js +14 -0
  81. package/dist/svelte/createDialKit.svelte.d.ts +8 -0
  82. package/dist/svelte/createDialKit.svelte.d.ts.map +1 -0
  83. package/dist/svelte/createDialKit.svelte.js +82 -0
  84. package/dist/svelte/index.d.ts +23 -0
  85. package/dist/svelte/index.d.ts.map +1 -0
  86. package/dist/svelte/index.js +23 -0
  87. package/dist/svelte/shims.d.ts +9 -0
  88. package/dist/svelte/theme-css.d.ts +2 -0
  89. package/dist/svelte/theme-css.d.ts.map +1 -0
  90. package/dist/svelte/theme-css.js +1328 -0
  91. package/dist/vue/index.cjs +3056 -0
  92. package/dist/vue/index.cjs.map +1 -0
  93. package/dist/vue/index.d.cts +675 -0
  94. package/dist/vue/index.d.ts +675 -0
  95. package/dist/vue/index.js +3014 -0
  96. package/dist/vue/index.js.map +1 -0
  97. package/package.json +50 -6
package/dist/index.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, 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(() => {
@@ -579,6 +936,7 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
579
936
  return () => ro.disconnect();
580
937
  }, [isOpen]);
581
938
  const handleToggle = () => {
939
+ if (inline && isRoot) return;
582
940
  const next = !isOpen;
583
941
  setIsOpen(next);
584
942
  if (next) {
@@ -588,50 +946,43 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
588
946
  }
589
947
  onOpenChange?.(next);
590
948
  };
591
- const folderContent = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: isRoot ? contentRef : void 0, className: `dialkit-folder ${isRoot ? "dialkit-folder-root" : ""}`, children: [
592
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `dialkit-folder-header ${isRoot ? "dialkit-panel-header" : ""}`, onClick: handleToggle, children: [
593
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dialkit-folder-header-top", children: [
594
- 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 }) }),
595
- isRoot ? (
596
- // Root panel icon — fixed position, container morphs around it
597
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
598
- "svg",
599
- {
600
- className: "dialkit-panel-icon",
601
- viewBox: "0 0 16 16",
602
- fill: "none",
603
- children: [
604
- /* @__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" }),
605
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "8", r: "0.998596", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
606
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.4999", cy: "3.5", r: "0.998657", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" }),
607
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9.75015", cy: "12.5", r: "0.997986", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })
608
- ]
609
- }
610
- )
611
- ) : (
612
- // Section folders use rotating chevron with gentle spring
613
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
614
- import_react3.motion.svg,
615
- {
616
- className: "dialkit-folder-icon",
617
- viewBox: "0 0 24 24",
618
- fill: "none",
619
- stroke: "currentColor",
620
- strokeWidth: "2.5",
621
- strokeLinecap: "round",
622
- strokeLinejoin: "round",
623
- initial: false,
624
- animate: { rotate: isOpen ? 0 : 180 },
625
- transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
626
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 9.5L12 15.5L18 9.5" })
627
- }
628
- )
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)(
954
+ "svg",
955
+ {
956
+ className: "dialkit-panel-icon",
957
+ viewBox: "0 0 16 16",
958
+ fill: "none",
959
+ children: [
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))
962
+ ]
963
+ }
964
+ ),
965
+ !isRoot && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
966
+ import_react4.motion.svg,
967
+ {
968
+ className: "dialkit-folder-icon",
969
+ viewBox: "0 0 24 24",
970
+ fill: "none",
971
+ stroke: "currentColor",
972
+ strokeWidth: "2.5",
973
+ strokeLinecap: "round",
974
+ strokeLinejoin: "round",
975
+ initial: false,
976
+ animate: { rotate: isOpen ? 0 : 180 },
977
+ transition: { type: "spring", visualDuration: 0.35, bounce: 0.15 },
978
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: ICON_CHEVRON })
979
+ }
629
980
  )
630
981
  ] }),
631
- 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 })
632
983
  ] }),
633
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react3.AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
634
- 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,
635
986
  {
636
987
  className: "dialkit-folder-content",
637
988
  initial: isRoot ? void 0 : { height: 0, opacity: 0 },
@@ -639,14 +990,17 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
639
990
  exit: isRoot ? void 0 : { height: 0, opacity: 0 },
640
991
  transition: isRoot ? void 0 : { type: "spring", visualDuration: 0.35, bounce: 0.1 },
641
992
  style: isRoot ? void 0 : { clipPath: "inset(0 -20px)" },
642
- 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 })
643
994
  }
644
995
  ) })
645
996
  ] });
646
997
  if (isRoot) {
647
- 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" };
648
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
649
- import_react3.motion.div,
998
+ if (inline) {
999
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-panel-inner dialkit-panel-inline", children: folderContent });
1000
+ }
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,
650
1004
  {
651
1005
  className: "dialkit-panel-inner",
652
1006
  style: panelStyle,
@@ -662,30 +1016,13 @@ function Folder({ title, children, defaultOpen = true, isRoot = false, onOpenCha
662
1016
  }
663
1017
 
664
1018
  // src/components/Slider.tsx
665
- var import_react4 = require("react");
666
- var import_react5 = require("motion/react");
667
- 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");
668
1022
  var CLICK_THRESHOLD = 3;
669
1023
  var DEAD_ZONE = 32;
670
1024
  var MAX_CURSOR_RANGE = 200;
671
1025
  var MAX_STRETCH = 8;
672
- function decimalsForStep(step) {
673
- const s = step.toString();
674
- const dot = s.indexOf(".");
675
- return dot === -1 ? 0 : s.length - dot - 1;
676
- }
677
- function roundValue(val, step) {
678
- const raw = Math.round(val / step) * step;
679
- return parseFloat(raw.toFixed(decimalsForStep(step)));
680
- }
681
- function snapToDecile(rawValue, min, max) {
682
- const normalized = (rawValue - min) / (max - min);
683
- const nearest = Math.round(normalized * 10) / 10;
684
- if (Math.abs(normalized - nearest) <= 0.03125) {
685
- return min + nearest * (max - min);
686
- }
687
- return rawValue;
688
- }
689
1026
  function Slider({
690
1027
  label,
691
1028
  value,
@@ -693,49 +1030,51 @@ function Slider({
693
1030
  min = 0,
694
1031
  max = 1,
695
1032
  step = 0.01,
696
- unit
1033
+ unit,
1034
+ shortcut,
1035
+ shortcutActive
697
1036
  }) {
698
- const wrapperRef = (0, import_react4.useRef)(null);
699
- const trackRef = (0, import_react4.useRef)(null);
700
- const inputRef = (0, import_react4.useRef)(null);
701
- const labelRef = (0, import_react4.useRef)(null);
702
- const valueSpanRef = (0, import_react4.useRef)(null);
703
- const [isInteracting, setIsInteracting] = (0, import_react4.useState)(false);
704
- const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
705
- const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
706
- const [isValueHovered, setIsValueHovered] = (0, import_react4.useState)(false);
707
- const [isValueEditable, setIsValueEditable] = (0, import_react4.useState)(false);
708
- const [showInput, setShowInput] = (0, import_react4.useState)(false);
709
- const [inputValue, setInputValue] = (0, import_react4.useState)("");
710
- const hoverTimeoutRef = (0, import_react4.useRef)(null);
711
- const pointerDownPos = (0, import_react4.useRef)(null);
712
- const isClickRef = (0, import_react4.useRef)(true);
713
- const animRef = (0, import_react4.useRef)(null);
714
- const wrapperRectRef = (0, import_react4.useRef)(null);
715
- 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);
716
1055
  const percentage = (value - min) / (max - min) * 100;
717
1056
  const isActive = isInteracting || isHovered;
718
- const fillPercent = (0, import_react5.useMotionValue)(percentage);
719
- const fillWidth = (0, import_react5.useTransform)(fillPercent, (pct) => `${pct}%`);
720
- 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)(
721
1060
  fillPercent,
722
1061
  (pct) => `max(5px, calc(${pct}% - 9px))`
723
1062
  );
724
- const rubberStretchPx = (0, import_react5.useMotionValue)(0);
725
- const rubberBandWidth = (0, import_react5.useTransform)(
1063
+ const rubberStretchPx = (0, import_react6.useMotionValue)(0);
1064
+ const rubberBandWidth = (0, import_react6.useTransform)(
726
1065
  rubberStretchPx,
727
1066
  (stretch) => `calc(100% + ${Math.abs(stretch)}px)`
728
1067
  );
729
- const rubberBandX = (0, import_react5.useTransform)(
1068
+ const rubberBandX = (0, import_react6.useTransform)(
730
1069
  rubberStretchPx,
731
1070
  (stretch) => stretch < 0 ? stretch : 0
732
1071
  );
733
- (0, import_react4.useEffect)(() => {
1072
+ (0, import_react5.useEffect)(() => {
734
1073
  if (!isInteracting && !animRef.current) {
735
1074
  fillPercent.jump(percentage);
736
1075
  }
737
1076
  }, [percentage, isInteracting, fillPercent]);
738
- const positionToValue = (0, import_react4.useCallback)(
1077
+ const positionToValue = (0, import_react5.useCallback)(
739
1078
  (clientX) => {
740
1079
  const rect = wrapperRectRef.current;
741
1080
  if (!rect) return value;
@@ -748,11 +1087,11 @@ function Slider({
748
1087
  },
749
1088
  [min, max, value]
750
1089
  );
751
- const percentFromValue = (0, import_react4.useCallback)(
1090
+ const percentFromValue = (0, import_react5.useCallback)(
752
1091
  (v) => (v - min) / (max - min) * 100,
753
1092
  [min, max]
754
1093
  );
755
- const computeRubberStretch = (0, import_react4.useCallback)(
1094
+ const computeRubberStretch = (0, import_react5.useCallback)(
756
1095
  (clientX, sign) => {
757
1096
  const rect = wrapperRectRef.current;
758
1097
  if (!rect) return 0;
@@ -762,7 +1101,7 @@ function Slider({
762
1101
  },
763
1102
  []
764
1103
  );
765
- const handlePointerDown = (0, import_react4.useCallback)(
1104
+ const handlePointerDown = (0, import_react5.useCallback)(
766
1105
  (e) => {
767
1106
  if (showInput) return;
768
1107
  e.preventDefault();
@@ -778,7 +1117,7 @@ function Slider({
778
1117
  },
779
1118
  [showInput]
780
1119
  );
781
- const handlePointerMove = (0, import_react4.useCallback)(
1120
+ const handlePointerMove = (0, import_react5.useCallback)(
782
1121
  (e) => {
783
1122
  if (!isInteracting || !pointerDownPos.current) return;
784
1123
  const dx = e.clientX - pointerDownPos.current.x;
@@ -819,7 +1158,7 @@ function Slider({
819
1158
  computeRubberStretch
820
1159
  ]
821
1160
  );
822
- const handlePointerUp = (0, import_react4.useCallback)(
1161
+ const handlePointerUp = (0, import_react5.useCallback)(
823
1162
  (e) => {
824
1163
  if (!isInteracting) return;
825
1164
  if (isClickRef.current) {
@@ -830,7 +1169,7 @@ function Slider({
830
1169
  if (animRef.current) {
831
1170
  animRef.current.stop();
832
1171
  }
833
- animRef.current = (0, import_react5.animate)(fillPercent, newPct, {
1172
+ animRef.current = (0, import_react6.animate)(fillPercent, newPct, {
834
1173
  type: "spring",
835
1174
  stiffness: 300,
836
1175
  damping: 25,
@@ -842,7 +1181,7 @@ function Slider({
842
1181
  onChange(roundValue(snappedValue, step));
843
1182
  }
844
1183
  if (rubberStretchPx.get() !== 0) {
845
- (0, import_react5.animate)(rubberStretchPx, 0, {
1184
+ (0, import_react6.animate)(rubberStretchPx, 0, {
846
1185
  type: "spring",
847
1186
  visualDuration: 0.35,
848
1187
  bounce: 0.15
@@ -863,7 +1202,7 @@ function Slider({
863
1202
  rubberStretchPx
864
1203
  ]
865
1204
  );
866
- (0, import_react4.useEffect)(() => {
1205
+ (0, import_react5.useEffect)(() => {
867
1206
  if (isValueHovered && !showInput && !isValueEditable) {
868
1207
  hoverTimeoutRef.current = setTimeout(() => {
869
1208
  setIsValueEditable(true);
@@ -881,7 +1220,7 @@ function Slider({
881
1220
  }
882
1221
  };
883
1222
  }, [isValueHovered, showInput, isValueEditable]);
884
- (0, import_react4.useEffect)(() => {
1223
+ (0, import_react5.useEffect)(() => {
885
1224
  if (showInput && inputRef.current) {
886
1225
  inputRef.current.focus();
887
1226
  inputRef.current.select();
@@ -936,11 +1275,10 @@ function Slider({
936
1275
  }
937
1276
  const valueDodge = percentage < leftThreshold || percentage > rightThreshold;
938
1277
  const handleOpacity = !isActive ? 0 : valueDodge ? 0.1 : isDragging ? 0.9 : 0.5;
939
- const fillBackground = isActive ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.11)";
940
1278
  const discreteSteps = (max - min) / step;
941
1279
  const hashMarks = discreteSteps <= 10 ? Array.from({ length: discreteSteps - 1 }, (_, i) => {
942
1280
  const pct = (i + 1) * step / (max - min) * 100;
943
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1281
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
944
1282
  "div",
945
1283
  {
946
1284
  className: "dialkit-slider-hashmark",
@@ -950,7 +1288,7 @@ function Slider({
950
1288
  );
951
1289
  }) : Array.from({ length: 9 }, (_, i) => {
952
1290
  const pct = (i + 1) * 10;
953
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1291
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
954
1292
  "div",
955
1293
  {
956
1294
  className: "dialkit-slider-hashmark",
@@ -959,8 +1297,8 @@ function Slider({
959
1297
  i
960
1298
  );
961
1299
  });
962
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: wrapperRef, className: "dialkit-slider-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
963
- 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,
964
1302
  {
965
1303
  ref: trackRef,
966
1304
  className: `dialkit-slider ${isActive ? "dialkit-slider-active" : ""}`,
@@ -971,26 +1309,23 @@ function Slider({
971
1309
  onMouseLeave: () => setIsHovered(false),
972
1310
  style: { width: rubberBandWidth, x: rubberBandX },
973
1311
  children: [
974
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dialkit-slider-hashmarks", children: hashMarks }),
975
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
976
- 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,
977
1315
  {
978
1316
  className: "dialkit-slider-fill",
979
1317
  style: {
980
- background: fillBackground,
981
- width: fillWidth,
982
- transition: "background 0.15s"
1318
+ width: fillWidth
983
1319
  }
984
1320
  }
985
1321
  ),
986
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
987
- import_react5.motion.div,
1322
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1323
+ import_react6.motion.div,
988
1324
  {
989
1325
  className: "dialkit-slider-handle",
990
1326
  style: {
991
1327
  left: handleLeft,
992
- y: "-50%",
993
- background: "rgba(255, 255, 255, 0.9)"
1328
+ y: "-50%"
994
1329
  },
995
1330
  animate: {
996
1331
  opacity: handleOpacity,
@@ -1004,8 +1339,11 @@ function Slider({
1004
1339
  }
1005
1340
  }
1006
1341
  ),
1007
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { ref: labelRef, className: "dialkit-slider-label", children: label }),
1008
- 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)(
1009
1347
  "input",
1010
1348
  {
1011
1349
  ref: inputRef,
@@ -1018,7 +1356,7 @@ function Slider({
1018
1356
  onClick: (e) => e.stopPropagation(),
1019
1357
  onMouseDown: (e) => e.stopPropagation()
1020
1358
  }
1021
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1359
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1022
1360
  "span",
1023
1361
  {
1024
1362
  ref: valueSpanRef,
@@ -1037,51 +1375,48 @@ function Slider({
1037
1375
  }
1038
1376
 
1039
1377
  // src/components/SegmentedControl.tsx
1040
- var import_react6 = require("react");
1041
- var import_react7 = require("motion/react");
1042
- var import_jsx_runtime3 = require("react/jsx-runtime");
1378
+ var import_react7 = require("react");
1379
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1043
1380
  function SegmentedControl({
1044
1381
  options,
1045
1382
  value,
1046
1383
  onChange
1047
1384
  }) {
1048
- const containerRef = (0, import_react6.useRef)(null);
1049
- const buttonRefs = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
1050
- const [pillStyle, setPillStyle] = (0, import_react6.useState)(null);
1051
- const hasAnimated = (0, import_react6.useRef)(false);
1052
- (0, import_react6.useLayoutEffect)(() => {
1053
- 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)(() => {
1054
1389
  const container = containerRef.current;
1055
- if (button && container) {
1056
- const containerRect = container.getBoundingClientRect();
1057
- const buttonRect = button.getBoundingClientRect();
1058
- setPillStyle({
1059
- left: buttonRect.left - containerRect.left,
1060
- width: buttonRect.width
1061
- });
1062
- }
1063
- }, [value]);
1064
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: containerRef, className: "dialkit-segmented", children: [
1065
- pillStyle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1066
- 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",
1067
1406
  {
1068
1407
  className: "dialkit-segmented-pill",
1069
- style: { left: pillStyle.left, width: pillStyle.width },
1070
- animate: { left: pillStyle.left, width: pillStyle.width },
1071
- transition: hasAnimated.current ? { type: "spring", visualDuration: 0.2, bounce: 0.15 } : { duration: 0 },
1072
- onAnimationComplete: () => {
1073
- 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"
1074
1412
  }
1075
1413
  }
1076
1414
  ),
1077
1415
  options.map((option) => {
1078
1416
  const isActive = value === option.value;
1079
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1417
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1080
1418
  "button",
1081
1419
  {
1082
- ref: (el) => {
1083
- if (el) buttonRefs.current.set(option.value, el);
1084
- },
1085
1420
  onClick: () => onChange(option.value),
1086
1421
  className: "dialkit-segmented-button",
1087
1422
  "data-active": String(isActive),
@@ -1094,11 +1429,14 @@ function SegmentedControl({
1094
1429
  }
1095
1430
 
1096
1431
  // src/components/Toggle.tsx
1097
- var import_jsx_runtime4 = require("react/jsx-runtime");
1098
- function Toggle({ label, checked, onChange }) {
1099
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dialkit-labeled-control", children: [
1100
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dialkit-labeled-control-label", children: label }),
1101
- /* @__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)(
1102
1440
  SegmentedControl,
1103
1441
  {
1104
1442
  options: [
@@ -1113,7 +1451,7 @@ function Toggle({ label, checked, onChange }) {
1113
1451
  }
1114
1452
 
1115
1453
  // src/components/SpringVisualization.tsx
1116
- var import_jsx_runtime5 = require("react/jsx-runtime");
1454
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1117
1455
  function generateSpringCurve(stiffness, damping, mass, duration) {
1118
1456
  const points = [];
1119
1457
  const steps = 100;
@@ -1168,13 +1506,13 @@ function SpringVisualization({ spring, isSimpleMode }) {
1168
1506
  const x = width / 4 * i;
1169
1507
  const y = height / 4 * i;
1170
1508
  gridLines.push(
1171
- /* @__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}`),
1172
- /* @__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}`)
1173
1511
  );
1174
1512
  }
1175
- 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: [
1176
1514
  gridLines,
1177
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1515
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1178
1516
  "line",
1179
1517
  {
1180
1518
  x1: 0,
@@ -1186,7 +1524,7 @@ function SpringVisualization({ spring, isSimpleMode }) {
1186
1524
  strokeDasharray: "4,4"
1187
1525
  }
1188
1526
  ),
1189
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1190
1528
  "path",
1191
1529
  {
1192
1530
  d: pathData,
@@ -1202,7 +1540,7 @@ function SpringVisualization({ spring, isSimpleMode }) {
1202
1540
 
1203
1541
  // src/components/SpringControl.tsx
1204
1542
  var import_react8 = require("react");
1205
- var import_jsx_runtime6 = require("react/jsx-runtime");
1543
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1206
1544
  function SpringControl({ panelId, path, label, spring, onChange }) {
1207
1545
  const mode = (0, import_react8.useSyncExternalStore)(
1208
1546
  (cb) => DialStore.subscribe(panelId, cb),
@@ -1210,25 +1548,21 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1210
1548
  () => DialStore.getSpringMode(panelId, path)
1211
1549
  );
1212
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
+ }
1213
1560
  const handleModeChange = (newMode) => {
1214
1561
  DialStore.updateSpringMode(panelId, path, newMode);
1215
1562
  if (newMode === "simple") {
1216
- const { stiffness, damping, mass, ...rest } = spring;
1217
- onChange({
1218
- ...rest,
1219
- type: "spring",
1220
- visualDuration: spring.visualDuration ?? 0.3,
1221
- bounce: spring.bounce ?? 0.2
1222
- });
1563
+ onChange(cache.current.simple);
1223
1564
  } else {
1224
- const { visualDuration, bounce, ...rest } = spring;
1225
- onChange({
1226
- ...rest,
1227
- type: "spring",
1228
- stiffness: spring.stiffness ?? 200,
1229
- damping: spring.damping ?? 25,
1230
- mass: spring.mass ?? 1
1231
- });
1565
+ onChange(cache.current.advanced);
1232
1566
  }
1233
1567
  };
1234
1568
  const handleUpdate = (key, value) => {
@@ -1240,11 +1574,11 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1240
1574
  onChange({ ...rest, [key]: value });
1241
1575
  }
1242
1576
  };
1243
- 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: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpringVisualization, { spring, isSimpleMode }),
1245
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dialkit-labeled-control", children: [
1246
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1247
- /* @__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)(
1248
1582
  SegmentedControl,
1249
1583
  {
1250
1584
  options: [
@@ -1256,8 +1590,8 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1256
1590
  }
1257
1591
  )
1258
1592
  ] }),
1259
- isSimpleMode ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1260
- /* @__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)(
1261
1595
  Slider,
1262
1596
  {
1263
1597
  label: "Duration",
@@ -1269,7 +1603,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1269
1603
  unit: "s"
1270
1604
  }
1271
1605
  ),
1272
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1606
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1273
1607
  Slider,
1274
1608
  {
1275
1609
  label: "Bounce",
@@ -1280,8 +1614,8 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1280
1614
  step: 0.05
1281
1615
  }
1282
1616
  )
1283
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1284
- /* @__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)(
1285
1619
  Slider,
1286
1620
  {
1287
1621
  label: "Stiffness",
@@ -1292,7 +1626,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1292
1626
  step: 10
1293
1627
  }
1294
1628
  ),
1295
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1629
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1296
1630
  Slider,
1297
1631
  {
1298
1632
  label: "Damping",
@@ -1303,7 +1637,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1303
1637
  step: 1
1304
1638
  }
1305
1639
  ),
1306
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1640
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1307
1641
  Slider,
1308
1642
  {
1309
1643
  label: "Mass",
@@ -1319,7 +1653,7 @@ function SpringControl({ panelId, path, label, spring, onChange }) {
1319
1653
  }
1320
1654
 
1321
1655
  // src/components/EasingVisualization.tsx
1322
- var import_jsx_runtime7 = require("react/jsx-runtime");
1656
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1323
1657
  function EasingVisualization({ easing }) {
1324
1658
  const ease = easing.ease;
1325
1659
  const s = 200;
@@ -1335,14 +1669,14 @@ function EasingVisualization({ easing }) {
1335
1669
  const p1 = toSvg(ease[0], ease[1]);
1336
1670
  const p2 = toSvg(ease[2], ease[3]);
1337
1671
  const curvePath = `M ${start.x} ${start.y} C ${p1.x} ${p1.y}, ${p2.x} ${p2.y}, ${end.x} ${end.y}`;
1338
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1672
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1339
1673
  "svg",
1340
1674
  {
1341
1675
  viewBox: `0 0 ${s} ${s}`,
1342
1676
  preserveAspectRatio: "xMidYMid slice",
1343
1677
  className: "dialkit-spring-viz dialkit-easing-viz",
1344
1678
  children: [
1345
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1679
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1346
1680
  "line",
1347
1681
  {
1348
1682
  x1: start.x,
@@ -1354,7 +1688,7 @@ function EasingVisualization({ easing }) {
1354
1688
  strokeDasharray: "4,4"
1355
1689
  }
1356
1690
  ),
1357
- /* @__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" })
1358
1692
  ]
1359
1693
  }
1360
1694
  );
@@ -1362,7 +1696,7 @@ function EasingVisualization({ easing }) {
1362
1696
 
1363
1697
  // src/components/TransitionControl.tsx
1364
1698
  var import_react9 = require("react");
1365
- var import_jsx_runtime8 = require("react/jsx-runtime");
1699
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1366
1700
  function TransitionControl({ panelId, path, label, value, onChange }) {
1367
1701
  const mode = (0, import_react9.useSyncExternalStore)(
1368
1702
  (cb) => DialStore.subscribe(panelId, cb),
@@ -1371,26 +1705,28 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1371
1705
  );
1372
1706
  const isEasing = mode === "easing";
1373
1707
  const isSimpleSpring = mode === "simple";
1374
- const spring = value.type === "spring" ? value : { type: "spring", visualDuration: 0.3, bounce: 0.2 };
1375
- 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;
1376
1722
  const handleModeChange = (newMode) => {
1377
1723
  DialStore.updateTransitionMode(panelId, path, newMode);
1378
1724
  if (newMode === "easing") {
1379
- const duration = value.type === "spring" ? value.visualDuration ?? 0.3 : value.duration;
1380
- onChange({ type: "easing", duration, ease: easing.ease });
1725
+ onChange(cache.current.easing);
1381
1726
  } else if (newMode === "simple") {
1382
- onChange({
1383
- type: "spring",
1384
- visualDuration: spring.visualDuration ?? (value.type === "easing" ? value.duration : 0.3),
1385
- bounce: spring.bounce ?? 0.2
1386
- });
1727
+ onChange(cache.current.simple);
1387
1728
  } else {
1388
- onChange({
1389
- type: "spring",
1390
- stiffness: spring.stiffness ?? 200,
1391
- damping: spring.damping ?? 25,
1392
- mass: spring.mass ?? 1
1393
- });
1729
+ onChange(cache.current.advanced);
1394
1730
  }
1395
1731
  };
1396
1732
  const handleSpringUpdate = (key, val) => {
@@ -1407,11 +1743,11 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1407
1743
  newEase[index] = val;
1408
1744
  onChange({ ...easing, ease: newEase });
1409
1745
  };
1410
- 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: [
1411
- isEasing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EasingVisualization, { easing }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpringVisualization, { spring, isSimpleMode: isSimpleSpring }),
1412
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dialkit-labeled-control", children: [
1413
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-labeled-control-label", children: "Type" }),
1414
- /* @__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)(
1415
1751
  SegmentedControl,
1416
1752
  {
1417
1753
  options: [
@@ -1424,20 +1760,20 @@ function TransitionControl({ panelId, path, label, value, onChange }) {
1424
1760
  }
1425
1761
  )
1426
1762
  ] }),
1427
- isEasing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1428
- /* @__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 }),
1429
- /* @__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 }),
1430
- /* @__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 }),
1431
- /* @__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 }),
1432
- /* @__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" }),
1433
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EaseTextInput, { ease: easing.ease, onChange: (newEase) => onChange({ ...easing, ease: newEase }) })
1434
- ] }) : isSimpleSpring ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1435
- /* @__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" }),
1436
- /* @__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 })
1437
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1438
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Stiffness", value: spring.stiffness ?? 400, onChange: (v) => handleSpringUpdate("stiffness", v), min: 1, max: 1e3, step: 10 }),
1439
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Slider, { label: "Damping", value: spring.damping ?? 17, onChange: (v) => handleSpringUpdate("damping", v), min: 1, max: 100, step: 1 }),
1440
- /* @__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 })
1441
1777
  ] })
1442
1778
  ] }) });
1443
1779
  }
@@ -1468,9 +1804,9 @@ function EaseTextInput({ ease, onChange }) {
1468
1804
  e.target.blur();
1469
1805
  }
1470
1806
  };
1471
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dialkit-labeled-control", children: [
1472
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dialkit-labeled-control-label", children: "Ease" }),
1473
- /* @__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)(
1474
1810
  "input",
1475
1811
  {
1476
1812
  type: "text",
@@ -1487,11 +1823,11 @@ function EaseTextInput({ ease, onChange }) {
1487
1823
  }
1488
1824
 
1489
1825
  // src/components/TextControl.tsx
1490
- var import_jsx_runtime9 = require("react/jsx-runtime");
1826
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1491
1827
  function TextControl({ label, value, onChange, placeholder }) {
1492
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dialkit-text-control", children: [
1493
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "dialkit-text-label", children: label }),
1494
- /* @__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)(
1495
1831
  "input",
1496
1832
  {
1497
1833
  type: "text",
@@ -1508,7 +1844,7 @@ function TextControl({ label, value, onChange, placeholder }) {
1508
1844
  var import_react10 = require("react");
1509
1845
  var import_react_dom = require("react-dom");
1510
1846
  var import_react11 = require("motion/react");
1511
- var import_jsx_runtime10 = require("react/jsx-runtime");
1847
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1512
1848
  function toTitleCase(s) {
1513
1849
  return s.replace(/\b\w/g, (c) => c.toUpperCase());
1514
1850
  }
@@ -1558,8 +1894,8 @@ function SelectControl({ label, value, options, onChange }) {
1558
1894
  document.addEventListener("mousedown", handleClick);
1559
1895
  return () => document.removeEventListener("mousedown", handleClick);
1560
1896
  }, [isOpen]);
1561
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-select-row", children: [
1562
- /* @__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)(
1563
1899
  "button",
1564
1900
  {
1565
1901
  ref: triggerRef,
@@ -1567,10 +1903,10 @@ function SelectControl({ label, value, options, onChange }) {
1567
1903
  onClick: () => setIsOpen(!isOpen),
1568
1904
  "data-open": String(isOpen),
1569
1905
  children: [
1570
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-select-label", children: label }),
1571
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dialkit-select-right", children: [
1572
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dialkit-select-value", children: selectedOption?.label ?? value }),
1573
- /* @__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)(
1574
1910
  import_react11.motion.svg,
1575
1911
  {
1576
1912
  className: "dialkit-select-chevron",
@@ -1582,7 +1918,7 @@ function SelectControl({ label, value, options, onChange }) {
1582
1918
  strokeLinejoin: "round",
1583
1919
  animate: { rotate: isOpen ? 180 : 0 },
1584
1920
  transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1585
- 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 })
1586
1922
  }
1587
1923
  )
1588
1924
  ] })
@@ -1590,7 +1926,7 @@ function SelectControl({ label, value, options, onChange }) {
1590
1926
  }
1591
1927
  ),
1592
1928
  portalTarget && (0, import_react_dom.createPortal)(
1593
- /* @__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)(
1594
1930
  import_react11.motion.div,
1595
1931
  {
1596
1932
  ref: dropdownRef,
@@ -1605,7 +1941,7 @@ function SelectControl({ label, value, options, onChange }) {
1605
1941
  width: pos.width,
1606
1942
  ...pos.above ? { bottom: window.innerHeight - pos.top, transformOrigin: "bottom" } : { top: pos.top, transformOrigin: "top" }
1607
1943
  },
1608
- children: normalized.map((option) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1944
+ children: normalized.map((option) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1609
1945
  "button",
1610
1946
  {
1611
1947
  className: "dialkit-select-option",
@@ -1627,7 +1963,7 @@ function SelectControl({ label, value, options, onChange }) {
1627
1963
 
1628
1964
  // src/components/ColorControl.tsx
1629
1965
  var import_react12 = require("react");
1630
- var import_jsx_runtime11 = require("react/jsx-runtime");
1966
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1631
1967
  var HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
1632
1968
  function ColorControl({ label, value, onChange }) {
1633
1969
  const [isEditing, setIsEditing] = (0, import_react12.useState)(false);
@@ -1654,10 +1990,10 @@ function ColorControl({ label, value, onChange }) {
1654
1990
  setEditValue(value);
1655
1991
  }
1656
1992
  }
1657
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dialkit-color-control", children: [
1658
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dialkit-color-label", children: label }),
1659
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dialkit-color-inputs", children: [
1660
- 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)(
1661
1997
  "input",
1662
1998
  {
1663
1999
  type: "text",
@@ -1668,7 +2004,7 @@ function ColorControl({ label, value, onChange }) {
1668
2004
  onKeyDown: handleKeyDown,
1669
2005
  autoFocus: true
1670
2006
  }
1671
- ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2007
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1672
2008
  "span",
1673
2009
  {
1674
2010
  className: "dialkit-color-hex",
@@ -1676,7 +2012,7 @@ function ColorControl({ label, value, onChange }) {
1676
2012
  children: (value ?? "").toUpperCase()
1677
2013
  }
1678
2014
  ),
1679
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2015
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1680
2016
  "button",
1681
2017
  {
1682
2018
  className: "dialkit-color-swatch",
@@ -1685,7 +2021,7 @@ function ColorControl({ label, value, onChange }) {
1685
2021
  title: "Pick color"
1686
2022
  }
1687
2023
  ),
1688
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2024
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1689
2025
  "input",
1690
2026
  {
1691
2027
  ref: colorInputRef,
@@ -1707,7 +2043,7 @@ function expandShorthandHex(hex) {
1707
2043
  var import_react13 = require("react");
1708
2044
  var import_react_dom2 = require("react-dom");
1709
2045
  var import_react14 = require("motion/react");
1710
- var import_jsx_runtime12 = require("react/jsx-runtime");
2046
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1711
2047
  function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1712
2048
  const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
1713
2049
  const triggerRef = (0, import_react13.useRef)(null);
@@ -1750,8 +2086,8 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1750
2086
  e.stopPropagation();
1751
2087
  DialStore.deletePreset(panelId, presetId);
1752
2088
  };
1753
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dialkit-preset-manager", children: [
1754
- /* @__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)(
1755
2091
  "button",
1756
2092
  {
1757
2093
  ref: triggerRef,
@@ -1761,8 +2097,8 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1761
2097
  "data-has-preset": String(!!activePreset),
1762
2098
  "data-disabled": String(!hasPresets),
1763
2099
  children: [
1764
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dialkit-preset-label", children: activePreset ? activePreset.name : "Version 1" }),
1765
- /* @__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)(
1766
2102
  import_react14.motion.svg,
1767
2103
  {
1768
2104
  className: "dialkit-select-chevron",
@@ -1774,14 +2110,14 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1774
2110
  strokeLinejoin: "round",
1775
2111
  animate: { rotate: isOpen ? 180 : 0, opacity: hasPresets ? 0.6 : 0.25 },
1776
2112
  transition: { type: "spring", visualDuration: 0.2, bounce: 0.15 },
1777
- 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 })
1778
2114
  }
1779
2115
  )
1780
2116
  ]
1781
2117
  }
1782
2118
  ),
1783
2119
  (0, import_react_dom2.createPortal)(
1784
- /* @__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)(
1785
2121
  import_react14.motion.div,
1786
2122
  {
1787
2123
  ref: dropdownRef,
@@ -1792,36 +2128,30 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1792
2128
  exit: { opacity: 0, y: 4, scale: 0.97, pointerEvents: "none" },
1793
2129
  transition: { type: "spring", visualDuration: 0.15, bounce: 0 },
1794
2130
  children: [
1795
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2131
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1796
2132
  "div",
1797
2133
  {
1798
2134
  className: "dialkit-preset-item",
1799
2135
  "data-active": String(!activePresetId),
1800
2136
  onClick: () => handleSelect(null),
1801
- 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" })
1802
2138
  }
1803
2139
  ),
1804
- presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2140
+ presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1805
2141
  "div",
1806
2142
  {
1807
2143
  className: "dialkit-preset-item",
1808
2144
  "data-active": String(preset.id === activePresetId),
1809
2145
  onClick: () => handleSelect(preset.id),
1810
2146
  children: [
1811
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dialkit-preset-name", children: preset.name }),
1812
- /* @__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)(
1813
2149
  "button",
1814
2150
  {
1815
2151
  className: "dialkit-preset-delete",
1816
2152
  onClick: (e) => handleDelete(e, preset.id),
1817
2153
  title: "Delete preset",
1818
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1819
- /* @__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" }),
1820
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M10 11V16" }),
1821
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 11V16" }),
1822
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3.5 6H20.5" }),
1823
- /* @__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" })
1824
- ] })
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)) })
1825
2155
  }
1826
2156
  )
1827
2157
  ]
@@ -1837,10 +2167,12 @@ function PresetManager({ panelId, presets, activePresetId, onAdd }) {
1837
2167
  }
1838
2168
 
1839
2169
  // src/components/Panel.tsx
1840
- var import_jsx_runtime13 = require("react/jsx-runtime");
1841
- function Panel({ panel, defaultOpen = true }) {
2170
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2171
+ function Panel({ panel, defaultOpen = true, inline = false }) {
1842
2172
  const [copied, setCopied] = (0, import_react15.useState)(false);
1843
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;
1844
2176
  const values = (0, import_react15.useSyncExternalStore)(
1845
2177
  (cb) => DialStore.subscribe(panel.id, cb),
1846
2178
  () => DialStore.getValues(panel.id),
@@ -1869,7 +2201,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1869
2201
  const value = values[control.path];
1870
2202
  switch (control.type) {
1871
2203
  case "slider":
1872
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2204
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1873
2205
  Slider,
1874
2206
  {
1875
2207
  label: control.label,
@@ -1877,22 +2209,26 @@ Apply these values as the new defaults in the useDialKit call.`;
1877
2209
  onChange: (v) => DialStore.updateValue(panel.id, control.path, v),
1878
2210
  min: control.min,
1879
2211
  max: control.max,
1880
- step: control.step
2212
+ step: control.step,
2213
+ shortcut: control.shortcut,
2214
+ shortcutActive: shortcutCtx.activePanelId === panel.id && shortcutCtx.activePath === control.path
1881
2215
  },
1882
2216
  control.path
1883
2217
  );
1884
2218
  case "toggle":
1885
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2219
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1886
2220
  Toggle,
1887
2221
  {
1888
2222
  label: control.label,
1889
2223
  checked: value,
1890
- 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
1891
2227
  },
1892
2228
  control.path
1893
2229
  );
1894
2230
  case "spring":
1895
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2231
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1896
2232
  SpringControl,
1897
2233
  {
1898
2234
  panelId: panel.id,
@@ -1904,7 +2240,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1904
2240
  control.path
1905
2241
  );
1906
2242
  case "transition":
1907
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2243
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1908
2244
  TransitionControl,
1909
2245
  {
1910
2246
  panelId: panel.id,
@@ -1916,9 +2252,9 @@ Apply these values as the new defaults in the useDialKit call.`;
1916
2252
  control.path
1917
2253
  );
1918
2254
  case "folder":
1919
- 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);
1920
2256
  case "text":
1921
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2257
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1922
2258
  TextControl,
1923
2259
  {
1924
2260
  label: control.label,
@@ -1929,7 +2265,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1929
2265
  control.path
1930
2266
  );
1931
2267
  case "select":
1932
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2268
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1933
2269
  SelectControl,
1934
2270
  {
1935
2271
  label: control.label,
@@ -1940,7 +2276,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1940
2276
  control.path
1941
2277
  );
1942
2278
  case "color":
1943
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2279
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1944
2280
  ColorControl,
1945
2281
  {
1946
2282
  label: control.label,
@@ -1950,7 +2286,7 @@ Apply these values as the new defaults in the useDialKit call.`;
1950
2286
  control.path
1951
2287
  );
1952
2288
  case "action":
1953
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2289
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1954
2290
  "button",
1955
2291
  {
1956
2292
  className: "dialkit-button",
@@ -1967,8 +2303,8 @@ Apply these values as the new defaults in the useDialKit call.`;
1967
2303
  return panel.controls.map(renderControl);
1968
2304
  };
1969
2305
  const iconTransition = { type: "spring", visualDuration: 0.4, bounce: 0.1 };
1970
- const toolbar = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1971
- /* @__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)(
1972
2308
  import_react16.motion.button,
1973
2309
  {
1974
2310
  className: "dialkit-toolbar-add",
@@ -1976,16 +2312,10 @@ Apply these values as the new defaults in the useDialKit call.`;
1976
2312
  title: "Add preset",
1977
2313
  whileTap: { scale: 0.9 },
1978
2314
  transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
1979
- 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: [
1980
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 6H20" }),
1981
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 12H10" }),
1982
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M15 15L21 15" }),
1983
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M18 12V18" }),
1984
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M4 18H10" })
1985
- ] })
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)) })
1986
2316
  }
1987
2317
  ),
1988
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2318
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1989
2319
  PresetManager,
1990
2320
  {
1991
2321
  panelId: panel.id,
@@ -1994,63 +2324,71 @@ Apply these values as the new defaults in the useDialKit call.`;
1994
2324
  onAdd: handleAddPreset
1995
2325
  }
1996
2326
  ),
1997
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2327
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1998
2328
  import_react16.motion.button,
1999
2329
  {
2000
- className: "dialkit-toolbar-copy",
2330
+ className: "dialkit-toolbar-add",
2001
2331
  onClick: handleCopy,
2002
2332
  title: "Copy parameters",
2003
- whileTap: { scale: 0.95 },
2333
+ whileTap: { scale: 0.9 },
2004
2334
  transition: { type: "spring", visualDuration: 0.15, bounce: 0.3 },
2005
- children: [
2006
- /* @__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)(
2007
- import_react16.motion.svg,
2008
- {
2009
- className: "dialkit-toolbar-copy-icon",
2010
- viewBox: "0 0 24 24",
2011
- fill: "none",
2012
- stroke: "currentColor",
2013
- strokeWidth: "2",
2014
- strokeLinecap: "round",
2015
- strokeLinejoin: "round",
2016
- initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2017
- animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
2018
- exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2019
- transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
2020
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M5 12.75L10 19L19 5" })
2021
- },
2022
- "check"
2023
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2024
- import_react16.motion.svg,
2025
- {
2026
- className: "dialkit-toolbar-copy-icon",
2027
- viewBox: "0 0 24 24",
2028
- fill: "none",
2029
- initial: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2030
- animate: { scale: 1, opacity: 1, filter: "blur(0px)" },
2031
- exit: { scale: 0.5, opacity: 0, filter: "blur(4px)" },
2032
- transition: { type: "spring", visualDuration: 0.3, bounce: 0.2 },
2033
- children: [
2034
- /* @__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" }),
2035
- /* @__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" }),
2036
- /* @__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" })
2037
- ]
2038
- },
2039
- "clipboard"
2040
- ) }) }),
2041
- "Copy"
2042
- ]
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
+ ) }) })
2043
2370
  }
2044
2371
  )
2045
2372
  ] });
2046
- 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, 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() }) });
2047
2374
  }
2048
2375
 
2049
2376
  // src/components/DialRoot.tsx
2050
- var import_jsx_runtime14 = require("react/jsx-runtime");
2051
- function DialRoot({ position = "top-right", defaultOpen = true }) {
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;
2052
2382
  const [panels, setPanels] = (0, import_react17.useState)([]);
2053
2383
  const [mounted, setMounted] = (0, import_react17.useState)(false);
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,20 +2397,105 @@ function DialRoot({ position = "top-right", defaultOpen = true }) {
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", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dialkit-panel", "data-position": position, children: panels.map((panel) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Panel, { panel, defaultOpen }, 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
+ ) }) });
2489
+ if (inline) {
2490
+ return content;
2491
+ }
2069
2492
  return (0, import_react_dom3.createPortal)(content, document.body);
2070
2493
  }
2071
2494
 
2072
2495
  // src/components/ButtonGroup.tsx
2073
- var import_jsx_runtime15 = require("react/jsx-runtime");
2496
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2074
2497
  function ButtonGroup({ buttons }) {
2075
- 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)(
2076
2499
  "button",
2077
2500
  {
2078
2501
  className: "dialkit-button",
@@ -2082,6 +2505,124 @@ function ButtonGroup({ buttons }) {
2082
2505
  index
2083
2506
  )) });
2084
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
+ }
2085
2626
  // Annotate the CommonJS export names for ESM import in node:
2086
2627
  0 && (module.exports = {
2087
2628
  ButtonGroup,
@@ -2092,6 +2633,7 @@ function ButtonGroup({ buttons }) {
2092
2633
  Folder,
2093
2634
  PresetManager,
2094
2635
  SelectControl,
2636
+ ShortcutsMenu,
2095
2637
  Slider,
2096
2638
  SpringControl,
2097
2639
  SpringVisualization,