@pixerate/editor-react 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +5 -4
  2. package/dist/canvas/index.cjs +329 -84
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +82 -4
  5. package/dist/canvas/index.d.ts +82 -4
  6. package/dist/canvas/index.js +266 -18
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-4ZC2WCUO.js +196 -0
  9. package/dist/chunk-4ZC2WCUO.js.map +1 -0
  10. package/dist/{chunk-TL27COKM.js → chunk-LPFZMZ3L.js} +45 -2
  11. package/dist/chunk-LPFZMZ3L.js.map +1 -0
  12. package/dist/{chunk-MHTAR3RS.js → chunk-QVQYYHJ5.js} +87 -29
  13. package/dist/chunk-QVQYYHJ5.js.map +1 -0
  14. package/dist/commands/index.cjs +232 -0
  15. package/dist/commands/index.cjs.map +1 -0
  16. package/dist/commands/index.d.cts +69 -0
  17. package/dist/commands/index.d.ts +69 -0
  18. package/dist/commands/index.js +13 -0
  19. package/dist/commands/index.js.map +1 -0
  20. package/dist/image-editor/index.cjs +41 -0
  21. package/dist/image-editor/index.cjs.map +1 -1
  22. package/dist/image-editor/index.d.cts +2 -0
  23. package/dist/image-editor/index.d.ts +2 -0
  24. package/dist/image-editor/index.js +1 -1
  25. package/dist/index.cjs +624 -268
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +57 -9
  28. package/dist/index.d.ts +57 -9
  29. package/dist/index.js +147 -71
  30. package/dist/index.js.map +1 -1
  31. package/dist/spreadsheet/index.cjs +79 -22
  32. package/dist/spreadsheet/index.cjs.map +1 -1
  33. package/dist/spreadsheet/index.d.cts +2 -3
  34. package/dist/spreadsheet/index.d.ts +2 -3
  35. package/dist/spreadsheet/index.js +1 -1
  36. package/package.json +10 -2
  37. package/dist/chunk-MHTAR3RS.js.map +0 -1
  38. package/dist/chunk-TL27COKM.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -31,13 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  BubbleMenu: () => BubbleMenu,
34
+ CommandPalette: () => CommandPalette,
34
35
  CommentButton: () => CommentButton,
35
36
  CommentsPanel: () => CommentsPanel,
36
- DirtyTracker: () => import_editor11.DirtyTracker,
37
+ DirtyTracker: () => import_editor12.DirtyTracker,
37
38
  EditorContent: () => EditorContent,
38
39
  EditorSlotsProvider: () => EditorSlotsProvider,
39
40
  FormulaHints: () => FormulaHints,
40
- HistoryManager: () => import_editor11.HistoryManager,
41
+ HistoryManager: () => import_editor12.HistoryManager,
41
42
  ImageEditor: () => ImageEditor,
42
43
  OutlinePanel: () => OutlinePanel,
43
44
  ReviewToolbar: () => ReviewToolbar,
@@ -46,10 +47,13 @@ __export(src_exports, {
46
47
  SpreadsheetGroupRow: () => SpreadsheetGroupRow,
47
48
  TemplateRenderer: () => TemplateRenderer,
48
49
  VariableForm: () => VariableForm,
49
- createDirtyTracker: () => import_editor11.createDirtyTracker,
50
- createHistoryManager: () => import_editor11.createHistoryManager,
51
- defaultIsEqual: () => import_editor11.defaultIsEqual,
50
+ bindDirtyToHistory: () => import_editor12.bindDirtyToHistory,
51
+ createDirtyTracker: () => import_editor12.createDirtyTracker,
52
+ createHistoryManager: () => import_editor12.createHistoryManager,
53
+ defaultIsEqual: () => import_editor12.defaultIsEqual,
52
54
  useAgentToolProvider: () => useAgentToolProvider,
55
+ useCommandPalette: () => useCommandPalette,
56
+ useCommandShortcuts: () => useCommandShortcuts,
53
57
  useComments: () => useComments,
54
58
  useDismissGuard: () => useDismissGuard,
55
59
  useEditor: () => useEditor2,
@@ -57,14 +61,17 @@ __export(src_exports, {
57
61
  useFileImport: () => useFileImport,
58
62
  useFillHandle: () => useFillHandle,
59
63
  useHistory: () => useHistory,
64
+ useHistoryDirty: () => useHistoryDirty,
60
65
  useHistoryShortcuts: () => useHistoryShortcuts,
61
66
  useImageEditor: () => useImageEditor,
62
67
  useNavigationGuard: () => useNavigationGuard,
63
68
  useOutline: () => useOutline,
64
69
  usePromptEditor: () => usePromptEditor,
70
+ useRegisterCommands: () => useRegisterCommands,
65
71
  useSpreadsheetClipboard: () => useSpreadsheetClipboard,
66
72
  useSpreadsheetEditor: () => useSpreadsheetEditor,
67
- useTrackChanges: () => useTrackChanges
73
+ useTrackChanges: () => useTrackChanges,
74
+ withoutDirty: () => import_editor12.withoutDirty
68
75
  });
69
76
  module.exports = __toCommonJS(src_exports);
70
77
 
@@ -470,6 +477,7 @@ function useHistory(managerOrOptions) {
470
477
  () => manager.getState(),
471
478
  () => manager.getState()
472
479
  );
480
+ const entries = (0, import_react6.useMemo)(() => manager.getEntries(), [manager, state]);
473
481
  const undo = (0, import_react6.useCallback)(() => manager.undo(), [manager]);
474
482
  const redo = (0, import_react6.useCallback)(() => manager.redo(), [manager]);
475
483
  const clear = (0, import_react6.useCallback)(() => manager.clear(), [manager]);
@@ -485,60 +493,102 @@ function useHistory(managerOrOptions) {
485
493
  (name, fn) => manager.batch(name, fn),
486
494
  [manager]
487
495
  );
496
+ const goTo = (0, import_react6.useCallback)((index) => manager.goTo(index), [manager]);
497
+ const mark = (0, import_react6.useCallback)(() => manager.mark(), [manager]);
498
+ const revertTo = (0, import_react6.useCallback)(
499
+ (m) => manager.revertTo(m),
500
+ [manager]
501
+ );
502
+ const beginGesture = (0, import_react6.useCallback)(
503
+ (key) => manager.beginGesture(key),
504
+ [manager]
505
+ );
506
+ const endGesture = (0, import_react6.useCallback)(() => manager.endGesture(), [manager]);
488
507
  return {
489
508
  ...state,
490
509
  manager,
510
+ entries,
491
511
  undo,
492
512
  redo,
493
513
  clear,
494
514
  execute,
495
515
  executeMerged,
496
- batch
516
+ batch,
517
+ goTo,
518
+ mark,
519
+ revertTo,
520
+ beginGesture,
521
+ endGesture
497
522
  };
498
523
  }
499
524
 
500
- // src/hooks/useHistoryShortcuts.ts
525
+ // src/hooks/useHistoryDirty.ts
501
526
  var import_react7 = require("react");
527
+ var import_editor4 = require("@pixerate/editor");
528
+ function useHistoryDirty(options) {
529
+ const getValueRef = (0, import_react7.useRef)(options.getValue);
530
+ getValueRef.current = options.getValue;
531
+ const [tracker] = (0, import_react7.useState)(
532
+ () => options.tracker ?? new import_editor4.DirtyTracker(options.getValue(), {
533
+ isEqual: options.isEqual,
534
+ normalize: options.normalize
535
+ })
536
+ );
537
+ const bindingRef = (0, import_react7.useRef)(null);
538
+ (0, import_react7.useEffect)(() => {
539
+ const binding = (0, import_editor4.bindDirtyToHistory)({
540
+ history: options.history,
541
+ getValue: () => getValueRef.current(),
542
+ tracker
543
+ });
544
+ bindingRef.current = binding;
545
+ return () => {
546
+ binding.destroy();
547
+ if (bindingRef.current === binding) bindingRef.current = null;
548
+ };
549
+ }, [options.history, tracker]);
550
+ const isDirty = (0, import_react7.useSyncExternalStore)(
551
+ (0, import_react7.useCallback)((onChange) => tracker.subscribe(onChange), [tracker]),
552
+ () => tracker.dirty,
553
+ () => tracker.dirty
554
+ );
555
+ const markClean = (0, import_react7.useCallback)(
556
+ () => tracker.markClean(getValueRef.current()),
557
+ [tracker]
558
+ );
559
+ const suppress = (0, import_react7.useCallback)(
560
+ (fn) => bindingRef.current ? bindingRef.current.suppress(fn) : tracker.suppress(fn),
561
+ [tracker]
562
+ );
563
+ const refresh = (0, import_react7.useCallback)(
564
+ () => tracker.update(getValueRef.current()),
565
+ [tracker]
566
+ );
567
+ return { isDirty, markClean, suppress, refresh, tracker };
568
+ }
569
+
570
+ // src/hooks/useHistoryShortcuts.ts
571
+ var import_react8 = require("react");
572
+ var import_commands = require("@pixerate/editor/commands");
502
573
  function useHistoryShortcuts(manager, options = {}) {
503
574
  const {
504
575
  enabled = true,
505
576
  target,
506
577
  ignoreEditableElements = true
507
578
  } = options;
508
- (0, import_react7.useEffect)(() => {
579
+ (0, import_react8.useEffect)(() => {
509
580
  if (!enabled) return;
510
581
  const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
511
582
  if (!eventTarget) return;
512
583
  const handleKeyDown = (event) => {
513
584
  const e = event;
514
- const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
515
- const modifier = isMac ? e.metaKey : e.ctrlKey;
516
- if (!modifier) return;
517
- if (ignoreEditableElements && typeof document !== "undefined") {
518
- const targetEl = e.target || document.activeElement;
519
- if (targetEl && targetEl.tagName) {
520
- const tagName = targetEl.tagName.toLowerCase();
521
- const isInput = tagName === "input" || tagName === "textarea";
522
- const isContentEditable = targetEl.isContentEditable || targetEl.getAttribute?.("contenteditable") === "true";
523
- if (isInput || isContentEditable) {
524
- return;
525
- }
526
- }
527
- }
528
- const key = e.key.toLowerCase();
529
- if (key === "z" && !e.shiftKey) {
530
- if (manager.getState().canUndo) {
531
- e.preventDefault();
532
- manager.undo();
533
- }
534
- return;
535
- }
536
- const isRedoKey = key === "z" && e.shiftKey || !isMac && key === "y" && !e.shiftKey;
537
- if (isRedoKey) {
538
- if (manager.getState().canRedo) {
539
- e.preventDefault();
540
- manager.redo();
541
- }
585
+ const action = (0, import_commands.matchHistoryShortcut)(e, { allowInEditable: !ignoreEditableElements });
586
+ if (action === "undo" && manager.getState().canUndo) {
587
+ e.preventDefault();
588
+ manager.undo();
589
+ } else if (action === "redo" && manager.getState().canRedo) {
590
+ e.preventDefault();
591
+ manager.redo();
542
592
  }
543
593
  };
544
594
  eventTarget.addEventListener("keydown", handleKeyDown);
@@ -549,22 +599,22 @@ function useHistoryShortcuts(manager, options = {}) {
549
599
  }
550
600
 
551
601
  // src/hooks/useFileImport.ts
552
- var import_react8 = require("react");
553
- var import_editor4 = require("@pixerate/editor");
602
+ var import_react9 = require("react");
603
+ var import_editor5 = require("@pixerate/editor");
554
604
  function useFileImport(editor, options = {}) {
555
- const editorRef = (0, import_react8.useRef)(editor);
605
+ const editorRef = (0, import_react9.useRef)(editor);
556
606
  editorRef.current = editor;
557
- const controller = (0, import_react8.useMemo)(
558
- () => new import_editor4.FileImportController(() => {
607
+ const controller = (0, import_react9.useMemo)(
608
+ () => new import_editor5.FileImportController(() => {
559
609
  const current = editorRef.current;
560
610
  return current && !current.isDestroyed ? current : null;
561
611
  }, options),
562
612
  []
563
613
  );
564
- (0, import_react8.useEffect)(() => {
614
+ (0, import_react9.useEffect)(() => {
565
615
  controller.setOptions(options);
566
616
  });
567
- const state = (0, import_react8.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
617
+ const state = (0, import_react9.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
568
618
  return {
569
619
  ...state,
570
620
  accept: controller.accept,
@@ -574,27 +624,27 @@ function useFileImport(editor, options = {}) {
574
624
  }
575
625
 
576
626
  // src/hooks/useAgentToolProvider.ts
577
- var import_react9 = require("react");
627
+ var import_react10 = require("react");
578
628
  function useAgentToolProvider(registry, provider) {
579
- (0, import_react9.useEffect)(() => {
629
+ (0, import_react10.useEffect)(() => {
580
630
  if (!registry || !provider) return;
581
631
  return registry.register(provider);
582
632
  }, [registry, provider]);
583
633
  }
584
634
 
585
635
  // src/components/EditorContent.tsx
586
- var import_react10 = require("@tiptap/react");
636
+ var import_react11 = require("@tiptap/react");
587
637
  var import_jsx_runtime = require("react/jsx-runtime");
588
638
  var EditorContent = ({
589
639
  className,
590
640
  editor,
591
641
  ...props
592
642
  }) => {
593
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react10.EditorContent, { editor, ...props }) });
643
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react11.EditorContent, { editor, ...props }) });
594
644
  };
595
645
 
596
646
  // src/components/BubbleMenu.tsx
597
- var import_react11 = require("react");
647
+ var import_react12 = require("react");
598
648
  var import_react_dom = require("react-dom");
599
649
  var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
600
650
  var BubbleMenu = ({
@@ -606,12 +656,12 @@ var BubbleMenu = ({
606
656
  pluginKey = "bubbleMenu",
607
657
  updateDelay
608
658
  }) => {
609
- const menuEl = (0, import_react11.useRef)(null);
610
- const [mounted, setMounted] = (0, import_react11.useState)(false);
659
+ const menuEl = (0, import_react12.useRef)(null);
660
+ const [mounted, setMounted] = (0, import_react12.useState)(false);
611
661
  if (!menuEl.current && typeof document !== "undefined") {
612
662
  menuEl.current = document.createElement("div");
613
663
  }
614
- (0, import_react11.useEffect)(() => {
664
+ (0, import_react12.useEffect)(() => {
615
665
  if (!editor || !menuEl.current || editor.isDestroyed) {
616
666
  return;
617
667
  }
@@ -642,8 +692,8 @@ var BubbleMenu = ({
642
692
  };
643
693
 
644
694
  // src/components/TemplateRenderer.tsx
645
- var import_react12 = require("react");
646
- var import_editor5 = require("@pixerate/editor");
695
+ var import_react13 = require("react");
696
+ var import_editor6 = require("@pixerate/editor");
647
697
  var import_jsx_runtime2 = require("react/jsx-runtime");
648
698
  var CONDITIONAL_PREFIX = { if: "?", unless: "!", end: "/" };
649
699
  var TemplateRenderer = ({
@@ -657,12 +707,12 @@ var TemplateRenderer = ({
657
707
  onTemplateClick,
658
708
  onVariableClick
659
709
  }) => {
660
- const displayText = (0, import_react12.useMemo)(() => {
710
+ const displayText = (0, import_react13.useMemo)(() => {
661
711
  if (!resolve || templates.length === 0) return content;
662
- return (0, import_editor5.resolveTemplates)(content, templates, { bindings });
712
+ return (0, import_editor6.resolveTemplates)(content, templates, { bindings });
663
713
  }, [content, templates, resolve, bindings]);
664
- const tokens = (0, import_react12.useMemo)(() => {
665
- return (0, import_editor5.evaluatePromptTokens)(displayText, values);
714
+ const tokens = (0, import_react13.useMemo)(() => {
715
+ return (0, import_editor6.evaluatePromptTokens)(displayText, values);
666
716
  }, [displayText, values]);
667
717
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, children: tokens.map((token, index) => {
668
718
  if (token.type === "template") {
@@ -750,8 +800,8 @@ var TemplateRenderer = ({
750
800
  };
751
801
 
752
802
  // src/components/VariableForm.tsx
753
- var import_react13 = require("react");
754
- var import_editor6 = require("@pixerate/editor");
803
+ var import_react14 = require("react");
804
+ var import_editor7 = require("@pixerate/editor");
755
805
  var import_jsx_runtime3 = require("react/jsx-runtime");
756
806
  var VariableForm = ({
757
807
  content,
@@ -762,19 +812,19 @@ var VariableForm = ({
762
812
  onChange,
763
813
  className = "flex flex-col gap-3 text-sm"
764
814
  }) => {
765
- const idPrefix = (0, import_react13.useId)();
766
- const [internal, setInternal] = (0, import_react13.useState)(
815
+ const idPrefix = (0, import_react14.useId)();
816
+ const [internal, setInternal] = (0, import_react14.useState)(
767
817
  () => defaultValues ?? {}
768
818
  );
769
819
  const entered = values ?? internal;
770
- const fields = (0, import_react13.useMemo)(
771
- () => (0, import_editor6.getVariableFormFields)(content, { templates, bindings }),
820
+ const fields = (0, import_react14.useMemo)(
821
+ () => (0, import_editor7.getVariableFormFields)(content, { templates, bindings }),
772
822
  [content, templates, bindings]
773
823
  );
774
824
  const update = (name, value) => {
775
825
  const next = { ...entered, [name]: value };
776
826
  if (values === void 0) setInternal(next);
777
- onChange?.((0, import_editor6.compactVariableValues)(next), next);
827
+ onChange?.((0, import_editor7.compactVariableValues)(next), next);
778
828
  };
779
829
  if (fields.length === 0) return null;
780
830
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { className, onSubmit: (event) => event.preventDefault(), children: fields.map((field) => {
@@ -835,15 +885,15 @@ var VariableForm = ({
835
885
  };
836
886
 
837
887
  // src/hooks/useOutline.ts
838
- var import_react14 = require("react");
839
- var import_editor7 = require("@pixerate/editor");
888
+ var import_react15 = require("react");
889
+ var import_editor8 = require("@pixerate/editor");
840
890
  function useOutline(editor) {
841
- const store = (0, import_react14.useMemo)(() => (0, import_editor7.createOutlineStore)(editor), [editor]);
842
- return (0, import_react14.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
891
+ const store = (0, import_react15.useMemo)(() => (0, import_editor8.createOutlineStore)(editor), [editor]);
892
+ return (0, import_react15.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
843
893
  }
844
894
 
845
895
  // src/components/OutlinePanel.tsx
846
- var import_editor8 = require("@pixerate/editor");
896
+ var import_editor9 = require("@pixerate/editor");
847
897
  var import_jsx_runtime4 = require("react/jsx-runtime");
848
898
  var OutlinePanel = ({
849
899
  editor,
@@ -872,7 +922,7 @@ var OutlinePanel = ({
872
922
  "data-heading-id": heading.id,
873
923
  onClick: () => {
874
924
  if (!editor) return;
875
- if ((0, import_editor8.navigateToHeading)(editor, heading, { focus: focusOnNavigate })) {
925
+ if ((0, import_editor9.navigateToHeading)(editor, heading, { focus: focusOnNavigate })) {
876
926
  onNavigate?.(heading);
877
927
  }
878
928
  },
@@ -884,16 +934,16 @@ var OutlinePanel = ({
884
934
  };
885
935
 
886
936
  // src/index.ts
887
- var import_editor11 = require("@pixerate/editor");
937
+ var import_editor12 = require("@pixerate/editor");
888
938
 
889
939
  // src/spreadsheet/useSpreadsheetEditor.ts
890
- var import_react15 = require("react");
940
+ var import_react16 = require("react");
891
941
  var import_spreadsheet = require("@pixerate/editor/spreadsheet");
892
942
  function useSpreadsheetEditor(options = {}) {
893
- const optionsRef = (0, import_react15.useRef)(options);
943
+ const optionsRef = (0, import_react16.useRef)(options);
894
944
  optionsRef.current = options;
895
- const settersRef = (0, import_react15.useRef)({});
896
- const [controller] = (0, import_react15.useState)(
945
+ const settersRef = (0, import_react16.useRef)({});
946
+ const [controller] = (0, import_react16.useState)(
897
947
  () => new import_spreadsheet.SpreadsheetController({
898
948
  ...options,
899
949
  onDocumentChange: (updated) => {
@@ -910,93 +960,97 @@ function useSpreadsheetEditor(options = {}) {
910
960
  }
911
961
  })
912
962
  );
913
- const [doc, setDoc] = (0, import_react15.useState)(() => ({ ...controller.document }));
914
- const [activeCell, setActiveCell] = (0, import_react15.useState)(controller.activeCell);
915
- const [selectedRange, setSelectedRange] = (0, import_react15.useState)(controller.selectedRange);
916
- const [editingCell, setEditingCell] = (0, import_react15.useState)(null);
917
- const [draftValue, setDraftValue] = (0, import_react15.useState)("");
963
+ const [doc, setDoc] = (0, import_react16.useState)(() => ({ ...controller.document }));
964
+ const [activeCell, setActiveCell] = (0, import_react16.useState)(controller.activeCell);
965
+ const [selectedRange, setSelectedRange] = (0, import_react16.useState)(controller.selectedRange);
966
+ const [editingCell, setEditingCell] = (0, import_react16.useState)(null);
967
+ const [draftValue, setDraftState] = (0, import_react16.useState)("");
918
968
  settersRef.current = { setDoc, setActiveCell, setSelectedRange };
919
- const setCellValue = (0, import_react15.useCallback)((rowId, colId, raw) => {
969
+ const setDraftValue = (0, import_react16.useCallback)((value) => {
970
+ controller.draftValue = value;
971
+ setDraftState(value);
972
+ }, [controller]);
973
+ const setCellValue = (0, import_react16.useCallback)((rowId, colId, raw) => {
920
974
  controller.setCellValue(rowId, colId, raw);
921
975
  setDoc({ ...controller.document });
922
976
  }, [controller]);
923
- const selectCell = (0, import_react15.useCallback)((row, col, extendSelection = false) => {
977
+ const selectCell = (0, import_react16.useCallback)((row, col, extendSelection = false) => {
924
978
  controller.selectCell(row, col, extendSelection);
925
979
  setActiveCell(controller.activeCell);
926
980
  setSelectedRange(controller.selectedRange);
927
981
  }, [controller]);
928
- const selectRange = (0, import_react15.useCallback)((range) => {
982
+ const selectRange = (0, import_react16.useCallback)((range) => {
929
983
  controller.selectRange(range);
930
984
  setSelectedRange(controller.selectedRange);
931
985
  }, [controller]);
932
- const startEditing = (0, import_react15.useCallback)((row, col, initialVal) => {
986
+ const startEditing = (0, import_react16.useCallback)((row, col, initialVal) => {
933
987
  controller.startEditing(row, col, initialVal);
934
988
  setActiveCell(controller.activeCell);
935
989
  setEditingCell(controller.editingCell);
936
990
  setDraftValue(controller.draftValue);
937
991
  }, [controller]);
938
- const commitEditing = (0, import_react15.useCallback)((val) => {
992
+ const commitEditing = (0, import_react16.useCallback)((val) => {
939
993
  controller.commitEditing(val);
940
994
  setEditingCell(null);
941
995
  setDraftValue("");
942
996
  setDoc({ ...controller.document });
943
997
  }, [controller]);
944
- const cancelEditing = (0, import_react15.useCallback)(() => {
998
+ const cancelEditing = (0, import_react16.useCallback)(() => {
945
999
  controller.cancelEditing();
946
1000
  setEditingCell(null);
947
1001
  setDraftValue("");
948
1002
  }, [controller]);
949
- const moveCursor = (0, import_react15.useCallback)((dir, extend = false) => {
1003
+ const moveCursor = (0, import_react16.useCallback)((dir, extend = false) => {
950
1004
  controller.moveCursor(dir, extend);
951
1005
  setActiveCell(controller.activeCell);
952
1006
  setSelectedRange(controller.selectedRange);
953
1007
  }, [controller]);
954
- const insertColumn = (0, import_react15.useCallback)((index, config) => {
1008
+ const insertColumn = (0, import_react16.useCallback)((index, config) => {
955
1009
  controller.insertColumn(index, config);
956
1010
  setDoc({ ...controller.document });
957
1011
  }, [controller]);
958
- const deleteColumn = (0, import_react15.useCallback)((colId) => {
1012
+ const deleteColumn = (0, import_react16.useCallback)((colId) => {
959
1013
  controller.deleteColumn(colId);
960
1014
  setDoc({ ...controller.document });
961
1015
  }, [controller]);
962
- const insertRow = (0, import_react15.useCallback)((index, config) => {
1016
+ const insertRow = (0, import_react16.useCallback)((index, config) => {
963
1017
  controller.insertRow(index, config);
964
1018
  setDoc({ ...controller.document });
965
1019
  }, [controller]);
966
- const deleteRow = (0, import_react15.useCallback)((rowId) => {
1020
+ const deleteRow = (0, import_react16.useCallback)((rowId) => {
967
1021
  controller.deleteRow(rowId);
968
1022
  setDoc({ ...controller.document });
969
1023
  }, [controller]);
970
- const setColumnWidth = (0, import_react15.useCallback)((colId, width) => {
1024
+ const setColumnWidth = (0, import_react16.useCallback)((colId, width) => {
971
1025
  controller.setColumnWidth(colId, width);
972
1026
  setDoc({ ...controller.document });
973
1027
  }, [controller]);
974
- const autoFitColumnWidth = (0, import_react15.useCallback)((colId, customWidth) => {
1028
+ const autoFitColumnWidth = (0, import_react16.useCallback)((colId, customWidth) => {
975
1029
  controller.autoFitColumnWidth(colId, customWidth);
976
1030
  setDoc({ ...controller.document });
977
1031
  }, [controller]);
978
- const registerDataSource = (0, import_react15.useCallback)((ds) => {
1032
+ const registerDataSource = (0, import_react16.useCallback)((ds) => {
979
1033
  controller.registerDataSource(ds);
980
1034
  setDoc({ ...controller.document });
981
1035
  }, [controller]);
982
- const setPrimaryDataSource = (0, import_react15.useCallback)((id) => {
1036
+ const setPrimaryDataSource = (0, import_react16.useCallback)((id) => {
983
1037
  controller.setPrimaryDataSource(id);
984
1038
  setDoc({ ...controller.document });
985
1039
  }, [controller]);
986
- const recalculateAll = (0, import_react15.useCallback)(() => {
1040
+ const recalculateAll = (0, import_react16.useCallback)(() => {
987
1041
  controller.recalculateAll();
988
1042
  setDoc({ ...controller.document });
989
1043
  }, [controller]);
990
- const undo = (0, import_react15.useCallback)(() => {
1044
+ const undo = (0, import_react16.useCallback)(() => {
991
1045
  controller.undo();
992
1046
  setDoc({ ...controller.document });
993
1047
  }, [controller]);
994
- const redo = (0, import_react15.useCallback)(() => {
1048
+ const redo = (0, import_react16.useCallback)(() => {
995
1049
  controller.redo();
996
1050
  setDoc({ ...controller.document });
997
1051
  }, [controller]);
998
- const tableActions = (0, import_react15.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
999
- const viewModel = (0, import_react15.useMemo)(() => controller.getViewModel(), [controller, doc]);
1052
+ const tableActions = (0, import_react16.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
1053
+ const viewModel = (0, import_react16.useMemo)(() => controller.getViewModel(), [controller, doc]);
1000
1054
  return {
1001
1055
  document: doc,
1002
1056
  activeCell,
@@ -1048,17 +1102,32 @@ function useSpreadsheetEditor(options = {}) {
1048
1102
  }
1049
1103
 
1050
1104
  // src/spreadsheet/SpreadsheetEditor.tsx
1051
- var import_react19 = require("react");
1052
- var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
1105
+ var import_react20 = require("react");
1106
+ var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
1053
1107
 
1054
1108
  // src/spreadsheet/SpreadsheetContextMenu.tsx
1055
- var import_react16 = __toESM(require("react"), 1);
1109
+ var import_react17 = __toESM(require("react"), 1);
1110
+ var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
1056
1111
  var import_jsx_runtime5 = require("react/jsx-runtime");
1112
+ var VIEWPORT_MARGIN = 4;
1057
1113
  var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1058
- const menuRef = (0, import_react16.useRef)(null);
1059
- const onCloseRef = (0, import_react16.useRef)(onClose);
1114
+ const menuRef = (0, import_react17.useRef)(null);
1115
+ const onCloseRef = (0, import_react17.useRef)(onClose);
1060
1116
  onCloseRef.current = onClose;
1061
- (0, import_react16.useEffect)(() => {
1117
+ const [position, setPosition] = (0, import_react17.useState)({ x, y });
1118
+ (0, import_react17.useLayoutEffect)(() => {
1119
+ const menu = menuRef.current;
1120
+ if (!menu || typeof window === "undefined") return;
1121
+ const { width, height } = menu.getBoundingClientRect();
1122
+ const next = (0, import_spreadsheet2.clampMenuPosition)(
1123
+ { x, y },
1124
+ { width, height },
1125
+ { width: window.innerWidth, height: window.innerHeight },
1126
+ VIEWPORT_MARGIN
1127
+ );
1128
+ setPosition((prev) => prev.x === next.x && prev.y === next.y ? prev : next);
1129
+ }, [x, y, items]);
1130
+ (0, import_react17.useEffect)(() => {
1062
1131
  const handlePointer = (e) => {
1063
1132
  if (!menuRef.current?.contains(e.target)) onCloseRef.current();
1064
1133
  };
@@ -1079,10 +1148,16 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1079
1148
  role: "menu",
1080
1149
  "aria-label": "Table actions",
1081
1150
  className: "fixed z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg",
1082
- style: { left: x, top: y, background: "var(--popover, var(--background, #fff))" },
1151
+ style: {
1152
+ left: position.x,
1153
+ top: position.y,
1154
+ maxHeight: `calc(100vh - ${VIEWPORT_MARGIN * 2}px)`,
1155
+ overflowY: "auto",
1156
+ background: "var(--popover, var(--background, #fff))"
1157
+ },
1083
1158
  onContextMenu: (e) => e.preventDefault(),
1084
1159
  onKeyDown: (e) => e.stopPropagation(),
1085
- children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react16.default.Fragment, { children: [
1160
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react17.default.Fragment, { children: [
1086
1161
  item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
1087
1162
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1088
1163
  "button",
@@ -1116,7 +1191,7 @@ var SpreadsheetGroupRow = ({
1116
1191
  }) => {
1117
1192
  const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
1118
1193
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
1119
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/60 border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1194
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1120
1195
  "button",
1121
1196
  {
1122
1197
  type: "button",
@@ -1147,8 +1222,8 @@ var SpreadsheetGroupRow = ({
1147
1222
  };
1148
1223
 
1149
1224
  // src/spreadsheet/useSpreadsheetTableInteractions.ts
1150
- var import_react17 = require("react");
1151
- var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
1225
+ var import_react18 = require("react");
1226
+ var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
1152
1227
  function gridHasFocus(grid) {
1153
1228
  if (!grid || typeof document === "undefined") return false;
1154
1229
  const active = document.activeElement;
@@ -1156,12 +1231,12 @@ function gridHasFocus(grid) {
1156
1231
  return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
1157
1232
  }
1158
1233
  function useSpreadsheetClipboard(gridRef, state, readOnly) {
1159
- const stateRef = (0, import_react17.useRef)(state);
1234
+ const stateRef = (0, import_react18.useRef)(state);
1160
1235
  stateRef.current = state;
1161
- const readOnlyRef = (0, import_react17.useRef)(readOnly);
1236
+ const readOnlyRef = (0, import_react18.useRef)(readOnly);
1162
1237
  readOnlyRef.current = readOnly;
1163
- const copiedRef = (0, import_react17.useRef)(false);
1164
- (0, import_react17.useEffect)(() => {
1238
+ const copiedRef = (0, import_react18.useRef)(false);
1239
+ (0, import_react18.useEffect)(() => {
1165
1240
  if (typeof document === "undefined") return;
1166
1241
  const handleCopy = (e) => {
1167
1242
  const s = stateRef.current;
@@ -1184,24 +1259,25 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
1184
1259
  document.removeEventListener("paste", handlePaste);
1185
1260
  };
1186
1261
  }, [gridRef]);
1187
- const copyShortcutFallback = (0, import_react17.useCallback)(() => {
1262
+ const copyShortcutFallback = (0, import_react18.useCallback)(() => {
1188
1263
  copiedRef.current = false;
1189
1264
  setTimeout(() => {
1190
1265
  if (copiedRef.current) return;
1191
1266
  const s = stateRef.current;
1192
- navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange));
1267
+ Promise.resolve(navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange))).catch(() => {
1268
+ });
1193
1269
  }, 0);
1194
1270
  }, []);
1195
1271
  return { copyShortcutFallback };
1196
1272
  }
1197
1273
  function useFillHandle(state) {
1198
- const [drag, setDrag] = (0, import_react17.useState)(null);
1199
- const dragRef = (0, import_react17.useRef)(drag);
1274
+ const [drag, setDrag] = (0, import_react18.useState)(null);
1275
+ const dragRef = (0, import_react18.useRef)(drag);
1200
1276
  dragRef.current = drag;
1201
- const stateRef = (0, import_react17.useRef)(state);
1277
+ const stateRef = (0, import_react18.useRef)(state);
1202
1278
  stateRef.current = state;
1203
1279
  const dragging = drag !== null;
1204
- (0, import_react17.useEffect)(() => {
1280
+ (0, import_react18.useEffect)(() => {
1205
1281
  if (!dragging) return;
1206
1282
  const handleMouseUp = () => {
1207
1283
  const current = dragRef.current;
@@ -1211,49 +1287,49 @@ function useFillHandle(state) {
1211
1287
  window.addEventListener("mouseup", handleMouseUp);
1212
1288
  return () => window.removeEventListener("mouseup", handleMouseUp);
1213
1289
  }, [dragging]);
1214
- const startFill = (0, import_react17.useCallback)((e) => {
1290
+ const startFill = (0, import_react18.useCallback)((e) => {
1215
1291
  e.preventDefault();
1216
1292
  e.stopPropagation();
1217
1293
  const range = stateRef.current.selectedRange;
1218
- if (range) setDrag({ source: (0, import_spreadsheet2.normalizeRange)(range), target: null });
1294
+ if (range) setDrag({ source: (0, import_spreadsheet3.normalizeRange)(range), target: null });
1219
1295
  }, []);
1220
- const extendFill = (0, import_react17.useCallback)((row, col) => {
1296
+ const extendFill = (0, import_react18.useCallback)((row, col) => {
1221
1297
  const current = dragRef.current;
1222
1298
  if (!current) return;
1223
- setDrag({ source: current.source, target: (0, import_spreadsheet2.computeFillTarget)(current.source, { row, col }) });
1299
+ setDrag({ source: current.source, target: (0, import_spreadsheet3.computeFillTarget)(current.source, { row, col }) });
1224
1300
  }, []);
1225
1301
  return { fillTarget: drag?.target ?? null, isFilling: dragging, startFill, extendFill };
1226
1302
  }
1227
1303
 
1228
1304
  // src/spreadsheet/FormulaHints.tsx
1229
- var import_react18 = require("react");
1230
- var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
1305
+ var import_react19 = require("react");
1306
+ var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
1231
1307
  var import_jsx_runtime7 = require("react/jsx-runtime");
1232
1308
  var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1233
- const [caret, setCaret] = (0, import_react18.useState)(null);
1234
- const [focused, setFocused] = (0, import_react18.useState)(false);
1235
- const [selected, setSelected] = (0, import_react18.useState)(0);
1236
- const [dismissed, setDismissed] = (0, import_react18.useState)(false);
1237
- const [position, setPosition] = (0, import_react18.useState)(null);
1238
- const pendingCaret = (0, import_react18.useRef)(null);
1239
- const hints = (0, import_react18.useMemo)(
1240
- () => (0, import_spreadsheet3.getFormulaHints)(value, caret ?? value.length, { limit }),
1309
+ const [caret, setCaret] = (0, import_react19.useState)(null);
1310
+ const [focused, setFocused] = (0, import_react19.useState)(false);
1311
+ const [selected, setSelected] = (0, import_react19.useState)(0);
1312
+ const [dismissed, setDismissed] = (0, import_react19.useState)(false);
1313
+ const [position, setPosition] = (0, import_react19.useState)(null);
1314
+ const pendingCaret = (0, import_react19.useRef)(null);
1315
+ const hints = (0, import_react19.useMemo)(
1316
+ () => (0, import_spreadsheet4.getFormulaHints)(value, caret ?? value.length, { limit }),
1241
1317
  [value, caret, limit]
1242
1318
  );
1243
1319
  const suggestions = dismissed ? [] : hints.suggestions;
1244
1320
  const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
1245
1321
  const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
1246
- const latest = (0, import_react18.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
1322
+ const latest = (0, import_react19.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
1247
1323
  latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
1248
1324
  const accept = (index) => {
1249
1325
  const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
1250
1326
  const suggestion = list[index];
1251
1327
  if (!suggestion) return;
1252
- const next = (0, import_spreadsheet3.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
1328
+ const next = (0, import_spreadsheet4.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
1253
1329
  pendingCaret.current = next.caret;
1254
1330
  commit(next.value);
1255
1331
  };
1256
- (0, import_react18.useEffect)(() => {
1332
+ (0, import_react19.useEffect)(() => {
1257
1333
  const input = inputRef.current;
1258
1334
  if (!input) return;
1259
1335
  const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
@@ -1303,7 +1379,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1303
1379
  input.removeEventListener("keydown", onKeyDown);
1304
1380
  };
1305
1381
  }, [inputRef]);
1306
- (0, import_react18.useLayoutEffect)(() => {
1382
+ (0, import_react19.useLayoutEffect)(() => {
1307
1383
  const input = inputRef.current;
1308
1384
  const at = pendingCaret.current;
1309
1385
  if (!input || at === null || input.value !== value) return;
@@ -1312,7 +1388,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1312
1388
  setCaret(at);
1313
1389
  setSelected(0);
1314
1390
  }, [value, inputRef]);
1315
- (0, import_react18.useLayoutEffect)(() => {
1391
+ (0, import_react19.useLayoutEffect)(() => {
1316
1392
  const input = inputRef.current;
1317
1393
  if (!visible || !input) return;
1318
1394
  const rect = input.getBoundingClientRect();
@@ -1389,6 +1465,20 @@ function frozenCellStyle(viewRow, viewCol) {
1389
1465
  background: "var(--background, #fff)"
1390
1466
  };
1391
1467
  }
1468
+ var coordKey = (cell) => cell ? `${(0, import_spreadsheet5.indexToColName)(cell.col)}${cell.row + 1}` : "";
1469
+ function spillBorderStyle(range, row, col) {
1470
+ const line = "1px solid color-mix(in srgb, var(--primary, #6366f1) 60%, transparent)";
1471
+ return {
1472
+ position: "absolute",
1473
+ inset: 0,
1474
+ pointerEvents: "none",
1475
+ zIndex: 5,
1476
+ borderTop: row === range.startRow ? line : void 0,
1477
+ borderBottom: row === range.endRow ? line : void 0,
1478
+ borderLeft: col === range.startCol ? line : void 0,
1479
+ borderRight: col === range.endCol ? line : void 0
1480
+ };
1481
+ }
1392
1482
  var SpreadsheetEditor = ({
1393
1483
  state: externalState,
1394
1484
  readOnly = false,
@@ -1399,47 +1489,53 @@ var SpreadsheetEditor = ({
1399
1489
  }) => {
1400
1490
  const defaultState = useSpreadsheetEditor();
1401
1491
  const state = externalState || defaultState;
1402
- const gridRef = (0, import_react19.useRef)(null);
1403
- const formulaBarRef = (0, import_react19.useRef)(null);
1404
- const cellInputRef = (0, import_react19.useRef)(null);
1405
- const [resizingColId, setResizingColId] = (0, import_react19.useState)(null);
1406
- const [hoveredColId, setHoveredColId] = (0, import_react19.useState)(null);
1407
- const [resizeStartX, setResizeStartX] = (0, import_react19.useState)(0);
1408
- const [resizeStartWidth, setResizeStartWidth] = (0, import_react19.useState)(0);
1492
+ const gridRef = (0, import_react20.useRef)(null);
1493
+ const formulaBarRef = (0, import_react20.useRef)(null);
1494
+ const cellInputRef = (0, import_react20.useRef)(null);
1495
+ const [resizingColId, setResizingColId] = (0, import_react20.useState)(null);
1496
+ const [hoveredColId, setHoveredColId] = (0, import_react20.useState)(null);
1497
+ const [resizeStartX, setResizeStartX] = (0, import_react20.useState)(0);
1498
+ const [resizeStartWidth, setResizeStartWidth] = (0, import_react20.useState)(0);
1409
1499
  const view = state.viewModel;
1410
1500
  const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
1411
1501
  const { fillTarget, startFill, extendFill } = useFillHandle(state);
1412
- const [menu, setMenu] = (0, import_react19.useState)(null);
1502
+ const [menu, setMenu] = (0, import_react20.useState)(null);
1413
1503
  const filter = state.document.table?.filter;
1414
- const filteredColIds = (0, import_react19.useMemo)(
1504
+ const filteredColIds = (0, import_react20.useMemo)(
1415
1505
  () => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
1416
1506
  [filter]
1417
1507
  );
1418
- const handleCellContextMenu = (0, import_react19.useCallback)((rIdx, cIdx, e) => {
1508
+ const handleCellContextMenu = (0, import_react20.useCallback)((rIdx, cIdx, e) => {
1419
1509
  if (readOnly) return;
1420
1510
  e.preventDefault();
1421
1511
  if (state.editingCell) state.commitEditing();
1422
- if (!state.selectedRange || !(0, import_spreadsheet4.rangeContains)(state.selectedRange, rIdx, cIdx)) {
1512
+ if (!state.selectedRange || !(0, import_spreadsheet5.rangeContains)(state.selectedRange, rIdx, cIdx)) {
1423
1513
  state.selectCell(rIdx, cIdx);
1424
1514
  }
1425
- setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet4.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
1515
+ setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet5.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
1426
1516
  }, [readOnly, state]);
1427
- const activeRawValue = (0, import_react19.useMemo)(() => {
1517
+ const activeRawValue = (0, import_react20.useMemo)(() => {
1428
1518
  if (!state.activeCell) return "";
1429
1519
  const row = state.document.rows[state.activeCell.row];
1430
1520
  const col = state.document.columns[state.activeCell.col];
1431
1521
  if (!row || !col) return "";
1432
1522
  return state.getCell(row.id, col.id).raw;
1433
1523
  }, [state.activeCell, state.document]);
1434
- const coordLabel = (0, import_react19.useMemo)(() => {
1524
+ const activeSpill = (0, import_react20.useMemo)(
1525
+ () => state.activeCell ? state.controller.getSpillAt(state.activeCell.row, state.activeCell.col) : null,
1526
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1527
+ [state.controller, state.activeCell, state.document]
1528
+ );
1529
+ const spillFormula = activeSpill && !activeRawValue && activeSpill.key !== coordKey(state.activeCell) ? activeSpill.formula : "";
1530
+ const coordLabel = (0, import_react20.useMemo)(() => {
1435
1531
  if (!state.activeCell) return "--";
1436
- const colName = (0, import_spreadsheet4.indexToColName)(state.activeCell.col);
1532
+ const colName = (0, import_spreadsheet5.indexToColName)(state.activeCell.col);
1437
1533
  return `${colName}${state.activeCell.row + 1}`;
1438
1534
  }, [state.activeCell]);
1439
- const [formulaDraft, setFormulaDraft] = (0, import_react19.useState)(null);
1535
+ const [formulaDraft, setFormulaDraft] = (0, import_react20.useState)(null);
1440
1536
  const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
1441
1537
  const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
1442
- (0, import_react19.useEffect)(() => {
1538
+ (0, import_react20.useEffect)(() => {
1443
1539
  setFormulaDraft(null);
1444
1540
  }, [state.activeCell?.row, state.activeCell?.col]);
1445
1541
  const commitFormulaBar = () => {
@@ -1455,19 +1551,19 @@ var SpreadsheetEditor = ({
1455
1551
  }
1456
1552
  setFormulaDraft(null);
1457
1553
  };
1458
- const handleCellClick = (0, import_react19.useCallback)((rIdx, cIdx, e) => {
1554
+ const handleCellClick = (0, import_react20.useCallback)((rIdx, cIdx, e) => {
1459
1555
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
1460
1556
  state.commitEditing();
1461
1557
  }
1462
1558
  state.selectCell(rIdx, cIdx, e.shiftKey);
1463
1559
  }, [state]);
1464
- const handleCellDblClick = (0, import_react19.useCallback)((rIdx, cIdx) => {
1560
+ const handleCellDblClick = (0, import_react20.useCallback)((rIdx, cIdx) => {
1465
1561
  if (readOnly) return;
1466
1562
  const col = state.document.columns[cIdx];
1467
1563
  if (col?.readOnly) return;
1468
1564
  state.startEditing(rIdx, cIdx);
1469
1565
  }, [readOnly, state]);
1470
- const handleKeyDown = (0, import_react19.useCallback)((e) => {
1566
+ const handleKeyDown = (0, import_react20.useCallback)((e) => {
1471
1567
  if (state.editingCell) {
1472
1568
  if (e.key === "Enter") {
1473
1569
  e.preventDefault();
@@ -1480,7 +1576,10 @@ var SpreadsheetEditor = ({
1480
1576
  e.preventDefault();
1481
1577
  state.commitEditing();
1482
1578
  state.moveCursor(e.shiftKey ? "left" : "right");
1579
+ } else {
1580
+ return;
1483
1581
  }
1582
+ gridRef.current?.focus();
1484
1583
  return;
1485
1584
  }
1486
1585
  if (!state.activeCell) return;
@@ -1528,15 +1627,16 @@ var SpreadsheetEditor = ({
1528
1627
  } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
1529
1628
  const col = state.document.columns[state.activeCell.col];
1530
1629
  if (!col?.readOnly) {
1630
+ e.preventDefault();
1531
1631
  state.startEditing(state.activeCell.row, state.activeCell.col, e.key);
1532
1632
  }
1533
1633
  }
1534
1634
  }, [readOnly, state, copyShortcutFallback]);
1535
- const stateRef = (0, import_react19.useRef)(state);
1635
+ const stateRef = (0, import_react20.useRef)(state);
1536
1636
  stateRef.current = state;
1537
- const onColumnResizeRef = (0, import_react19.useRef)(onColumnResize);
1637
+ const onColumnResizeRef = (0, import_react20.useRef)(onColumnResize);
1538
1638
  onColumnResizeRef.current = onColumnResize;
1539
- (0, import_react19.useEffect)(() => {
1639
+ (0, import_react20.useEffect)(() => {
1540
1640
  if (!resizingColId) return;
1541
1641
  if (typeof document !== "undefined") {
1542
1642
  document.body.style.cursor = "col-resize";
@@ -1587,7 +1687,7 @@ var SpreadsheetEditor = ({
1587
1687
  window.removeEventListener("mouseup", handleMouseUp);
1588
1688
  };
1589
1689
  }, [resizingColId, resizeStartX, resizeStartWidth]);
1590
- const measureElementContentWidth = (0, import_react19.useCallback)((el, container) => {
1690
+ const measureElementContentWidth = (0, import_react20.useCallback)((el, container) => {
1591
1691
  if (typeof document === "undefined") return 0;
1592
1692
  let maxW = el.scrollWidth || 0;
1593
1693
  const firstChild = el.firstElementChild;
@@ -1635,7 +1735,7 @@ var SpreadsheetEditor = ({
1635
1735
  }
1636
1736
  return maxW;
1637
1737
  }, []);
1638
- const autoFitColumn = (0, import_react19.useCallback)((colId, colIndex) => {
1738
+ const autoFitColumn = (0, import_react20.useCallback)((colId, colIndex) => {
1639
1739
  const col = state.document.columns.find((c) => c.id === colId);
1640
1740
  if (!col) return;
1641
1741
  let calculatedWidth = 0;
@@ -1671,12 +1771,12 @@ var SpreadsheetEditor = ({
1671
1771
  onColumnResize(updatedCol.id, updatedCol.width || 130);
1672
1772
  }
1673
1773
  }, [state, measureElementContentWidth, onColumnResize]);
1674
- const handleResizeDblClick = (0, import_react19.useCallback)((colId, colIndex, e) => {
1774
+ const handleResizeDblClick = (0, import_react20.useCallback)((colId, colIndex, e) => {
1675
1775
  e.preventDefault();
1676
1776
  e.stopPropagation();
1677
1777
  autoFitColumn(colId, colIndex);
1678
1778
  }, [autoFitColumn]);
1679
- const handleHeaderDblClick = (0, import_react19.useCallback)((colId, colIndex, e) => {
1779
+ const handleHeaderDblClick = (0, import_react20.useCallback)((colId, colIndex, e) => {
1680
1780
  const target = e.currentTarget;
1681
1781
  if (target) {
1682
1782
  const rect = target.getBoundingClientRect();
@@ -1720,7 +1820,9 @@ var SpreadsheetEditor = ({
1720
1820
  placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
1721
1821
  "aria-label": "Formula bar",
1722
1822
  readOnly: formulaBarReadOnly,
1723
- value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1823
+ "data-spill-formula": !state.editingCell && formulaDraft === null && spillFormula ? "true" : void 0,
1824
+ style: !state.editingCell && formulaDraft === null && spillFormula ? { color: "var(--muted-foreground, #94a3b8)" } : void 0,
1825
+ value: state.editingCell ? state.draftValue : formulaDraft ?? (activeRawValue || spillFormula),
1724
1826
  onFocus: () => {
1725
1827
  if (!state.editingCell) setFormulaDraft(activeRawValue);
1726
1828
  },
@@ -1755,8 +1857,8 @@ var SpreadsheetEditor = ({
1755
1857
  )
1756
1858
  ] }),
1757
1859
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
1758
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { className: "h-7 bg-muted/60 sticky top-0 z-20 border-b border-border shadow-xs", children: [
1759
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted/90 border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
1860
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { className: "h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs", children: [
1861
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
1760
1862
  view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1761
1863
  "th",
1762
1864
  {
@@ -1842,7 +1944,7 @@ var SpreadsheetEditor = ({
1842
1944
  );
1843
1945
  }
1844
1946
  const displayRowIndex = viewRow.index;
1845
- const selection = state.selectedRange ? (0, import_spreadsheet4.normalizeRange)(state.selectedRange) : null;
1947
+ const selection = state.selectedRange ? (0, import_spreadsheet5.normalizeRange)(state.selectedRange) : null;
1846
1948
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1847
1949
  "tr",
1848
1950
  {
@@ -1853,7 +1955,7 @@ var SpreadsheetEditor = ({
1853
1955
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1854
1956
  "td",
1855
1957
  {
1856
- className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/50 border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
1958
+ className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
1857
1959
  style: viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop, zIndex: 15 } : void 0,
1858
1960
  children: displayRowIndex + 1
1859
1961
  }
@@ -1869,8 +1971,8 @@ var SpreadsheetEditor = ({
1869
1971
  const selected = isCellSelected(rIdx, cIdx);
1870
1972
  const active = isActiveCell(rIdx, cIdx);
1871
1973
  const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1872
- const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet4.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1873
- const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet4.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1974
+ const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet5.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1975
+ const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet5.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1874
1976
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1875
1977
  "td",
1876
1978
  {
@@ -1900,6 +2002,10 @@ var SpreadsheetEditor = ({
1900
2002
  value: state.draftValue,
1901
2003
  onChange: (e) => state.setDraftValue(e.target.value),
1902
2004
  onBlur: () => state.commitEditing(),
2005
+ onFocus: (e) => {
2006
+ const end = e.currentTarget.value.length;
2007
+ e.currentTarget.setSelectionRange(end, end);
2008
+ },
1903
2009
  autoFocus: true
1904
2010
  }
1905
2011
  ),
@@ -1909,9 +2015,10 @@ var SpreadsheetEditor = ({
1909
2015
  {
1910
2016
  className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
1911
2017
  title: cell.error ? cell.errorMessage ?? cell.error : void 0,
1912
- children: cell.error || (cell.value ?? "")
2018
+ children: (0, import_spreadsheet5.formatCellDisplay)(cell)
1913
2019
  }
1914
2020
  ),
2021
+ activeSpill && (0, import_spreadsheet5.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
1915
2022
  showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1916
2023
  "div",
1917
2024
  {
@@ -1969,7 +2076,7 @@ var SpreadsheetEditor = ({
1969
2076
  };
1970
2077
 
1971
2078
  // src/slots.tsx
1972
- var import_react20 = require("react");
2079
+ var import_react21 = require("react");
1973
2080
  var import_jsx_runtime9 = require("react/jsx-runtime");
1974
2081
  var defaultSlots = {
1975
2082
  Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -1991,102 +2098,103 @@ var defaultSlots = {
1991
2098
  props.onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
1992
2099
  ] })
1993
2100
  };
1994
- var EditorSlotsContext = (0, import_react20.createContext)(defaultSlots);
2101
+ var EditorSlotsContext = (0, import_react21.createContext)(defaultSlots);
1995
2102
  function EditorSlotsProvider({ slots, children }) {
1996
- const mergedSlots = (0, import_react20.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
2103
+ const mergedSlots = (0, import_react21.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
1997
2104
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
1998
2105
  }
1999
2106
  function useEditorSlots() {
2000
- return (0, import_react20.useContext)(EditorSlotsContext);
2107
+ return (0, import_react21.useContext)(EditorSlotsContext);
2001
2108
  }
2002
2109
 
2003
2110
  // src/image-editor/useImageEditor.ts
2004
- var import_react21 = require("react");
2111
+ var import_react22 = require("react");
2005
2112
  var import_image_editor = require("@pixerate/editor/image-editor");
2006
2113
  function useImageEditor(options = {}) {
2007
- const controllerRef = (0, import_react21.useRef)(null);
2114
+ const controllerRef = (0, import_react22.useRef)(null);
2008
2115
  if (!controllerRef.current) {
2009
2116
  controllerRef.current = new import_image_editor.ImageEditorController(options);
2010
2117
  }
2011
2118
  const controller = controllerRef.current;
2012
- const [state, setState] = (0, import_react21.useState)(() => controller.getState());
2013
- const [historyTicks, setHistoryTicks] = (0, import_react21.useState)(0);
2014
- (0, import_react21.useEffect)(() => {
2119
+ const [state, setState] = (0, import_react22.useState)(() => controller.getState());
2120
+ const [historyTicks, setHistoryTicks] = (0, import_react22.useState)(0);
2121
+ (0, import_react22.useEffect)(() => {
2015
2122
  const unsub = controller.subscribe((next) => {
2016
2123
  setState(next);
2017
2124
  setHistoryTicks((t) => t + 1);
2018
2125
  });
2019
2126
  return unsub;
2020
2127
  }, [controller]);
2021
- const loadedImageRef = (0, import_react21.useRef)(options.image);
2022
- (0, import_react21.useEffect)(() => {
2128
+ const loadedImageRef = (0, import_react22.useRef)(options.image);
2129
+ (0, import_react22.useEffect)(() => {
2023
2130
  if (options.image === loadedImageRef.current) return;
2024
2131
  loadedImageRef.current = options.image;
2025
2132
  if (options.image) {
2026
2133
  controller.loadImage(options.image);
2027
2134
  }
2028
2135
  }, [options.image, controller]);
2029
- const setTool = (0, import_react21.useCallback)((tool) => controller.setTool(tool), [controller]);
2030
- const setCrop = (0, import_react21.useCallback)(
2136
+ const setTool = (0, import_react22.useCallback)((tool) => controller.setTool(tool), [controller]);
2137
+ const setCrop = (0, import_react22.useCallback)(
2031
2138
  (crop, addToHistory = true) => controller.setCrop(crop, addToHistory),
2032
2139
  [controller]
2033
2140
  );
2034
- const applyCropPreset = (0, import_react21.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
2035
- const resetCrop = (0, import_react21.useCallback)(() => controller.resetCrop(), [controller]);
2036
- const rotate = (0, import_react21.useCallback)((deg) => controller.rotate(deg), [controller]);
2037
- const setRotation = (0, import_react21.useCallback)((deg) => controller.setRotation(deg), [controller]);
2038
- const flipHorizontal = (0, import_react21.useCallback)(() => controller.flipHorizontal(), [controller]);
2039
- const flipVertical = (0, import_react21.useCallback)(() => controller.flipVertical(), [controller]);
2040
- const setAdjustments = (0, import_react21.useCallback)(
2141
+ const applyCropPreset = (0, import_react22.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
2142
+ const resetCrop = (0, import_react22.useCallback)(() => controller.resetCrop(), [controller]);
2143
+ const rotate = (0, import_react22.useCallback)((deg) => controller.rotate(deg), [controller]);
2144
+ const setRotation = (0, import_react22.useCallback)((deg) => controller.setRotation(deg), [controller]);
2145
+ const flipHorizontal = (0, import_react22.useCallback)(() => controller.flipHorizontal(), [controller]);
2146
+ const flipVertical = (0, import_react22.useCallback)(() => controller.flipVertical(), [controller]);
2147
+ const setAdjustments = (0, import_react22.useCallback)(
2041
2148
  (partial, commitToHistory = true) => controller.setAdjustments(partial, commitToHistory),
2042
2149
  [controller]
2043
2150
  );
2044
- const resetAdjustments = (0, import_react21.useCallback)(() => controller.resetAdjustments(), [controller]);
2045
- const setDepthMask = (0, import_react21.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
2046
- const resetDepthMask = (0, import_react21.useCallback)(() => controller.resetDepthMask(), [controller]);
2047
- const addAnnotation = (0, import_react21.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
2048
- const updateAnnotation = (0, import_react21.useCallback)(
2151
+ const resetAdjustments = (0, import_react22.useCallback)(() => controller.resetAdjustments(), [controller]);
2152
+ const upgradeProcess = (0, import_react22.useCallback)(() => controller.upgradeProcess(), [controller]);
2153
+ const setDepthMask = (0, import_react22.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
2154
+ const resetDepthMask = (0, import_react22.useCallback)(() => controller.resetDepthMask(), [controller]);
2155
+ const addAnnotation = (0, import_react22.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
2156
+ const updateAnnotation = (0, import_react22.useCallback)(
2049
2157
  (id, partial, commitToHistory = true) => controller.updateAnnotation(id, partial, commitToHistory),
2050
2158
  [controller]
2051
2159
  );
2052
- const removeAnnotation = (0, import_react21.useCallback)((id) => controller.removeAnnotation(id), [controller]);
2053
- const clearAnnotations = (0, import_react21.useCallback)(() => controller.clearAnnotations(), [controller]);
2054
- const selectAnnotation = (0, import_react21.useCallback)((id) => controller.selectAnnotation(id), [controller]);
2055
- const undo = (0, import_react21.useCallback)(() => controller.undo(), [controller]);
2056
- const redo = (0, import_react21.useCallback)(() => controller.redo(), [controller]);
2057
- const reset = (0, import_react21.useCallback)(() => controller.reset(), [controller]);
2058
- const zoomIn = (0, import_react21.useCallback)((factor) => controller.zoomIn(factor), [controller]);
2059
- const zoomOut = (0, import_react21.useCallback)((factor) => controller.zoomOut(factor), [controller]);
2060
- const resetZoom = (0, import_react21.useCallback)(() => controller.resetZoom(), [controller]);
2061
- const setZoom = (0, import_react21.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
2062
- const setPan = (0, import_react21.useCallback)((pan) => controller.setPan(pan), [controller]);
2063
- const setComparing = (0, import_react21.useCallback)((comp) => controller.setComparing(comp), [controller]);
2064
- const loadImage = (0, import_react21.useCallback)((src) => controller.loadImage(src), [controller]);
2065
- const toDataURL = (0, import_react21.useCallback)((opts) => controller.toDataURL(opts), [controller]);
2066
- const toBlob = (0, import_react21.useCallback)((opts) => controller.toBlob(opts), [controller]);
2067
- const toJSON = (0, import_react21.useCallback)(() => controller.toJSON(), [controller]);
2068
- const loadJSON = (0, import_react21.useCallback)((data) => controller.loadJSON(data), [controller]);
2069
- const renderMask = (0, import_react21.useCallback)(
2160
+ const removeAnnotation = (0, import_react22.useCallback)((id) => controller.removeAnnotation(id), [controller]);
2161
+ const clearAnnotations = (0, import_react22.useCallback)(() => controller.clearAnnotations(), [controller]);
2162
+ const selectAnnotation = (0, import_react22.useCallback)((id) => controller.selectAnnotation(id), [controller]);
2163
+ const undo = (0, import_react22.useCallback)(() => controller.undo(), [controller]);
2164
+ const redo = (0, import_react22.useCallback)(() => controller.redo(), [controller]);
2165
+ const reset = (0, import_react22.useCallback)(() => controller.reset(), [controller]);
2166
+ const zoomIn = (0, import_react22.useCallback)((factor) => controller.zoomIn(factor), [controller]);
2167
+ const zoomOut = (0, import_react22.useCallback)((factor) => controller.zoomOut(factor), [controller]);
2168
+ const resetZoom = (0, import_react22.useCallback)(() => controller.resetZoom(), [controller]);
2169
+ const setZoom = (0, import_react22.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
2170
+ const setPan = (0, import_react22.useCallback)((pan) => controller.setPan(pan), [controller]);
2171
+ const setComparing = (0, import_react22.useCallback)((comp) => controller.setComparing(comp), [controller]);
2172
+ const loadImage = (0, import_react22.useCallback)((src) => controller.loadImage(src), [controller]);
2173
+ const toDataURL = (0, import_react22.useCallback)((opts) => controller.toDataURL(opts), [controller]);
2174
+ const toBlob = (0, import_react22.useCallback)((opts) => controller.toBlob(opts), [controller]);
2175
+ const toJSON = (0, import_react22.useCallback)(() => controller.toJSON(), [controller]);
2176
+ const loadJSON = (0, import_react22.useCallback)((data) => controller.loadJSON(data), [controller]);
2177
+ const renderMask = (0, import_react22.useCallback)(
2070
2178
  (opts) => controller.renderMask(opts),
2071
2179
  [controller]
2072
2180
  );
2073
- const toMaskDataURL = (0, import_react21.useCallback)(
2181
+ const toMaskDataURL = (0, import_react22.useCallback)(
2074
2182
  (opts) => controller.toMaskDataURL(opts),
2075
2183
  [controller]
2076
2184
  );
2077
- const toSourceDataURL = (0, import_react21.useCallback)(
2185
+ const toSourceDataURL = (0, import_react22.useCallback)(
2078
2186
  (opts) => controller.toSourceDataURL(opts),
2079
2187
  [controller]
2080
2188
  );
2081
- const applyInpaintedImage = (0, import_react21.useCallback)(
2189
+ const applyInpaintedImage = (0, import_react22.useCallback)(
2082
2190
  (newDataUrl) => controller.applyInpaintedImage(newDataUrl),
2083
2191
  [controller]
2084
2192
  );
2085
- const applyBackgroundRemovedImage = (0, import_react21.useCallback)(
2193
+ const applyBackgroundRemovedImage = (0, import_react22.useCallback)(
2086
2194
  (newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
2087
2195
  [controller]
2088
2196
  );
2089
- return (0, import_react21.useMemo)(
2197
+ return (0, import_react22.useMemo)(
2090
2198
  () => ({
2091
2199
  controller,
2092
2200
  state,
@@ -2102,6 +2210,7 @@ function useImageEditor(options = {}) {
2102
2210
  flipVertical,
2103
2211
  setAdjustments,
2104
2212
  resetAdjustments,
2213
+ upgradeProcess,
2105
2214
  setDepthMask,
2106
2215
  resetDepthMask,
2107
2216
  addAnnotation,
@@ -2135,7 +2244,7 @@ function useImageEditor(options = {}) {
2135
2244
  }
2136
2245
 
2137
2246
  // src/image-editor/ImageEditor.tsx
2138
- var import_react22 = require("react");
2247
+ var import_react23 = require("react");
2139
2248
  var import_image_editor2 = require("@pixerate/editor/image-editor");
2140
2249
  var import_jsx_runtime10 = require("react/jsx-runtime");
2141
2250
  var ImageEditor = ({
@@ -2152,16 +2261,17 @@ var ImageEditor = ({
2152
2261
  const internalEditor = useImageEditor({ image: src });
2153
2262
  const editor = externalEditor || internalEditor;
2154
2263
  const { state, controller } = editor;
2155
- const canvasRef = (0, import_react22.useRef)(null);
2156
- const containerRef = (0, import_react22.useRef)(null);
2157
- const [activeTab, setActiveTab] = (0, import_react22.useState)("crop");
2158
- const [activeCropRatio, setActiveCropRatio] = (0, import_react22.useState)(null);
2159
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react22.useState)("");
2160
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react22.useState)("annotations");
2161
- const [isRemovingBg, setIsRemovingBg] = (0, import_react22.useState)(false);
2162
- const [isInpainting, setIsInpainting] = (0, import_react22.useState)(false);
2163
- const [aiError, setAiError] = (0, import_react22.useState)(null);
2164
- const [aiSuccess, setAiSuccess] = (0, import_react22.useState)(null);
2264
+ const legacyProcess = (0, import_image_editor2.normalizeProcessVersion)(state.adjustments.process) < import_image_editor2.LATEST_PROCESS_VERSION;
2265
+ const canvasRef = (0, import_react23.useRef)(null);
2266
+ const containerRef = (0, import_react23.useRef)(null);
2267
+ const [activeTab, setActiveTab] = (0, import_react23.useState)("crop");
2268
+ const [activeCropRatio, setActiveCropRatio] = (0, import_react23.useState)(null);
2269
+ const [inpaintPrompt, setInpaintPrompt] = (0, import_react23.useState)("");
2270
+ const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react23.useState)("annotations");
2271
+ const [isRemovingBg, setIsRemovingBg] = (0, import_react23.useState)(false);
2272
+ const [isInpainting, setIsInpainting] = (0, import_react23.useState)(false);
2273
+ const [aiError, setAiError] = (0, import_react23.useState)(null);
2274
+ const [aiSuccess, setAiSuccess] = (0, import_react23.useState)(null);
2165
2275
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
2166
2276
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
2167
2277
  const handleRemoveBackground = async () => {
@@ -2223,18 +2333,18 @@ var ImageEditor = ({
2223
2333
  setIsInpainting(false);
2224
2334
  }
2225
2335
  };
2226
- const [activeColor, setActiveColor] = (0, import_react22.useState)("#ef4444");
2227
- const [strokeWidth, setStrokeWidth] = (0, import_react22.useState)(3);
2228
- const [fontSize, setFontSize] = (0, import_react22.useState)(24);
2229
- const [textInput, setTextInput] = (0, import_react22.useState)("");
2230
- const draftAnnotationRef = (0, import_react22.useRef)(null);
2231
- const draftCropRef = (0, import_react22.useRef)(null);
2232
- const cropDragInfoRef = (0, import_react22.useRef)(null);
2233
- const annotationDragInfoRef = (0, import_react22.useRef)(null);
2234
- const isDrawingRef = (0, import_react22.useRef)(false);
2235
- const currentPathRef = (0, import_react22.useRef)([]);
2236
- const dragStartPointRef = (0, import_react22.useRef)(null);
2237
- const renderCanvas = (0, import_react22.useCallback)(
2336
+ const [activeColor, setActiveColor] = (0, import_react23.useState)("#ef4444");
2337
+ const [strokeWidth, setStrokeWidth] = (0, import_react23.useState)(3);
2338
+ const [fontSize, setFontSize] = (0, import_react23.useState)(24);
2339
+ const [textInput, setTextInput] = (0, import_react23.useState)("");
2340
+ const draftAnnotationRef = (0, import_react23.useRef)(null);
2341
+ const draftCropRef = (0, import_react23.useRef)(null);
2342
+ const cropDragInfoRef = (0, import_react23.useRef)(null);
2343
+ const annotationDragInfoRef = (0, import_react23.useRef)(null);
2344
+ const isDrawingRef = (0, import_react23.useRef)(false);
2345
+ const currentPathRef = (0, import_react23.useRef)([]);
2346
+ const dragStartPointRef = (0, import_react23.useRef)(null);
2347
+ const renderCanvas = (0, import_react23.useCallback)(
2238
2348
  async (draftAnnotation, draftCrop) => {
2239
2349
  const canvas = canvasRef.current;
2240
2350
  const container = containerRef.current;
@@ -2253,7 +2363,7 @@ var ImageEditor = ({
2253
2363
  },
2254
2364
  [controller, activeTab]
2255
2365
  );
2256
- (0, import_react22.useEffect)(() => {
2366
+ (0, import_react23.useEffect)(() => {
2257
2367
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
2258
2368
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
2259
2369
  const animId = raf(() => {
@@ -2261,12 +2371,12 @@ var ImageEditor = ({
2261
2371
  });
2262
2372
  return () => caf(animId);
2263
2373
  }, [renderCanvas, state]);
2264
- (0, import_react22.useEffect)(() => {
2374
+ (0, import_react23.useEffect)(() => {
2265
2375
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
2266
2376
  editor.setTool("crop");
2267
2377
  }
2268
2378
  }, [activeTab, editor]);
2269
- (0, import_react22.useEffect)(() => {
2379
+ (0, import_react23.useEffect)(() => {
2270
2380
  const container = containerRef.current;
2271
2381
  if (!container) return;
2272
2382
  if (typeof ResizeObserver !== "undefined") {
@@ -3011,6 +3121,44 @@ var ImageEditor = ({
3011
3121
  }
3012
3122
  )
3013
3123
  ] }),
3124
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3125
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3126
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Tint" }),
3127
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.tint ?? 0 })
3128
+ ] }),
3129
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3130
+ "input",
3131
+ {
3132
+ type: "range",
3133
+ min: "-100",
3134
+ max: "100",
3135
+ "aria-label": "Tint",
3136
+ disabled: legacyProcess,
3137
+ value: state.adjustments.tint ?? 0,
3138
+ onChange: (e) => editor.setAdjustments({ tint: Number(e.target.value) }),
3139
+ className: "w-full accent-indigo-500 disabled:opacity-40"
3140
+ }
3141
+ )
3142
+ ] }),
3143
+ legacyProcess && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3144
+ "div",
3145
+ {
3146
+ className: "flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200",
3147
+ "data-testid": "image-editor-process-notice",
3148
+ children: [
3149
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Legacy rendering (process 1). Tint needs the current process." }),
3150
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3151
+ "button",
3152
+ {
3153
+ type: "button",
3154
+ onClick: () => editor.upgradeProcess(),
3155
+ className: "shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30",
3156
+ children: "Update"
3157
+ }
3158
+ )
3159
+ ]
3160
+ }
3161
+ ),
3014
3162
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3015
3163
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3016
3164
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Blur" }),
@@ -3406,8 +3554,8 @@ var ImageEditor = ({
3406
3554
  };
3407
3555
 
3408
3556
  // src/review/useReview.ts
3409
- var import_react23 = require("react");
3410
- var import_editor9 = require("@pixerate/editor");
3557
+ var import_react24 = require("react");
3558
+ var import_editor10 = require("@pixerate/editor");
3411
3559
  var EMPTY_COMMENTS = Object.freeze({
3412
3560
  threads: [],
3413
3561
  activeThreadId: null
@@ -3424,24 +3572,24 @@ function isCommentStore(value) {
3424
3572
  return !!value && typeof value.getSnapshot === "function" && typeof value.createThread === "function";
3425
3573
  }
3426
3574
  function useComments(source) {
3427
- const store = isCommentStore(source) ? source : (0, import_editor9.getCommentStore)(source);
3428
- const subscribe = (0, import_react23.useCallback)(
3575
+ const store = isCommentStore(source) ? source : (0, import_editor10.getCommentStore)(source);
3576
+ const subscribe = (0, import_react24.useCallback)(
3429
3577
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3430
3578
  [store]
3431
3579
  );
3432
3580
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
3433
- const snapshot = (0, import_react23.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3434
- return (0, import_react23.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
3581
+ const snapshot = (0, import_react24.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3582
+ return (0, import_react24.useMemo)(() => ({ ...snapshot, store }), [snapshot, store]);
3435
3583
  }
3436
3584
  function useTrackChanges(editor) {
3437
- const store = (0, import_editor9.getTrackChangesStore)(editor ?? null);
3438
- const subscribe = (0, import_react23.useCallback)(
3585
+ const store = (0, import_editor10.getTrackChangesStore)(editor ?? null);
3586
+ const subscribe = (0, import_react24.useCallback)(
3439
3587
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3440
3588
  [store]
3441
3589
  );
3442
3590
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
3443
- const snapshot = (0, import_react23.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3444
- return (0, import_react23.useMemo)(() => {
3591
+ const snapshot = (0, import_react24.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3592
+ return (0, import_react24.useMemo)(() => {
3445
3593
  const run = (fn) => () => {
3446
3594
  if (editor && !editor.isDestroyed) fn(editor);
3447
3595
  };
@@ -3461,8 +3609,8 @@ function useTrackChanges(editor) {
3461
3609
  }
3462
3610
 
3463
3611
  // src/review/CommentsPanel.tsx
3464
- var import_react24 = require("react");
3465
- var import_editor10 = require("@pixerate/editor");
3612
+ var import_react25 = require("react");
3613
+ var import_editor11 = require("@pixerate/editor");
3466
3614
  var import_jsx_runtime11 = require("react/jsx-runtime");
3467
3615
  var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
3468
3616
  var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
@@ -3473,12 +3621,12 @@ var CommentsPanel = ({
3473
3621
  author: authorProp,
3474
3622
  showResolved: showResolvedInitial = false,
3475
3623
  className = "flex flex-col gap-2 text-sm text-foreground",
3476
- formatTime = (t) => (0, import_editor10.formatReviewTime)(t),
3624
+ formatTime = (t) => (0, import_editor11.formatReviewTime)(t),
3477
3625
  emptyMessage = "No comments yet. Select text and add a comment."
3478
3626
  }) => {
3479
3627
  const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
3480
- const [showResolved, setShowResolved] = (0, import_react24.useState)(showResolvedInitial);
3481
- const author = authorProp ?? (0, import_editor10.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
3628
+ const [showResolved, setShowResolved] = (0, import_react25.useState)(showResolvedInitial);
3629
+ const author = authorProp ?? (0, import_editor11.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
3482
3630
  const visible = threads.filter((t) => showResolved || !t.resolved);
3483
3631
  const resolvedCount = threads.filter((t) => t.resolved).length;
3484
3632
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("aside", { className, "aria-label": "Comments", children: [
@@ -3521,7 +3669,7 @@ var CommentThreadCard = ({
3521
3669
  author,
3522
3670
  formatTime
3523
3671
  }) => {
3524
- const [draft, setDraft] = (0, import_react24.useState)("");
3672
+ const [draft, setDraft] = (0, import_react25.useState)("");
3525
3673
  const isDraft = thread.body === "";
3526
3674
  const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
3527
3675
  const live = editor && !editor.isDestroyed ? editor : null;
@@ -3631,10 +3779,13 @@ var CommentThreadCard = ({
3631
3779
 
3632
3780
  // src/review/ReviewToolbar.tsx
3633
3781
  var import_jsx_runtime12 = require("react/jsx-runtime");
3782
+ var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
3634
3783
  var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
3635
3784
  var ReviewToolbar = ({
3636
3785
  editor,
3637
- className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm"
3786
+ className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm",
3787
+ showDescription = true,
3788
+ descriptionClassName = defaultDescriptionClass
3638
3789
  }) => {
3639
3790
  const review = useTrackChanges(editor);
3640
3791
  const count = review.changes.length;
@@ -3676,6 +3827,18 @@ var ReviewToolbar = ({
3676
3827
  children: "\u203A"
3677
3828
  }
3678
3829
  ),
3830
+ showDescription && current && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3831
+ "span",
3832
+ {
3833
+ className: descriptionClassName,
3834
+ "data-change-type": current.type,
3835
+ title: `${current.author.name}: ${current.description}`,
3836
+ children: [
3837
+ current.author.name ? `${current.author.name}: ` : "",
3838
+ current.description
3839
+ ]
3840
+ }
3841
+ ),
3679
3842
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
3680
3843
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3681
3844
  "button",
@@ -3718,9 +3881,196 @@ var CommentButton = ({
3718
3881
  children
3719
3882
  }
3720
3883
  );
3884
+
3885
+ // src/commands/useCommandPalette.ts
3886
+ var import_react26 = require("react");
3887
+ var import_commands2 = require("@pixerate/editor/commands");
3888
+ function useCommandPalette(registry, options = {}) {
3889
+ const optionsRef = (0, import_react26.useRef)(options);
3890
+ optionsRef.current = options;
3891
+ const controller = (0, import_react26.useMemo)(
3892
+ () => (0, import_commands2.createCommandPaletteController)(registry, {
3893
+ context: () => {
3894
+ const c = optionsRef.current.context;
3895
+ if (c === void 0) return registry.context;
3896
+ return typeof c === "function" ? c() : c;
3897
+ },
3898
+ scope: () => {
3899
+ const s = optionsRef.current.scope;
3900
+ return typeof s === "function" ? s() : s;
3901
+ },
3902
+ recents: options.recents,
3903
+ recentLimit: options.recentLimit,
3904
+ limit: options.limit,
3905
+ platform: options.platform,
3906
+ onRecentsChange: (recents) => optionsRef.current.onRecentsChange?.(recents),
3907
+ onRun: (result) => optionsRef.current.onRun?.(result)
3908
+ }),
3909
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3910
+ [registry]
3911
+ );
3912
+ const state = (0, import_react26.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
3913
+ const { hotkey, hotkeyTarget } = options;
3914
+ (0, import_react26.useEffect)(() => {
3915
+ if (!hotkey) return;
3916
+ const target = hotkeyTarget ?? (typeof window !== "undefined" ? window : null);
3917
+ if (!target) return;
3918
+ const onKeyDown = (event) => {
3919
+ const e = event;
3920
+ if (e.defaultPrevented || !(0, import_commands2.matchPaletteHotkey)(e, hotkey, optionsRef.current.platform)) return;
3921
+ e.preventDefault();
3922
+ controller.toggle();
3923
+ };
3924
+ target.addEventListener("keydown", onKeyDown);
3925
+ return () => target.removeEventListener("keydown", onKeyDown);
3926
+ }, [controller, hotkey, hotkeyTarget]);
3927
+ return {
3928
+ ...state,
3929
+ controller,
3930
+ setOpen: controller.setOpen,
3931
+ toggle: controller.toggle,
3932
+ setQuery: controller.setQuery,
3933
+ setActiveIndex: controller.setActiveIndex,
3934
+ move: controller.move,
3935
+ run: controller.run,
3936
+ runActive: controller.runActive,
3937
+ handleKeyDown: controller.handleKeyDown
3938
+ };
3939
+ }
3940
+
3941
+ // src/commands/CommandPalette.tsx
3942
+ var import_react27 = __toESM(require("react"), 1);
3943
+ var import_jsx_runtime14 = require("react/jsx-runtime");
3944
+ function HighlightedLabel({ item }) {
3945
+ if (item.labelIndices.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: item.label });
3946
+ const marked = new Set(item.labelIndices);
3947
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: Array.from(item.label).map(
3948
+ (ch, i) => marked.has(i) ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-semibold text-foreground", "data-match": "", children: ch }, i) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react27.default.Fragment, { children: ch }, i)
3949
+ ) });
3950
+ }
3951
+ var CommandPalette = ({
3952
+ palette,
3953
+ placeholder = "Type a command\u2026",
3954
+ emptyMessage = "No matching commands",
3955
+ className = ""
3956
+ }) => {
3957
+ const inputRef = (0, import_react27.useRef)(null);
3958
+ const listRef = (0, import_react27.useRef)(null);
3959
+ const baseId = (0, import_react27.useId)();
3960
+ const listId = `${baseId}-list`;
3961
+ const optionId = (index) => `${baseId}-option-${index}`;
3962
+ const { open, query, items, activeIndex } = palette;
3963
+ (0, import_react27.useEffect)(() => {
3964
+ if (open) inputRef.current?.focus();
3965
+ }, [open]);
3966
+ (0, import_react27.useEffect)(() => {
3967
+ if (!open) return;
3968
+ const active = listRef.current?.querySelector('[aria-selected="true"]');
3969
+ active?.scrollIntoView?.({ block: "nearest" });
3970
+ }, [open, activeIndex]);
3971
+ if (!open) return null;
3972
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3973
+ "div",
3974
+ {
3975
+ className: "fixed inset-0 z-50 flex items-start justify-center bg-black/30 px-4 pt-[15vh]",
3976
+ "data-command-palette-backdrop": "",
3977
+ onMouseDown: (e) => {
3978
+ if (e.target === e.currentTarget) palette.setOpen(false);
3979
+ },
3980
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3981
+ "div",
3982
+ {
3983
+ role: "dialog",
3984
+ "aria-modal": "true",
3985
+ "aria-label": "Command palette",
3986
+ className: `w-full max-w-lg overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-xl ${className}`,
3987
+ style: { background: "var(--popover, var(--background, #fff))" },
3988
+ children: [
3989
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3990
+ "input",
3991
+ {
3992
+ ref: inputRef,
3993
+ type: "text",
3994
+ role: "combobox",
3995
+ "aria-expanded": "true",
3996
+ "aria-controls": listId,
3997
+ "aria-autocomplete": "list",
3998
+ "aria-activedescendant": items.length ? optionId(activeIndex) : void 0,
3999
+ "aria-label": "Search commands",
4000
+ placeholder,
4001
+ value: query,
4002
+ onChange: (e) => palette.setQuery(e.target.value),
4003
+ onKeyDown: (e) => {
4004
+ if (palette.handleKeyDown(e.nativeEvent)) e.stopPropagation();
4005
+ },
4006
+ className: "w-full border-b border-border bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
4007
+ }
4008
+ ),
4009
+ items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { ref: listRef, id: listId, role: "listbox", "aria-label": "Commands", className: "max-h-80 overflow-y-auto py-1", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
4010
+ "li",
4011
+ {
4012
+ id: optionId(index),
4013
+ role: "option",
4014
+ "aria-selected": index === activeIndex,
4015
+ "aria-disabled": !item.enabled || void 0,
4016
+ "data-command-id": item.id,
4017
+ "data-recent": item.recent ? "" : void 0,
4018
+ title: item.disabledReason,
4019
+ onMouseMove: () => index !== activeIndex && palette.setActiveIndex(index),
4020
+ onMouseDown: (e) => e.preventDefault(),
4021
+ onClick: () => palette.run(item.id),
4022
+ className: `flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm ${index === activeIndex ? "bg-muted" : ""} ${item.enabled ? "" : "cursor-default opacity-50"}`,
4023
+ children: [
4024
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
4025
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HighlightedLabel, { item }),
4026
+ item.disabledReason && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ml-2 text-xs text-muted-foreground", "data-disabled-reason": "", children: item.disabledReason })
4027
+ ] }),
4028
+ item.group && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.group }),
4029
+ item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("kbd", { className: "shrink-0 rounded border border-border px-1.5 font-mono text-[10px] text-muted-foreground", children: item.shortcut })
4030
+ ]
4031
+ },
4032
+ item.id
4033
+ )) })
4034
+ ]
4035
+ }
4036
+ )
4037
+ }
4038
+ );
4039
+ };
4040
+
4041
+ // src/commands/useCommandShortcuts.ts
4042
+ var import_react28 = require("react");
4043
+ function useCommandShortcuts(registry, options = {}) {
4044
+ const optionsRef = (0, import_react28.useRef)(options);
4045
+ optionsRef.current = options;
4046
+ const { enabled = true, target } = options;
4047
+ (0, import_react28.useEffect)(() => {
4048
+ if (!registry || !enabled) return;
4049
+ const eventTarget = target ?? (typeof window !== "undefined" ? window : null);
4050
+ if (!eventTarget) return;
4051
+ const onKeyDown = (event) => {
4052
+ const { scopes, context, allowInEditable, onRun } = optionsRef.current;
4053
+ const result = registry.handleKeyDown(event, {
4054
+ scopes,
4055
+ ...allowInEditable !== void 0 ? { allowInEditable } : {},
4056
+ ...context !== void 0 ? { context } : {}
4057
+ });
4058
+ if (result) onRun?.(result);
4059
+ };
4060
+ eventTarget.addEventListener("keydown", onKeyDown);
4061
+ return () => eventTarget.removeEventListener("keydown", onKeyDown);
4062
+ }, [registry, enabled, target]);
4063
+ }
4064
+ function useRegisterCommands(registry, specs) {
4065
+ (0, import_react28.useEffect)(() => {
4066
+ if (!registry || !specs || specs.length === 0) return;
4067
+ return registry.register(specs);
4068
+ }, [registry, specs]);
4069
+ }
3721
4070
  // Annotate the CommonJS export names for ESM import in node:
3722
4071
  0 && (module.exports = {
3723
4072
  BubbleMenu,
4073
+ CommandPalette,
3724
4074
  CommentButton,
3725
4075
  CommentsPanel,
3726
4076
  DirtyTracker,
@@ -3736,10 +4086,13 @@ var CommentButton = ({
3736
4086
  SpreadsheetGroupRow,
3737
4087
  TemplateRenderer,
3738
4088
  VariableForm,
4089
+ bindDirtyToHistory,
3739
4090
  createDirtyTracker,
3740
4091
  createHistoryManager,
3741
4092
  defaultIsEqual,
3742
4093
  useAgentToolProvider,
4094
+ useCommandPalette,
4095
+ useCommandShortcuts,
3743
4096
  useComments,
3744
4097
  useDismissGuard,
3745
4098
  useEditor,
@@ -3747,13 +4100,16 @@ var CommentButton = ({
3747
4100
  useFileImport,
3748
4101
  useFillHandle,
3749
4102
  useHistory,
4103
+ useHistoryDirty,
3750
4104
  useHistoryShortcuts,
3751
4105
  useImageEditor,
3752
4106
  useNavigationGuard,
3753
4107
  useOutline,
3754
4108
  usePromptEditor,
4109
+ useRegisterCommands,
3755
4110
  useSpreadsheetClipboard,
3756
4111
  useSpreadsheetEditor,
3757
- useTrackChanges
4112
+ useTrackChanges,
4113
+ withoutDirty
3758
4114
  });
3759
4115
  //# sourceMappingURL=index.cjs.map