@pixerate/editor-react 0.10.5 → 0.12.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.
package/dist/index.cjs CHANGED
@@ -31,25 +31,40 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  BubbleMenu: () => BubbleMenu,
34
- DirtyTracker: () => import_editor7.DirtyTracker,
34
+ CommentButton: () => CommentButton,
35
+ CommentsPanel: () => CommentsPanel,
36
+ DirtyTracker: () => import_editor15.DirtyTracker,
35
37
  EditorContent: () => EditorContent,
36
38
  EditorSlotsProvider: () => EditorSlotsProvider,
37
- HistoryManager: () => import_editor7.HistoryManager,
39
+ FormulaHints: () => FormulaHints,
40
+ HistoryManager: () => import_editor15.HistoryManager,
38
41
  ImageEditor: () => ImageEditor,
42
+ OutlinePanel: () => OutlinePanel,
43
+ ReviewToolbar: () => ReviewToolbar,
44
+ SpreadsheetContextMenu: () => SpreadsheetContextMenu,
39
45
  SpreadsheetEditor: () => SpreadsheetEditor,
46
+ SpreadsheetGroupRow: () => SpreadsheetGroupRow,
40
47
  TemplateRenderer: () => TemplateRenderer,
41
- createDirtyTracker: () => import_editor7.createDirtyTracker,
42
- createHistoryManager: () => import_editor7.createHistoryManager,
43
- defaultIsEqual: () => import_editor7.defaultIsEqual,
48
+ VariableForm: () => VariableForm,
49
+ createDirtyTracker: () => import_editor15.createDirtyTracker,
50
+ createHistoryManager: () => import_editor15.createHistoryManager,
51
+ defaultIsEqual: () => import_editor15.defaultIsEqual,
52
+ useAgentToolProvider: () => useAgentToolProvider,
53
+ useComments: () => useComments,
44
54
  useDismissGuard: () => useDismissGuard,
45
55
  useEditor: () => useEditor2,
46
56
  useEditorSlots: () => useEditorSlots,
57
+ useFileImport: () => useFileImport,
58
+ useFillHandle: () => useFillHandle,
47
59
  useHistory: () => useHistory,
48
60
  useHistoryShortcuts: () => useHistoryShortcuts,
49
61
  useImageEditor: () => useImageEditor,
50
62
  useNavigationGuard: () => useNavigationGuard,
63
+ useOutline: () => useOutline,
51
64
  usePromptEditor: () => usePromptEditor,
52
- useSpreadsheetEditor: () => useSpreadsheetEditor
65
+ useSpreadsheetClipboard: () => useSpreadsheetClipboard,
66
+ useSpreadsheetEditor: () => useSpreadsheetEditor,
67
+ useTrackChanges: () => useTrackChanges
53
68
  });
54
69
  module.exports = __toCommonJS(src_exports);
55
70
 
@@ -83,8 +98,12 @@ function usePromptEditor({
83
98
  onMilestone,
84
99
  containerId = "editor-container",
85
100
  jsonVariables,
86
- speed = 2
101
+ speed = 2,
102
+ fileImport
87
103
  }) {
104
+ const fileImportRef = (0, import_react.useRef)(fileImport);
105
+ fileImportRef.current = fileImport;
106
+ const fileImportEnabled = !!fileImport;
88
107
  const slashCommandActiveRef = (0, import_react.useRef)(slashCommandActive);
89
108
  slashCommandActiveRef.current = slashCommandActive;
90
109
  const clearSlashCommandRef = (0, import_react.useRef)(clearSlashCommand);
@@ -139,7 +158,14 @@ function usePromptEditor({
139
158
  import_editor.TemplateSuggestions.configure({
140
159
  templates
141
160
  }),
142
- import_editor.LoadingNode
161
+ import_editor.LoadingNode,
162
+ ...fileImportEnabled ? [
163
+ import_editor.FileImport.configure({
164
+ ...typeof fileImportRef.current === "object" ? fileImportRef.current : {},
165
+ // Prompt editors only ever receive plain text.
166
+ asPlainText: true
167
+ })
168
+ ] : []
143
169
  ],
144
170
  content: (0, import_editor.plainTextToTipTapHtml)(content),
145
171
  onUpdate: ({ editor: ed }) => {
@@ -236,7 +262,7 @@ function usePromptEditor({
236
262
  },
237
263
  // Only options baked into extensions recreate the editor; editable state,
238
264
  // placeholder, class names and content are applied to the live instance.
239
- [resolvedColorMap, templateKey, speed, jsonVariablesKey]
265
+ [resolvedColorMap, templateKey, speed, jsonVariablesKey, fileImportEnabled]
240
266
  );
241
267
  (0, import_react.useEffect)(() => {
242
268
  if (editor && !editor.isDestroyed) {
@@ -284,8 +310,18 @@ function usePromptEditor({
284
310
  var import_react3 = require("@tiptap/react");
285
311
  var import_editor2 = require("@pixerate/editor");
286
312
  function useEditor2(options = {}) {
287
- const { richTextOptions, extensions = [], ...editorOptions } = options;
288
- const preset = (0, import_editor2.createRichTextPreset)(richTextOptions);
313
+ const {
314
+ richTextOptions,
315
+ fileImport,
316
+ richClipboard,
317
+ extensions = [],
318
+ ...editorOptions
319
+ } = options;
320
+ const preset = (0, import_editor2.createRichTextPreset)({
321
+ ...fileImport !== void 0 ? { fileImport } : {},
322
+ ...richClipboard !== void 0 ? { richClipboard } : {},
323
+ ...richTextOptions
324
+ });
289
325
  const combinedExtensions = [...preset, ...extensions];
290
326
  return (0, import_react3.useEditor)({
291
327
  extensions: combinedExtensions,
@@ -512,19 +548,53 @@ function useHistoryShortcuts(manager, options = {}) {
512
548
  }, [manager, enabled, target, ignoreEditableElements]);
513
549
  }
514
550
 
551
+ // src/hooks/useFileImport.ts
552
+ var import_react8 = require("react");
553
+ var import_editor4 = require("@pixerate/editor");
554
+ function useFileImport(editor, options = {}) {
555
+ const editorRef = (0, import_react8.useRef)(editor);
556
+ editorRef.current = editor;
557
+ const controller = (0, import_react8.useMemo)(
558
+ () => new import_editor4.FileImportController(() => {
559
+ const current = editorRef.current;
560
+ return current && !current.isDestroyed ? current : null;
561
+ }, options),
562
+ []
563
+ );
564
+ (0, import_react8.useEffect)(() => {
565
+ controller.setOptions(options);
566
+ });
567
+ const state = (0, import_react8.useSyncExternalStore)(controller.subscribe, controller.getState, controller.getState);
568
+ return {
569
+ ...state,
570
+ accept: controller.accept,
571
+ openFilePicker: controller.openFilePicker,
572
+ importFiles: controller.importFiles
573
+ };
574
+ }
575
+
576
+ // src/hooks/useAgentToolProvider.ts
577
+ var import_react9 = require("react");
578
+ function useAgentToolProvider(registry, provider) {
579
+ (0, import_react9.useEffect)(() => {
580
+ if (!registry || !provider) return;
581
+ return registry.register(provider);
582
+ }, [registry, provider]);
583
+ }
584
+
515
585
  // src/components/EditorContent.tsx
516
- var import_react8 = require("@tiptap/react");
586
+ var import_react10 = require("@tiptap/react");
517
587
  var import_jsx_runtime = require("react/jsx-runtime");
518
588
  var EditorContent = ({
519
589
  className,
520
590
  editor,
521
591
  ...props
522
592
  }) => {
523
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react8.EditorContent, { editor, ...props }) });
593
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react10.EditorContent, { editor, ...props }) });
524
594
  };
525
595
 
526
596
  // src/components/BubbleMenu.tsx
527
- var import_react9 = require("react");
597
+ var import_react11 = require("react");
528
598
  var import_react_dom = require("react-dom");
529
599
  var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
530
600
  var BubbleMenu = ({
@@ -536,12 +606,12 @@ var BubbleMenu = ({
536
606
  pluginKey = "bubbleMenu",
537
607
  updateDelay
538
608
  }) => {
539
- const menuEl = (0, import_react9.useRef)(null);
540
- const [mounted, setMounted] = (0, import_react9.useState)(false);
609
+ const menuEl = (0, import_react11.useRef)(null);
610
+ const [mounted, setMounted] = (0, import_react11.useState)(false);
541
611
  if (!menuEl.current && typeof document !== "undefined") {
542
612
  menuEl.current = document.createElement("div");
543
613
  }
544
- (0, import_react9.useEffect)(() => {
614
+ (0, import_react11.useEffect)(() => {
545
615
  if (!editor || !menuEl.current || editor.isDestroyed) {
546
616
  return;
547
617
  }
@@ -572,25 +642,28 @@ var BubbleMenu = ({
572
642
  };
573
643
 
574
644
  // src/components/TemplateRenderer.tsx
575
- var import_react10 = require("react");
576
- var import_editor4 = require("@pixerate/editor");
645
+ var import_react12 = require("react");
646
+ var import_editor5 = require("@pixerate/editor");
577
647
  var import_jsx_runtime2 = require("react/jsx-runtime");
648
+ var CONDITIONAL_PREFIX = { if: "?", unless: "!", end: "/" };
578
649
  var TemplateRenderer = ({
579
650
  content,
580
651
  templates = [],
581
652
  templateColorMap,
582
653
  resolve = false,
654
+ bindings,
655
+ values,
583
656
  className = "font-mono text-sm leading-relaxed whitespace-pre-wrap break-words",
584
657
  onTemplateClick,
585
658
  onVariableClick
586
659
  }) => {
587
- const displayText = (0, import_react10.useMemo)(() => {
660
+ const displayText = (0, import_react12.useMemo)(() => {
588
661
  if (!resolve || templates.length === 0) return content;
589
- return (0, import_editor4.resolveTemplates)(content, templates);
590
- }, [content, templates, resolve]);
591
- const tokens = (0, import_react10.useMemo)(() => {
592
- return (0, import_editor4.tokenizePrompt)(displayText);
593
- }, [displayText]);
662
+ return (0, import_editor5.resolveTemplates)(content, templates, { bindings });
663
+ }, [content, templates, resolve, bindings]);
664
+ const tokens = (0, import_react12.useMemo)(() => {
665
+ return (0, import_editor5.evaluatePromptTokens)(displayText, values);
666
+ }, [displayText, values]);
594
667
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, children: tokens.map((token, index) => {
595
668
  if (token.type === "template") {
596
669
  const colors = templateColorMap?.get(token.value) || {
@@ -614,13 +687,49 @@ var TemplateRenderer = ({
614
687
  );
615
688
  }
616
689
  if (token.type === "variable") {
617
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
690
+ const details = [
691
+ `Variable: ${token.value}`,
692
+ token.description,
693
+ token.defaultValue !== void 0 ? `Default: ${token.defaultValue || "(empty)"}` : void 0
694
+ ].filter(Boolean).join("\n");
695
+ if (token.resolvedValue !== void 0) {
696
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
697
+ "span",
698
+ {
699
+ onClick: () => onVariableClick?.(token.value),
700
+ "data-variable": token.value,
701
+ "data-value-source": token.valueSource,
702
+ className: token.valueSource === "value" ? "inline px-1 mx-0.5 rounded-md bg-emerald-400/15 text-emerald-700 dark:text-emerald-300 border-b border-emerald-400/60 cursor-pointer" : "inline px-1 mx-0.5 rounded-md bg-violet-400/10 text-violet-600 dark:text-violet-300 italic border-b border-dashed border-violet-400/60 cursor-pointer",
703
+ title: details,
704
+ children: token.resolvedValue
705
+ },
706
+ index
707
+ );
708
+ }
709
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
618
710
  "span",
619
711
  {
620
712
  onClick: () => onVariableClick?.(token.value),
713
+ "data-variable": token.value,
621
714
  className: "inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-violet-400/40 bg-violet-400/15 text-violet-600 dark:text-violet-300 hover:brightness-110 cursor-pointer transition-all",
622
- title: `Variable: ${token.value}`,
623
- children: `{${token.value}}`
715
+ title: details,
716
+ children: [
717
+ `{${token.value}`,
718
+ token.defaultValue !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "opacity-60", children: `|${token.defaultValue}` }),
719
+ "}"
720
+ ]
721
+ },
722
+ index
723
+ );
724
+ }
725
+ if (token.type === "conditional" && token.conditional) {
726
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
727
+ "span",
728
+ {
729
+ "data-conditional": token.conditional,
730
+ className: "inline-flex items-center px-1 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-amber-400/40 bg-amber-400/15 text-amber-700 dark:text-amber-300",
731
+ title: token.conditional === "end" ? `End of section: ${token.value}` : token.conditional === "if" ? `Shown when "${token.value}" has a value` : `Shown when "${token.value}" is empty`,
732
+ children: `{${CONDITIONAL_PREFIX[token.conditional]}${token.value}}`
624
733
  },
625
734
  index
626
735
  );
@@ -640,18 +749,152 @@ var TemplateRenderer = ({
640
749
  }) });
641
750
  };
642
751
 
643
- // src/index.ts
752
+ // src/components/VariableForm.tsx
753
+ var import_react13 = require("react");
754
+ var import_editor6 = require("@pixerate/editor");
755
+ var import_jsx_runtime3 = require("react/jsx-runtime");
756
+ var VariableForm = ({
757
+ content,
758
+ templates,
759
+ bindings,
760
+ values,
761
+ defaultValues,
762
+ onChange,
763
+ className = "flex flex-col gap-3 text-sm"
764
+ }) => {
765
+ const idPrefix = (0, import_react13.useId)();
766
+ const [internal, setInternal] = (0, import_react13.useState)(
767
+ () => defaultValues ?? {}
768
+ );
769
+ const entered = values ?? internal;
770
+ const fields = (0, import_react13.useMemo)(
771
+ () => (0, import_editor6.getVariableFormFields)(content, { templates, bindings }),
772
+ [content, templates, bindings]
773
+ );
774
+ const update = (name, value) => {
775
+ const next = { ...entered, [name]: value };
776
+ if (values === void 0) setInternal(next);
777
+ onChange?.((0, import_editor6.compactVariableValues)(next), next);
778
+ };
779
+ if (fields.length === 0) return null;
780
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { className, onSubmit: (event) => event.preventDefault(), children: fields.map((field) => {
781
+ const id = `${idPrefix}-${field.name}`;
782
+ if (field.conditionOnly) {
783
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
784
+ "label",
785
+ {
786
+ htmlFor: id,
787
+ className: "flex items-center gap-2",
788
+ "data-variable-field": field.name,
789
+ children: [
790
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
791
+ "input",
792
+ {
793
+ id,
794
+ name: field.name,
795
+ type: "checkbox",
796
+ checked: (entered[field.name] ?? "") !== "",
797
+ onChange: (event) => update(field.name, event.target.checked ? "true" : "")
798
+ }
799
+ ),
800
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: field.label })
801
+ ]
802
+ },
803
+ field.name
804
+ );
805
+ }
806
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
807
+ "label",
808
+ {
809
+ htmlFor: id,
810
+ className: "flex flex-col gap-1",
811
+ "data-variable-field": field.name,
812
+ children: [
813
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "font-medium", children: [
814
+ field.label,
815
+ field.required && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-rose-500", "aria-hidden": "true", children: " *" })
816
+ ] }),
817
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
818
+ "input",
819
+ {
820
+ id,
821
+ name: field.name,
822
+ type: "text",
823
+ value: entered[field.name] ?? "",
824
+ placeholder: field.placeholder,
825
+ "aria-required": field.required || void 0,
826
+ onChange: (event) => update(field.name, event.target.value),
827
+ className: "px-2 py-1 rounded-md border border-violet-400/40 bg-transparent"
828
+ }
829
+ )
830
+ ]
831
+ },
832
+ field.name
833
+ );
834
+ }) });
835
+ };
836
+
837
+ // src/hooks/useOutline.ts
838
+ var import_react14 = require("react");
644
839
  var import_editor7 = require("@pixerate/editor");
840
+ 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);
843
+ }
844
+
845
+ // src/components/OutlinePanel.tsx
846
+ var import_editor8 = require("@pixerate/editor");
847
+ var import_jsx_runtime4 = require("react/jsx-runtime");
848
+ var OutlinePanel = ({
849
+ editor,
850
+ title = "Outline",
851
+ emptyState = "Add headings to see the outline.",
852
+ maxLevel = 6,
853
+ focusOnNavigate = true,
854
+ className = "flex flex-col gap-1 text-sm",
855
+ itemClassName = "w-full truncate rounded px-2 py-1 text-left hover:bg-black/5",
856
+ activeItemClassName = "font-semibold bg-black/5",
857
+ onNavigate
858
+ }) => {
859
+ const { headings, activeId } = useOutline(editor);
860
+ const visible = headings.filter((h) => h.level <= maxLevel);
861
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("nav", { className, "aria-label": "Document outline", "data-outline-panel": "", children: [
862
+ title != null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pixerate-outline-title", children: title }),
863
+ visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pixerate-outline-empty", children: emptyState }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { role: "list", style: { listStyle: "none", margin: 0, padding: 0 }, children: visible.map((heading) => {
864
+ const active = heading.id === activeId;
865
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { "data-level": heading.level, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
866
+ "button",
867
+ {
868
+ type: "button",
869
+ className: active ? `${itemClassName} ${activeItemClassName}` : itemClassName,
870
+ style: { paddingLeft: `${0.5 + heading.depth * 0.75}rem` },
871
+ "aria-current": active ? "location" : void 0,
872
+ "data-heading-id": heading.id,
873
+ onClick: () => {
874
+ if (!editor) return;
875
+ if ((0, import_editor8.navigateToHeading)(editor, heading, { focus: focusOnNavigate })) {
876
+ onNavigate?.(heading);
877
+ }
878
+ },
879
+ children: heading.text || "Untitled"
880
+ }
881
+ ) }, heading.id);
882
+ }) })
883
+ ] });
884
+ };
885
+
886
+ // src/index.ts
887
+ var import_editor15 = require("@pixerate/editor");
645
888
 
646
889
  // src/spreadsheet/useSpreadsheetEditor.ts
647
- var import_react11 = require("react");
648
- var import_editor5 = require("@pixerate/editor");
890
+ var import_react15 = require("react");
891
+ var import_editor9 = require("@pixerate/editor");
649
892
  function useSpreadsheetEditor(options = {}) {
650
- const optionsRef = (0, import_react11.useRef)(options);
893
+ const optionsRef = (0, import_react15.useRef)(options);
651
894
  optionsRef.current = options;
652
- const settersRef = (0, import_react11.useRef)({});
653
- const [controller] = (0, import_react11.useState)(
654
- () => new import_editor5.SpreadsheetController({
895
+ const settersRef = (0, import_react15.useRef)({});
896
+ const [controller] = (0, import_react15.useState)(
897
+ () => new import_editor9.SpreadsheetController({
655
898
  ...options,
656
899
  onDocumentChange: (updated) => {
657
900
  settersRef.current.setDoc?.({ ...updated });
@@ -667,91 +910,93 @@ function useSpreadsheetEditor(options = {}) {
667
910
  }
668
911
  })
669
912
  );
670
- const [doc, setDoc] = (0, import_react11.useState)(() => ({ ...controller.document }));
671
- const [activeCell, setActiveCell] = (0, import_react11.useState)(controller.activeCell);
672
- const [selectedRange, setSelectedRange] = (0, import_react11.useState)(controller.selectedRange);
673
- const [editingCell, setEditingCell] = (0, import_react11.useState)(null);
674
- const [draftValue, setDraftValue] = (0, import_react11.useState)("");
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)("");
675
918
  settersRef.current = { setDoc, setActiveCell, setSelectedRange };
676
- const setCellValue = (0, import_react11.useCallback)((rowId, colId, raw) => {
919
+ const setCellValue = (0, import_react15.useCallback)((rowId, colId, raw) => {
677
920
  controller.setCellValue(rowId, colId, raw);
678
921
  setDoc({ ...controller.document });
679
922
  }, [controller]);
680
- const selectCell = (0, import_react11.useCallback)((row, col, extendSelection = false) => {
923
+ const selectCell = (0, import_react15.useCallback)((row, col, extendSelection = false) => {
681
924
  controller.selectCell(row, col, extendSelection);
682
925
  setActiveCell(controller.activeCell);
683
926
  setSelectedRange(controller.selectedRange);
684
927
  }, [controller]);
685
- const selectRange = (0, import_react11.useCallback)((range) => {
928
+ const selectRange = (0, import_react15.useCallback)((range) => {
686
929
  controller.selectRange(range);
687
930
  setSelectedRange(controller.selectedRange);
688
931
  }, [controller]);
689
- const startEditing = (0, import_react11.useCallback)((row, col, initialVal) => {
932
+ const startEditing = (0, import_react15.useCallback)((row, col, initialVal) => {
690
933
  controller.startEditing(row, col, initialVal);
691
934
  setActiveCell(controller.activeCell);
692
935
  setEditingCell(controller.editingCell);
693
936
  setDraftValue(controller.draftValue);
694
937
  }, [controller]);
695
- const commitEditing = (0, import_react11.useCallback)((val) => {
938
+ const commitEditing = (0, import_react15.useCallback)((val) => {
696
939
  controller.commitEditing(val);
697
940
  setEditingCell(null);
698
941
  setDraftValue("");
699
942
  setDoc({ ...controller.document });
700
943
  }, [controller]);
701
- const cancelEditing = (0, import_react11.useCallback)(() => {
944
+ const cancelEditing = (0, import_react15.useCallback)(() => {
702
945
  controller.cancelEditing();
703
946
  setEditingCell(null);
704
947
  setDraftValue("");
705
948
  }, [controller]);
706
- const moveCursor = (0, import_react11.useCallback)((dir, extend = false) => {
949
+ const moveCursor = (0, import_react15.useCallback)((dir, extend = false) => {
707
950
  controller.moveCursor(dir, extend);
708
951
  setActiveCell(controller.activeCell);
709
952
  setSelectedRange(controller.selectedRange);
710
953
  }, [controller]);
711
- const insertColumn = (0, import_react11.useCallback)((index, config) => {
954
+ const insertColumn = (0, import_react15.useCallback)((index, config) => {
712
955
  controller.insertColumn(index, config);
713
956
  setDoc({ ...controller.document });
714
957
  }, [controller]);
715
- const deleteColumn = (0, import_react11.useCallback)((colId) => {
958
+ const deleteColumn = (0, import_react15.useCallback)((colId) => {
716
959
  controller.deleteColumn(colId);
717
960
  setDoc({ ...controller.document });
718
961
  }, [controller]);
719
- const insertRow = (0, import_react11.useCallback)((index, config) => {
962
+ const insertRow = (0, import_react15.useCallback)((index, config) => {
720
963
  controller.insertRow(index, config);
721
964
  setDoc({ ...controller.document });
722
965
  }, [controller]);
723
- const deleteRow = (0, import_react11.useCallback)((rowId) => {
966
+ const deleteRow = (0, import_react15.useCallback)((rowId) => {
724
967
  controller.deleteRow(rowId);
725
968
  setDoc({ ...controller.document });
726
969
  }, [controller]);
727
- const setColumnWidth = (0, import_react11.useCallback)((colId, width) => {
970
+ const setColumnWidth = (0, import_react15.useCallback)((colId, width) => {
728
971
  controller.setColumnWidth(colId, width);
729
972
  setDoc({ ...controller.document });
730
973
  }, [controller]);
731
- const autoFitColumnWidth = (0, import_react11.useCallback)((colId, customWidth) => {
974
+ const autoFitColumnWidth = (0, import_react15.useCallback)((colId, customWidth) => {
732
975
  controller.autoFitColumnWidth(colId, customWidth);
733
976
  setDoc({ ...controller.document });
734
977
  }, [controller]);
735
- const registerDataSource = (0, import_react11.useCallback)((ds) => {
978
+ const registerDataSource = (0, import_react15.useCallback)((ds) => {
736
979
  controller.registerDataSource(ds);
737
980
  setDoc({ ...controller.document });
738
981
  }, [controller]);
739
- const setPrimaryDataSource = (0, import_react11.useCallback)((id) => {
982
+ const setPrimaryDataSource = (0, import_react15.useCallback)((id) => {
740
983
  controller.setPrimaryDataSource(id);
741
984
  setDoc({ ...controller.document });
742
985
  }, [controller]);
743
- const recalculateAll = (0, import_react11.useCallback)(() => {
986
+ const recalculateAll = (0, import_react15.useCallback)(() => {
744
987
  controller.recalculateAll();
745
988
  setDoc({ ...controller.document });
746
989
  }, [controller]);
747
- const undo = (0, import_react11.useCallback)(() => {
990
+ const undo = (0, import_react15.useCallback)(() => {
748
991
  controller.undo();
749
992
  setDoc({ ...controller.document });
750
993
  }, [controller]);
751
- const redo = (0, import_react11.useCallback)(() => {
994
+ const redo = (0, import_react15.useCallback)(() => {
752
995
  controller.redo();
753
996
  setDoc({ ...controller.document });
754
997
  }, [controller]);
998
+ const tableActions = (0, import_react15.useMemo)(() => (0, import_editor9.bindSpreadsheetTableActions)(controller), [controller]);
999
+ const viewModel = (0, import_react15.useMemo)(() => controller.getViewModel(), [controller, doc]);
755
1000
  return {
756
1001
  document: doc,
757
1002
  activeCell,
@@ -795,15 +1040,355 @@ function useSpreadsheetEditor(options = {}) {
795
1040
  controller.importFromTsv(data);
796
1041
  setDoc({ ...controller.document });
797
1042
  },
1043
+ ...tableActions,
1044
+ viewModel,
798
1045
  undo,
799
1046
  redo
800
1047
  };
801
1048
  }
802
1049
 
803
1050
  // src/spreadsheet/SpreadsheetEditor.tsx
804
- var import_react12 = require("react");
805
- var import_editor6 = require("@pixerate/editor");
806
- var import_jsx_runtime3 = require("react/jsx-runtime");
1051
+ var import_react19 = require("react");
1052
+ var import_editor12 = require("@pixerate/editor");
1053
+
1054
+ // src/spreadsheet/SpreadsheetContextMenu.tsx
1055
+ var import_react16 = __toESM(require("react"), 1);
1056
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1057
+ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1058
+ const menuRef = (0, import_react16.useRef)(null);
1059
+ const onCloseRef = (0, import_react16.useRef)(onClose);
1060
+ onCloseRef.current = onClose;
1061
+ (0, import_react16.useEffect)(() => {
1062
+ const handlePointer = (e) => {
1063
+ if (!menuRef.current?.contains(e.target)) onCloseRef.current();
1064
+ };
1065
+ const handleKey = (e) => {
1066
+ if (e.key === "Escape") onCloseRef.current();
1067
+ };
1068
+ window.addEventListener("mousedown", handlePointer);
1069
+ window.addEventListener("keydown", handleKey);
1070
+ return () => {
1071
+ window.removeEventListener("mousedown", handlePointer);
1072
+ window.removeEventListener("keydown", handleKey);
1073
+ };
1074
+ }, []);
1075
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1076
+ "div",
1077
+ {
1078
+ ref: menuRef,
1079
+ role: "menu",
1080
+ "aria-label": "Table actions",
1081
+ 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))" },
1083
+ onContextMenu: (e) => e.preventDefault(),
1084
+ onKeyDown: (e) => e.stopPropagation(),
1085
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react16.default.Fragment, { children: [
1086
+ item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1088
+ "button",
1089
+ {
1090
+ type: "button",
1091
+ role: "menuitem",
1092
+ "data-action": item.id,
1093
+ disabled: item.disabled,
1094
+ className: "block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent",
1095
+ onClick: () => {
1096
+ onClose();
1097
+ item.run();
1098
+ },
1099
+ children: item.label
1100
+ }
1101
+ )
1102
+ ] }, item.id))
1103
+ }
1104
+ );
1105
+ };
1106
+
1107
+ // src/spreadsheet/SpreadsheetGroupRow.tsx
1108
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1109
+ var formatSummary = (value) => value === null ? "\u2013" : String(value);
1110
+ var SpreadsheetGroupRow = ({
1111
+ group,
1112
+ columns,
1113
+ groupColId,
1114
+ onToggle,
1115
+ trailingCell
1116
+ }) => {
1117
+ const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
1118
+ 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)(
1120
+ "button",
1121
+ {
1122
+ type: "button",
1123
+ "aria-label": `${group.collapsed ? "Expand" : "Collapse"} group ${group.label}`,
1124
+ "aria-expanded": !group.collapsed,
1125
+ className: "h-5 w-5 rounded text-[10px] text-muted-foreground hover:bg-muted",
1126
+ onClick: (e) => {
1127
+ e.stopPropagation();
1128
+ onToggle(group.key);
1129
+ },
1130
+ children: group.collapsed ? "\u25B8" : "\u25BE"
1131
+ }
1132
+ ) }),
1133
+ columns.map(({ column }) => {
1134
+ const summary = group.summaries[column.id];
1135
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-r border-border px-2 py-1 truncate max-w-0 text-foreground", style: { maxWidth: 0, overflow: "hidden" }, children: column.id === labelColId ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "truncate", children: [
1136
+ group.label,
1137
+ " ",
1138
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
1139
+ "(",
1140
+ group.rowIndices.length,
1141
+ ")"
1142
+ ] })
1143
+ ] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
1144
+ }),
1145
+ trailingCell && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1146
+ ] });
1147
+ };
1148
+
1149
+ // src/spreadsheet/useSpreadsheetTableInteractions.ts
1150
+ var import_react17 = require("react");
1151
+ var import_editor10 = require("@pixerate/editor");
1152
+ function gridHasFocus(grid) {
1153
+ if (!grid || typeof document === "undefined") return false;
1154
+ const active = document.activeElement;
1155
+ if (!active || !grid.contains(active)) return false;
1156
+ return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
1157
+ }
1158
+ function useSpreadsheetClipboard(gridRef, state, readOnly) {
1159
+ const stateRef = (0, import_react17.useRef)(state);
1160
+ stateRef.current = state;
1161
+ const readOnlyRef = (0, import_react17.useRef)(readOnly);
1162
+ readOnlyRef.current = readOnly;
1163
+ const copiedRef = (0, import_react17.useRef)(false);
1164
+ (0, import_react17.useEffect)(() => {
1165
+ if (typeof document === "undefined") return;
1166
+ const handleCopy = (e) => {
1167
+ const s = stateRef.current;
1168
+ if (!gridHasFocus(gridRef.current) || s.editingCell || !e.clipboardData || !s.selectedRange) return;
1169
+ for (const [type, data] of Object.entries(s.copyToClipboardData(s.selectedRange))) {
1170
+ e.clipboardData.setData(type, data);
1171
+ }
1172
+ copiedRef.current = true;
1173
+ e.preventDefault();
1174
+ };
1175
+ const handlePaste = (e) => {
1176
+ const s = stateRef.current;
1177
+ if (readOnlyRef.current || !gridHasFocus(gridRef.current) || s.editingCell || !e.clipboardData) return;
1178
+ if (s.pasteClipboardData(e.clipboardData)) e.preventDefault();
1179
+ };
1180
+ document.addEventListener("copy", handleCopy);
1181
+ document.addEventListener("paste", handlePaste);
1182
+ return () => {
1183
+ document.removeEventListener("copy", handleCopy);
1184
+ document.removeEventListener("paste", handlePaste);
1185
+ };
1186
+ }, [gridRef]);
1187
+ const copyShortcutFallback = (0, import_react17.useCallback)(() => {
1188
+ copiedRef.current = false;
1189
+ setTimeout(() => {
1190
+ if (copiedRef.current) return;
1191
+ const s = stateRef.current;
1192
+ navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange));
1193
+ }, 0);
1194
+ }, []);
1195
+ return { copyShortcutFallback };
1196
+ }
1197
+ function useFillHandle(state) {
1198
+ const [drag, setDrag] = (0, import_react17.useState)(null);
1199
+ const dragRef = (0, import_react17.useRef)(drag);
1200
+ dragRef.current = drag;
1201
+ const stateRef = (0, import_react17.useRef)(state);
1202
+ stateRef.current = state;
1203
+ const dragging = drag !== null;
1204
+ (0, import_react17.useEffect)(() => {
1205
+ if (!dragging) return;
1206
+ const handleMouseUp = () => {
1207
+ const current = dragRef.current;
1208
+ setDrag(null);
1209
+ if (current?.target) stateRef.current.autofill(current.source, current.target);
1210
+ };
1211
+ window.addEventListener("mouseup", handleMouseUp);
1212
+ return () => window.removeEventListener("mouseup", handleMouseUp);
1213
+ }, [dragging]);
1214
+ const startFill = (0, import_react17.useCallback)((e) => {
1215
+ e.preventDefault();
1216
+ e.stopPropagation();
1217
+ const range = stateRef.current.selectedRange;
1218
+ if (range) setDrag({ source: (0, import_editor10.normalizeRange)(range), target: null });
1219
+ }, []);
1220
+ const extendFill = (0, import_react17.useCallback)((row, col) => {
1221
+ const current = dragRef.current;
1222
+ if (!current) return;
1223
+ setDrag({ source: current.source, target: (0, import_editor10.computeFillTarget)(current.source, { row, col }) });
1224
+ }, []);
1225
+ return { fillTarget: drag?.target ?? null, isFilling: dragging, startFill, extendFill };
1226
+ }
1227
+
1228
+ // src/spreadsheet/FormulaHints.tsx
1229
+ var import_react18 = require("react");
1230
+ var import_editor11 = require("@pixerate/editor");
1231
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1232
+ 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_editor11.getFormulaHints)(value, caret ?? value.length, { limit }),
1241
+ [value, caret, limit]
1242
+ );
1243
+ const suggestions = dismissed ? [] : hints.suggestions;
1244
+ const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
1245
+ const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
1246
+ const latest = (0, import_react18.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
1247
+ latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
1248
+ const accept = (index) => {
1249
+ const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
1250
+ const suggestion = list[index];
1251
+ if (!suggestion) return;
1252
+ const next = (0, import_editor11.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
1253
+ pendingCaret.current = next.caret;
1254
+ commit(next.value);
1255
+ };
1256
+ (0, import_react18.useEffect)(() => {
1257
+ const input = inputRef.current;
1258
+ if (!input) return;
1259
+ const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
1260
+ const onInput = () => {
1261
+ setDismissed(false);
1262
+ setSelected(0);
1263
+ syncCaret();
1264
+ };
1265
+ const onFocus = () => {
1266
+ setFocused(true);
1267
+ syncCaret();
1268
+ };
1269
+ const onBlur = () => setFocused(false);
1270
+ const onKeyDown = (e) => {
1271
+ const { suggestions: list } = latest.current;
1272
+ if (list.length === 0) return;
1273
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
1274
+ e.preventDefault();
1275
+ e.stopPropagation();
1276
+ const step = e.key === "ArrowDown" ? 1 : -1;
1277
+ setSelected((i) => (i + step + list.length) % list.length);
1278
+ } else if (e.key === "Tab" && !e.shiftKey) {
1279
+ e.preventDefault();
1280
+ e.stopPropagation();
1281
+ accept(latest.current.selected);
1282
+ } else if (e.key === "Escape") {
1283
+ e.preventDefault();
1284
+ e.stopPropagation();
1285
+ setDismissed(true);
1286
+ }
1287
+ };
1288
+ input.addEventListener("input", onInput);
1289
+ input.addEventListener("keyup", syncCaret);
1290
+ input.addEventListener("click", syncCaret);
1291
+ input.addEventListener("select", syncCaret);
1292
+ input.addEventListener("focus", onFocus);
1293
+ input.addEventListener("blur", onBlur);
1294
+ input.addEventListener("keydown", onKeyDown);
1295
+ if (typeof document !== "undefined" && document.activeElement === input) onFocus();
1296
+ return () => {
1297
+ input.removeEventListener("input", onInput);
1298
+ input.removeEventListener("keyup", syncCaret);
1299
+ input.removeEventListener("click", syncCaret);
1300
+ input.removeEventListener("select", syncCaret);
1301
+ input.removeEventListener("focus", onFocus);
1302
+ input.removeEventListener("blur", onBlur);
1303
+ input.removeEventListener("keydown", onKeyDown);
1304
+ };
1305
+ }, [inputRef]);
1306
+ (0, import_react18.useLayoutEffect)(() => {
1307
+ const input = inputRef.current;
1308
+ const at = pendingCaret.current;
1309
+ if (!input || at === null || input.value !== value) return;
1310
+ pendingCaret.current = null;
1311
+ input.setSelectionRange(at, at);
1312
+ setCaret(at);
1313
+ setSelected(0);
1314
+ }, [value, inputRef]);
1315
+ (0, import_react18.useLayoutEffect)(() => {
1316
+ const input = inputRef.current;
1317
+ if (!visible || !input) return;
1318
+ const rect = input.getBoundingClientRect();
1319
+ setPosition({ top: rect.bottom + 2, left: rect.left, width: rect.width });
1320
+ }, [visible, value, inputRef]);
1321
+ if (!visible) return null;
1322
+ const { activeCall } = hints;
1323
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1324
+ "div",
1325
+ {
1326
+ "data-formula-hints": "",
1327
+ className: "rounded-md border border-border bg-popover text-popover-foreground shadow-md text-xs font-sans overflow-hidden",
1328
+ style: {
1329
+ position: "fixed",
1330
+ top: position?.top ?? 0,
1331
+ left: position?.left ?? 0,
1332
+ minWidth: Math.max(260, position?.width ?? 0),
1333
+ maxWidth: 440,
1334
+ zIndex: 50,
1335
+ visibility: position ? "visible" : "hidden"
1336
+ },
1337
+ onMouseDown: (e) => e.preventDefault(),
1338
+ children: [
1339
+ activeCall && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
1340
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1341
+ "span",
1342
+ {
1343
+ "data-active": part.active ? "" : void 0,
1344
+ className: part.active ? "font-bold text-primary underline" : "",
1345
+ children: part.text
1346
+ },
1347
+ i
1348
+ )) }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1350
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
1351
+ ": ",
1352
+ activeCall.argument.description
1353
+ ] }) : activeCall.description })
1354
+ ] }),
1355
+ suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { role: "listbox", "aria-label": "Formula suggestions", className: "max-h-64 overflow-auto py-1 m-0 list-none p-0", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1356
+ "li",
1357
+ {
1358
+ role: "option",
1359
+ "aria-selected": i === selectedIndex,
1360
+ className: `cursor-pointer px-2 py-1 ${i === selectedIndex ? "bg-primary/10" : "hover:bg-muted/50"}`,
1361
+ onMouseEnter: () => setSelected(i),
1362
+ onMouseDown: (e) => {
1363
+ e.preventDefault();
1364
+ accept(i);
1365
+ },
1366
+ children: [
1367
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
1368
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
1369
+ ]
1370
+ },
1371
+ s.name
1372
+ )) })
1373
+ ]
1374
+ }
1375
+ );
1376
+ };
1377
+
1378
+ // src/spreadsheet/SpreadsheetEditor.tsx
1379
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1380
+ var COLUMN_HEADER_HEIGHT = 28;
1381
+ var ROW_HEADER_WIDTH = 48;
1382
+ function frozenCellStyle(viewRow, viewCol) {
1383
+ if (!viewRow.frozen && !viewCol.frozen) return void 0;
1384
+ return {
1385
+ position: "sticky",
1386
+ ...viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop } : {},
1387
+ ...viewCol.frozen ? { left: ROW_HEADER_WIDTH + viewCol.stickyLeft } : {},
1388
+ zIndex: viewRow.frozen && viewCol.frozen ? 14 : viewRow.frozen ? 12 : 11,
1389
+ background: "var(--background, #fff)"
1390
+ };
1391
+ }
807
1392
  var SpreadsheetEditor = ({
808
1393
  state: externalState,
809
1394
  readOnly = false,
@@ -814,27 +1399,47 @@ var SpreadsheetEditor = ({
814
1399
  }) => {
815
1400
  const defaultState = useSpreadsheetEditor();
816
1401
  const state = externalState || defaultState;
817
- const gridRef = (0, import_react12.useRef)(null);
818
- const [resizingColId, setResizingColId] = (0, import_react12.useState)(null);
819
- const [hoveredColId, setHoveredColId] = (0, import_react12.useState)(null);
820
- const [resizeStartX, setResizeStartX] = (0, import_react12.useState)(0);
821
- const [resizeStartWidth, setResizeStartWidth] = (0, import_react12.useState)(0);
822
- const activeRawValue = (0, import_react12.useMemo)(() => {
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);
1409
+ const view = state.viewModel;
1410
+ const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
1411
+ const { fillTarget, startFill, extendFill } = useFillHandle(state);
1412
+ const [menu, setMenu] = (0, import_react19.useState)(null);
1413
+ const filter = state.document.table?.filter;
1414
+ const filteredColIds = (0, import_react19.useMemo)(
1415
+ () => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
1416
+ [filter]
1417
+ );
1418
+ const handleCellContextMenu = (0, import_react19.useCallback)((rIdx, cIdx, e) => {
1419
+ if (readOnly) return;
1420
+ e.preventDefault();
1421
+ if (state.editingCell) state.commitEditing();
1422
+ if (!state.selectedRange || !(0, import_editor12.rangeContains)(state.selectedRange, rIdx, cIdx)) {
1423
+ state.selectCell(rIdx, cIdx);
1424
+ }
1425
+ setMenu({ x: e.clientX, y: e.clientY, items: (0, import_editor12.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
1426
+ }, [readOnly, state]);
1427
+ const activeRawValue = (0, import_react19.useMemo)(() => {
823
1428
  if (!state.activeCell) return "";
824
1429
  const row = state.document.rows[state.activeCell.row];
825
1430
  const col = state.document.columns[state.activeCell.col];
826
1431
  if (!row || !col) return "";
827
1432
  return state.getCell(row.id, col.id).raw;
828
1433
  }, [state.activeCell, state.document]);
829
- const coordLabel = (0, import_react12.useMemo)(() => {
1434
+ const coordLabel = (0, import_react19.useMemo)(() => {
830
1435
  if (!state.activeCell) return "--";
831
- const colName = (0, import_editor6.indexToColName)(state.activeCell.col);
1436
+ const colName = (0, import_editor12.indexToColName)(state.activeCell.col);
832
1437
  return `${colName}${state.activeCell.row + 1}`;
833
1438
  }, [state.activeCell]);
834
- const [formulaDraft, setFormulaDraft] = (0, import_react12.useState)(null);
1439
+ const [formulaDraft, setFormulaDraft] = (0, import_react19.useState)(null);
835
1440
  const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
836
1441
  const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
837
- (0, import_react12.useEffect)(() => {
1442
+ (0, import_react19.useEffect)(() => {
838
1443
  setFormulaDraft(null);
839
1444
  }, [state.activeCell?.row, state.activeCell?.col]);
840
1445
  const commitFormulaBar = () => {
@@ -850,19 +1455,19 @@ var SpreadsheetEditor = ({
850
1455
  }
851
1456
  setFormulaDraft(null);
852
1457
  };
853
- const handleCellClick = (0, import_react12.useCallback)((rIdx, cIdx, e) => {
1458
+ const handleCellClick = (0, import_react19.useCallback)((rIdx, cIdx, e) => {
854
1459
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
855
1460
  state.commitEditing();
856
1461
  }
857
1462
  state.selectCell(rIdx, cIdx, e.shiftKey);
858
1463
  }, [state]);
859
- const handleCellDblClick = (0, import_react12.useCallback)((rIdx, cIdx) => {
1464
+ const handleCellDblClick = (0, import_react19.useCallback)((rIdx, cIdx) => {
860
1465
  if (readOnly) return;
861
1466
  const col = state.document.columns[cIdx];
862
1467
  if (col?.readOnly) return;
863
1468
  state.startEditing(rIdx, cIdx);
864
1469
  }, [readOnly, state]);
865
- const handleKeyDown = (0, import_react12.useCallback)((e) => {
1470
+ const handleKeyDown = (0, import_react19.useCallback)((e) => {
866
1471
  if (state.editingCell) {
867
1472
  if (e.key === "Enter") {
868
1473
  e.preventDefault();
@@ -919,21 +1524,19 @@ var SpreadsheetEditor = ({
919
1524
  e.preventDefault();
920
1525
  state.redo();
921
1526
  } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") {
922
- e.preventDefault();
923
- const tsv = state.exportToTsv(state.selectedRange);
924
- navigator.clipboard?.writeText(tsv);
1527
+ copyShortcutFallback();
925
1528
  } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
926
1529
  const col = state.document.columns[state.activeCell.col];
927
1530
  if (!col?.readOnly) {
928
1531
  state.startEditing(state.activeCell.row, state.activeCell.col, e.key);
929
1532
  }
930
1533
  }
931
- }, [readOnly, state]);
932
- const stateRef = (0, import_react12.useRef)(state);
1534
+ }, [readOnly, state, copyShortcutFallback]);
1535
+ const stateRef = (0, import_react19.useRef)(state);
933
1536
  stateRef.current = state;
934
- const onColumnResizeRef = (0, import_react12.useRef)(onColumnResize);
1537
+ const onColumnResizeRef = (0, import_react19.useRef)(onColumnResize);
935
1538
  onColumnResizeRef.current = onColumnResize;
936
- (0, import_react12.useEffect)(() => {
1539
+ (0, import_react19.useEffect)(() => {
937
1540
  if (!resizingColId) return;
938
1541
  if (typeof document !== "undefined") {
939
1542
  document.body.style.cursor = "col-resize";
@@ -984,7 +1587,7 @@ var SpreadsheetEditor = ({
984
1587
  window.removeEventListener("mouseup", handleMouseUp);
985
1588
  };
986
1589
  }, [resizingColId, resizeStartX, resizeStartWidth]);
987
- const measureElementContentWidth = (0, import_react12.useCallback)((el, container) => {
1590
+ const measureElementContentWidth = (0, import_react19.useCallback)((el, container) => {
988
1591
  if (typeof document === "undefined") return 0;
989
1592
  let maxW = el.scrollWidth || 0;
990
1593
  const firstChild = el.firstElementChild;
@@ -1032,15 +1635,14 @@ var SpreadsheetEditor = ({
1032
1635
  }
1033
1636
  return maxW;
1034
1637
  }, []);
1035
- const autoFitColumn = (0, import_react12.useCallback)((colId, colIndex) => {
1638
+ const autoFitColumn = (0, import_react19.useCallback)((colId, colIndex) => {
1036
1639
  const col = state.document.columns.find((c) => c.id === colId);
1037
1640
  if (!col) return;
1038
1641
  let calculatedWidth = 0;
1039
1642
  if (gridRef.current && typeof document !== "undefined") {
1040
1643
  const table = gridRef.current.querySelector("table");
1041
1644
  if (table) {
1042
- const thElements = table.querySelectorAll("thead th");
1043
- const th = thElements[colIndex + 1];
1645
+ const th = table.querySelector(`thead th[data-col="${colIndex}"]`);
1044
1646
  if (th) {
1045
1647
  const headerInner = th.firstElementChild || th;
1046
1648
  const headerW = measureElementContentWidth(headerInner, gridRef.current);
@@ -1050,7 +1652,7 @@ var SpreadsheetEditor = ({
1050
1652
  const sampleLimit = Math.min(rows.length, 150);
1051
1653
  for (let r = 0; r < sampleLimit; r++) {
1052
1654
  const tr = rows[r];
1053
- const td = tr.children[colIndex + 1];
1655
+ const td = tr.querySelector(`td[data-col="${colIndex}"][colspan="1"]`);
1054
1656
  if (td) {
1055
1657
  const cellW = measureElementContentWidth(td, gridRef.current);
1056
1658
  calculatedWidth = Math.max(calculatedWidth, cellW + 20);
@@ -1069,12 +1671,12 @@ var SpreadsheetEditor = ({
1069
1671
  onColumnResize(updatedCol.id, updatedCol.width || 130);
1070
1672
  }
1071
1673
  }, [state, measureElementContentWidth, onColumnResize]);
1072
- const handleResizeDblClick = (0, import_react12.useCallback)((colId, colIndex, e) => {
1674
+ const handleResizeDblClick = (0, import_react19.useCallback)((colId, colIndex, e) => {
1073
1675
  e.preventDefault();
1074
1676
  e.stopPropagation();
1075
1677
  autoFitColumn(colId, colIndex);
1076
1678
  }, [autoFitColumn]);
1077
- const handleHeaderDblClick = (0, import_react12.useCallback)((colId, colIndex, e) => {
1679
+ const handleHeaderDblClick = (0, import_react19.useCallback)((colId, colIndex, e) => {
1078
1680
  const target = e.currentTarget;
1079
1681
  if (target) {
1080
1682
  const rect = target.getBoundingClientRect();
@@ -1096,7 +1698,7 @@ var SpreadsheetEditor = ({
1096
1698
  const isActiveCell = (rIdx, cIdx) => {
1097
1699
  return state.activeCell?.row === rIdx && state.activeCell?.col === cIdx;
1098
1700
  };
1099
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1701
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1100
1702
  "div",
1101
1703
  {
1102
1704
  ref: gridRef,
@@ -1106,12 +1708,13 @@ var SpreadsheetEditor = ({
1106
1708
  role: "grid",
1107
1709
  "aria-label": "Spreadsheet",
1108
1710
  children: [
1109
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
1110
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
1111
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
1112
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1711
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
1713
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
1714
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1113
1715
  "input",
1114
1716
  {
1717
+ ref: formulaBarRef,
1115
1718
  type: "text",
1116
1719
  className: "flex-1 bg-transparent px-2 py-0.5 outline-none font-sans text-xs text-foreground placeholder:text-muted-foreground/60 focus:bg-background focus:ring-1 focus:ring-primary rounded",
1117
1720
  placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
@@ -1141,28 +1744,44 @@ var SpreadsheetEditor = ({
1141
1744
  }
1142
1745
  }
1143
1746
  }
1747
+ ),
1748
+ !formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1749
+ FormulaHints,
1750
+ {
1751
+ inputRef: formulaBarRef,
1752
+ value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1753
+ onAccept: (next) => state.editingCell ? state.setDraftValue(next) : setFormulaDraft(next)
1754
+ }
1144
1755
  )
1145
1756
  ] }),
1146
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
1147
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { className: "h-7 bg-muted/60 sticky top-0 z-20 border-b border-border shadow-xs", children: [
1148
- /* @__PURE__ */ (0, import_jsx_runtime3.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: "#" }),
1149
- state.document.columns.map((col, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1757
+ /* @__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: "#" }),
1760
+ view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1150
1761
  "th",
1151
1762
  {
1763
+ "data-col": cIdx,
1152
1764
  className: `relative border-r border-border px-2 text-left font-medium text-muted-foreground hover:bg-muted/90 transition-colors group overflow-hidden max-w-0 ${onColumnHeaderClick ? "cursor-pointer" : "cursor-default"}`,
1153
- style: { width: col.width || 130, minWidth: col.width || 130, maxWidth: col.width || 130, overflow: "hidden" },
1765
+ style: {
1766
+ width: col.width || 130,
1767
+ minWidth: col.width || 130,
1768
+ maxWidth: col.width || 130,
1769
+ overflow: "hidden",
1770
+ ...frozen ? { position: "sticky", left: ROW_HEADER_WIDTH + stickyLeft, zIndex: 25, background: "var(--muted, #f4f4f5)" } : {}
1771
+ },
1154
1772
  onClick: () => onColumnHeaderClick?.(col),
1155
1773
  onDoubleClick: (e) => handleHeaderDblClick(col.id, cIdx, e),
1156
1774
  children: [
1157
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
1158
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
1159
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
1775
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
1776
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
1777
+ filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
1778
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
1160
1779
  "(",
1161
1780
  col.key,
1162
1781
  ")"
1163
1782
  ] })
1164
1783
  ] }),
1165
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1784
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1166
1785
  "div",
1167
1786
  {
1168
1787
  className: "col-resize-handle",
@@ -1179,7 +1798,7 @@ var SpreadsheetEditor = ({
1179
1798
  onDoubleClick: (e) => handleResizeDblClick(col.id, cIdx, e),
1180
1799
  role: "separator",
1181
1800
  "aria-orientation": "vertical",
1182
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1801
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1183
1802
  "div",
1184
1803
  {
1185
1804
  className: "col-resize-handle-line",
@@ -1198,49 +1817,136 @@ var SpreadsheetEditor = ({
1198
1817
  },
1199
1818
  col.id
1200
1819
  )),
1201
- onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1820
+ onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1202
1821
  "th",
1203
1822
  {
1204
1823
  className: "w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors",
1205
1824
  onClick: onAddColumnClick,
1206
1825
  title: "Add Column",
1207
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
1826
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
1208
1827
  }
1209
1828
  )
1210
1829
  ] }) }),
1211
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: state.document.rows.map((row, rIdx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { className: "border-b border-border hover:bg-muted/10 transition-colors", style: { height: row.height || 32 }, children: [
1212
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { 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", children: rIdx + 1 }),
1213
- state.document.columns.map((col, cIdx) => {
1214
- const cell = state.getCell(row.id, col.id);
1215
- const selected = isCellSelected(rIdx, cIdx);
1216
- const active = isActiveCell(rIdx, cIdx);
1217
- const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1218
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1219
- "td",
1830
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tbody", { children: view.rows.map((viewRow) => {
1831
+ if (viewRow.kind === "group") {
1832
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1833
+ SpreadsheetGroupRow,
1220
1834
  {
1221
- className: `relative border-r border-border px-2 py-1 truncate max-w-0 text-foreground transition-colors ${selected ? "bg-primary/10" : ""} ${active ? "ring-2 ring-primary ring-inset z-10" : ""}`,
1222
- style: { maxWidth: 0, overflow: "hidden" },
1223
- onClick: (e) => handleCellClick(rIdx, cIdx, e),
1224
- onDoubleClick: () => handleCellDblClick(rIdx, cIdx),
1225
- children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1226
- "input",
1227
- {
1228
- type: "text",
1229
- className: "absolute inset-0 w-full h-full px-2 py-1 bg-background text-foreground outline-none font-sans text-xs border-2 border-primary z-20",
1230
- value: state.draftValue,
1231
- onChange: (e) => state.setDraftValue(e.target.value),
1232
- onBlur: () => state.commitEditing(),
1233
- autoFocus: true
1234
- }
1235
- ) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`, children: cell.error || (cell.value ?? "") })
1835
+ group: viewRow.group,
1836
+ columns: view.columns,
1837
+ groupColId: state.document.table?.grouping?.colId ?? "",
1838
+ onToggle: state.toggleGroupCollapsed,
1839
+ trailingCell: !!onAddColumnClick
1236
1840
  },
1237
- col.id
1841
+ `group:${viewRow.group.key}`
1238
1842
  );
1239
- }),
1240
- onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1241
- ] }, row.id)) })
1843
+ }
1844
+ const displayRowIndex = viewRow.index;
1845
+ const selection = state.selectedRange ? (0, import_editor12.normalizeRange)(state.selectedRange) : null;
1846
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1847
+ "tr",
1848
+ {
1849
+ "data-row": displayRowIndex,
1850
+ className: `border-b border-border hover:bg-muted/10 transition-colors ${viewRow.section !== "body" ? "bg-muted/30 font-semibold" : ""}`,
1851
+ style: { height: viewRow.row.height || 32 },
1852
+ children: [
1853
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1854
+ "td",
1855
+ {
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",
1857
+ style: viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop, zIndex: 15 } : void 0,
1858
+ children: displayRowIndex + 1
1859
+ }
1860
+ ),
1861
+ view.columns.map((viewCol) => {
1862
+ const span = view.getCellSpan(displayRowIndex, viewCol.index);
1863
+ if (!span) return null;
1864
+ const rIdx = span.source.row;
1865
+ const cIdx = span.source.col;
1866
+ const row = state.document.rows[rIdx];
1867
+ const col = state.document.columns[cIdx];
1868
+ const cell = state.getCell(row.id, col.id);
1869
+ const selected = isCellSelected(rIdx, cIdx);
1870
+ const active = isActiveCell(rIdx, cIdx);
1871
+ const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1872
+ const inFillPreview = !!fillTarget && !selected && (0, import_editor12.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1873
+ const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_editor12.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1874
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1875
+ "td",
1876
+ {
1877
+ "data-row": rIdx,
1878
+ "data-col": cIdx,
1879
+ rowSpan: span.rowSpan,
1880
+ colSpan: span.colSpan,
1881
+ className: `relative border-r border-border px-2 py-1 truncate max-w-0 text-foreground transition-colors ${selected ? "bg-primary/10" : ""} ${active ? "ring-2 ring-primary ring-inset z-10" : ""} ${viewCol.header ? "font-semibold" : ""}`,
1882
+ style: {
1883
+ maxWidth: 0,
1884
+ overflow: "hidden",
1885
+ ...frozenCellStyle(viewRow, viewCol),
1886
+ ...inFillPreview ? { outline: "1px dashed var(--primary, #6366f1)", outlineOffset: -1 } : {}
1887
+ },
1888
+ onClick: (e) => handleCellClick(rIdx, cIdx, e),
1889
+ onDoubleClick: () => handleCellDblClick(rIdx, cIdx),
1890
+ onContextMenu: (e) => handleCellContextMenu(rIdx, cIdx, e),
1891
+ onMouseOver: () => extendFill(displayRowIndex, viewCol.index),
1892
+ children: [
1893
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1894
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1895
+ "input",
1896
+ {
1897
+ ref: cellInputRef,
1898
+ type: "text",
1899
+ className: "absolute inset-0 w-full h-full px-2 py-1 bg-background text-foreground outline-none font-sans text-xs border-2 border-primary z-20",
1900
+ value: state.draftValue,
1901
+ onChange: (e) => state.setDraftValue(e.target.value),
1902
+ onBlur: () => state.commitEditing(),
1903
+ autoFocus: true
1904
+ }
1905
+ ),
1906
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
1907
+ ] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1908
+ "span",
1909
+ {
1910
+ className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
1911
+ title: cell.error ? cell.errorMessage ?? cell.error : void 0,
1912
+ children: cell.error || (cell.value ?? "")
1913
+ }
1914
+ ),
1915
+ showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1916
+ "div",
1917
+ {
1918
+ className: "spreadsheet-fill-handle",
1919
+ "aria-label": "Fill handle",
1920
+ onMouseDown: startFill,
1921
+ onClick: (e) => e.stopPropagation(),
1922
+ style: { position: "absolute", right: 0, bottom: 0, width: 7, height: 7, background: "var(--primary, #6366f1)", cursor: "crosshair", zIndex: 25 }
1923
+ }
1924
+ )
1925
+ ]
1926
+ },
1927
+ viewCol.column.id
1928
+ );
1929
+ }),
1930
+ onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1931
+ ]
1932
+ },
1933
+ viewRow.row.id
1934
+ );
1935
+ }) })
1242
1936
  ] }) }),
1243
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
1937
+ menu && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1938
+ SpreadsheetContextMenu,
1939
+ {
1940
+ items: menu.items,
1941
+ x: menu.x,
1942
+ y: menu.y,
1943
+ onClose: () => {
1944
+ setMenu(null);
1945
+ gridRef.current?.focus();
1946
+ }
1947
+ }
1948
+ ),
1949
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: `
1244
1950
  .col-resize-handle:hover .col-resize-handle-line {
1245
1951
  background-color: var(--primary, #6366f1);
1246
1952
  opacity: 0.75;
@@ -1263,10 +1969,10 @@ var SpreadsheetEditor = ({
1263
1969
  };
1264
1970
 
1265
1971
  // src/slots.tsx
1266
- var import_react13 = require("react");
1267
- var import_jsx_runtime4 = require("react/jsx-runtime");
1972
+ var import_react20 = require("react");
1973
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1268
1974
  var defaultSlots = {
1269
- Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1975
+ Button: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1270
1976
  "button",
1271
1977
  {
1272
1978
  type: "button",
@@ -1276,107 +1982,111 @@ var defaultSlots = {
1276
1982
  children: props.children
1277
1983
  }
1278
1984
  ),
1279
- Tooltip: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
1280
- NodeHeader: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
1281
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
1985
+ Tooltip: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
1986
+ NodeHeader: (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
1987
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
1282
1988
  props.icon,
1283
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "font-semibold", children: props.title })
1989
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold", children: props.title })
1284
1990
  ] }),
1285
- props.onClose && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
1991
+ props.onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
1286
1992
  ] })
1287
1993
  };
1288
- var EditorSlotsContext = (0, import_react13.createContext)(defaultSlots);
1994
+ var EditorSlotsContext = (0, import_react20.createContext)(defaultSlots);
1289
1995
  function EditorSlotsProvider({ slots, children }) {
1290
- const mergedSlots = (0, import_react13.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
1291
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
1996
+ const mergedSlots = (0, import_react20.useMemo)(() => ({ ...defaultSlots, ...slots }), [slots]);
1997
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorSlotsContext.Provider, { value: mergedSlots, children });
1292
1998
  }
1293
1999
  function useEditorSlots() {
1294
- return (0, import_react13.useContext)(EditorSlotsContext);
2000
+ return (0, import_react20.useContext)(EditorSlotsContext);
1295
2001
  }
1296
2002
 
1297
2003
  // src/image-editor/useImageEditor.ts
1298
- var import_react14 = require("react");
2004
+ var import_react21 = require("react");
1299
2005
  var import_image_editor = require("@pixerate/editor/image-editor");
1300
2006
  function useImageEditor(options = {}) {
1301
- const controllerRef = (0, import_react14.useRef)(null);
2007
+ const controllerRef = (0, import_react21.useRef)(null);
1302
2008
  if (!controllerRef.current) {
1303
2009
  controllerRef.current = new import_image_editor.ImageEditorController(options);
1304
2010
  }
1305
2011
  const controller = controllerRef.current;
1306
- const [state, setState] = (0, import_react14.useState)(() => controller.getState());
1307
- const [historyTicks, setHistoryTicks] = (0, import_react14.useState)(0);
1308
- (0, import_react14.useEffect)(() => {
2012
+ const [state, setState] = (0, import_react21.useState)(() => controller.getState());
2013
+ const [historyTicks, setHistoryTicks] = (0, import_react21.useState)(0);
2014
+ (0, import_react21.useEffect)(() => {
1309
2015
  const unsub = controller.subscribe((next) => {
1310
2016
  setState(next);
1311
2017
  setHistoryTicks((t) => t + 1);
1312
2018
  });
1313
2019
  return unsub;
1314
2020
  }, [controller]);
1315
- const loadedImageRef = (0, import_react14.useRef)(options.image);
1316
- (0, import_react14.useEffect)(() => {
2021
+ const loadedImageRef = (0, import_react21.useRef)(options.image);
2022
+ (0, import_react21.useEffect)(() => {
1317
2023
  if (options.image === loadedImageRef.current) return;
1318
2024
  loadedImageRef.current = options.image;
1319
2025
  if (options.image) {
1320
2026
  controller.loadImage(options.image);
1321
2027
  }
1322
2028
  }, [options.image, controller]);
1323
- const setTool = (0, import_react14.useCallback)((tool) => controller.setTool(tool), [controller]);
1324
- const setCrop = (0, import_react14.useCallback)(
2029
+ const setTool = (0, import_react21.useCallback)((tool) => controller.setTool(tool), [controller]);
2030
+ const setCrop = (0, import_react21.useCallback)(
1325
2031
  (crop, addToHistory = true) => controller.setCrop(crop, addToHistory),
1326
2032
  [controller]
1327
2033
  );
1328
- const applyCropPreset = (0, import_react14.useCallback)((ratio) => controller.applyCropPreset(ratio), [controller]);
1329
- const resetCrop = (0, import_react14.useCallback)(() => controller.resetCrop(), [controller]);
1330
- const rotate = (0, import_react14.useCallback)((deg) => controller.rotate(deg), [controller]);
1331
- const setRotation = (0, import_react14.useCallback)((deg) => controller.setRotation(deg), [controller]);
1332
- const flipHorizontal = (0, import_react14.useCallback)(() => controller.flipHorizontal(), [controller]);
1333
- const flipVertical = (0, import_react14.useCallback)(() => controller.flipVertical(), [controller]);
1334
- const setAdjustments = (0, import_react14.useCallback)(
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)(
1335
2041
  (partial, commitToHistory = true) => controller.setAdjustments(partial, commitToHistory),
1336
2042
  [controller]
1337
2043
  );
1338
- const resetAdjustments = (0, import_react14.useCallback)(() => controller.resetAdjustments(), [controller]);
1339
- const setDepthMask = (0, import_react14.useCallback)((partial) => controller.setDepthMask(partial), [controller]);
1340
- const resetDepthMask = (0, import_react14.useCallback)(() => controller.resetDepthMask(), [controller]);
1341
- const addAnnotation = (0, import_react14.useCallback)((ann) => controller.addAnnotation(ann), [controller]);
1342
- const updateAnnotation = (0, import_react14.useCallback)(
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)(
1343
2049
  (id, partial, commitToHistory = true) => controller.updateAnnotation(id, partial, commitToHistory),
1344
2050
  [controller]
1345
2051
  );
1346
- const removeAnnotation = (0, import_react14.useCallback)((id) => controller.removeAnnotation(id), [controller]);
1347
- const clearAnnotations = (0, import_react14.useCallback)(() => controller.clearAnnotations(), [controller]);
1348
- const selectAnnotation = (0, import_react14.useCallback)((id) => controller.selectAnnotation(id), [controller]);
1349
- const undo = (0, import_react14.useCallback)(() => controller.undo(), [controller]);
1350
- const redo = (0, import_react14.useCallback)(() => controller.redo(), [controller]);
1351
- const reset = (0, import_react14.useCallback)(() => controller.reset(), [controller]);
1352
- const zoomIn = (0, import_react14.useCallback)((factor) => controller.zoomIn(factor), [controller]);
1353
- const zoomOut = (0, import_react14.useCallback)((factor) => controller.zoomOut(factor), [controller]);
1354
- const resetZoom = (0, import_react14.useCallback)(() => controller.resetZoom(), [controller]);
1355
- const setZoom = (0, import_react14.useCallback)((zoom) => controller.setZoom(zoom), [controller]);
1356
- const setPan = (0, import_react14.useCallback)((pan) => controller.setPan(pan), [controller]);
1357
- const setComparing = (0, import_react14.useCallback)((comp) => controller.setComparing(comp), [controller]);
1358
- const loadImage = (0, import_react14.useCallback)((src) => controller.loadImage(src), [controller]);
1359
- const toDataURL = (0, import_react14.useCallback)((opts) => controller.toDataURL(opts), [controller]);
1360
- const toBlob = (0, import_react14.useCallback)((opts) => controller.toBlob(opts), [controller]);
1361
- const toJSON = (0, import_react14.useCallback)(() => controller.toJSON(), [controller]);
1362
- const loadJSON = (0, import_react14.useCallback)((data) => controller.loadJSON(data), [controller]);
1363
- const renderMask = (0, import_react14.useCallback)(
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)(
1364
2070
  (opts) => controller.renderMask(opts),
1365
2071
  [controller]
1366
2072
  );
1367
- const toMaskDataURL = (0, import_react14.useCallback)(
2073
+ const toMaskDataURL = (0, import_react21.useCallback)(
1368
2074
  (opts) => controller.toMaskDataURL(opts),
1369
2075
  [controller]
1370
2076
  );
1371
- const applyInpaintedImage = (0, import_react14.useCallback)(
2077
+ const toSourceDataURL = (0, import_react21.useCallback)(
2078
+ (opts) => controller.toSourceDataURL(opts),
2079
+ [controller]
2080
+ );
2081
+ const applyInpaintedImage = (0, import_react21.useCallback)(
1372
2082
  (newDataUrl) => controller.applyInpaintedImage(newDataUrl),
1373
2083
  [controller]
1374
2084
  );
1375
- const applyBackgroundRemovedImage = (0, import_react14.useCallback)(
2085
+ const applyBackgroundRemovedImage = (0, import_react21.useCallback)(
1376
2086
  (newDataUrl) => controller.applyBackgroundRemovedImage(newDataUrl),
1377
2087
  [controller]
1378
2088
  );
1379
- return (0, import_react14.useMemo)(
2089
+ return (0, import_react21.useMemo)(
1380
2090
  () => ({
1381
2091
  controller,
1382
2092
  state,
@@ -1415,6 +2125,7 @@ function useImageEditor(options = {}) {
1415
2125
  loadJSON,
1416
2126
  renderMask,
1417
2127
  toMaskDataURL,
2128
+ toSourceDataURL,
1418
2129
  applyInpaintedImage,
1419
2130
  applyBackgroundRemovedImage
1420
2131
  }),
@@ -1424,9 +2135,9 @@ function useImageEditor(options = {}) {
1424
2135
  }
1425
2136
 
1426
2137
  // src/image-editor/ImageEditor.tsx
1427
- var import_react15 = require("react");
2138
+ var import_react22 = require("react");
1428
2139
  var import_image_editor2 = require("@pixerate/editor/image-editor");
1429
- var import_jsx_runtime5 = require("react/jsx-runtime");
2140
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1430
2141
  var ImageEditor = ({
1431
2142
  editor: externalEditor,
1432
2143
  src,
@@ -1441,16 +2152,16 @@ var ImageEditor = ({
1441
2152
  const internalEditor = useImageEditor({ image: src });
1442
2153
  const editor = externalEditor || internalEditor;
1443
2154
  const { state, controller } = editor;
1444
- const canvasRef = (0, import_react15.useRef)(null);
1445
- const containerRef = (0, import_react15.useRef)(null);
1446
- const [activeTab, setActiveTab] = (0, import_react15.useState)("crop");
1447
- const [activeCropRatio, setActiveCropRatio] = (0, import_react15.useState)(null);
1448
- const [inpaintPrompt, setInpaintPrompt] = (0, import_react15.useState)("");
1449
- const [inpaintMaskMode, setInpaintMaskMode] = (0, import_react15.useState)("annotations");
1450
- const [isRemovingBg, setIsRemovingBg] = (0, import_react15.useState)(false);
1451
- const [isInpainting, setIsInpainting] = (0, import_react15.useState)(false);
1452
- const [aiError, setAiError] = (0, import_react15.useState)(null);
1453
- const [aiSuccess, setAiSuccess] = (0, import_react15.useState)(null);
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);
1454
2165
  const effectiveOnInpaint = onInpaint || aiHooks?.onInpaint;
1455
2166
  const effectiveOnRemoveBg = onRemoveBackground || aiHooks?.onRemoveBackground;
1456
2167
  const handleRemoveBackground = async () => {
@@ -1462,7 +2173,7 @@ var ImageEditor = ({
1462
2173
  setAiSuccess(null);
1463
2174
  setIsRemovingBg(true);
1464
2175
  try {
1465
- const imageDataUrl = await editor.toDataURL(exportOptions);
2176
+ const imageDataUrl = await editor.toSourceDataURL();
1466
2177
  const result = await effectiveOnRemoveBg({ imageDataUrl });
1467
2178
  if (typeof result === "string") {
1468
2179
  await editor.applyBackgroundRemovedImage(result);
@@ -1489,7 +2200,7 @@ var ImageEditor = ({
1489
2200
  setAiSuccess(null);
1490
2201
  setIsInpainting(true);
1491
2202
  try {
1492
- const imageDataUrl = await editor.toDataURL(exportOptions);
2203
+ const imageDataUrl = await editor.toSourceDataURL();
1493
2204
  const maskDataUrl = await editor.toMaskDataURL({
1494
2205
  useAnnotations: inpaintMaskMode === "annotations",
1495
2206
  useCrop: inpaintMaskMode === "crop"
@@ -1512,18 +2223,18 @@ var ImageEditor = ({
1512
2223
  setIsInpainting(false);
1513
2224
  }
1514
2225
  };
1515
- const [activeColor, setActiveColor] = (0, import_react15.useState)("#ef4444");
1516
- const [strokeWidth, setStrokeWidth] = (0, import_react15.useState)(3);
1517
- const [fontSize, setFontSize] = (0, import_react15.useState)(24);
1518
- const [textInput, setTextInput] = (0, import_react15.useState)("");
1519
- const draftAnnotationRef = (0, import_react15.useRef)(null);
1520
- const draftCropRef = (0, import_react15.useRef)(null);
1521
- const cropDragInfoRef = (0, import_react15.useRef)(null);
1522
- const annotationDragInfoRef = (0, import_react15.useRef)(null);
1523
- const isDrawingRef = (0, import_react15.useRef)(false);
1524
- const currentPathRef = (0, import_react15.useRef)([]);
1525
- const dragStartPointRef = (0, import_react15.useRef)(null);
1526
- const renderCanvas = (0, import_react15.useCallback)(
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)(
1527
2238
  async (draftAnnotation, draftCrop) => {
1528
2239
  const canvas = canvasRef.current;
1529
2240
  const container = containerRef.current;
@@ -1542,7 +2253,7 @@ var ImageEditor = ({
1542
2253
  },
1543
2254
  [controller, activeTab]
1544
2255
  );
1545
- (0, import_react15.useEffect)(() => {
2256
+ (0, import_react22.useEffect)(() => {
1546
2257
  const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
1547
2258
  const caf = typeof cancelAnimationFrame !== "undefined" ? cancelAnimationFrame : (id) => clearTimeout(id);
1548
2259
  const animId = raf(() => {
@@ -1550,12 +2261,12 @@ var ImageEditor = ({
1550
2261
  });
1551
2262
  return () => caf(animId);
1552
2263
  }, [renderCanvas, state]);
1553
- (0, import_react15.useEffect)(() => {
2264
+ (0, import_react22.useEffect)(() => {
1554
2265
  if (activeTab === "crop" && editor.state.activeTool !== "crop") {
1555
2266
  editor.setTool("crop");
1556
2267
  }
1557
2268
  }, [activeTab, editor]);
1558
- (0, import_react15.useEffect)(() => {
2269
+ (0, import_react22.useEffect)(() => {
1559
2270
  const container = containerRef.current;
1560
2271
  if (!container) return;
1561
2272
  if (typeof ResizeObserver !== "undefined") {
@@ -1675,7 +2386,8 @@ var ImageEditor = ({
1675
2386
  currentImgPt,
1676
2387
  state.imageDimensions.width,
1677
2388
  state.imageDimensions.height,
1678
- activeCropRatio
2389
+ activeCropRatio,
2390
+ state.transform
1679
2391
  );
1680
2392
  }
1681
2393
  draftCropRef.current = newCrop;
@@ -1688,7 +2400,7 @@ var ImageEditor = ({
1688
2400
  height: state.imageDimensions.height
1689
2401
  };
1690
2402
  const handle = (0, import_image_editor2.hitTestCrop)(canvasPt, state, canvas.width, canvas.height, 14, activeCrop);
1691
- canvas.style.cursor = (0, import_image_editor2.getCropCursor)(handle);
2403
+ canvas.style.cursor = (0, import_image_editor2.getCropCursor)(handle, state.transform);
1692
2404
  }
1693
2405
  return;
1694
2406
  }
@@ -1912,15 +2624,15 @@ var ImageEditor = ({
1912
2624
  link.click();
1913
2625
  }
1914
2626
  };
1915
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2627
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1916
2628
  "div",
1917
2629
  {
1918
2630
  className: `flex flex-col bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl text-slate-100 ${className}`,
1919
2631
  style,
1920
2632
  children: [
1921
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
1922
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
1923
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2633
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-wrap items-center justify-between px-4 py-3 bg-slate-950/80 border-b border-slate-800 gap-3", children: [
2634
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-1 bg-slate-900 p-1 rounded-lg border border-slate-800 text-xs", children: [
2635
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1924
2636
  "button",
1925
2637
  {
1926
2638
  type: "button",
@@ -1932,7 +2644,7 @@ var ImageEditor = ({
1932
2644
  children: "Transform & Crop"
1933
2645
  }
1934
2646
  ),
1935
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2647
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1936
2648
  "button",
1937
2649
  {
1938
2650
  type: "button",
@@ -1944,7 +2656,7 @@ var ImageEditor = ({
1944
2656
  children: "Adjustments"
1945
2657
  }
1946
2658
  ),
1947
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2659
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1948
2660
  "button",
1949
2661
  {
1950
2662
  type: "button",
@@ -1956,7 +2668,7 @@ var ImageEditor = ({
1956
2668
  children: "Annotations"
1957
2669
  }
1958
2670
  ),
1959
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2671
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1960
2672
  "button",
1961
2673
  {
1962
2674
  type: "button",
@@ -1968,7 +2680,7 @@ var ImageEditor = ({
1968
2680
  children: "Depth Mask"
1969
2681
  }
1970
2682
  ),
1971
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2683
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1972
2684
  "button",
1973
2685
  {
1974
2686
  type: "button",
@@ -1978,14 +2690,14 @@ var ImageEditor = ({
1978
2690
  },
1979
2691
  className: `px-3 py-1.5 rounded-md font-medium transition-colors flex items-center gap-1.5 ${activeTab === "ai" ? "bg-indigo-600 text-white shadow" : "text-slate-400 hover:text-slate-200"}`,
1980
2692
  children: [
1981
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
2693
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
1982
2694
  "AI Tools"
1983
2695
  ]
1984
2696
  }
1985
2697
  )
1986
2698
  ] }),
1987
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
1988
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2699
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
2700
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1989
2701
  "button",
1990
2702
  {
1991
2703
  type: "button",
@@ -1996,7 +2708,7 @@ var ImageEditor = ({
1996
2708
  children: "\u21A9"
1997
2709
  }
1998
2710
  ),
1999
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2711
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2000
2712
  "button",
2001
2713
  {
2002
2714
  type: "button",
@@ -2007,7 +2719,7 @@ var ImageEditor = ({
2007
2719
  children: "\u21AA"
2008
2720
  }
2009
2721
  ),
2010
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2722
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2011
2723
  "button",
2012
2724
  {
2013
2725
  type: "button",
@@ -2016,8 +2728,8 @@ var ImageEditor = ({
2016
2728
  children: "Reset"
2017
2729
  }
2018
2730
  ),
2019
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
2020
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2731
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
2732
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2021
2733
  "button",
2022
2734
  {
2023
2735
  type: "button",
@@ -2027,11 +2739,11 @@ var ImageEditor = ({
2027
2739
  children: "-"
2028
2740
  }
2029
2741
  ),
2030
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
2742
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono text-slate-400 w-10 text-center", children: [
2031
2743
  Math.round(state.zoom * 100),
2032
2744
  "%"
2033
2745
  ] }),
2034
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2746
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2035
2747
  "button",
2036
2748
  {
2037
2749
  type: "button",
@@ -2041,8 +2753,8 @@ var ImageEditor = ({
2041
2753
  children: "+"
2042
2754
  }
2043
2755
  ),
2044
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
2045
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2756
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-4 w-px bg-slate-800 mx-1" }),
2757
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2046
2758
  "button",
2047
2759
  {
2048
2760
  type: "button",
@@ -2053,7 +2765,7 @@ var ImageEditor = ({
2053
2765
  children: "Hold Compare"
2054
2766
  }
2055
2767
  ),
2056
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2768
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2057
2769
  "button",
2058
2770
  {
2059
2771
  type: "button",
@@ -2064,13 +2776,13 @@ var ImageEditor = ({
2064
2776
  )
2065
2777
  ] })
2066
2778
  ] }),
2067
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
2068
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2779
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col md:flex-row flex-1 min-h-[520px] md:h-[560px]", children: [
2780
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2069
2781
  "div",
2070
2782
  {
2071
2783
  ref: containerRef,
2072
2784
  className: "relative flex-1 h-[360px] md:h-full min-h-[320px] bg-slate-950 overflow-hidden select-none",
2073
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2785
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2074
2786
  "canvas",
2075
2787
  {
2076
2788
  ref: canvasRef,
@@ -2083,11 +2795,11 @@ var ImageEditor = ({
2083
2795
  )
2084
2796
  }
2085
2797
  ),
2086
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
2087
- activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
2088
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
2089
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
2090
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2798
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "w-full md:w-80 bg-slate-900/90 border-t md:border-t-0 md:border-l border-slate-800 p-4 overflow-y-auto h-auto md:h-full md:max-h-[560px]", children: [
2799
+ activeTab === "crop" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
2800
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Aspect Ratio Presets" }),
2801
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
2802
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2091
2803
  "button",
2092
2804
  {
2093
2805
  type: "button",
@@ -2099,7 +2811,7 @@ var ImageEditor = ({
2099
2811
  children: "Freeform"
2100
2812
  }
2101
2813
  ),
2102
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2814
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2103
2815
  "button",
2104
2816
  {
2105
2817
  type: "button",
@@ -2111,7 +2823,7 @@ var ImageEditor = ({
2111
2823
  children: "1:1 Square"
2112
2824
  }
2113
2825
  ),
2114
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2826
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2115
2827
  "button",
2116
2828
  {
2117
2829
  type: "button",
@@ -2123,7 +2835,7 @@ var ImageEditor = ({
2123
2835
  children: "4:3 Standard"
2124
2836
  }
2125
2837
  ),
2126
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2838
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2127
2839
  "button",
2128
2840
  {
2129
2841
  type: "button",
@@ -2135,7 +2847,7 @@ var ImageEditor = ({
2135
2847
  children: "16:9 Wide"
2136
2848
  }
2137
2849
  ),
2138
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2850
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2139
2851
  "button",
2140
2852
  {
2141
2853
  type: "button",
@@ -2147,7 +2859,7 @@ var ImageEditor = ({
2147
2859
  children: "3:2 Photo"
2148
2860
  }
2149
2861
  ),
2150
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2862
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2151
2863
  "button",
2152
2864
  {
2153
2865
  type: "button",
@@ -2160,10 +2872,10 @@ var ImageEditor = ({
2160
2872
  }
2161
2873
  )
2162
2874
  ] }),
2163
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
2164
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
2165
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
2166
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2875
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
2876
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Orientation & Flips" }),
2877
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
2878
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2167
2879
  "button",
2168
2880
  {
2169
2881
  type: "button",
@@ -2172,7 +2884,7 @@ var ImageEditor = ({
2172
2884
  children: "Rotate -90\xB0"
2173
2885
  }
2174
2886
  ),
2175
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2887
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2176
2888
  "button",
2177
2889
  {
2178
2890
  type: "button",
@@ -2181,7 +2893,7 @@ var ImageEditor = ({
2181
2893
  children: "Rotate +90\xB0"
2182
2894
  }
2183
2895
  ),
2184
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2896
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2185
2897
  "button",
2186
2898
  {
2187
2899
  type: "button",
@@ -2190,7 +2902,7 @@ var ImageEditor = ({
2190
2902
  children: "Flip Horizontal"
2191
2903
  }
2192
2904
  ),
2193
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2905
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2194
2906
  "button",
2195
2907
  {
2196
2908
  type: "button",
@@ -2201,10 +2913,10 @@ var ImageEditor = ({
2201
2913
  )
2202
2914
  ] })
2203
2915
  ] }),
2204
- activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
2205
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
2206
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
2207
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2916
+ activeTab === "adjust" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
2917
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
2918
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Color Adjustments" }),
2919
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2208
2920
  "button",
2209
2921
  {
2210
2922
  type: "button",
@@ -2214,12 +2926,12 @@ var ImageEditor = ({
2214
2926
  }
2215
2927
  )
2216
2928
  ] }),
2217
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2218
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2219
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Brightness" }),
2220
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.brightness })
2929
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
2930
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2931
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Brightness" }),
2932
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.brightness })
2221
2933
  ] }),
2222
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2934
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2223
2935
  "input",
2224
2936
  {
2225
2937
  type: "range",
@@ -2231,12 +2943,12 @@ var ImageEditor = ({
2231
2943
  }
2232
2944
  )
2233
2945
  ] }),
2234
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2235
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2236
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Contrast" }),
2237
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
2946
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
2947
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2948
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Contrast" }),
2949
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.contrast })
2238
2950
  ] }),
2239
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2951
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2240
2952
  "input",
2241
2953
  {
2242
2954
  type: "range",
@@ -2248,12 +2960,12 @@ var ImageEditor = ({
2248
2960
  }
2249
2961
  )
2250
2962
  ] }),
2251
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2252
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2253
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Saturation" }),
2254
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
2963
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
2964
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2965
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Saturation" }),
2966
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.saturation })
2255
2967
  ] }),
2256
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2968
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2257
2969
  "input",
2258
2970
  {
2259
2971
  type: "range",
@@ -2265,12 +2977,12 @@ var ImageEditor = ({
2265
2977
  }
2266
2978
  )
2267
2979
  ] }),
2268
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2269
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2270
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Exposure" }),
2271
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
2980
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
2981
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2982
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Exposure" }),
2983
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.exposure })
2272
2984
  ] }),
2273
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2985
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2274
2986
  "input",
2275
2987
  {
2276
2988
  type: "range",
@@ -2282,12 +2994,12 @@ var ImageEditor = ({
2282
2994
  }
2283
2995
  )
2284
2996
  ] }),
2285
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2286
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2287
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Warmth / Temperature" }),
2288
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
2997
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
2998
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2999
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Warmth / Temperature" }),
3000
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.adjustments.temperature })
2289
3001
  ] }),
2290
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3002
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2291
3003
  "input",
2292
3004
  {
2293
3005
  type: "range",
@@ -2299,15 +3011,15 @@ var ImageEditor = ({
2299
3011
  }
2300
3012
  )
2301
3013
  ] }),
2302
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2303
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2304
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Blur" }),
2305
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
3014
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3015
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3016
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Blur" }),
3017
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
2306
3018
  state.adjustments.blur,
2307
3019
  "px"
2308
3020
  ] })
2309
3021
  ] }),
2310
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3022
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2311
3023
  "input",
2312
3024
  {
2313
3025
  type: "range",
@@ -2319,15 +3031,15 @@ var ImageEditor = ({
2319
3031
  }
2320
3032
  )
2321
3033
  ] }),
2322
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2323
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2324
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Opacity" }),
2325
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
3034
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3035
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3036
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Opacity" }),
3037
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
2326
3038
  Math.round(state.adjustments.opacity * 100),
2327
3039
  "%"
2328
3040
  ] })
2329
3041
  ] }),
2330
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3042
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2331
3043
  "input",
2332
3044
  {
2333
3045
  type: "range",
@@ -2341,9 +3053,9 @@ var ImageEditor = ({
2341
3053
  )
2342
3054
  ] })
2343
3055
  ] }),
2344
- activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
2345
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
2346
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3056
+ activeTab === "annotate" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3057
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Draw & Annotate" }),
3058
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-3 gap-1.5", children: ["pen", "rect", "circle", "arrow", "line"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2347
3059
  "button",
2348
3060
  {
2349
3061
  type: "button",
@@ -2353,9 +3065,9 @@ var ImageEditor = ({
2353
3065
  },
2354
3066
  t
2355
3067
  )) }),
2356
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
2357
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-slate-400", children: "Color:" }),
2358
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3068
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2 mt-2", children: [
3069
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-slate-400", children: "Color:" }),
3070
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2359
3071
  "input",
2360
3072
  {
2361
3073
  type: "color",
@@ -2364,15 +3076,15 @@ var ImageEditor = ({
2364
3076
  className: "w-8 h-8 rounded border border-slate-700 cursor-pointer bg-transparent"
2365
3077
  }
2366
3078
  ),
2367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 ml-2", children: [
2368
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
2369
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Width:" }),
2370
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
3079
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex-1 ml-2", children: [
3080
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-0.5", children: [
3081
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Width:" }),
3082
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
2371
3083
  strokeWidth,
2372
3084
  "px"
2373
3085
  ] })
2374
3086
  ] }),
2375
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3087
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2376
3088
  "input",
2377
3089
  {
2378
3090
  type: "range",
@@ -2385,10 +3097,10 @@ var ImageEditor = ({
2385
3097
  )
2386
3098
  ] })
2387
3099
  ] }),
2388
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
2389
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
2390
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2", children: [
2391
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3100
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3101
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Add Text" }),
3102
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2", children: [
3103
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2392
3104
  "input",
2393
3105
  {
2394
3106
  type: "text",
@@ -2399,7 +3111,7 @@ var ImageEditor = ({
2399
3111
  className: "flex-1 bg-slate-950 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-slate-100 placeholder-slate-500 focus:outline-none focus:border-indigo-500"
2400
3112
  }
2401
3113
  ),
2402
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3114
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2403
3115
  "button",
2404
3116
  {
2405
3117
  type: "button",
@@ -2409,14 +3121,14 @@ var ImageEditor = ({
2409
3121
  }
2410
3122
  )
2411
3123
  ] }),
2412
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
2413
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Font Size:" }),
2414
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-mono", children: [
3124
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between text-slate-400", children: [
3125
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Font Size:" }),
3126
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-mono", children: [
2415
3127
  fontSize,
2416
3128
  "px"
2417
3129
  ] })
2418
3130
  ] }),
2419
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3131
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2420
3132
  "input",
2421
3133
  {
2422
3134
  type: "range",
@@ -2427,14 +3139,14 @@ var ImageEditor = ({
2427
3139
  className: "w-full accent-indigo-500"
2428
3140
  }
2429
3141
  ),
2430
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
2431
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
2432
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-slate-400", children: [
3142
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-px bg-slate-800 my-1" }),
3143
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3144
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-slate-400", children: [
2433
3145
  "Layers (",
2434
3146
  state.annotations.length,
2435
3147
  ")"
2436
3148
  ] }),
2437
- state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3149
+ state.selectedAnnotationId && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2438
3150
  "button",
2439
3151
  {
2440
3152
  type: "button",
@@ -2443,7 +3155,7 @@ var ImageEditor = ({
2443
3155
  children: "Delete Selected"
2444
3156
  }
2445
3157
  ),
2446
- state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3158
+ state.annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2447
3159
  "button",
2448
3160
  {
2449
3161
  type: "button",
@@ -2454,10 +3166,10 @@ var ImageEditor = ({
2454
3166
  )
2455
3167
  ] })
2456
3168
  ] }),
2457
- activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
2458
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
2459
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
2460
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3169
+ activeTab === "depth" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-3 text-xs", children: [
3170
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3171
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px]", children: "Depth Masking" }),
3172
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2461
3173
  "button",
2462
3174
  {
2463
3175
  type: "button",
@@ -2467,8 +3179,8 @@ var ImageEditor = ({
2467
3179
  }
2468
3180
  )
2469
3181
  ] }),
2470
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
2471
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3182
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300", children: [
3183
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2472
3184
  "input",
2473
3185
  {
2474
3186
  type: "checkbox",
@@ -2477,12 +3189,12 @@ var ImageEditor = ({
2477
3189
  className: "rounded accent-indigo-500"
2478
3190
  }
2479
3191
  ),
2480
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Enable Depth / Alpha Mask" })
3192
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Enable Depth / Alpha Mask" })
2481
3193
  ] }),
2482
- state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2483
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2484
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
2485
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3194
+ state.depthMask.enabled && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3195
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3196
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-slate-400 block mb-1", children: "Mask Image URL:" }),
3197
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2486
3198
  "input",
2487
3199
  {
2488
3200
  type: "text",
@@ -2493,8 +3205,8 @@ var ImageEditor = ({
2493
3205
  }
2494
3206
  )
2495
3207
  ] }),
2496
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2", children: [
2497
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3208
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2", children: [
3209
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2498
3210
  "button",
2499
3211
  {
2500
3212
  type: "button",
@@ -2503,7 +3215,7 @@ var ImageEditor = ({
2503
3215
  children: "Threshold Mode"
2504
3216
  }
2505
3217
  ),
2506
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3218
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2507
3219
  "button",
2508
3220
  {
2509
3221
  type: "button",
@@ -2513,12 +3225,12 @@ var ImageEditor = ({
2513
3225
  }
2514
3226
  )
2515
3227
  ] }),
2516
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2517
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2518
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Depth Start (Lower):" }),
2519
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
3228
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3229
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3230
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Depth Start (Lower):" }),
3231
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[0] })
2520
3232
  ] }),
2521
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3233
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2522
3234
  "input",
2523
3235
  {
2524
3236
  type: "range",
@@ -2532,12 +3244,12 @@ var ImageEditor = ({
2532
3244
  }
2533
3245
  )
2534
3246
  ] }),
2535
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2536
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2537
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Depth Stop (Upper):" }),
2538
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
3247
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3248
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3249
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Depth Stop (Upper):" }),
3250
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.depthMask.depthRange[1] })
2539
3251
  ] }),
2540
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3252
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2541
3253
  "input",
2542
3254
  {
2543
3255
  type: "range",
@@ -2551,12 +3263,12 @@ var ImageEditor = ({
2551
3263
  }
2552
3264
  )
2553
3265
  ] }),
2554
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2555
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
2556
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Softness:" }),
2557
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
3266
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
3267
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between text-slate-400 mb-1", children: [
3268
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Softness:" }),
3269
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-mono", children: state.depthMask.softness })
2558
3270
  ] }),
2559
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3271
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2560
3272
  "input",
2561
3273
  {
2562
3274
  type: "range",
@@ -2568,8 +3280,8 @@ var ImageEditor = ({
2568
3280
  }
2569
3281
  )
2570
3282
  ] }),
2571
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
2572
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3283
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer text-slate-300 mt-1", children: [
3284
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2573
3285
  "input",
2574
3286
  {
2575
3287
  type: "checkbox",
@@ -2578,52 +3290,52 @@ var ImageEditor = ({
2578
3290
  className: "rounded accent-indigo-500"
2579
3291
  }
2580
3292
  ),
2581
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Invert Mask" })
3293
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "Invert Mask" })
2582
3294
  ] })
2583
3295
  ] })
2584
3296
  ] }),
2585
- activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
2586
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
2587
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
2588
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
2589
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
3297
+ activeTab === "ai" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-4 text-xs", children: [
3298
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
3299
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3300
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
3301
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5 text-indigo-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" }) }),
2590
3302
  "Background Removal"
2591
3303
  ] }),
2592
- isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
3304
+ isRemovingBg && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-900/60 text-indigo-300 animate-pulse", children: "Removing..." })
2593
3305
  ] }),
2594
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
2595
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3306
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Isolate foreground subject with AI alpha transparency segmentation." }),
3307
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2596
3308
  "button",
2597
3309
  {
2598
3310
  type: "button",
2599
3311
  onClick: handleRemoveBackground,
2600
3312
  disabled: isRemovingBg || isInpainting,
2601
3313
  className: "mt-1 px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
2602
- children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2603
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
3314
+ children: isRemovingBg ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3315
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
2604
3316
  "Removing Background..."
2605
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2606
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
3317
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3318
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
2607
3319
  "Remove Background"
2608
3320
  ] })
2609
3321
  }
2610
3322
  )
2611
3323
  ] }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
2613
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
2614
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
2615
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2616
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
2617
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m14 7 3 3" })
3324
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "p-3 rounded-lg bg-slate-950/60 border border-slate-800 flex flex-col gap-2.5", children: [
3325
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
3326
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-semibold text-slate-200 uppercase tracking-wider text-[11px] flex items-center gap-1.5", children: [
3327
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { className: "w-3.5 h-3.5 text-purple-400", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
3328
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m21.64 3.64-1.28-1.28a1.2 1.2 0 0 0-1.7 0l-9.72 9.72-1.94 4.86 4.86-1.94 9.72-9.72a1.2 1.2 0 0 0 .06-1.64z" }),
3329
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "m14 7 3 3" })
2618
3330
  ] }),
2619
3331
  "Generative Inpainting"
2620
3332
  ] }),
2621
- isInpainting && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
3333
+ isInpainting && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-purple-900/60 text-purple-300 animate-pulse", children: "Inpainting..." })
2622
3334
  ] }),
2623
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
2624
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
2625
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
2626
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3335
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-slate-400 text-[11px] leading-relaxed", children: "Replace, modify, or fill selected regions using prompt-guided generative inpainting." }),
3336
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
3337
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { htmlFor: "ai-inpaint-prompt-react", className: "text-slate-300 text-[11px] font-medium", children: "Prompt" }),
3338
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2627
3339
  "textarea",
2628
3340
  {
2629
3341
  id: "ai-inpaint-prompt-react",
@@ -2635,10 +3347,10 @@ var ImageEditor = ({
2635
3347
  }
2636
3348
  )
2637
3349
  ] }),
2638
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
2639
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
2640
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
2641
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3350
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-1", children: [
3351
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-slate-300 text-[11px] font-medium", children: "Target Mask Region" }),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid grid-cols-2 gap-1.5", children: [
3353
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2642
3354
  "button",
2643
3355
  {
2644
3356
  type: "button",
@@ -2651,7 +3363,7 @@ var ImageEditor = ({
2651
3363
  ]
2652
3364
  }
2653
3365
  ),
2654
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3366
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2655
3367
  "button",
2656
3368
  {
2657
3369
  type: "button",
@@ -2666,25 +3378,25 @@ var ImageEditor = ({
2666
3378
  )
2667
3379
  ] })
2668
3380
  ] }),
2669
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3381
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2670
3382
  "button",
2671
3383
  {
2672
3384
  type: "button",
2673
3385
  onClick: handleInpaint,
2674
3386
  disabled: isInpainting || isRemovingBg || !inpaintPrompt.trim(),
2675
3387
  className: "mt-1 px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 disabled:opacity-50 text-white font-medium shadow transition-colors flex items-center justify-center gap-2",
2676
- children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2677
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
3388
+ children: isInpainting ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3389
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
2678
3390
  "Generating Inpaint..."
2679
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2680
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
3391
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3392
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" }) }),
2681
3393
  "Apply Inpainting"
2682
3394
  ] })
2683
3395
  }
2684
3396
  )
2685
3397
  ] }),
2686
- aiError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
2687
- aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
3398
+ aiError && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "p-2.5 rounded-lg bg-rose-950/60 border border-rose-800 text-rose-300 text-[11px]", children: aiError }),
3399
+ aiSuccess && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "p-2.5 rounded-lg bg-emerald-950/60 border border-emerald-800 text-emerald-300 text-[11px]", children: aiSuccess })
2688
3400
  ] })
2689
3401
  ] })
2690
3402
  ] })
@@ -2692,27 +3404,356 @@ var ImageEditor = ({
2692
3404
  }
2693
3405
  );
2694
3406
  };
3407
+
3408
+ // src/review/useReview.ts
3409
+ var import_react23 = require("react");
3410
+ var import_editor13 = require("@pixerate/editor");
3411
+ var EMPTY_COMMENTS = Object.freeze({
3412
+ threads: [],
3413
+ activeThreadId: null
3414
+ });
3415
+ var EMPTY_TRACK_CHANGES = Object.freeze({
3416
+ enabled: false,
3417
+ author: { id: "anonymous", name: "Anonymous" },
3418
+ changes: [],
3419
+ currentChangeId: null
3420
+ });
3421
+ var noopUnsubscribe = () => {
3422
+ };
3423
+ function isCommentStore(value) {
3424
+ return !!value && typeof value.getSnapshot === "function" && typeof value.createThread === "function";
3425
+ }
3426
+ function useComments(source) {
3427
+ const store = isCommentStore(source) ? source : (0, import_editor13.getCommentStore)(source);
3428
+ const subscribe = (0, import_react23.useCallback)(
3429
+ (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3430
+ [store]
3431
+ );
3432
+ 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]);
3435
+ }
3436
+ function useTrackChanges(editor) {
3437
+ const store = (0, import_editor13.getTrackChangesStore)(editor ?? null);
3438
+ const subscribe = (0, import_react23.useCallback)(
3439
+ (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
3440
+ [store]
3441
+ );
3442
+ const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
3443
+ const snapshot = (0, import_react23.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
3444
+ return (0, import_react23.useMemo)(() => {
3445
+ const run = (fn) => () => {
3446
+ if (editor && !editor.isDestroyed) fn(editor);
3447
+ };
3448
+ return {
3449
+ ...snapshot,
3450
+ currentChange: snapshot.changes.find((c) => c.id === snapshot.currentChangeId) ?? null,
3451
+ setEnabled: (enabled) => run((e) => e.commands.setTrackChanges(enabled))(),
3452
+ toggle: run((e) => e.commands.toggleTrackChanges()),
3453
+ accept: (id) => run((e) => e.commands.acceptChange(id))(),
3454
+ reject: (id) => run((e) => e.commands.rejectChange(id))(),
3455
+ acceptAll: run((e) => e.commands.acceptAllChanges()),
3456
+ rejectAll: run((e) => e.commands.rejectAllChanges()),
3457
+ next: run((e) => e.chain().focus().selectNextChange().run()),
3458
+ previous: run((e) => e.chain().focus().selectPreviousChange().run())
3459
+ };
3460
+ }, [snapshot, editor]);
3461
+ }
3462
+
3463
+ // src/review/CommentsPanel.tsx
3464
+ var import_react24 = require("react");
3465
+ var import_editor14 = require("@pixerate/editor");
3466
+ var import_jsx_runtime11 = require("react/jsx-runtime");
3467
+ var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
3468
+ var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
3469
+ var textareaClass = "w-full resize-y rounded-md border border-input bg-background p-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary";
3470
+ var CommentsPanel = ({
3471
+ editor,
3472
+ store: storeProp,
3473
+ author: authorProp,
3474
+ showResolved: showResolvedInitial = false,
3475
+ className = "flex flex-col gap-2 text-sm text-foreground",
3476
+ formatTime = (t) => (0, import_editor14.formatReviewTime)(t),
3477
+ emptyMessage = "No comments yet. Select text and add a comment."
3478
+ }) => {
3479
+ const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
3480
+ const [showResolved, setShowResolved] = (0, import_react24.useState)(showResolvedInitial);
3481
+ const author = authorProp ?? (0, import_editor14.getCommentAuthor)(editor) ?? { id: "anonymous", name: "Anonymous" };
3482
+ const visible = threads.filter((t) => showResolved || !t.resolved);
3483
+ const resolvedCount = threads.filter((t) => t.resolved).length;
3484
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("aside", { className, "aria-label": "Comments", children: [
3485
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
3486
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
3487
+ threads.length - resolvedCount,
3488
+ " open",
3489
+ resolvedCount > 0 ? ` \xB7 ${resolvedCount} resolved` : ""
3490
+ ] }),
3491
+ resolvedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3492
+ "button",
3493
+ {
3494
+ type: "button",
3495
+ className: buttonClass,
3496
+ "aria-pressed": showResolved,
3497
+ onClick: () => setShowResolved((v) => !v),
3498
+ children: showResolved ? "Hide resolved" : "Show resolved"
3499
+ }
3500
+ )
3501
+ ] }),
3502
+ visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3503
+ CommentThreadCard,
3504
+ {
3505
+ thread,
3506
+ active: thread.id === activeThreadId,
3507
+ editor,
3508
+ store,
3509
+ author,
3510
+ formatTime
3511
+ },
3512
+ thread.id
3513
+ )) })
3514
+ ] });
3515
+ };
3516
+ var CommentThreadCard = ({
3517
+ thread,
3518
+ active,
3519
+ editor,
3520
+ store,
3521
+ author,
3522
+ formatTime
3523
+ }) => {
3524
+ const [draft, setDraft] = (0, import_react24.useState)("");
3525
+ const isDraft = thread.body === "";
3526
+ const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
3527
+ const live = editor && !editor.isDestroyed ? editor : null;
3528
+ const focus = () => {
3529
+ if (!active) {
3530
+ if (live && !thread.orphaned) live.commands.focusComment(thread.id);
3531
+ else store?.setActiveThread(thread.id);
3532
+ }
3533
+ };
3534
+ const remove = () => {
3535
+ if (live) live.commands.deleteComment(thread.id);
3536
+ else store?.deleteThread(thread.id);
3537
+ };
3538
+ const setResolved = (resolved) => {
3539
+ if (live) live.commands.resolveComment(thread.id, resolved);
3540
+ else store?.resolveThread(thread.id, resolved, author);
3541
+ };
3542
+ const submit = () => {
3543
+ const body = draft.trim();
3544
+ if (!body || !store) return;
3545
+ if (isDraft) store.editComment(thread.id, body);
3546
+ else store.addReply(thread.id, { body, author });
3547
+ setDraft("");
3548
+ };
3549
+ const stop = (e) => e.stopPropagation();
3550
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3551
+ "li",
3552
+ {
3553
+ className: `flex flex-col gap-2 rounded-md border bg-background p-3 cursor-pointer ${active ? "border-primary ring-1 ring-primary" : "border-border"} ${thread.resolved ? "opacity-70" : ""}`,
3554
+ "data-thread-id": thread.id,
3555
+ "data-active": active || void 0,
3556
+ "aria-current": active || void 0,
3557
+ tabIndex: 0,
3558
+ onClick: focus,
3559
+ onKeyDown: (e) => {
3560
+ if (e.target === e.currentTarget && (e.key === "Enter" || e.key === " ")) {
3561
+ e.preventDefault();
3562
+ focus();
3563
+ }
3564
+ },
3565
+ children: [
3566
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
3567
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-medium", children: thread.author.name }),
3568
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
3569
+ ] }),
3570
+ quote && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
3571
+ thread.orphaned && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
3572
+ thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
3573
+ !isDraft && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
3574
+ thread.replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
3575
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-xs", children: [
3576
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-medium", children: reply.author.name }),
3577
+ " ",
3578
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
3579
+ ] }),
3580
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
3581
+ ] }, reply.id)) }),
3582
+ (active || isDraft) && !thread.resolved && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-1", onClick: stop, children: [
3583
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3584
+ "textarea",
3585
+ {
3586
+ className: textareaClass,
3587
+ rows: 2,
3588
+ value: draft,
3589
+ "aria-label": isDraft ? "Comment" : "Reply",
3590
+ placeholder: isDraft ? "Add a comment\u2026" : "Reply\u2026",
3591
+ autoFocus: isDraft,
3592
+ onChange: (e) => setDraft(e.target.value),
3593
+ onKeyDown: (e) => {
3594
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3595
+ e.preventDefault();
3596
+ submit();
3597
+ }
3598
+ }
3599
+ }
3600
+ ),
3601
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-end gap-1", children: [
3602
+ isDraft && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
3603
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3604
+ "button",
3605
+ {
3606
+ type: "button",
3607
+ className: primaryButtonClass,
3608
+ disabled: !draft.trim(),
3609
+ onClick: submit,
3610
+ children: isDraft ? "Comment" : "Reply"
3611
+ }
3612
+ )
3613
+ ] })
3614
+ ] }),
3615
+ !isDraft && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-end gap-1", onClick: stop, children: [
3616
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
3617
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3618
+ "button",
3619
+ {
3620
+ type: "button",
3621
+ className: `${buttonClass} hover:text-destructive`,
3622
+ onClick: remove,
3623
+ children: "Delete"
3624
+ }
3625
+ )
3626
+ ] })
3627
+ ]
3628
+ }
3629
+ );
3630
+ };
3631
+
3632
+ // src/review/ReviewToolbar.tsx
3633
+ var import_jsx_runtime12 = require("react/jsx-runtime");
3634
+ var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
3635
+ var ReviewToolbar = ({
3636
+ editor,
3637
+ className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm"
3638
+ }) => {
3639
+ const review = useTrackChanges(editor);
3640
+ const count = review.changes.length;
3641
+ const current = review.currentChange;
3642
+ const index = current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0;
3643
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className, role: "toolbar", "aria-label": "Review", children: [
3644
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3645
+ "button",
3646
+ {
3647
+ type: "button",
3648
+ className: `${buttonClass2} ${review.enabled ? "bg-primary/10 text-primary" : ""}`,
3649
+ "aria-pressed": review.enabled,
3650
+ disabled: !editor,
3651
+ onClick: review.toggle,
3652
+ children: review.enabled ? "Tracking on" : "Track changes"
3653
+ }
3654
+ ),
3655
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
3656
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3657
+ "button",
3658
+ {
3659
+ type: "button",
3660
+ className: buttonClass2,
3661
+ "aria-label": "Previous change",
3662
+ disabled: count === 0,
3663
+ onClick: review.previous,
3664
+ children: "\u2039"
3665
+ }
3666
+ ),
3667
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
3668
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3669
+ "button",
3670
+ {
3671
+ type: "button",
3672
+ className: buttonClass2,
3673
+ "aria-label": "Next change",
3674
+ disabled: count === 0,
3675
+ onClick: review.next,
3676
+ children: "\u203A"
3677
+ }
3678
+ ),
3679
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
3680
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3681
+ "button",
3682
+ {
3683
+ type: "button",
3684
+ className: buttonClass2,
3685
+ disabled: !current,
3686
+ onClick: () => current && review.accept(current.id),
3687
+ children: "Accept"
3688
+ }
3689
+ ),
3690
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3691
+ "button",
3692
+ {
3693
+ type: "button",
3694
+ className: buttonClass2,
3695
+ disabled: !current,
3696
+ onClick: () => current && review.reject(current.id),
3697
+ children: "Reject"
3698
+ }
3699
+ ),
3700
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
3701
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
3702
+ ] });
3703
+ };
3704
+
3705
+ // src/review/CommentButton.tsx
3706
+ var import_jsx_runtime13 = require("react/jsx-runtime");
3707
+ var CommentButton = ({
3708
+ editor,
3709
+ className = "rounded px-2 py-1 text-xs text-popover-foreground hover:bg-muted",
3710
+ children = "Comment"
3711
+ }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3712
+ "button",
3713
+ {
3714
+ type: "button",
3715
+ className,
3716
+ disabled: !editor,
3717
+ onClick: () => editor?.chain().focus().addComment().run(),
3718
+ children
3719
+ }
3720
+ );
2695
3721
  // Annotate the CommonJS export names for ESM import in node:
2696
3722
  0 && (module.exports = {
2697
3723
  BubbleMenu,
3724
+ CommentButton,
3725
+ CommentsPanel,
2698
3726
  DirtyTracker,
2699
3727
  EditorContent,
2700
3728
  EditorSlotsProvider,
3729
+ FormulaHints,
2701
3730
  HistoryManager,
2702
3731
  ImageEditor,
3732
+ OutlinePanel,
3733
+ ReviewToolbar,
3734
+ SpreadsheetContextMenu,
2703
3735
  SpreadsheetEditor,
3736
+ SpreadsheetGroupRow,
2704
3737
  TemplateRenderer,
3738
+ VariableForm,
2705
3739
  createDirtyTracker,
2706
3740
  createHistoryManager,
2707
3741
  defaultIsEqual,
3742
+ useAgentToolProvider,
3743
+ useComments,
2708
3744
  useDismissGuard,
2709
3745
  useEditor,
2710
3746
  useEditorSlots,
3747
+ useFileImport,
3748
+ useFillHandle,
2711
3749
  useHistory,
2712
3750
  useHistoryShortcuts,
2713
3751
  useImageEditor,
2714
3752
  useNavigationGuard,
3753
+ useOutline,
2715
3754
  usePromptEditor,
2716
- useSpreadsheetEditor
3755
+ useSpreadsheetClipboard,
3756
+ useSpreadsheetEditor,
3757
+ useTrackChanges
2717
3758
  });
2718
3759
  //# sourceMappingURL=index.cjs.map