@pixerate/editor-react 0.14.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 +2 -1
  2. package/dist/canvas/index.cjs +252 -119
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +55 -7
  5. package/dist/canvas/index.d.ts +55 -7
  6. package/dist/canvas/index.js +155 -19
  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-25S5EHH4.js → chunk-QVQYYHJ5.js} +4 -3
  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 +5 -3
  23. package/dist/image-editor/index.d.ts +5 -3
  24. package/dist/image-editor/index.js +1 -1
  25. package/dist/index.cjs +535 -251
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +61 -22
  28. package/dist/index.d.ts +61 -22
  29. package/dist/index.js +131 -70
  30. package/dist/index.js.map +1 -1
  31. package/dist/spreadsheet/index.cjs +1 -1
  32. package/dist/spreadsheet/index.cjs.map +1 -1
  33. package/dist/spreadsheet/index.d.cts +7 -7
  34. package/dist/spreadsheet/index.d.ts +7 -7
  35. package/dist/spreadsheet/index.js +1 -1
  36. package/package.json +10 -2
  37. package/dist/chunk-25S5EHH4.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,97 +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, setDraftState] = (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 setDraftValue = (0, import_react15.useCallback)((value) => {
969
+ const setDraftValue = (0, import_react16.useCallback)((value) => {
920
970
  controller.draftValue = value;
921
971
  setDraftState(value);
922
972
  }, [controller]);
923
- const setCellValue = (0, import_react15.useCallback)((rowId, colId, raw) => {
973
+ const setCellValue = (0, import_react16.useCallback)((rowId, colId, raw) => {
924
974
  controller.setCellValue(rowId, colId, raw);
925
975
  setDoc({ ...controller.document });
926
976
  }, [controller]);
927
- const selectCell = (0, import_react15.useCallback)((row, col, extendSelection = false) => {
977
+ const selectCell = (0, import_react16.useCallback)((row, col, extendSelection = false) => {
928
978
  controller.selectCell(row, col, extendSelection);
929
979
  setActiveCell(controller.activeCell);
930
980
  setSelectedRange(controller.selectedRange);
931
981
  }, [controller]);
932
- const selectRange = (0, import_react15.useCallback)((range) => {
982
+ const selectRange = (0, import_react16.useCallback)((range) => {
933
983
  controller.selectRange(range);
934
984
  setSelectedRange(controller.selectedRange);
935
985
  }, [controller]);
936
- const startEditing = (0, import_react15.useCallback)((row, col, initialVal) => {
986
+ const startEditing = (0, import_react16.useCallback)((row, col, initialVal) => {
937
987
  controller.startEditing(row, col, initialVal);
938
988
  setActiveCell(controller.activeCell);
939
989
  setEditingCell(controller.editingCell);
940
990
  setDraftValue(controller.draftValue);
941
991
  }, [controller]);
942
- const commitEditing = (0, import_react15.useCallback)((val) => {
992
+ const commitEditing = (0, import_react16.useCallback)((val) => {
943
993
  controller.commitEditing(val);
944
994
  setEditingCell(null);
945
995
  setDraftValue("");
946
996
  setDoc({ ...controller.document });
947
997
  }, [controller]);
948
- const cancelEditing = (0, import_react15.useCallback)(() => {
998
+ const cancelEditing = (0, import_react16.useCallback)(() => {
949
999
  controller.cancelEditing();
950
1000
  setEditingCell(null);
951
1001
  setDraftValue("");
952
1002
  }, [controller]);
953
- const moveCursor = (0, import_react15.useCallback)((dir, extend = false) => {
1003
+ const moveCursor = (0, import_react16.useCallback)((dir, extend = false) => {
954
1004
  controller.moveCursor(dir, extend);
955
1005
  setActiveCell(controller.activeCell);
956
1006
  setSelectedRange(controller.selectedRange);
957
1007
  }, [controller]);
958
- const insertColumn = (0, import_react15.useCallback)((index, config) => {
1008
+ const insertColumn = (0, import_react16.useCallback)((index, config) => {
959
1009
  controller.insertColumn(index, config);
960
1010
  setDoc({ ...controller.document });
961
1011
  }, [controller]);
962
- const deleteColumn = (0, import_react15.useCallback)((colId) => {
1012
+ const deleteColumn = (0, import_react16.useCallback)((colId) => {
963
1013
  controller.deleteColumn(colId);
964
1014
  setDoc({ ...controller.document });
965
1015
  }, [controller]);
966
- const insertRow = (0, import_react15.useCallback)((index, config) => {
1016
+ const insertRow = (0, import_react16.useCallback)((index, config) => {
967
1017
  controller.insertRow(index, config);
968
1018
  setDoc({ ...controller.document });
969
1019
  }, [controller]);
970
- const deleteRow = (0, import_react15.useCallback)((rowId) => {
1020
+ const deleteRow = (0, import_react16.useCallback)((rowId) => {
971
1021
  controller.deleteRow(rowId);
972
1022
  setDoc({ ...controller.document });
973
1023
  }, [controller]);
974
- const setColumnWidth = (0, import_react15.useCallback)((colId, width) => {
1024
+ const setColumnWidth = (0, import_react16.useCallback)((colId, width) => {
975
1025
  controller.setColumnWidth(colId, width);
976
1026
  setDoc({ ...controller.document });
977
1027
  }, [controller]);
978
- const autoFitColumnWidth = (0, import_react15.useCallback)((colId, customWidth) => {
1028
+ const autoFitColumnWidth = (0, import_react16.useCallback)((colId, customWidth) => {
979
1029
  controller.autoFitColumnWidth(colId, customWidth);
980
1030
  setDoc({ ...controller.document });
981
1031
  }, [controller]);
982
- const registerDataSource = (0, import_react15.useCallback)((ds) => {
1032
+ const registerDataSource = (0, import_react16.useCallback)((ds) => {
983
1033
  controller.registerDataSource(ds);
984
1034
  setDoc({ ...controller.document });
985
1035
  }, [controller]);
986
- const setPrimaryDataSource = (0, import_react15.useCallback)((id) => {
1036
+ const setPrimaryDataSource = (0, import_react16.useCallback)((id) => {
987
1037
  controller.setPrimaryDataSource(id);
988
1038
  setDoc({ ...controller.document });
989
1039
  }, [controller]);
990
- const recalculateAll = (0, import_react15.useCallback)(() => {
1040
+ const recalculateAll = (0, import_react16.useCallback)(() => {
991
1041
  controller.recalculateAll();
992
1042
  setDoc({ ...controller.document });
993
1043
  }, [controller]);
994
- const undo = (0, import_react15.useCallback)(() => {
1044
+ const undo = (0, import_react16.useCallback)(() => {
995
1045
  controller.undo();
996
1046
  setDoc({ ...controller.document });
997
1047
  }, [controller]);
998
- const redo = (0, import_react15.useCallback)(() => {
1048
+ const redo = (0, import_react16.useCallback)(() => {
999
1049
  controller.redo();
1000
1050
  setDoc({ ...controller.document });
1001
1051
  }, [controller]);
1002
- const tableActions = (0, import_react15.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
1003
- 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]);
1004
1054
  return {
1005
1055
  document: doc,
1006
1056
  activeCell,
@@ -1052,20 +1102,20 @@ function useSpreadsheetEditor(options = {}) {
1052
1102
  }
1053
1103
 
1054
1104
  // src/spreadsheet/SpreadsheetEditor.tsx
1055
- var import_react19 = require("react");
1105
+ var import_react20 = require("react");
1056
1106
  var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
1057
1107
 
1058
1108
  // src/spreadsheet/SpreadsheetContextMenu.tsx
1059
- var import_react16 = __toESM(require("react"), 1);
1109
+ var import_react17 = __toESM(require("react"), 1);
1060
1110
  var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
1061
1111
  var import_jsx_runtime5 = require("react/jsx-runtime");
1062
1112
  var VIEWPORT_MARGIN = 4;
1063
1113
  var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1064
- const menuRef = (0, import_react16.useRef)(null);
1065
- const onCloseRef = (0, import_react16.useRef)(onClose);
1114
+ const menuRef = (0, import_react17.useRef)(null);
1115
+ const onCloseRef = (0, import_react17.useRef)(onClose);
1066
1116
  onCloseRef.current = onClose;
1067
- const [position, setPosition] = (0, import_react16.useState)({ x, y });
1068
- (0, import_react16.useLayoutEffect)(() => {
1117
+ const [position, setPosition] = (0, import_react17.useState)({ x, y });
1118
+ (0, import_react17.useLayoutEffect)(() => {
1069
1119
  const menu = menuRef.current;
1070
1120
  if (!menu || typeof window === "undefined") return;
1071
1121
  const { width, height } = menu.getBoundingClientRect();
@@ -1077,7 +1127,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1077
1127
  );
1078
1128
  setPosition((prev) => prev.x === next.x && prev.y === next.y ? prev : next);
1079
1129
  }, [x, y, items]);
1080
- (0, import_react16.useEffect)(() => {
1130
+ (0, import_react17.useEffect)(() => {
1081
1131
  const handlePointer = (e) => {
1082
1132
  if (!menuRef.current?.contains(e.target)) onCloseRef.current();
1083
1133
  };
@@ -1107,7 +1157,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1107
1157
  },
1108
1158
  onContextMenu: (e) => e.preventDefault(),
1109
1159
  onKeyDown: (e) => e.stopPropagation(),
1110
- 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: [
1111
1161
  item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
1112
1162
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1113
1163
  "button",
@@ -1172,7 +1222,7 @@ var SpreadsheetGroupRow = ({
1172
1222
  };
1173
1223
 
1174
1224
  // src/spreadsheet/useSpreadsheetTableInteractions.ts
1175
- var import_react17 = require("react");
1225
+ var import_react18 = require("react");
1176
1226
  var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
1177
1227
  function gridHasFocus(grid) {
1178
1228
  if (!grid || typeof document === "undefined") return false;
@@ -1181,12 +1231,12 @@ function gridHasFocus(grid) {
1181
1231
  return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
1182
1232
  }
1183
1233
  function useSpreadsheetClipboard(gridRef, state, readOnly) {
1184
- const stateRef = (0, import_react17.useRef)(state);
1234
+ const stateRef = (0, import_react18.useRef)(state);
1185
1235
  stateRef.current = state;
1186
- const readOnlyRef = (0, import_react17.useRef)(readOnly);
1236
+ const readOnlyRef = (0, import_react18.useRef)(readOnly);
1187
1237
  readOnlyRef.current = readOnly;
1188
- const copiedRef = (0, import_react17.useRef)(false);
1189
- (0, import_react17.useEffect)(() => {
1238
+ const copiedRef = (0, import_react18.useRef)(false);
1239
+ (0, import_react18.useEffect)(() => {
1190
1240
  if (typeof document === "undefined") return;
1191
1241
  const handleCopy = (e) => {
1192
1242
  const s = stateRef.current;
@@ -1209,7 +1259,7 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
1209
1259
  document.removeEventListener("paste", handlePaste);
1210
1260
  };
1211
1261
  }, [gridRef]);
1212
- const copyShortcutFallback = (0, import_react17.useCallback)(() => {
1262
+ const copyShortcutFallback = (0, import_react18.useCallback)(() => {
1213
1263
  copiedRef.current = false;
1214
1264
  setTimeout(() => {
1215
1265
  if (copiedRef.current) return;
@@ -1221,13 +1271,13 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
1221
1271
  return { copyShortcutFallback };
1222
1272
  }
1223
1273
  function useFillHandle(state) {
1224
- const [drag, setDrag] = (0, import_react17.useState)(null);
1225
- const dragRef = (0, import_react17.useRef)(drag);
1274
+ const [drag, setDrag] = (0, import_react18.useState)(null);
1275
+ const dragRef = (0, import_react18.useRef)(drag);
1226
1276
  dragRef.current = drag;
1227
- const stateRef = (0, import_react17.useRef)(state);
1277
+ const stateRef = (0, import_react18.useRef)(state);
1228
1278
  stateRef.current = state;
1229
1279
  const dragging = drag !== null;
1230
- (0, import_react17.useEffect)(() => {
1280
+ (0, import_react18.useEffect)(() => {
1231
1281
  if (!dragging) return;
1232
1282
  const handleMouseUp = () => {
1233
1283
  const current = dragRef.current;
@@ -1237,13 +1287,13 @@ function useFillHandle(state) {
1237
1287
  window.addEventListener("mouseup", handleMouseUp);
1238
1288
  return () => window.removeEventListener("mouseup", handleMouseUp);
1239
1289
  }, [dragging]);
1240
- const startFill = (0, import_react17.useCallback)((e) => {
1290
+ const startFill = (0, import_react18.useCallback)((e) => {
1241
1291
  e.preventDefault();
1242
1292
  e.stopPropagation();
1243
1293
  const range = stateRef.current.selectedRange;
1244
1294
  if (range) setDrag({ source: (0, import_spreadsheet3.normalizeRange)(range), target: null });
1245
1295
  }, []);
1246
- const extendFill = (0, import_react17.useCallback)((row, col) => {
1296
+ const extendFill = (0, import_react18.useCallback)((row, col) => {
1247
1297
  const current = dragRef.current;
1248
1298
  if (!current) return;
1249
1299
  setDrag({ source: current.source, target: (0, import_spreadsheet3.computeFillTarget)(current.source, { row, col }) });
@@ -1252,24 +1302,24 @@ function useFillHandle(state) {
1252
1302
  }
1253
1303
 
1254
1304
  // src/spreadsheet/FormulaHints.tsx
1255
- var import_react18 = require("react");
1305
+ var import_react19 = require("react");
1256
1306
  var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
1257
1307
  var import_jsx_runtime7 = require("react/jsx-runtime");
1258
1308
  var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1259
- const [caret, setCaret] = (0, import_react18.useState)(null);
1260
- const [focused, setFocused] = (0, import_react18.useState)(false);
1261
- const [selected, setSelected] = (0, import_react18.useState)(0);
1262
- const [dismissed, setDismissed] = (0, import_react18.useState)(false);
1263
- const [position, setPosition] = (0, import_react18.useState)(null);
1264
- const pendingCaret = (0, import_react18.useRef)(null);
1265
- const hints = (0, import_react18.useMemo)(
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)(
1266
1316
  () => (0, import_spreadsheet4.getFormulaHints)(value, caret ?? value.length, { limit }),
1267
1317
  [value, caret, limit]
1268
1318
  );
1269
1319
  const suggestions = dismissed ? [] : hints.suggestions;
1270
1320
  const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
1271
1321
  const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
1272
- 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 });
1273
1323
  latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
1274
1324
  const accept = (index) => {
1275
1325
  const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
@@ -1279,7 +1329,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1279
1329
  pendingCaret.current = next.caret;
1280
1330
  commit(next.value);
1281
1331
  };
1282
- (0, import_react18.useEffect)(() => {
1332
+ (0, import_react19.useEffect)(() => {
1283
1333
  const input = inputRef.current;
1284
1334
  if (!input) return;
1285
1335
  const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
@@ -1329,7 +1379,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1329
1379
  input.removeEventListener("keydown", onKeyDown);
1330
1380
  };
1331
1381
  }, [inputRef]);
1332
- (0, import_react18.useLayoutEffect)(() => {
1382
+ (0, import_react19.useLayoutEffect)(() => {
1333
1383
  const input = inputRef.current;
1334
1384
  const at = pendingCaret.current;
1335
1385
  if (!input || at === null || input.value !== value) return;
@@ -1338,7 +1388,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1338
1388
  setCaret(at);
1339
1389
  setSelected(0);
1340
1390
  }, [value, inputRef]);
1341
- (0, import_react18.useLayoutEffect)(() => {
1391
+ (0, import_react19.useLayoutEffect)(() => {
1342
1392
  const input = inputRef.current;
1343
1393
  if (!visible || !input) return;
1344
1394
  const rect = input.getBoundingClientRect();
@@ -1439,23 +1489,23 @@ var SpreadsheetEditor = ({
1439
1489
  }) => {
1440
1490
  const defaultState = useSpreadsheetEditor();
1441
1491
  const state = externalState || defaultState;
1442
- const gridRef = (0, import_react19.useRef)(null);
1443
- const formulaBarRef = (0, import_react19.useRef)(null);
1444
- const cellInputRef = (0, import_react19.useRef)(null);
1445
- const [resizingColId, setResizingColId] = (0, import_react19.useState)(null);
1446
- const [hoveredColId, setHoveredColId] = (0, import_react19.useState)(null);
1447
- const [resizeStartX, setResizeStartX] = (0, import_react19.useState)(0);
1448
- 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);
1449
1499
  const view = state.viewModel;
1450
1500
  const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
1451
1501
  const { fillTarget, startFill, extendFill } = useFillHandle(state);
1452
- const [menu, setMenu] = (0, import_react19.useState)(null);
1502
+ const [menu, setMenu] = (0, import_react20.useState)(null);
1453
1503
  const filter = state.document.table?.filter;
1454
- const filteredColIds = (0, import_react19.useMemo)(
1504
+ const filteredColIds = (0, import_react20.useMemo)(
1455
1505
  () => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
1456
1506
  [filter]
1457
1507
  );
1458
- const handleCellContextMenu = (0, import_react19.useCallback)((rIdx, cIdx, e) => {
1508
+ const handleCellContextMenu = (0, import_react20.useCallback)((rIdx, cIdx, e) => {
1459
1509
  if (readOnly) return;
1460
1510
  e.preventDefault();
1461
1511
  if (state.editingCell) state.commitEditing();
@@ -1464,28 +1514,28 @@ var SpreadsheetEditor = ({
1464
1514
  }
1465
1515
  setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet5.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
1466
1516
  }, [readOnly, state]);
1467
- const activeRawValue = (0, import_react19.useMemo)(() => {
1517
+ const activeRawValue = (0, import_react20.useMemo)(() => {
1468
1518
  if (!state.activeCell) return "";
1469
1519
  const row = state.document.rows[state.activeCell.row];
1470
1520
  const col = state.document.columns[state.activeCell.col];
1471
1521
  if (!row || !col) return "";
1472
1522
  return state.getCell(row.id, col.id).raw;
1473
1523
  }, [state.activeCell, state.document]);
1474
- const activeSpill = (0, import_react19.useMemo)(
1524
+ const activeSpill = (0, import_react20.useMemo)(
1475
1525
  () => state.activeCell ? state.controller.getSpillAt(state.activeCell.row, state.activeCell.col) : null,
1476
1526
  // eslint-disable-next-line react-hooks/exhaustive-deps
1477
1527
  [state.controller, state.activeCell, state.document]
1478
1528
  );
1479
1529
  const spillFormula = activeSpill && !activeRawValue && activeSpill.key !== coordKey(state.activeCell) ? activeSpill.formula : "";
1480
- const coordLabel = (0, import_react19.useMemo)(() => {
1530
+ const coordLabel = (0, import_react20.useMemo)(() => {
1481
1531
  if (!state.activeCell) return "--";
1482
1532
  const colName = (0, import_spreadsheet5.indexToColName)(state.activeCell.col);
1483
1533
  return `${colName}${state.activeCell.row + 1}`;
1484
1534
  }, [state.activeCell]);
1485
- const [formulaDraft, setFormulaDraft] = (0, import_react19.useState)(null);
1535
+ const [formulaDraft, setFormulaDraft] = (0, import_react20.useState)(null);
1486
1536
  const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
1487
1537
  const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
1488
- (0, import_react19.useEffect)(() => {
1538
+ (0, import_react20.useEffect)(() => {
1489
1539
  setFormulaDraft(null);
1490
1540
  }, [state.activeCell?.row, state.activeCell?.col]);
1491
1541
  const commitFormulaBar = () => {
@@ -1501,19 +1551,19 @@ var SpreadsheetEditor = ({
1501
1551
  }
1502
1552
  setFormulaDraft(null);
1503
1553
  };
1504
- const handleCellClick = (0, import_react19.useCallback)((rIdx, cIdx, e) => {
1554
+ const handleCellClick = (0, import_react20.useCallback)((rIdx, cIdx, e) => {
1505
1555
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
1506
1556
  state.commitEditing();
1507
1557
  }
1508
1558
  state.selectCell(rIdx, cIdx, e.shiftKey);
1509
1559
  }, [state]);
1510
- const handleCellDblClick = (0, import_react19.useCallback)((rIdx, cIdx) => {
1560
+ const handleCellDblClick = (0, import_react20.useCallback)((rIdx, cIdx) => {
1511
1561
  if (readOnly) return;
1512
1562
  const col = state.document.columns[cIdx];
1513
1563
  if (col?.readOnly) return;
1514
1564
  state.startEditing(rIdx, cIdx);
1515
1565
  }, [readOnly, state]);
1516
- const handleKeyDown = (0, import_react19.useCallback)((e) => {
1566
+ const handleKeyDown = (0, import_react20.useCallback)((e) => {
1517
1567
  if (state.editingCell) {
1518
1568
  if (e.key === "Enter") {
1519
1569
  e.preventDefault();
@@ -1582,11 +1632,11 @@ var SpreadsheetEditor = ({
1582
1632
  }
1583
1633
  }
1584
1634
  }, [readOnly, state, copyShortcutFallback]);
1585
- const stateRef = (0, import_react19.useRef)(state);
1635
+ const stateRef = (0, import_react20.useRef)(state);
1586
1636
  stateRef.current = state;
1587
- const onColumnResizeRef = (0, import_react19.useRef)(onColumnResize);
1637
+ const onColumnResizeRef = (0, import_react20.useRef)(onColumnResize);
1588
1638
  onColumnResizeRef.current = onColumnResize;
1589
- (0, import_react19.useEffect)(() => {
1639
+ (0, import_react20.useEffect)(() => {
1590
1640
  if (!resizingColId) return;
1591
1641
  if (typeof document !== "undefined") {
1592
1642
  document.body.style.cursor = "col-resize";
@@ -1637,7 +1687,7 @@ var SpreadsheetEditor = ({
1637
1687
  window.removeEventListener("mouseup", handleMouseUp);
1638
1688
  };
1639
1689
  }, [resizingColId, resizeStartX, resizeStartWidth]);
1640
- const measureElementContentWidth = (0, import_react19.useCallback)((el, container) => {
1690
+ const measureElementContentWidth = (0, import_react20.useCallback)((el, container) => {
1641
1691
  if (typeof document === "undefined") return 0;
1642
1692
  let maxW = el.scrollWidth || 0;
1643
1693
  const firstChild = el.firstElementChild;
@@ -1685,7 +1735,7 @@ var SpreadsheetEditor = ({
1685
1735
  }
1686
1736
  return maxW;
1687
1737
  }, []);
1688
- const autoFitColumn = (0, import_react19.useCallback)((colId, colIndex) => {
1738
+ const autoFitColumn = (0, import_react20.useCallback)((colId, colIndex) => {
1689
1739
  const col = state.document.columns.find((c) => c.id === colId);
1690
1740
  if (!col) return;
1691
1741
  let calculatedWidth = 0;
@@ -1721,12 +1771,12 @@ var SpreadsheetEditor = ({
1721
1771
  onColumnResize(updatedCol.id, updatedCol.width || 130);
1722
1772
  }
1723
1773
  }, [state, measureElementContentWidth, onColumnResize]);
1724
- const handleResizeDblClick = (0, import_react19.useCallback)((colId, colIndex, e) => {
1774
+ const handleResizeDblClick = (0, import_react20.useCallback)((colId, colIndex, e) => {
1725
1775
  e.preventDefault();
1726
1776
  e.stopPropagation();
1727
1777
  autoFitColumn(colId, colIndex);
1728
1778
  }, [autoFitColumn]);
1729
- const handleHeaderDblClick = (0, import_react19.useCallback)((colId, colIndex, e) => {
1779
+ const handleHeaderDblClick = (0, import_react20.useCallback)((colId, colIndex, e) => {
1730
1780
  const target = e.currentTarget;
1731
1781
  if (target) {
1732
1782
  const rect = target.getBoundingClientRect();
@@ -1965,7 +2015,7 @@ var SpreadsheetEditor = ({
1965
2015
  {
1966
2016
  className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
1967
2017
  title: cell.error ? cell.errorMessage ?? cell.error : void 0,
1968
- children: cell.error || (cell.value ?? "")
2018
+ children: (0, import_spreadsheet5.formatCellDisplay)(cell)
1969
2019
  }
1970
2020
  ),
1971
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) }),
@@ -2026,7 +2076,7 @@ var SpreadsheetEditor = ({
2026
2076
  };
2027
2077
 
2028
2078
  // src/slots.tsx
2029
- var import_react20 = require("react");
2079
+ var import_react21 = require("react");
2030
2080
  var import_jsx_runtime9 = require("react/jsx-runtime");
2031
2081
  var defaultSlots = {
2032
2082
  Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -2048,102 +2098,103 @@ var defaultSlots = {
2048
2098
  props.onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
2049
2099
  ] })
2050
2100
  };
2051
- var EditorSlotsContext = (0, import_react20.createContext)(defaultSlots);
2101
+ var EditorSlotsContext = (0, import_react21.createContext)(defaultSlots);
2052
2102
  function EditorSlotsProvider({ slots, children }) {
2053
- const mergedSlots = (0, import_react20.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
2103
+ const mergedSlots = (0, import_react21.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
2054
2104
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
2055
2105
  }
2056
2106
  function useEditorSlots() {
2057
- return (0, import_react20.useContext)(EditorSlotsContext);
2107
+ return (0, import_react21.useContext)(EditorSlotsContext);
2058
2108
  }
2059
2109
 
2060
2110
  // src/image-editor/useImageEditor.ts
2061
- var import_react21 = require("react");
2111
+ var import_react22 = require("react");
2062
2112
  var import_image_editor = require("@pixerate/editor/image-editor");
2063
2113
  function useImageEditor(options = {}) {
2064
- const controllerRef = (0, import_react21.useRef)(null);
2114
+ const controllerRef = (0, import_react22.useRef)(null);
2065
2115
  if (!controllerRef.current) {
2066
2116
  controllerRef.current = new import_image_editor.ImageEditorController(options);
2067
2117
  }
2068
2118
  const controller = controllerRef.current;
2069
- const [state, setState] = (0, import_react21.useState)(() => controller.getState());
2070
- const [historyTicks, setHistoryTicks] = (0, import_react21.useState)(0);
2071
- (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)(() => {
2072
2122
  const unsub = controller.subscribe((next) => {
2073
2123
  setState(next);
2074
2124
  setHistoryTicks((t) => t + 1);
2075
2125
  });
2076
2126
  return unsub;
2077
2127
  }, [controller]);
2078
- const loadedImageRef = (0, import_react21.useRef)(options.image);
2079
- (0, import_react21.useEffect)(() => {
2128
+ const loadedImageRef = (0, import_react22.useRef)(options.image);
2129
+ (0, import_react22.useEffect)(() => {
2080
2130
  if (options.image === loadedImageRef.current) return;
2081
2131
  loadedImageRef.current = options.image;
2082
2132
  if (options.image) {
2083
2133
  controller.loadImage(options.image);
2084
2134
  }
2085
2135
  }, [options.image, controller]);
2086
- const setTool = (0, import_react21.useCallback)((tool) => controller.setTool(tool), [controller]);
2087
- 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)(
2088
2138
  (crop, addToHistory = true) => controller.setCrop(crop, addToHistory),
2089
2139
  [controller]
2090
2140
  );
2091
- const applyCropPreset = (0, import_react21.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
2092
- const resetCrop = (0, import_react21.useCallback)(() => controller.resetCrop(), [controller]);
2093
- const rotate = (0, import_react21.useCallback)((deg) => controller.rotate(deg), [controller]);
2094
- const setRotation = (0, import_react21.useCallback)((deg) => controller.setRotation(deg), [controller]);
2095
- const flipHorizontal = (0, import_react21.useCallback)(() => controller.flipHorizontal(), [controller]);
2096
- const flipVertical = (0, import_react21.useCallback)(() => controller.flipVertical(), [controller]);
2097
- 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)(
2098
2148
  (partial, commitToHistory = true) => controller.setAdjustments(partial, commitToHistory),
2099
2149
  [controller]
2100
2150
  );
2101
- const resetAdjustments = (0, import_react21.useCallback)(() => controller.resetAdjustments(), [controller]);
2102
- const setDepthMask = (0, import_react21.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
2103
- const resetDepthMask = (0, import_react21.useCallback)(() => controller.resetDepthMask(), [controller]);
2104
- const addAnnotation = (0, import_react21.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
2105
- 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)(
2106
2157
  (id, partial, commitToHistory = true) => controller.updateAnnotation(id, partial, commitToHistory),
2107
2158
  [controller]
2108
2159
  );
2109
- const removeAnnotation = (0, import_react21.useCallback)((id) => controller.removeAnnotation(id), [controller]);
2110
- const clearAnnotations = (0, import_react21.useCallback)(() => controller.clearAnnotations(), [controller]);
2111
- const selectAnnotation = (0, import_react21.useCallback)((id) => controller.selectAnnotation(id), [controller]);
2112
- const undo = (0, import_react21.useCallback)(() => controller.undo(), [controller]);
2113
- const redo = (0, import_react21.useCallback)(() => controller.redo(), [controller]);
2114
- const reset = (0, import_react21.useCallback)(() => controller.reset(), [controller]);
2115
- const zoomIn = (0, import_react21.useCallback)((factor) => controller.zoomIn(factor), [controller]);
2116
- const zoomOut = (0, import_react21.useCallback)((factor) => controller.zoomOut(factor), [controller]);
2117
- const resetZoom = (0, import_react21.useCallback)(() => controller.resetZoom(), [controller]);
2118
- const setZoom = (0, import_react21.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
2119
- const setPan = (0, import_react21.useCallback)((pan) => controller.setPan(pan), [controller]);
2120
- const setComparing = (0, import_react21.useCallback)((comp) => controller.setComparing(comp), [controller]);
2121
- const loadImage = (0, import_react21.useCallback)((src) => controller.loadImage(src), [controller]);
2122
- const toDataURL = (0, import_react21.useCallback)((opts) => controller.toDataURL(opts), [controller]);
2123
- const toBlob = (0, import_react21.useCallback)((opts) => controller.toBlob(opts), [controller]);
2124
- const toJSON = (0, import_react21.useCallback)(() => controller.toJSON(), [controller]);
2125
- const loadJSON = (0, import_react21.useCallback)((data) => controller.loadJSON(data), [controller]);
2126
- 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)(
2127
2178
  (opts) => controller.renderMask(opts),
2128
2179
  [controller]
2129
2180
  );
2130
- const toMaskDataURL = (0, import_react21.useCallback)(
2181
+ const toMaskDataURL = (0, import_react22.useCallback)(
2131
2182
  (opts) => controller.toMaskDataURL(opts),
2132
2183
  [controller]
2133
2184
  );
2134
- const toSourceDataURL = (0, import_react21.useCallback)(
2185
+ const toSourceDataURL = (0, import_react22.useCallback)(
2135
2186
  (opts) => controller.toSourceDataURL(opts),
2136
2187
  [controller]
2137
2188
  );
2138
- const applyInpaintedImage = (0, import_react21.useCallback)(
2189
+ const applyInpaintedImage = (0, import_react22.useCallback)(
2139
2190
  (newDataUrl) => controller.applyInpaintedImage(newDataUrl),
2140
2191
  [controller]
2141
2192
  );
2142
- const applyBackgroundRemovedImage = (0, import_react21.useCallback)(
2193
+ const applyBackgroundRemovedImage = (0, import_react22.useCallback)(
2143
2194
  (newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
2144
2195
  [controller]
2145
2196
  );
2146
- return (0, import_react21.useMemo)(
2197
+ return (0, import_react22.useMemo)(
2147
2198
  () => ({
2148
2199
  controller,
2149
2200
  state,
@@ -2159,6 +2210,7 @@ function useImageEditor(options = {}) {
2159
2210
  flipVertical,
2160
2211
  setAdjustments,
2161
2212
  resetAdjustments,
2213
+ upgradeProcess,
2162
2214
  setDepthMask,
2163
2215
  resetDepthMask,
2164
2216
  addAnnotation,
@@ -2192,7 +2244,7 @@ function useImageEditor(options = {}) {
2192
2244
  }
2193
2245
 
2194
2246
  // src/image-editor/ImageEditor.tsx
2195
- var import_react22 = require("react");
2247
+ var import_react23 = require("react");
2196
2248
  var import_image_editor2 = require("@pixerate/editor/image-editor");
2197
2249
  var import_jsx_runtime10 = require("react/jsx-runtime");
2198
2250
  var ImageEditor = ({
@@ -2209,16 +2261,17 @@ var ImageEditor = ({
2209
2261
  const internalEditor = useImageEditor({ image: src });
2210
2262
  const editor = externalEditor || internalEditor;
2211
2263
  const { state, controller } = editor;
2212
- const canvasRef = (0, import_react22.useRef)(null);
2213
- const containerRef = (0, import_react22.useRef)(null);
2214
- const [activeTab, setActiveTab] = (0, import_react22.useState)("crop");
2215
- const [activeCropRatio, setActiveCropRatio] = (0, import_react22.useState)(null);
2216
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react22.useState)("");
2217
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react22.useState)("annotations");
2218
- const [isRemovingBg, setIsRemovingBg] = (0, import_react22.useState)(false);
2219
- const [isInpainting, setIsInpainting] = (0, import_react22.useState)(false);
2220
- const [aiError, setAiError] = (0, import_react22.useState)(null);
2221
- 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);
2222
2275
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
2223
2276
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
2224
2277
  const handleRemoveBackground = async () => {
@@ -2280,18 +2333,18 @@ var ImageEditor = ({
2280
2333
  setIsInpainting(false);
2281
2334
  }
2282
2335
  };
2283
- const [activeColor, setActiveColor] = (0, import_react22.useState)("#ef4444");
2284
- const [strokeWidth, setStrokeWidth] = (0, import_react22.useState)(3);
2285
- const [fontSize, setFontSize] = (0, import_react22.useState)(24);
2286
- const [textInput, setTextInput] = (0, import_react22.useState)("");
2287
- const draftAnnotationRef = (0, import_react22.useRef)(null);
2288
- const draftCropRef = (0, import_react22.useRef)(null);
2289
- const cropDragInfoRef = (0, import_react22.useRef)(null);
2290
- const annotationDragInfoRef = (0, import_react22.useRef)(null);
2291
- const isDrawingRef = (0, import_react22.useRef)(false);
2292
- const currentPathRef = (0, import_react22.useRef)([]);
2293
- const dragStartPointRef = (0, import_react22.useRef)(null);
2294
- 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)(
2295
2348
  async (draftAnnotation, draftCrop) => {
2296
2349
  const canvas = canvasRef.current;
2297
2350
  const container = containerRef.current;
@@ -2310,7 +2363,7 @@ var ImageEditor = ({
2310
2363
  },
2311
2364
  [controller, activeTab]
2312
2365
  );
2313
- (0, import_react22.useEffect)(() => {
2366
+ (0, import_react23.useEffect)(() => {
2314
2367
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
2315
2368
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
2316
2369
  const animId = raf(() => {
@@ -2318,12 +2371,12 @@ var ImageEditor = ({
2318
2371
  });
2319
2372
  return () => caf(animId);
2320
2373
  }, [renderCanvas, state]);
2321
- (0, import_react22.useEffect)(() => {
2374
+ (0, import_react23.useEffect)(() => {
2322
2375
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
2323
2376
  editor.setTool("crop");
2324
2377
  }
2325
2378
  }, [activeTab, editor]);
2326
- (0, import_react22.useEffect)(() => {
2379
+ (0, import_react23.useEffect)(() => {
2327
2380
  const container = containerRef.current;
2328
2381
  if (!container) return;
2329
2382
  if (typeof ResizeObserver !== "undefined") {
@@ -3068,6 +3121,44 @@ var ImageEditor = ({
3068
3121
  }
3069
3122
  )
3070
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
+ ),
3071
3162
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3072
3163
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3073
3164
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Blur" }),
@@ -3463,8 +3554,8 @@ var ImageEditor = ({
3463
3554
  };
3464
3555
 
3465
3556
  // src/review/useReview.ts
3466
- var import_react23 = require("react");
3467
- var import_editor9 = require("@pixerate/editor");
3557
+ var import_react24 = require("react");
3558
+ var import_editor10 = require("@pixerate/editor");
3468
3559
  var EMPTY_COMMENTS = Object.freeze({
3469
3560
  threads: [],
3470
3561
  activeThreadId: null
@@ -3481,24 +3572,24 @@ function isCommentStore(value) {
3481
3572
  return !!value && typeof value.getSnapshot === "function" && typeof value.createThread === "function";
3482
3573
  }
3483
3574
  function useComments(source) {
3484
- const store = isCommentStore(source) ? source : (0, import_editor9.getCommentStore)(source);
3485
- const subscribe = (0, import_react23.useCallback)(
3575
+ const store = isCommentStore(source) ? source : (0, import_editor10.getCommentStore)(source);
3576
+ const subscribe = (0, import_react24.useCallback)(
3486
3577
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3487
3578
  [store]
3488
3579
  );
3489
3580
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
3490
- const snapshot = (0, import_react23.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3491
- 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]);
3492
3583
  }
3493
3584
  function useTrackChanges(editor) {
3494
- const store = (0, import_editor9.getTrackChangesStore)(editor ?? null);
3495
- const subscribe = (0, import_react23.useCallback)(
3585
+ const store = (0, import_editor10.getTrackChangesStore)(editor ?? null);
3586
+ const subscribe = (0, import_react24.useCallback)(
3496
3587
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3497
3588
  [store]
3498
3589
  );
3499
3590
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
3500
- const snapshot = (0, import_react23.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3501
- return (0, import_react23.useMemo)(() => {
3591
+ const snapshot = (0, import_react24.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3592
+ return (0, import_react24.useMemo)(() => {
3502
3593
  const run = (fn) => () => {
3503
3594
  if (editor && !editor.isDestroyed) fn(editor);
3504
3595
  };
@@ -3518,8 +3609,8 @@ function useTrackChanges(editor) {
3518
3609
  }
3519
3610
 
3520
3611
  // src/review/CommentsPanel.tsx
3521
- var import_react24 = require("react");
3522
- var import_editor10 = require("@pixerate/editor");
3612
+ var import_react25 = require("react");
3613
+ var import_editor11 = require("@pixerate/editor");
3523
3614
  var import_jsx_runtime11 = require("react/jsx-runtime");
3524
3615
  var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
3525
3616
  var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
@@ -3530,12 +3621,12 @@ var CommentsPanel = ({
3530
3621
  author: authorProp,
3531
3622
  showResolved: showResolvedInitial = false,
3532
3623
  className = "flex flex-col gap-2 text-sm text-foreground",
3533
- formatTime = (t) => (0, import_editor10.formatReviewTime)(t),
3624
+ formatTime = (t) => (0, import_editor11.formatReviewTime)(t),
3534
3625
  emptyMessage = "No comments yet. Select text and add a comment."
3535
3626
  }) => {
3536
3627
  const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
3537
- const [showResolved, setShowResolved] = (0, import_react24.useState)(showResolvedInitial);
3538
- 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" };
3539
3630
  const visible = threads.filter((t) => showResolved || !t.resolved);
3540
3631
  const resolvedCount = threads.filter((t) => t.resolved).length;
3541
3632
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("aside", { className, "aria-label": "Comments", children: [
@@ -3578,7 +3669,7 @@ var CommentThreadCard = ({
3578
3669
  author,
3579
3670
  formatTime
3580
3671
  }) => {
3581
- const [draft, setDraft] = (0, import_react24.useState)("");
3672
+ const [draft, setDraft] = (0, import_react25.useState)("");
3582
3673
  const isDraft = thread.body === "";
3583
3674
  const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
3584
3675
  const live = editor && !editor.isDestroyed ? editor : null;
@@ -3790,9 +3881,196 @@ var CommentButton = ({
3790
3881
  children
3791
3882
  }
3792
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
+ }
3793
4070
  // Annotate the CommonJS export names for ESM import in node:
3794
4071
  0 && (module.exports = {
3795
4072
  BubbleMenu,
4073
+ CommandPalette,
3796
4074
  CommentButton,
3797
4075
  CommentsPanel,
3798
4076
  DirtyTracker,
@@ -3808,10 +4086,13 @@ var CommentButton = ({
3808
4086
  SpreadsheetGroupRow,
3809
4087
  TemplateRenderer,
3810
4088
  VariableForm,
4089
+ bindDirtyToHistory,
3811
4090
  createDirtyTracker,
3812
4091
  createHistoryManager,
3813
4092
  defaultIsEqual,
3814
4093
  useAgentToolProvider,
4094
+ useCommandPalette,
4095
+ useCommandShortcuts,
3815
4096
  useComments,
3816
4097
  useDismissGuard,
3817
4098
  useEditor,
@@ -3819,13 +4100,16 @@ var CommentButton = ({
3819
4100
  useFileImport,
3820
4101
  useFillHandle,
3821
4102
  useHistory,
4103
+ useHistoryDirty,
3822
4104
  useHistoryShortcuts,
3823
4105
  useImageEditor,
3824
4106
  useNavigationGuard,
3825
4107
  useOutline,
3826
4108
  usePromptEditor,
4109
+ useRegisterCommands,
3827
4110
  useSpreadsheetClipboard,
3828
4111
  useSpreadsheetEditor,
3829
- useTrackChanges
4112
+ useTrackChanges,
4113
+ withoutDirty
3830
4114
  });
3831
4115
  //# sourceMappingURL=index.cjs.map