dialkit 1.1.0 → 1.2.1

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