@pixerate/editor-react 0.17.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,8 +10,9 @@ import {
10
10
  SpreadsheetValidationDialog,
11
11
  useFillHandle,
12
12
  useSpreadsheetClipboard,
13
- useSpreadsheetEditor
14
- } from "./chunk-JR5ZBDR7.js";
13
+ useSpreadsheetEditor,
14
+ useSpreadsheetViewport
15
+ } from "./chunk-34HEZX7J.js";
15
16
  import {
16
17
  AdjustmentLayersPanel,
17
18
  ControlSlider,
@@ -613,10 +614,63 @@ function useFileImport(editor, options = {}) {
613
614
  };
614
615
  }
615
616
 
617
+ // src/hooks/useDocxExport.ts
618
+ import { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef7, useSyncExternalStore as useSyncExternalStore4 } from "react";
619
+ import {
620
+ DocxExportController
621
+ } from "@pixerate/editor/export";
622
+ function useDocxExport(editor, options = {}) {
623
+ const editorRef = useRef7(editor);
624
+ editorRef.current = editor;
625
+ const controller = useMemo4(
626
+ () => new DocxExportController(() => {
627
+ const current = editorRef.current;
628
+ return current && !current.isDestroyed ? current : null;
629
+ }, options),
630
+ []
631
+ );
632
+ useEffect6(() => {
633
+ controller.setOptions(options);
634
+ });
635
+ const state = useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
636
+ return {
637
+ ...state,
638
+ download: (fileName) => controller.download(fileName),
639
+ exportBlob: () => controller.exportBlob()
640
+ };
641
+ }
642
+
643
+ // src/components/ExportDocxButton.tsx
644
+ import { jsx } from "react/jsx-runtime";
645
+ var ExportDocxButton = ({
646
+ editor,
647
+ className = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-50",
648
+ children = "Export to Word",
649
+ onExported,
650
+ ...options
651
+ }) => {
652
+ const docx = useDocxExport(editor, options);
653
+ return /* @__PURE__ */ jsx(
654
+ "button",
655
+ {
656
+ type: "button",
657
+ className,
658
+ disabled: !editor || docx.isExporting,
659
+ "aria-busy": docx.isExporting || void 0,
660
+ title: docx.error ? `Export failed: ${docx.error.message}` : void 0,
661
+ onClick: async () => {
662
+ const report = await docx.download();
663
+ if (report) onExported?.(report);
664
+ },
665
+ children
666
+ }
667
+ );
668
+ };
669
+
616
670
  // src/hooks/useAgentToolProvider.ts
617
- import { useEffect as useEffect6 } from "react";
671
+ import { useEffect as useEffect7 } from "react";
618
672
  function useAgentToolProvider(registry, provider) {
619
- useEffect6(() => {
673
+ useEffect7(() => {
620
674
  if (!registry || !provider) return;
621
675
  return registry.register(provider);
622
676
  }, [registry, provider]);
@@ -626,17 +680,17 @@ function useAgentToolProvider(registry, provider) {
626
680
  import {
627
681
  EditorContent as TipTapEditorContent
628
682
  } from "@tiptap/react";
629
- import { jsx } from "react/jsx-runtime";
683
+ import { jsx as jsx2 } from "react/jsx-runtime";
630
684
  var EditorContent = ({
631
685
  className,
632
686
  editor,
633
687
  ...props
634
688
  }) => {
635
- return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(TipTapEditorContent, { editor, ...props }) });
689
+ return /* @__PURE__ */ jsx2("div", { className, children: /* @__PURE__ */ jsx2(TipTapEditorContent, { editor, ...props }) });
636
690
  };
637
691
 
638
692
  // src/components/BubbleMenu.tsx
639
- import { useEffect as useEffect7, useRef as useRef7, useState as useState4 } from "react";
693
+ import { useEffect as useEffect8, useRef as useRef8, useState as useState4 } from "react";
640
694
  import { createPortal } from "react-dom";
641
695
  import {
642
696
  BubbleMenuPlugin
@@ -650,12 +704,12 @@ var BubbleMenu = ({
650
704
  pluginKey = "bubbleMenu",
651
705
  updateDelay
652
706
  }) => {
653
- const menuEl = useRef7(null);
707
+ const menuEl = useRef8(null);
654
708
  const [mounted, setMounted] = useState4(false);
655
709
  if (!menuEl.current && typeof document !== "undefined") {
656
710
  menuEl.current = document.createElement("div");
657
711
  }
658
- useEffect7(() => {
712
+ useEffect8(() => {
659
713
  if (!editor || !menuEl.current || editor.isDestroyed) {
660
714
  return;
661
715
  }
@@ -686,14 +740,14 @@ var BubbleMenu = ({
686
740
  };
687
741
 
688
742
  // src/components/TemplateRenderer.tsx
689
- import { useMemo as useMemo4 } from "react";
743
+ import { useMemo as useMemo5 } from "react";
690
744
  import {
691
745
  describeConditionalTag,
692
746
  evaluatePromptTokens,
693
747
  formatConditionalTag,
694
748
  resolveTemplates
695
749
  } from "@pixerate/editor";
696
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
750
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
697
751
  var TemplateRenderer = ({
698
752
  content,
699
753
  templates = [],
@@ -705,14 +759,14 @@ var TemplateRenderer = ({
705
759
  onTemplateClick,
706
760
  onVariableClick
707
761
  }) => {
708
- const displayText = useMemo4(() => {
762
+ const displayText = useMemo5(() => {
709
763
  if (!resolve || templates.length === 0) return content;
710
764
  return resolveTemplates(content, templates, { bindings });
711
765
  }, [content, templates, resolve, bindings]);
712
- const tokens = useMemo4(() => {
766
+ const tokens = useMemo5(() => {
713
767
  return evaluatePromptTokens(displayText, values);
714
768
  }, [displayText, values]);
715
- return /* @__PURE__ */ jsx2("div", { className, children: tokens.map((token, index) => {
769
+ return /* @__PURE__ */ jsx3("div", { className, children: tokens.map((token, index) => {
716
770
  if (token.type === "template") {
717
771
  const colors = templateColorMap?.get(token.value) || {
718
772
  from: "#f43f5e",
@@ -722,7 +776,7 @@ var TemplateRenderer = ({
722
776
  "--color-from": colors.from,
723
777
  "--color-to": colors.to
724
778
  };
725
- return /* @__PURE__ */ jsx2(
779
+ return /* @__PURE__ */ jsx3(
726
780
  "span",
727
781
  {
728
782
  style,
@@ -741,7 +795,7 @@ var TemplateRenderer = ({
741
795
  token.defaultValue !== void 0 ? `Default: ${token.defaultValue || "(empty)"}` : void 0
742
796
  ].filter(Boolean).join("\n");
743
797
  if (token.resolvedValue !== void 0) {
744
- return /* @__PURE__ */ jsx2(
798
+ return /* @__PURE__ */ jsx3(
745
799
  "span",
746
800
  {
747
801
  onClick: () => onVariableClick?.(token.value),
@@ -763,7 +817,7 @@ var TemplateRenderer = ({
763
817
  title: details,
764
818
  children: [
765
819
  `{${token.value}`,
766
- token.defaultValue !== void 0 && /* @__PURE__ */ jsx2("span", { className: "opacity-60", children: `|${token.defaultValue}` }),
820
+ token.defaultValue !== void 0 && /* @__PURE__ */ jsx3("span", { className: "opacity-60", children: `|${token.defaultValue}` }),
767
821
  "}"
768
822
  ]
769
823
  },
@@ -771,7 +825,7 @@ var TemplateRenderer = ({
771
825
  );
772
826
  }
773
827
  if (token.type === "conditional" && token.conditional) {
774
- return /* @__PURE__ */ jsx2(
828
+ return /* @__PURE__ */ jsx3(
775
829
  "span",
776
830
  {
777
831
  "data-conditional": token.conditional,
@@ -783,7 +837,7 @@ var TemplateRenderer = ({
783
837
  );
784
838
  }
785
839
  if (token.type === "instruction") {
786
- return /* @__PURE__ */ jsx2(
840
+ return /* @__PURE__ */ jsx3(
787
841
  "span",
788
842
  {
789
843
  className: "inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300",
@@ -793,17 +847,17 @@ var TemplateRenderer = ({
793
847
  index
794
848
  );
795
849
  }
796
- return /* @__PURE__ */ jsx2("span", { children: token.value }, index);
850
+ return /* @__PURE__ */ jsx3("span", { children: token.value }, index);
797
851
  }) });
798
852
  };
799
853
 
800
854
  // src/components/VariableForm.tsx
801
- import { useId, useMemo as useMemo5, useState as useState5 } from "react";
855
+ import { useId, useMemo as useMemo6, useState as useState5 } from "react";
802
856
  import {
803
857
  compactVariableValues,
804
858
  getVariableFormFields
805
859
  } from "@pixerate/editor";
806
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
860
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
807
861
  var VariableForm = ({
808
862
  content,
809
863
  templates,
@@ -818,7 +872,7 @@ var VariableForm = ({
818
872
  () => defaultValues ?? {}
819
873
  );
820
874
  const entered = values ?? internal;
821
- const fields = useMemo5(
875
+ const fields = useMemo6(
822
876
  () => getVariableFormFields(content, { templates, bindings }),
823
877
  [content, templates, bindings]
824
878
  );
@@ -828,7 +882,7 @@ var VariableForm = ({
828
882
  onChange?.(compactVariableValues(next), next);
829
883
  };
830
884
  if (fields.length === 0) return null;
831
- return /* @__PURE__ */ jsx3("form", { className, onSubmit: (event) => event.preventDefault(), children: fields.map((field) => {
885
+ return /* @__PURE__ */ jsx4("form", { className, onSubmit: (event) => event.preventDefault(), children: fields.map((field) => {
832
886
  const id = `${idPrefix}-${field.name}`;
833
887
  if (field.conditionOnly) {
834
888
  return /* @__PURE__ */ jsxs2(
@@ -838,7 +892,7 @@ var VariableForm = ({
838
892
  className: "flex items-center gap-2",
839
893
  "data-variable-field": field.name,
840
894
  children: [
841
- /* @__PURE__ */ jsx3(
895
+ /* @__PURE__ */ jsx4(
842
896
  "input",
843
897
  {
844
898
  id,
@@ -848,7 +902,7 @@ var VariableForm = ({
848
902
  onChange: (event) => update(field.name, event.target.checked ? "true" : "")
849
903
  }
850
904
  ),
851
- /* @__PURE__ */ jsx3("span", { children: field.label })
905
+ /* @__PURE__ */ jsx4("span", { children: field.label })
852
906
  ]
853
907
  },
854
908
  field.name
@@ -863,9 +917,9 @@ var VariableForm = ({
863
917
  children: [
864
918
  /* @__PURE__ */ jsxs2("span", { className: "font-medium", children: [
865
919
  field.label,
866
- field.required && /* @__PURE__ */ jsx3("span", { className: "text-rose-500", "aria-hidden": "true", children: " *" })
920
+ field.required && /* @__PURE__ */ jsx4("span", { className: "text-rose-500", "aria-hidden": "true", children: " *" })
867
921
  ] }),
868
- /* @__PURE__ */ jsx3(
922
+ /* @__PURE__ */ jsx4(
869
923
  "input",
870
924
  {
871
925
  id,
@@ -889,7 +943,7 @@ var VariableForm = ({
889
943
  import { useId as useId2, useState as useState7 } from "react";
890
944
 
891
945
  // src/hooks/useMergePreview.ts
892
- import { useCallback as useCallback5, useMemo as useMemo6, useState as useState6 } from "react";
946
+ import { useCallback as useCallback5, useMemo as useMemo7, useState as useState6 } from "react";
893
947
  import {
894
948
  extractVariables,
895
949
  matchFields,
@@ -904,15 +958,15 @@ function useMergePreview(options) {
904
958
  const [overrides, setOverrides] = useState6({});
905
959
  const [index, setIndexState] = useState6(0);
906
960
  const [result, setResult] = useState6(null);
907
- const source = useMemo6(
961
+ const source = useMemo7(
908
962
  () => parseDataSource(sourceText, parseOptions),
909
963
  [sourceText, parseOptions]
910
964
  );
911
- const variables = useMemo6(
965
+ const variables = useMemo7(
912
966
  () => extractVariables(template, { templates, bindings }).map((v) => v.name),
913
967
  [template, templates, bindings]
914
968
  );
915
- const mapping = useMemo6(() => {
969
+ const mapping = useMemo7(() => {
916
970
  const merged = {
917
971
  ...matchFields(variables, source.fields).mapping
918
972
  };
@@ -926,7 +980,7 @@ function useMergePreview(options) {
926
980
  const count = source.records.length;
927
981
  const current = count === 0 ? 0 : Math.min(index, count - 1);
928
982
  const record = source.records[current];
929
- const values = useMemo6(
983
+ const values = useMemo7(
930
984
  () => record ? recordToValues(record, mapping) : {},
931
985
  [record, mapping]
932
986
  );
@@ -987,7 +1041,7 @@ function useMergePreview(options) {
987
1041
  }
988
1042
 
989
1043
  // src/components/MergePreview.tsx
990
- import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1044
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
991
1045
  function downloadText(text, fileName, type) {
992
1046
  if (typeof document === "undefined" || typeof URL.createObjectURL !== "function") {
993
1047
  return;
@@ -1036,8 +1090,8 @@ var MergePreview = ({
1036
1090
  };
1037
1091
  return /* @__PURE__ */ jsxs3("div", { className, "data-merge-preview": "", children: [
1038
1092
  /* @__PURE__ */ jsxs3("label", { htmlFor: `${id}-source`, className: "flex flex-col gap-1", children: [
1039
- /* @__PURE__ */ jsx4("span", { className: "font-medium", children: "Data source (CSV, TSV or JSON)" }),
1040
- /* @__PURE__ */ jsx4(
1093
+ /* @__PURE__ */ jsx5("span", { className: "font-medium", children: "Data source (CSV, TSV or JSON)" }),
1094
+ /* @__PURE__ */ jsx5(
1041
1095
  "textarea",
1042
1096
  {
1043
1097
  id: `${id}-source`,
@@ -1051,7 +1105,7 @@ var MergePreview = ({
1051
1105
  )
1052
1106
  ] }),
1053
1107
  /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-2", children: [
1054
- /* @__PURE__ */ jsx4(
1108
+ /* @__PURE__ */ jsx5(
1055
1109
  "input",
1056
1110
  {
1057
1111
  type: "file",
@@ -1064,18 +1118,18 @@ var MergePreview = ({
1064
1118
  }
1065
1119
  }
1066
1120
  ),
1067
- /* @__PURE__ */ jsx4("span", { "data-merge-summary": "", className: "opacity-70", children: `${source.records.length} record${source.records.length === 1 ? "" : "s"} \xB7 ${source.fields.length} field${source.fields.length === 1 ? "" : "s"}` })
1121
+ /* @__PURE__ */ jsx5("span", { "data-merge-summary": "", className: "opacity-70", children: `${source.records.length} record${source.records.length === 1 ? "" : "s"} \xB7 ${source.fields.length} field${source.fields.length === 1 ? "" : "s"}` })
1068
1122
  ] }),
1069
- source.warnings.length > 0 && merge.sourceText.trim() !== "" && /* @__PURE__ */ jsx4("ul", { "data-merge-warnings": "", className: "text-amber-600 text-xs", children: source.warnings.map((warning, i) => /* @__PURE__ */ jsx4("li", { children: warning.message }, i)) }),
1123
+ source.warnings.length > 0 && merge.sourceText.trim() !== "" && /* @__PURE__ */ jsx5("ul", { "data-merge-warnings": "", className: "text-amber-600 text-xs", children: source.warnings.map((warning, i) => /* @__PURE__ */ jsx5("li", { children: warning.message }, i)) }),
1070
1124
  merge.variables.length > 0 && /* @__PURE__ */ jsxs3("fieldset", { className: "flex flex-col gap-1", children: [
1071
- /* @__PURE__ */ jsx4("legend", { className: "font-medium", children: "Field mapping" }),
1125
+ /* @__PURE__ */ jsx5("legend", { className: "font-medium", children: "Field mapping" }),
1072
1126
  merge.variables.map((variable) => /* @__PURE__ */ jsxs3(
1073
1127
  "label",
1074
1128
  {
1075
1129
  className: "flex items-center gap-2",
1076
1130
  "data-merge-mapping": variable,
1077
1131
  children: [
1078
- /* @__PURE__ */ jsx4("span", { className: "min-w-24 font-mono", children: `{${variable}}` }),
1132
+ /* @__PURE__ */ jsx5("span", { className: "min-w-24 font-mono", children: `{${variable}}` }),
1079
1133
  /* @__PURE__ */ jsxs3(
1080
1134
  "select",
1081
1135
  {
@@ -1084,8 +1138,8 @@ var MergePreview = ({
1084
1138
  onChange: (event) => merge.setFieldMapping(variable, event.target.value || null),
1085
1139
  className: "px-1 py-0.5 rounded-md border border-violet-400/40 bg-transparent",
1086
1140
  children: [
1087
- /* @__PURE__ */ jsx4("option", { value: "", children: "(none)" }),
1088
- source.fields.map((field) => /* @__PURE__ */ jsx4("option", { value: field, children: field }, field))
1141
+ /* @__PURE__ */ jsx5("option", { value: "", children: "(none)" }),
1142
+ source.fields.map((field) => /* @__PURE__ */ jsx5("option", { value: field, children: field }, field))
1089
1143
  ]
1090
1144
  }
1091
1145
  )
@@ -1095,7 +1149,7 @@ var MergePreview = ({
1095
1149
  ))
1096
1150
  ] }),
1097
1151
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
1098
- /* @__PURE__ */ jsx4(
1152
+ /* @__PURE__ */ jsx5(
1099
1153
  "button",
1100
1154
  {
1101
1155
  type: "button",
@@ -1105,8 +1159,8 @@ var MergePreview = ({
1105
1159
  children: "Prev"
1106
1160
  }
1107
1161
  ),
1108
- /* @__PURE__ */ jsx4("span", { "data-merge-position": "", children: merge.count === 0 ? "No records" : `Record ${merge.index + 1} of ${merge.count}` }),
1109
- /* @__PURE__ */ jsx4(
1162
+ /* @__PURE__ */ jsx5("span", { "data-merge-position": "", children: merge.count === 0 ? "No records" : `Record ${merge.index + 1} of ${merge.count}` }),
1163
+ /* @__PURE__ */ jsx5(
1110
1164
  "button",
1111
1165
  {
1112
1166
  type: "button",
@@ -1117,7 +1171,7 @@ var MergePreview = ({
1117
1171
  }
1118
1172
  )
1119
1173
  ] }),
1120
- /* @__PURE__ */ jsx4("div", { "data-merge-output": "", children: /* @__PURE__ */ jsx4(
1174
+ /* @__PURE__ */ jsx5("div", { "data-merge-output": "", children: /* @__PURE__ */ jsx5(
1121
1175
  TemplateRenderer,
1122
1176
  {
1123
1177
  content,
@@ -1129,7 +1183,7 @@ var MergePreview = ({
1129
1183
  }
1130
1184
  ) }),
1131
1185
  /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-2", children: [
1132
- /* @__PURE__ */ jsx4(
1186
+ /* @__PURE__ */ jsx5(
1133
1187
  "button",
1134
1188
  {
1135
1189
  type: "button",
@@ -1144,9 +1198,9 @@ var MergePreview = ({
1144
1198
  }
1145
1199
  ),
1146
1200
  result && /* @__PURE__ */ jsxs3(Fragment, { children: [
1147
- /* @__PURE__ */ jsx4("span", { "data-merge-result": "", children: `${result.outputs.length} merged${result.skipped.length ? `, ${result.skipped.length} skipped` : ""}${result.truncated ? " (stopped early)" : ""}` }),
1148
- /* @__PURE__ */ jsx4("button", { type: "button", "data-merge-copy": "", onClick: copy, children: copied ? "Copied" : "Copy" }),
1149
- /* @__PURE__ */ jsx4(
1201
+ /* @__PURE__ */ jsx5("span", { "data-merge-result": "", children: `${result.outputs.length} merged${result.skipped.length ? `, ${result.skipped.length} skipped` : ""}${result.truncated ? " (stopped early)" : ""}` }),
1202
+ /* @__PURE__ */ jsx5("button", { type: "button", "data-merge-copy": "", onClick: copy, children: copied ? "Copied" : "Copy" }),
1203
+ /* @__PURE__ */ jsx5(
1150
1204
  "button",
1151
1205
  {
1152
1206
  type: "button",
@@ -1155,7 +1209,7 @@ var MergePreview = ({
1155
1209
  children: "Download .txt"
1156
1210
  }
1157
1211
  ),
1158
- /* @__PURE__ */ jsx4(
1212
+ /* @__PURE__ */ jsx5(
1159
1213
  "button",
1160
1214
  {
1161
1215
  type: "button",
@@ -1166,7 +1220,7 @@ var MergePreview = ({
1166
1220
  )
1167
1221
  ] })
1168
1222
  ] }),
1169
- result && /* @__PURE__ */ jsx4(
1223
+ result && /* @__PURE__ */ jsx5(
1170
1224
  "textarea",
1171
1225
  {
1172
1226
  readOnly: true,
@@ -1181,16 +1235,16 @@ var MergePreview = ({
1181
1235
  };
1182
1236
 
1183
1237
  // src/hooks/useOutline.ts
1184
- import { useMemo as useMemo7, useSyncExternalStore as useSyncExternalStore4 } from "react";
1238
+ import { useMemo as useMemo8, useSyncExternalStore as useSyncExternalStore5 } from "react";
1185
1239
  import { createOutlineStore } from "@pixerate/editor";
1186
1240
  function useOutline(editor) {
1187
- const store = useMemo7(() => createOutlineStore(editor), [editor]);
1188
- return useSyncExternalStore4(store.subscribe, store.getSnapshot, store.getSnapshot);
1241
+ const store = useMemo8(() => createOutlineStore(editor), [editor]);
1242
+ return useSyncExternalStore5(store.subscribe, store.getSnapshot, store.getSnapshot);
1189
1243
  }
1190
1244
 
1191
1245
  // src/components/OutlinePanel.tsx
1192
1246
  import { navigateToHeading } from "@pixerate/editor";
1193
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1247
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1194
1248
  var OutlinePanel = ({
1195
1249
  editor,
1196
1250
  title = "Outline",
@@ -1205,10 +1259,10 @@ var OutlinePanel = ({
1205
1259
  const { headings, activeId } = useOutline(editor);
1206
1260
  const visible = headings.filter((h) => h.level <= maxLevel);
1207
1261
  return /* @__PURE__ */ jsxs4("nav", { className, "aria-label": "Document outline", "data-outline-panel": "", children: [
1208
- title != null && /* @__PURE__ */ jsx5("div", { className: "pixerate-outline-title", children: title }),
1209
- visible.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "pixerate-outline-empty", children: emptyState }) : /* @__PURE__ */ jsx5("ul", { role: "list", style: { listStyle: "none", margin: 0, padding: 0 }, children: visible.map((heading) => {
1262
+ title != null && /* @__PURE__ */ jsx6("div", { className: "pixerate-outline-title", children: title }),
1263
+ visible.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "pixerate-outline-empty", children: emptyState }) : /* @__PURE__ */ jsx6("ul", { role: "list", style: { listStyle: "none", margin: 0, padding: 0 }, children: visible.map((heading) => {
1210
1264
  const active = heading.id === activeId;
1211
- return /* @__PURE__ */ jsx5("li", { "data-level": heading.level, children: /* @__PURE__ */ jsx5(
1265
+ return /* @__PURE__ */ jsx6("li", { "data-level": heading.level, children: /* @__PURE__ */ jsx6(
1212
1266
  "button",
1213
1267
  {
1214
1268
  type: "button",
@@ -1230,15 +1284,15 @@ var OutlinePanel = ({
1230
1284
  };
1231
1285
 
1232
1286
  // src/hooks/useParagraphStyles.ts
1233
- import { useMemo as useMemo8, useSyncExternalStore as useSyncExternalStore5 } from "react";
1287
+ import { useMemo as useMemo9, useSyncExternalStore as useSyncExternalStore6 } from "react";
1234
1288
  import {
1235
1289
  createParagraphStylesStore,
1236
1290
  getStyleRegistry
1237
1291
  } from "@pixerate/editor";
1238
1292
  function useParagraphStyles(editor) {
1239
- const store = useMemo8(() => createParagraphStylesStore(editor), [editor]);
1240
- const snapshot = useSyncExternalStore5(store.subscribe, store.getSnapshot, store.getSnapshot);
1241
- return useMemo8(() => {
1293
+ const store = useMemo9(() => createParagraphStylesStore(editor), [editor]);
1294
+ const snapshot = useSyncExternalStore6(store.subscribe, store.getSnapshot, store.getSnapshot);
1295
+ return useMemo9(() => {
1242
1296
  const registry = getStyleRegistry(editor);
1243
1297
  const live = () => editor && !editor.isDestroyed ? editor : null;
1244
1298
  return {
@@ -1262,7 +1316,7 @@ function useParagraphStyles(editor) {
1262
1316
 
1263
1317
  // src/components/StylesGallery.tsx
1264
1318
  import { useState as useState8 } from "react";
1265
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1319
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1266
1320
  var camel = (property) => property.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
1267
1321
  var keepSelection = (event) => event.preventDefault();
1268
1322
  var StylesGallery = ({
@@ -1326,9 +1380,9 @@ var StylesGallery = ({
1326
1380
  {
1327
1381
  className: variant === "dropdown" ? "absolute left-0 top-full z-20 mt-1 flex min-w-[12rem] flex-col gap-0.5 rounded-md border border-border bg-background p-1 shadow-md" : "flex flex-wrap items-center gap-1",
1328
1382
  children: [
1329
- /* @__PURE__ */ jsx6("div", { role: "listbox", "aria-label": "Styles", className: variant === "dropdown" ? "flex flex-col gap-0.5" : "flex flex-wrap gap-1", children: list.map((style) => {
1383
+ /* @__PURE__ */ jsx7("div", { role: "listbox", "aria-label": "Styles", className: variant === "dropdown" ? "flex flex-col gap-0.5" : "flex flex-wrap gap-1", children: list.map((style) => {
1330
1384
  const active = style.id === styles.currentStyleId;
1331
- return /* @__PURE__ */ jsx6(
1385
+ return /* @__PURE__ */ jsx7(
1332
1386
  "button",
1333
1387
  {
1334
1388
  type: "button",
@@ -1345,7 +1399,7 @@ var StylesGallery = ({
1345
1399
  );
1346
1400
  }) }),
1347
1401
  showActions && /* @__PURE__ */ jsxs5("div", { className: variant === "dropdown" ? "mt-1 flex flex-col gap-0.5 border-t border-border pt-1" : "flex items-center gap-1", children: [
1348
- /* @__PURE__ */ jsx6(
1402
+ /* @__PURE__ */ jsx7(
1349
1403
  "button",
1350
1404
  {
1351
1405
  type: "button",
@@ -1362,7 +1416,7 @@ var StylesGallery = ({
1362
1416
  }
1363
1417
  ),
1364
1418
  creating ? /* @__PURE__ */ jsxs5("form", { className: "flex items-center gap-1", onSubmit: create, children: [
1365
- /* @__PURE__ */ jsx6(
1419
+ /* @__PURE__ */ jsx7(
1366
1420
  "input",
1367
1421
  {
1368
1422
  "aria-label": "New style name",
@@ -1372,8 +1426,8 @@ var StylesGallery = ({
1372
1426
  onChange: (event) => setName(event.target.value)
1373
1427
  }
1374
1428
  ),
1375
- /* @__PURE__ */ jsx6("button", { type: "submit", className: itemClassName, disabled: !name.trim(), children: "Create" })
1376
- ] }) : /* @__PURE__ */ jsx6(
1429
+ /* @__PURE__ */ jsx7("button", { type: "submit", className: itemClassName, disabled: !name.trim(), children: "Create" })
1430
+ ] }) : /* @__PURE__ */ jsx7(
1377
1431
  "button",
1378
1432
  {
1379
1433
  type: "button",
@@ -1403,10 +1457,10 @@ import {
1403
1457
  } from "@pixerate/editor";
1404
1458
 
1405
1459
  // src/slots.tsx
1406
- import { createContext, useContext, useMemo as useMemo9 } from "react";
1407
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1460
+ import { createContext, useContext, useMemo as useMemo10 } from "react";
1461
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1408
1462
  var defaultSlots = {
1409
- Button: (props) => /* @__PURE__ */ jsx7(
1463
+ Button: (props) => /* @__PURE__ */ jsx8(
1410
1464
  "button",
1411
1465
  {
1412
1466
  type: "button",
@@ -1416,26 +1470,26 @@ var defaultSlots = {
1416
1470
  children: props.children
1417
1471
  }
1418
1472
  ),
1419
- Tooltip: (props) => /* @__PURE__ */ jsx7("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
1473
+ Tooltip: (props) => /* @__PURE__ */ jsx8("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
1420
1474
  NodeHeader: (props) => /* @__PURE__ */ jsxs6("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
1421
1475
  /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
1422
1476
  props.icon,
1423
- /* @__PURE__ */ jsx7("span", { className: "font-semibold", children: props.title })
1477
+ /* @__PURE__ */ jsx8("span", { className: "font-semibold", children: props.title })
1424
1478
  ] }),
1425
- props.onClose && /* @__PURE__ */ jsx7("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
1479
+ props.onClose && /* @__PURE__ */ jsx8("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
1426
1480
  ] })
1427
1481
  };
1428
1482
  var EditorSlotsContext = createContext(defaultSlots);
1429
1483
  function EditorSlotsProvider({ slots, children }) {
1430
- const mergedSlots = useMemo9(() => ({ ...defaultSlots, ...slots }), [slots]);
1431
- return /* @__PURE__ */ jsx7(EditorSlotsContext.Provider, { value: mergedSlots, children });
1484
+ const mergedSlots = useMemo10(() => ({ ...defaultSlots, ...slots }), [slots]);
1485
+ return /* @__PURE__ */ jsx8(EditorSlotsContext.Provider, { value: mergedSlots, children });
1432
1486
  }
1433
1487
  function useEditorSlots() {
1434
1488
  return useContext(EditorSlotsContext);
1435
1489
  }
1436
1490
 
1437
1491
  // src/review/useReview.ts
1438
- import { useCallback as useCallback6, useMemo as useMemo10, useSyncExternalStore as useSyncExternalStore6 } from "react";
1492
+ import { useCallback as useCallback6, useMemo as useMemo11, useSyncExternalStore as useSyncExternalStore7 } from "react";
1439
1493
  import {
1440
1494
  getCommentStore,
1441
1495
  getTrackChangesStore
@@ -1462,8 +1516,8 @@ function useComments(source) {
1462
1516
  [store]
1463
1517
  );
1464
1518
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
1465
- const snapshot = useSyncExternalStore6(subscribe, getSnapshot, getSnapshot);
1466
- return useMemo10(() => ({ ...snapshot, store }), [snapshot, store]);
1519
+ const snapshot = useSyncExternalStore7(subscribe, getSnapshot, getSnapshot);
1520
+ return useMemo11(() => ({ ...snapshot, store }), [snapshot, store]);
1467
1521
  }
1468
1522
  function useTrackChanges(editor) {
1469
1523
  const store = getTrackChangesStore(editor ?? null);
@@ -1472,8 +1526,8 @@ function useTrackChanges(editor) {
1472
1526
  [store]
1473
1527
  );
1474
1528
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
1475
- const snapshot = useSyncExternalStore6(subscribe, getSnapshot, getSnapshot);
1476
- return useMemo10(() => {
1529
+ const snapshot = useSyncExternalStore7(subscribe, getSnapshot, getSnapshot);
1530
+ return useMemo11(() => {
1477
1531
  const run = (fn) => () => {
1478
1532
  if (editor && !editor.isDestroyed) fn(editor);
1479
1533
  };
@@ -1501,7 +1555,7 @@ import {
1501
1555
  formatReviewTime,
1502
1556
  getCommentAuthor
1503
1557
  } from "@pixerate/editor";
1504
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1558
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1505
1559
  var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
1506
1560
  var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
1507
1561
  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";
@@ -1526,7 +1580,7 @@ var CommentsPanel = ({
1526
1580
  " open",
1527
1581
  resolvedCount > 0 ? ` \xB7 ${resolvedCount} resolved` : ""
1528
1582
  ] }),
1529
- resolvedCount > 0 && /* @__PURE__ */ jsx8(
1583
+ resolvedCount > 0 && /* @__PURE__ */ jsx9(
1530
1584
  "button",
1531
1585
  {
1532
1586
  type: "button",
@@ -1537,7 +1591,7 @@ var CommentsPanel = ({
1537
1591
  }
1538
1592
  )
1539
1593
  ] }),
1540
- visible.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx8("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ jsx8(
1594
+ visible.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx9("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ jsx9(
1541
1595
  CommentThreadCard,
1542
1596
  {
1543
1597
  thread,
@@ -1602,23 +1656,23 @@ var CommentThreadCard = ({
1602
1656
  },
1603
1657
  children: [
1604
1658
  /* @__PURE__ */ jsxs7("div", { className: "flex items-baseline justify-between gap-2", children: [
1605
- /* @__PURE__ */ jsx8("span", { className: "font-medium", children: thread.author.name }),
1606
- /* @__PURE__ */ jsx8("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
1659
+ /* @__PURE__ */ jsx9("span", { className: "font-medium", children: thread.author.name }),
1660
+ /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
1607
1661
  ] }),
1608
- quote && /* @__PURE__ */ jsx8("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
1609
- thread.orphaned && /* @__PURE__ */ jsx8("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
1610
- thread.resolved && /* @__PURE__ */ jsx8("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
1611
- !isDraft && /* @__PURE__ */ jsx8("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
1612
- thread.replies.length > 0 && /* @__PURE__ */ jsx8("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ jsxs7("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
1662
+ quote && /* @__PURE__ */ jsx9("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
1663
+ thread.orphaned && /* @__PURE__ */ jsx9("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
1664
+ thread.resolved && /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
1665
+ !isDraft && /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
1666
+ thread.replies.length > 0 && /* @__PURE__ */ jsx9("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ jsxs7("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
1613
1667
  /* @__PURE__ */ jsxs7("span", { className: "text-xs", children: [
1614
- /* @__PURE__ */ jsx8("span", { className: "font-medium", children: reply.author.name }),
1668
+ /* @__PURE__ */ jsx9("span", { className: "font-medium", children: reply.author.name }),
1615
1669
  " ",
1616
- /* @__PURE__ */ jsx8("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
1670
+ /* @__PURE__ */ jsx9("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
1617
1671
  ] }),
1618
- /* @__PURE__ */ jsx8("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
1672
+ /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
1619
1673
  ] }, reply.id)) }),
1620
1674
  (active || isDraft) && !thread.resolved && /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", onClick: stop, children: [
1621
- /* @__PURE__ */ jsx8(
1675
+ /* @__PURE__ */ jsx9(
1622
1676
  "textarea",
1623
1677
  {
1624
1678
  className: textareaClass,
@@ -1637,8 +1691,8 @@ var CommentThreadCard = ({
1637
1691
  }
1638
1692
  ),
1639
1693
  /* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-1", children: [
1640
- isDraft && /* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
1641
- /* @__PURE__ */ jsx8(
1694
+ isDraft && /* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
1695
+ /* @__PURE__ */ jsx9(
1642
1696
  "button",
1643
1697
  {
1644
1698
  type: "button",
@@ -1651,8 +1705,8 @@ var CommentThreadCard = ({
1651
1705
  ] })
1652
1706
  ] }),
1653
1707
  !isDraft && /* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-1", onClick: stop, children: [
1654
- /* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
1655
- /* @__PURE__ */ jsx8(
1708
+ /* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
1709
+ /* @__PURE__ */ jsx9(
1656
1710
  "button",
1657
1711
  {
1658
1712
  type: "button",
@@ -1668,7 +1722,7 @@ var CommentThreadCard = ({
1668
1722
  };
1669
1723
 
1670
1724
  // src/review/ReviewToolbar.tsx
1671
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1725
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1672
1726
  var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
1673
1727
  var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
1674
1728
  var ReviewToolbar = ({
@@ -1682,7 +1736,7 @@ var ReviewToolbar = ({
1682
1736
  const current = review.currentChange;
1683
1737
  const index = current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0;
1684
1738
  return /* @__PURE__ */ jsxs8("div", { className, role: "toolbar", "aria-label": "Review", children: [
1685
- /* @__PURE__ */ jsx9(
1739
+ /* @__PURE__ */ jsx10(
1686
1740
  "button",
1687
1741
  {
1688
1742
  type: "button",
@@ -1693,8 +1747,8 @@ var ReviewToolbar = ({
1693
1747
  children: review.enabled ? "Tracking on" : "Track changes"
1694
1748
  }
1695
1749
  ),
1696
- /* @__PURE__ */ jsx9("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1697
- /* @__PURE__ */ jsx9(
1750
+ /* @__PURE__ */ jsx10("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1751
+ /* @__PURE__ */ jsx10(
1698
1752
  "button",
1699
1753
  {
1700
1754
  type: "button",
@@ -1705,8 +1759,8 @@ var ReviewToolbar = ({
1705
1759
  children: "\u2039"
1706
1760
  }
1707
1761
  ),
1708
- /* @__PURE__ */ jsx9("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` }),
1709
- /* @__PURE__ */ jsx9(
1762
+ /* @__PURE__ */ jsx10("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` }),
1763
+ /* @__PURE__ */ jsx10(
1710
1764
  "button",
1711
1765
  {
1712
1766
  type: "button",
@@ -1729,8 +1783,8 @@ var ReviewToolbar = ({
1729
1783
  ]
1730
1784
  }
1731
1785
  ),
1732
- /* @__PURE__ */ jsx9("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1733
- /* @__PURE__ */ jsx9(
1786
+ /* @__PURE__ */ jsx10("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1787
+ /* @__PURE__ */ jsx10(
1734
1788
  "button",
1735
1789
  {
1736
1790
  type: "button",
@@ -1740,7 +1794,7 @@ var ReviewToolbar = ({
1740
1794
  children: "Accept"
1741
1795
  }
1742
1796
  ),
1743
- /* @__PURE__ */ jsx9(
1797
+ /* @__PURE__ */ jsx10(
1744
1798
  "button",
1745
1799
  {
1746
1800
  type: "button",
@@ -1750,18 +1804,18 @@ var ReviewToolbar = ({
1750
1804
  children: "Reject"
1751
1805
  }
1752
1806
  ),
1753
- /* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
1754
- /* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
1807
+ /* @__PURE__ */ jsx10("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
1808
+ /* @__PURE__ */ jsx10("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
1755
1809
  ] });
1756
1810
  };
1757
1811
 
1758
1812
  // src/review/CommentButton.tsx
1759
- import { jsx as jsx10 } from "react/jsx-runtime";
1813
+ import { jsx as jsx11 } from "react/jsx-runtime";
1760
1814
  var CommentButton = ({
1761
1815
  editor,
1762
1816
  className = "rounded px-2 py-1 text-xs text-popover-foreground hover:bg-muted",
1763
1817
  children = "Comment"
1764
- }) => /* @__PURE__ */ jsx10(
1818
+ }) => /* @__PURE__ */ jsx11(
1765
1819
  "button",
1766
1820
  {
1767
1821
  type: "button",
@@ -1773,13 +1827,13 @@ var CommentButton = ({
1773
1827
  );
1774
1828
 
1775
1829
  // src/review/useDocumentCompare.ts
1776
- import { useCallback as useCallback7, useMemo as useMemo11, useRef as useRef8, useState as useState10 } from "react";
1830
+ import { useCallback as useCallback7, useMemo as useMemo12, useRef as useRef9, useState as useState10 } from "react";
1777
1831
  import {
1778
1832
  applyDocumentComparison
1779
1833
  } from "@pixerate/editor";
1780
1834
  function useDocumentCompare(editor, options) {
1781
1835
  const [summary, setSummary] = useState10(null);
1782
- const defaults = useRef8(options);
1836
+ const defaults = useRef9(options);
1783
1837
  defaults.current = options;
1784
1838
  const compare = useCallback7(
1785
1839
  (original, revised, overrides) => {
@@ -1794,12 +1848,12 @@ function useDocumentCompare(editor, options) {
1794
1848
  [editor]
1795
1849
  );
1796
1850
  const clear = useCallback7(() => setSummary(null), []);
1797
- return useMemo11(() => ({ summary, compare, clear }), [summary, compare, clear]);
1851
+ return useMemo12(() => ({ summary, compare, clear }), [summary, compare, clear]);
1798
1852
  }
1799
1853
 
1800
1854
  // src/review/TrackedChangesList.tsx
1801
1855
  import { trackedChangeLabel } from "@pixerate/editor";
1802
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1856
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1803
1857
  var buttonClass3 = "rounded px-1.5 py-0.5 text-xs text-foreground hover:bg-muted disabled:opacity-40";
1804
1858
  var TrackedChangesList = ({
1805
1859
  editor,
@@ -1810,9 +1864,9 @@ var TrackedChangesList = ({
1810
1864
  }) => {
1811
1865
  const review = useTrackChanges(editor);
1812
1866
  if (review.changes.length === 0) {
1813
- return /* @__PURE__ */ jsx11("div", { className, "data-tracked-changes-list": "", children: /* @__PURE__ */ jsx11("div", { className: "pixerate-changes-empty text-xs text-muted-foreground", children: emptyState }) });
1867
+ return /* @__PURE__ */ jsx12("div", { className, "data-tracked-changes-list": "", children: /* @__PURE__ */ jsx12("div", { className: "pixerate-changes-empty text-xs text-muted-foreground", children: emptyState }) });
1814
1868
  }
1815
- return /* @__PURE__ */ jsx11("ul", { className, role: "list", "aria-label": "Tracked changes", "data-tracked-changes-list": "", children: review.changes.map((change) => {
1869
+ return /* @__PURE__ */ jsx12("ul", { className, role: "list", "aria-label": "Tracked changes", "data-tracked-changes-list": "", children: review.changes.map((change) => {
1816
1870
  const active = change.id === review.currentChangeId;
1817
1871
  return /* @__PURE__ */ jsxs9(
1818
1872
  "li",
@@ -1829,14 +1883,14 @@ var TrackedChangesList = ({
1829
1883
  title: `${change.author.name}: ${change.description}`,
1830
1884
  onClick: () => review.select(change.id),
1831
1885
  children: [
1832
- /* @__PURE__ */ jsx11("span", { className: "font-medium", children: trackedChangeLabel(change) }),
1886
+ /* @__PURE__ */ jsx12("span", { className: "font-medium", children: trackedChangeLabel(change) }),
1833
1887
  change.author.name ? ` \xB7 ${change.author.name}` : "",
1834
- /* @__PURE__ */ jsx11("span", { className: "block truncate text-muted-foreground", children: change.description })
1888
+ /* @__PURE__ */ jsx12("span", { className: "block truncate text-muted-foreground", children: change.description })
1835
1889
  ]
1836
1890
  }
1837
1891
  ),
1838
- /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass3, onClick: () => review.accept(change.id), children: "Accept" }),
1839
- /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass3, onClick: () => review.reject(change.id), children: "Reject" })
1892
+ /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass3, onClick: () => review.accept(change.id), children: "Accept" }),
1893
+ /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass3, onClick: () => review.reject(change.id), children: "Reject" })
1840
1894
  ]
1841
1895
  },
1842
1896
  `${change.type}:${change.id}`
@@ -1845,7 +1899,7 @@ var TrackedChangesList = ({
1845
1899
  };
1846
1900
 
1847
1901
  // src/find-replace/useFindReplace.ts
1848
- import { useCallback as useCallback8, useMemo as useMemo12, useSyncExternalStore as useSyncExternalStore7 } from "react";
1902
+ import { useCallback as useCallback8, useMemo as useMemo13, useSyncExternalStore as useSyncExternalStore8 } from "react";
1849
1903
  import {
1850
1904
  EMPTY_FIND_REPLACE_SNAPSHOT,
1851
1905
  getFindReplaceStore
@@ -1859,8 +1913,8 @@ function useFindReplace(editor) {
1859
1913
  [store]
1860
1914
  );
1861
1915
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_FIND_REPLACE_SNAPSHOT;
1862
- const snapshot = useSyncExternalStore7(subscribe, getSnapshot, getSnapshot);
1863
- return useMemo12(() => {
1916
+ const snapshot = useSyncExternalStore8(subscribe, getSnapshot, getSnapshot);
1917
+ return useMemo13(() => {
1864
1918
  const run = (fn) => {
1865
1919
  if (editor && !editor.isDestroyed && store) fn(editor);
1866
1920
  };
@@ -1884,9 +1938,9 @@ function useFindReplace(editor) {
1884
1938
  }
1885
1939
 
1886
1940
  // src/find-replace/FindReplaceBar.tsx
1887
- import { useEffect as useEffect8, useRef as useRef9, useState as useState11 } from "react";
1941
+ import { useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
1888
1942
  import { FIND_OPTION_TOGGLES, formatFindStatus } from "@pixerate/editor";
1889
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1943
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1890
1944
  var inputClass = "min-w-0 flex-1 rounded border border-input bg-background px-2 py-1 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring";
1891
1945
  var buttonClass4 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
1892
1946
  var toggleClass = (on) => `${buttonClass4} font-mono ${on ? "bg-primary/10 text-primary" : "text-muted-foreground"}`;
@@ -1899,10 +1953,10 @@ var FindReplaceBar = ({
1899
1953
  onClose
1900
1954
  }) => {
1901
1955
  const find = useFindReplace(editor);
1902
- const findInput = useRef9(null);
1956
+ const findInput = useRef10(null);
1903
1957
  const [moreOpen, setMoreOpen] = useState11(false);
1904
1958
  const visible = find.available && (alwaysOpen || find.open);
1905
- useEffect8(() => {
1959
+ useEffect9(() => {
1906
1960
  if (!visible || find.focusRequest === 0) return;
1907
1961
  findInput.current?.focus();
1908
1962
  findInput.current?.select();
@@ -1942,7 +1996,7 @@ var FindReplaceBar = ({
1942
1996
  };
1943
1997
  return /* @__PURE__ */ jsxs10("div", { className, role: "search", "aria-label": "Find and replace", onKeyDown: onBarKeyDown, children: [
1944
1998
  /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1", children: [
1945
- /* @__PURE__ */ jsx12(
1999
+ /* @__PURE__ */ jsx13(
1946
2000
  "button",
1947
2001
  {
1948
2002
  type: "button",
@@ -1953,7 +2007,7 @@ var FindReplaceBar = ({
1953
2007
  children: showReplace ? "\u25BE" : "\u25B8"
1954
2008
  }
1955
2009
  ),
1956
- /* @__PURE__ */ jsx12(
2010
+ /* @__PURE__ */ jsx13(
1957
2011
  "input",
1958
2012
  {
1959
2013
  ref: findInput,
@@ -1967,7 +2021,7 @@ var FindReplaceBar = ({
1967
2021
  onKeyDown: onFindKeyDown
1968
2022
  }
1969
2023
  ),
1970
- FIND_OPTION_TOGGLES.filter((t) => !t.advanced).map((t) => /* @__PURE__ */ jsx12(
2024
+ FIND_OPTION_TOGGLES.filter((t) => !t.advanced).map((t) => /* @__PURE__ */ jsx13(
1971
2025
  "button",
1972
2026
  {
1973
2027
  type: "button",
@@ -1980,7 +2034,7 @@ var FindReplaceBar = ({
1980
2034
  },
1981
2035
  t.key
1982
2036
  )),
1983
- /* @__PURE__ */ jsx12(
2037
+ /* @__PURE__ */ jsx13(
1984
2038
  "span",
1985
2039
  {
1986
2040
  className: `min-w-[5rem] px-1 text-center text-xs ${find.error ? "text-destructive" : "text-muted-foreground"}`,
@@ -1990,9 +2044,9 @@ var FindReplaceBar = ({
1990
2044
  children: status
1991
2045
  }
1992
2046
  ),
1993
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass4, "aria-label": "Previous match", disabled: !hasMatches, onClick: find.previous, children: "\u2191" }),
1994
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass4, "aria-label": "Next match", disabled: !hasMatches, onClick: find.next, children: "\u2193" }),
1995
- showMoreOptions && /* @__PURE__ */ jsx12(
2047
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass4, "aria-label": "Previous match", disabled: !hasMatches, onClick: find.previous, children: "\u2191" }),
2048
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass4, "aria-label": "Next match", disabled: !hasMatches, onClick: find.next, children: "\u2193" }),
2049
+ showMoreOptions && /* @__PURE__ */ jsx13(
1996
2050
  "button",
1997
2051
  {
1998
2052
  type: "button",
@@ -2003,10 +2057,10 @@ var FindReplaceBar = ({
2003
2057
  children: "\u2026"
2004
2058
  }
2005
2059
  ),
2006
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass4, "aria-label": "Close find", onClick: close, children: "\xD7" })
2060
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass4, "aria-label": "Close find", onClick: close, children: "\xD7" })
2007
2061
  ] }),
2008
2062
  showReplace && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 pl-7", children: [
2009
- /* @__PURE__ */ jsx12(
2063
+ /* @__PURE__ */ jsx13(
2010
2064
  "input",
2011
2065
  {
2012
2066
  type: "text",
@@ -2018,7 +2072,7 @@ var FindReplaceBar = ({
2018
2072
  onKeyDown: onReplaceKeyDown
2019
2073
  }
2020
2074
  ),
2021
- /* @__PURE__ */ jsx12(
2075
+ /* @__PURE__ */ jsx13(
2022
2076
  "button",
2023
2077
  {
2024
2078
  type: "button",
@@ -2028,11 +2082,11 @@ var FindReplaceBar = ({
2028
2082
  children: "Replace"
2029
2083
  }
2030
2084
  ),
2031
- /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass4, disabled: !hasMatches, onClick: find.replaceAll, children: "Replace all" }),
2032
- find.lastReplaceCount !== null && /* @__PURE__ */ jsx12("span", { className: "px-1 text-xs text-muted-foreground", "aria-live": "polite", children: find.lastReplaceCount === 1 ? "1 replaced" : `${find.lastReplaceCount} replaced` })
2085
+ /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass4, disabled: !hasMatches, onClick: find.replaceAll, children: "Replace all" }),
2086
+ find.lastReplaceCount !== null && /* @__PURE__ */ jsx13("span", { className: "px-1 text-xs text-muted-foreground", "aria-live": "polite", children: find.lastReplaceCount === 1 ? "1 replaced" : `${find.lastReplaceCount} replaced` })
2033
2087
  ] }),
2034
- showMoreOptions && moreOpen && /* @__PURE__ */ jsx12("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 pl-7 text-xs text-muted-foreground", children: FIND_OPTION_TOGGLES.filter((t) => t.advanced).map((t) => /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1", children: [
2035
- /* @__PURE__ */ jsx12(
2088
+ showMoreOptions && moreOpen && /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 pl-7 text-xs text-muted-foreground", children: FIND_OPTION_TOGGLES.filter((t) => t.advanced).map((t) => /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1", children: [
2089
+ /* @__PURE__ */ jsx13(
2036
2090
  "input",
2037
2091
  {
2038
2092
  type: "checkbox",
@@ -2044,6 +2098,149 @@ var FindReplaceBar = ({
2044
2098
  ] }, t.key)) })
2045
2099
  ] });
2046
2100
  };
2101
+
2102
+ // src/tables/useTableMenu.ts
2103
+ import { useMemo as useMemo14, useSyncExternalStore as useSyncExternalStore9 } from "react";
2104
+ import {
2105
+ createTableStore,
2106
+ getTableActionState,
2107
+ runTableAction
2108
+ } from "@pixerate/editor";
2109
+ function useTableMenu(editor) {
2110
+ const store = useMemo14(() => createTableStore(editor), [editor]);
2111
+ const snapshot = useSyncExternalStore9(store.subscribe, store.getSnapshot, store.getSnapshot);
2112
+ return useMemo14(
2113
+ () => ({
2114
+ ...snapshot,
2115
+ run: (action, params) => runTableAction(editor, action, params, { focus: true }),
2116
+ check: (action, params) => getTableActionState(editor, action, params)
2117
+ }),
2118
+ [snapshot, editor]
2119
+ );
2120
+ }
2121
+
2122
+ // src/tables/TableToolbar.tsx
2123
+ import React9 from "react";
2124
+ import { DEFAULT_TABLE_TOOLBAR } from "@pixerate/editor";
2125
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
2126
+ var keepSelection2 = (event) => event.preventDefault();
2127
+ var TableToolbar = ({
2128
+ editor,
2129
+ items = DEFAULT_TABLE_TOOLBAR,
2130
+ showWhenOutside = false,
2131
+ className = "flex flex-wrap items-center gap-1 text-xs",
2132
+ buttonClassName = "rounded px-2 py-1 text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent",
2133
+ activeButtonClassName = "bg-primary/10 text-primary",
2134
+ separatorClassName = "mx-1 h-4 w-px bg-border",
2135
+ onAction
2136
+ }) => {
2137
+ const table = useTableMenu(editor);
2138
+ if (!editor || !table.inTable && !showWhenOutside) return null;
2139
+ return /* @__PURE__ */ jsx14("div", { className, role: "toolbar", "aria-label": "Table", "data-table-toolbar": "", children: items.map((item, index) => {
2140
+ const status = table.check(item.action, item.params);
2141
+ const enabled = status === true;
2142
+ const active = item.active?.(table);
2143
+ const separator = index > 0 && items[index - 1].group !== item.group;
2144
+ return /* @__PURE__ */ jsxs11(React9.Fragment, { children: [
2145
+ separator && /* @__PURE__ */ jsx14("span", { className: separatorClassName, "aria-hidden": "true" }),
2146
+ /* @__PURE__ */ jsx14(
2147
+ "button",
2148
+ {
2149
+ type: "button",
2150
+ "data-action": item.id,
2151
+ "aria-label": item.title,
2152
+ title: enabled ? item.title : `${item.title}: ${status}`,
2153
+ ...item.active ? { "aria-pressed": !!active } : {},
2154
+ disabled: !enabled,
2155
+ className: active ? `${buttonClassName} ${activeButtonClassName}` : buttonClassName,
2156
+ onMouseDown: keepSelection2,
2157
+ onClick: () => {
2158
+ if (table.run(item.action, item.params)) onAction?.(item);
2159
+ },
2160
+ children: item.label
2161
+ }
2162
+ )
2163
+ ] }, item.id);
2164
+ }) });
2165
+ };
2166
+
2167
+ // src/tables/InsertTableGrid.tsx
2168
+ import { useState as useState12 } from "react";
2169
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
2170
+ var keepSelection3 = (event) => event.preventDefault();
2171
+ var InsertTableGrid = ({
2172
+ editor,
2173
+ rows = 8,
2174
+ cols = 8,
2175
+ withHeaderRow = true,
2176
+ className = "inline-flex flex-col gap-1 rounded-md border border-border bg-background p-2 text-xs shadow-md",
2177
+ cellClassName = "h-4 w-4 rounded-sm border border-border bg-background disabled:opacity-40",
2178
+ activeCellClassName = "border-primary bg-primary/20",
2179
+ onInsert
2180
+ }) => {
2181
+ const table = useTableMenu(editor);
2182
+ const [hover, setHover] = useState12(null);
2183
+ const status = table.actions.insertTable;
2184
+ const disabled = !editor || !status.enabled;
2185
+ const insert = (size) => {
2186
+ if (table.run("insertTable", { ...size, withHeaderRow })) onInsert?.(size);
2187
+ setHover(null);
2188
+ };
2189
+ return /* @__PURE__ */ jsxs12(
2190
+ "div",
2191
+ {
2192
+ className,
2193
+ role: "group",
2194
+ "aria-label": "Insert table",
2195
+ title: disabled ? status.reason : void 0,
2196
+ "data-insert-table-grid": "",
2197
+ onMouseLeave: () => setHover(null),
2198
+ children: [
2199
+ /* @__PURE__ */ jsx15(
2200
+ "div",
2201
+ {
2202
+ role: "grid",
2203
+ "aria-label": "Table size",
2204
+ style: { display: "grid", gridTemplateColumns: `repeat(${cols}, max-content)`, gap: 2 },
2205
+ children: Array.from({ length: rows * cols }, (_, i) => {
2206
+ const size = { rows: Math.floor(i / cols) + 1, cols: i % cols + 1 };
2207
+ const active = !!hover && size.rows <= hover.rows && size.cols <= hover.cols;
2208
+ return /* @__PURE__ */ jsx15(
2209
+ "button",
2210
+ {
2211
+ type: "button",
2212
+ "data-rows": size.rows,
2213
+ "data-cols": size.cols,
2214
+ "data-active": active,
2215
+ "aria-label": `${size.rows} \xD7 ${size.cols} table`,
2216
+ disabled,
2217
+ className: active ? `${cellClassName} ${activeCellClassName}` : cellClassName,
2218
+ onMouseDown: keepSelection3,
2219
+ onMouseEnter: () => setHover(size),
2220
+ onFocus: () => setHover(size),
2221
+ onClick: () => insert(size)
2222
+ },
2223
+ i
2224
+ );
2225
+ })
2226
+ }
2227
+ ),
2228
+ /* @__PURE__ */ jsx15("div", { "aria-live": "polite", "data-testid": "insert-table-size", children: disabled ? status.reason : hover ? `${hover.rows} \xD7 ${hover.cols}` : "Insert table" })
2229
+ ]
2230
+ }
2231
+ );
2232
+ };
2233
+
2234
+ // src/tables/TableBubbleMenu.tsx
2235
+ import { shouldShowTableBubbleMenu } from "@pixerate/editor";
2236
+ import { jsx as jsx16 } from "react/jsx-runtime";
2237
+ var TableBubbleMenu = ({
2238
+ editor,
2239
+ children,
2240
+ toolbarProps,
2241
+ pluginKey = "tableBubbleMenu",
2242
+ ...rest
2243
+ }) => /* @__PURE__ */ jsx16(BubbleMenu, { editor, pluginKey, shouldShow: shouldShowTableBubbleMenu, ...rest, children: children ?? /* @__PURE__ */ jsx16(TableToolbar, { editor, ...toolbarProps }) });
2047
2244
  export {
2048
2245
  AdjustmentLayersPanel,
2049
2246
  AssetMentionChips,
@@ -2056,6 +2253,7 @@ export {
2056
2253
  DirtyTracker2 as DirtyTracker,
2057
2254
  EditorContent,
2058
2255
  EditorSlotsProvider,
2256
+ ExportDocxButton,
2059
2257
  FindReplaceBar,
2060
2258
  FormulaHints,
2061
2259
  GenerationHistory,
@@ -2063,6 +2261,7 @@ export {
2063
2261
  Histogram,
2064
2262
  HistoryManager2 as HistoryManager,
2065
2263
  ImageEditor,
2264
+ InsertTableGrid,
2066
2265
  JobProgress,
2067
2266
  MergePreview,
2068
2267
  NameManager,
@@ -2079,6 +2278,8 @@ export {
2079
2278
  SpreadsheetGroupRow,
2080
2279
  SpreadsheetValidationDialog,
2081
2280
  StylesGallery,
2281
+ TableBubbleMenu,
2282
+ TableToolbar,
2082
2283
  TemplateRenderer,
2083
2284
  TrackedChangesList,
2084
2285
  VariableForm,
@@ -2093,6 +2294,7 @@ export {
2093
2294
  useComments,
2094
2295
  useDismissGuard,
2095
2296
  useDocumentCompare,
2297
+ useDocxExport,
2096
2298
  useEditor2 as useEditor,
2097
2299
  useEditorSlots,
2098
2300
  useFileImport,
@@ -2114,6 +2316,8 @@ export {
2114
2316
  useRegisterCommands,
2115
2317
  useSpreadsheetClipboard,
2116
2318
  useSpreadsheetEditor,
2319
+ useSpreadsheetViewport,
2320
+ useTableMenu,
2117
2321
  useTrackChanges,
2118
2322
  withoutDirty
2119
2323
  };