@arbidocs/blocks 0.4.99 → 0.4.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/{chunk-44TPPPRJ.cjs → chunk-FAGQTB3D.cjs} +958 -272
  2. package/dist/chunk-FAGQTB3D.cjs.map +1 -0
  3. package/dist/{chunk-C5RF47LD.js → chunk-G5JH2YMQ.js} +3 -3
  4. package/dist/{chunk-C5RF47LD.js.map → chunk-G5JH2YMQ.js.map} +1 -1
  5. package/dist/{chunk-4ZTIMQ7Z.js → chunk-MYALCKDR.js} +963 -277
  6. package/dist/chunk-MYALCKDR.js.map +1 -0
  7. package/dist/{chunk-MABEOI4S.cjs → chunk-Y5XW57U4.cjs} +5 -5
  8. package/dist/{chunk-MABEOI4S.cjs.map → chunk-Y5XW57U4.cjs.map} +1 -1
  9. package/dist/{chunk-3ICWR3BK.js → chunk-YGREW5TZ.js} +3 -3
  10. package/dist/{chunk-3ICWR3BK.js.map → chunk-YGREW5TZ.js.map} +1 -1
  11. package/dist/{chunk-QBEUCOC7.cjs → chunk-ZT2EBBSC.cjs} +5 -5
  12. package/dist/{chunk-QBEUCOC7.cjs.map → chunk-ZT2EBBSC.cjs.map} +1 -1
  13. package/dist/connect/docx.cjs +6 -6
  14. package/dist/connect/docx.js +4 -4
  15. package/dist/connect/index.cjs +3 -3
  16. package/dist/connect/index.js +2 -2
  17. package/dist/docx/NativeDocxEditor.d.ts +7 -2
  18. package/dist/docx/definitions/DefinedTerms.d.ts +1 -12
  19. package/dist/docx/definitions/commands.d.ts +3 -27
  20. package/dist/docx/index.cjs +38 -38
  21. package/dist/docx/index.d.ts +2 -1
  22. package/dist/docx/index.js +3 -3
  23. package/dist/docx/layout/GenerateImage.d.ts +50 -0
  24. package/dist/docx/layout/LayoutRibbon.d.ts +32 -0
  25. package/dist/docx/layout/LayoutTabs.d.ts +20 -0
  26. package/dist/docx/layout/PictureRibbon.d.ts +23 -0
  27. package/dist/docx/layout/TableKeys.d.ts +25 -0
  28. package/dist/docx/layout/TableRibbon.d.ts +11 -0
  29. package/dist/docx/layout/caption.d.ts +38 -0
  30. package/dist/docx/layout/presets.d.ts +40 -0
  31. package/dist/index.cjs +5 -5
  32. package/dist/index.js +1 -1
  33. package/package.json +5 -4
  34. package/src/docx/README.md +24 -3
  35. package/src/docx/layout/layout.css +120 -0
  36. package/src/docx/styles.css +2 -0
  37. package/dist/chunk-44TPPPRJ.cjs.map +0 -1
  38. package/dist/chunk-4ZTIMQ7Z.js.map +0 -1
@@ -12,6 +12,7 @@ var lucideReact = require('lucide-react');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
  var reactDom = require('react-dom');
14
14
  var template = require('@arbidocs/coedit/template');
15
+ var editor = require('@docx-editor.dev/core/editor');
15
16
  var server = require('react-dom/server');
16
17
  var Y = require('yjs');
17
18
  var coedit = require('@arbidocs/coedit');
@@ -896,18 +897,18 @@ function ReviewCardSlot({ children }) {
896
897
  var DocxEditorReview = Object.assign(DocxEditorReviewRail, { Card: ReviewCardSlot });
897
898
 
898
899
  // src/docx/mentions.ts
899
- function mentionCandidates(mentions2) {
900
+ function mentionCandidates(mentions) {
900
901
  return [
901
902
  { id: "arbi", label: "ARBI", kind: "assistant" },
902
- ...mentions2.filter((person) => person.name.trim()).map((person) => ({
903
+ ...mentions.filter((person) => person.name.trim()).map((person) => ({
903
904
  id: person.id,
904
905
  label: person.name,
905
906
  kind: person.kind === "agent" ? "assistant" : "user"
906
907
  }))
907
908
  ];
908
909
  }
909
- function mentionedDocxRecipients(text, mentions2) {
910
- return chunk7UTB4PHM_cjs.extractMentions(text, mentionCandidates(mentions2)).map((person) => person.id);
910
+ function mentionedDocxRecipients(text, mentions) {
911
+ return chunk7UTB4PHM_cjs.extractMentions(text, mentionCandidates(mentions)).map((person) => person.id);
911
912
  }
912
913
  var ActivityContext = React3.createContext({});
913
914
  function DocxCommentActivity() {
@@ -983,7 +984,7 @@ function DocxCommentActivity() {
983
984
  }
984
985
  function DocxMentions({
985
986
  children,
986
- mentions: mentions2 = [],
987
+ mentions = [],
987
988
  initialCommentId,
988
989
  requestAgent,
989
990
  getAgentStatus,
@@ -1042,7 +1043,7 @@ function DocxMentions({
1042
1043
  const [suggestions, setSuggestions] = React3.useState(null);
1043
1044
  const [selected, setSelected] = React3.useState(0);
1044
1045
  const candidates = chunk7UTB4PHM_cjs.filterMentionCandidates(
1045
- mentionCandidates(mentions2),
1046
+ mentionCandidates(mentions),
1046
1047
  suggestions?.query || ""
1047
1048
  ).slice(0, 8);
1048
1049
  React3.useEffect(() => {
@@ -1063,7 +1064,7 @@ function DocxMentions({
1063
1064
  }));
1064
1065
  };
1065
1066
  const hasAgent = submission.recipientIds.some(
1066
- (id) => id === "arbi" || mentions2.some((person) => person.id === id && person.kind === "agent")
1067
+ (id) => id === "arbi" || mentions.some((person) => person.id === id && person.kind === "agent")
1067
1068
  );
1068
1069
  if (hasAgent) update("delivering");
1069
1070
  void requestAgent({
@@ -1097,7 +1098,7 @@ function DocxMentions({
1097
1098
  "error"
1098
1099
  );
1099
1100
  });
1100
- }, [review.items, requestAgent, getAgentStatus, mentions2, notify]);
1101
+ }, [review.items, requestAgent, getAgentStatus, mentions, notify]);
1101
1102
  const choose = (name) => {
1102
1103
  const field = input.current;
1103
1104
  if (!field || !suggestions) return;
@@ -1115,7 +1116,7 @@ function DocxMentions({
1115
1116
  '[data-testid="review-draft-input"], [data-testid="review-reply-input"]'
1116
1117
  );
1117
1118
  if (!field || field.readOnly || !requestAgent) return false;
1118
- const recipientIds = mentionedDocxRecipients(field.value, mentions2);
1119
+ const recipientIds = mentionedDocxRecipients(field.value, mentions);
1119
1120
  if (!recipientIds.length) return false;
1120
1121
  pending.current = {
1121
1122
  ids: new Set(review.items.filter((i) => i.kind === "comment").map((i) => i.item.id)),
@@ -1162,7 +1163,7 @@ function DocxMentions({
1162
1163
  (entry) => entry.kind === "comment" && entry.parentId === commentId
1163
1164
  );
1164
1165
  const author = replies.at(-1)?.author || target.author;
1165
- const agent = mentions2.find((person) => person.kind === "agent" && person.name === author);
1166
+ const agent = mentions.find((person) => person.kind === "agent" && person.name === author);
1166
1167
  if (agent || author === "ARBI" || author === "Assistant")
1167
1168
  prefixMention(
1168
1169
  field,
@@ -1240,7 +1241,7 @@ function DocxMentions({
1240
1241
  onMouseDown: (event) => event.preventDefault(),
1241
1242
  onClick: (event) => {
1242
1243
  event.stopPropagation();
1243
- if (!mentionedDocxRecipients(field.value, mentions2).includes("arbi"))
1244
+ if (!mentionedDocxRecipients(field.value, mentions).includes("arbi"))
1244
1245
  prefixMention(field);
1245
1246
  else field.focus();
1246
1247
  },
@@ -1295,7 +1296,6 @@ function definitionTooltip(definition) {
1295
1296
  const line = `\u201C${definition.term}\u201D${definition.rest}`.replace(/\s+/g, " ").trim();
1296
1297
  return line.length > 255 ? `${line.slice(0, 254)}\u2026` : line;
1297
1298
  }
1298
- var definitionParagraphText = (definition) => `\u201C${definition.term}\u201D${definition.rest}`;
1299
1299
  function definitionParagraphXml(definition, bookmark, id = 0) {
1300
1300
  return `<w:p><w:bookmarkStart w:id="${id}" w:name="${chunkAOFATOT3_cjs.esc(bookmark)}"/>${textRun("\u201C")}${textRun(
1301
1301
  definition.term,
@@ -1416,95 +1416,6 @@ function insertDefinedTerm(port, definition, options = {}) {
1416
1416
  port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + definition.term.length });
1417
1417
  return { addedDefinition };
1418
1418
  }
1419
- var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1420
- var mentions = (text, term) => new RegExp(`(^|[^\\p{L}\\p{N}])${escapeRegExp(term)}($|[^\\p{L}\\p{N}])`, "iu").test(text);
1421
- function updateDefinitions(port, definitions) {
1422
- const result = {
1423
- added: [],
1424
- refreshed: [],
1425
- reordered: [],
1426
- unused: [],
1427
- unknown: []
1428
- };
1429
- let paragraphs = port.paragraphs();
1430
- const linked = new Set(
1431
- paragraphs.flatMap(
1432
- (p) => p.links.map((l) => l.anchor).filter((a) => a.startsWith(chunkAOFATOT3_cjs.DEFINITION_BOOKMARK_PREFIX))
1433
- )
1434
- );
1435
- const declared = /* @__PURE__ */ new Map();
1436
- for (const p of paragraphs)
1437
- for (const b of p.bookmarks)
1438
- if (b.startsWith(chunkAOFATOT3_cjs.DEFINITION_BOOKMARK_PREFIX) && !declared.has(b)) declared.set(b, p);
1439
- const factOf = (bookmark) => {
1440
- const id = chunkAOFATOT3_cjs.definitionIdOfBookmark(bookmark);
1441
- return id ? definitions.get(id) : void 0;
1442
- };
1443
- for (const bookmark of [...linked, ...declared.keys()])
1444
- if (!factOf(bookmark) && !result.unknown.includes(bookmark)) result.unknown.push(bookmark);
1445
- const section = findDefinitionsSection(paragraphs);
1446
- const rewrite = /* @__PURE__ */ new Map();
1447
- for (const [bookmark, paragraph] of declared) {
1448
- const fact = factOf(bookmark);
1449
- if (fact && paragraph.text !== definitionParagraphText(fact)) {
1450
- rewrite.set(paragraph.id, fact);
1451
- result.refreshed.push(fact.term);
1452
- }
1453
- }
1454
- if (section) {
1455
- let previous = null;
1456
- for (let i = section.start; i < section.end; i++) {
1457
- const p = paragraphs[i];
1458
- const term = termOf(p);
1459
- if (term === null) continue;
1460
- const bookmark = p.bookmarks.find((b) => b.startsWith(chunkAOFATOT3_cjs.DEFINITION_BOOKMARK_PREFIX));
1461
- const fact = bookmark ? factOf(bookmark) : void 0;
1462
- if (previous !== null && chunkAOFATOT3_cjs.compareTerms(term, previous) < 0 && fact) {
1463
- if (!rewrite.has(p.id)) result.reordered.push(fact.term);
1464
- rewrite.set(p.id, fact);
1465
- continue;
1466
- }
1467
- previous = term;
1468
- }
1469
- }
1470
- for (const id of [...rewrite.keys()].reverse()) {
1471
- const index = paragraphs.findIndex((p) => p.id === id);
1472
- const previous = paragraphs[index - 1];
1473
- const target = paragraphs[index];
1474
- if (!previous) continue;
1475
- port.deleteRange(
1476
- { paragraphId: previous.id, offset: previous.text.length },
1477
- { paragraphId: target.id, offset: target.text.length }
1478
- );
1479
- paragraphs = port.paragraphs();
1480
- }
1481
- const toAdd = [...rewrite.values()];
1482
- for (const bookmark of linked) {
1483
- const fact = factOf(bookmark);
1484
- if (fact && !declared.has(bookmark) && !toAdd.some((d) => d.id === fact.id)) {
1485
- toAdd.push(fact);
1486
- result.added.push(fact.term);
1487
- }
1488
- }
1489
- toAdd.sort((a, b) => chunkAOFATOT3_cjs.compareTerms(a.term, b.term));
1490
- for (const definition of toAdd) addDefinition(port, definition);
1491
- paragraphs = port.paragraphs();
1492
- const after = findDefinitionsSection(paragraphs);
1493
- if (after) {
1494
- const inSection = (i) => i >= after.title && i < after.end;
1495
- const outside = paragraphs.filter((_, i) => !inSection(i));
1496
- const linkedNow = new Set(outside.flatMap((p) => p.links.map((l) => l.anchor)));
1497
- const outsideText = outside.map((p) => p.text).join("\n");
1498
- for (let i = after.start; i < after.end; i++) {
1499
- const p = paragraphs[i];
1500
- const term = termOf(p);
1501
- if (term === null) continue;
1502
- const used = p.bookmarks.some((b) => linkedNow.has(b)) || mentions(outsideText, term);
1503
- if (!used) result.unused.push({ term, paragraphId: p.id });
1504
- }
1505
- }
1506
- return result;
1507
- }
1508
1419
  function definitionOfAnchor(anchor, definitions) {
1509
1420
  const id = chunkAOFATOT3_cjs.definitionIdOfBookmark(anchor.replace(/^#/, ""));
1510
1421
  return id ? definitions.get(id) : void 0;
@@ -1712,7 +1623,6 @@ var MAX_GROUPS = 8;
1712
1623
  var STALE_AFTER_MS = 3e4;
1713
1624
  var DEFAULT_DEFINED_TERMS_LABELS = {
1714
1625
  insert: "Defined term",
1715
- update: "Update definitions",
1716
1626
  search: "Search defined terms",
1717
1627
  allAgreements: "All definitions",
1718
1628
  loading: "Loading definitions\u2026",
@@ -1720,18 +1630,7 @@ var DEFAULT_DEFINED_TERMS_LABELS = {
1720
1630
  noMatch: "No defined term matches.",
1721
1631
  hint: "Enter to insert \xB7 Esc to close",
1722
1632
  preferred: "Preferred",
1723
- alsoIn: (source) => `Also defined${source ? ` in ${source}` : ""}`,
1724
- updated: "Definitions updated",
1725
- updateSummary: (r) => [
1726
- r.added.length ? `${r.added.length} added` : "",
1727
- r.refreshed.length ? `${r.refreshed.length} reworded` : "",
1728
- r.reordered.length ? `${r.reordered.length} reordered` : ""
1729
- ].filter(Boolean).join(" \xB7 ") || "The Definitions section is up to date.",
1730
- unusedTitle: "Defined but never used",
1731
- unusedNone: "Every definition in the section is used.",
1732
- unknownTitle: "Links to definitions that no longer exist",
1733
- show: "Show",
1734
- close: "Close"
1633
+ alsoIn: (source) => `Also defined${source ? ` in ${source}` : ""}`
1735
1634
  };
1736
1635
  var DefinedTermsContext = React3.createContext(null);
1737
1636
  var useDefinedTerms = () => React3.useContext(DefinedTermsContext);
@@ -1757,7 +1656,6 @@ function DefinedTermsProvider({
1757
1656
  const [labelId, setLabelId] = React3.useState(null);
1758
1657
  const [picker, setPicker] = React3.useState(null);
1759
1658
  const [selected, setSelected] = React3.useState(0);
1760
- const [report, setReport] = React3.useState(null);
1761
1659
  const marker = React3.useRef(null);
1762
1660
  const dismissedAt = React3.useRef(null);
1763
1661
  const refresh = React3.useCallback(
@@ -1916,31 +1814,9 @@ function DefinedTermsProvider({
1916
1814
  window.addEventListener("keydown", keys, true);
1917
1815
  return () => window.removeEventListener("keydown", keys, true);
1918
1816
  }, [picker, rows, selected, pick]);
1919
- const update = React3.useCallback(() => {
1920
- if (!editor || !editable) return;
1921
- void refresh(true).then((list) => {
1922
- const definitions = new Map(
1923
- list.flatMap((s) => {
1924
- const d = chunkAOFATOT3_cjs.toDefinition(s);
1925
- return d ? [[d.id, d]] : [];
1926
- })
1927
- );
1928
- const surface = editor.surface;
1929
- const before = surface?.state().selection?.head;
1930
- const paraId = before && surface?.session.paraIdOf(before.paragraphId);
1931
- const result = run(() => updateDefinitions(editorPort(editor), definitions));
1932
- const node = paraId && surface?.session.nodeIdOf(paraId);
1933
- if (result && before && node) {
1934
- const point = { paragraphId: node, offset: before.offset };
1935
- surface.setSelection({ anchor: point, head: point });
1936
- holdCaret(editor);
1937
- }
1938
- if (result) setReport(result);
1939
- });
1940
- }, [editor, editable, refresh, run]);
1941
1817
  const value = React3.useMemo(
1942
- () => load && editable ? { openPicker, update, labels: l } : null,
1943
- [load, editable, openPicker, update, l]
1818
+ () => load && editable ? { openPicker, labels: l } : null,
1819
+ [load, editable, openPicker, l]
1944
1820
  );
1945
1821
  return /* @__PURE__ */ jsxRuntime.jsxs(DefinedTermsContext.Provider, { value, children: [
1946
1822
  /* @__PURE__ */ jsxRuntime.jsx("span", { ref: marker, hidden: true }),
@@ -1975,50 +1851,7 @@ function DefinedTermsProvider({
1975
1851
  }
1976
1852
  ),
1977
1853
  document.body
1978
- ),
1979
- /* @__PURE__ */ jsxRuntime.jsx(ui.Dialog, { open: !!report, onOpenChange: (open) => !open && setReport(null), children: /* @__PURE__ */ jsxRuntime.jsxs(
1980
- ui.DialogContent,
1981
- {
1982
- className: "max-h-[85dvh] overflow-y-auto",
1983
- "data-testid": "docx-definitions-report",
1984
- children: [
1985
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogHeader, { children: [
1986
- /* @__PURE__ */ jsxRuntime.jsx(ui.DialogTitle, { children: l.updated }),
1987
- /* @__PURE__ */ jsxRuntime.jsx(ui.DialogDescription, { children: report && l.updateSummary(report) })
1988
- ] }),
1989
- report && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 text-sm", children: [
1990
- /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
1991
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-1 font-medium", children: l.unusedTitle }),
1992
- report.unused.length ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", "data-testid": "docx-definitions-unused", children: report.unused.map((u) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-2", children: [
1993
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: u.term }),
1994
- /* @__PURE__ */ jsxRuntime.jsx(
1995
- ui.Button,
1996
- {
1997
- size: "sm",
1998
- variant: "ghost",
1999
- onClick: () => {
2000
- const surface = editor?.surface;
2001
- surface?.setSelection({
2002
- anchor: { paragraphId: u.paragraphId, offset: 0 },
2003
- head: { paragraphId: u.paragraphId, offset: 0 }
2004
- });
2005
- surface?.revealParagraph(u.paragraphId);
2006
- setReport(null);
2007
- },
2008
- children: l.show
2009
- }
2010
- )
2011
- ] }, u.paragraphId)) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: l.unusedNone })
2012
- ] }),
2013
- report.unknown.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
2014
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-1 font-medium", children: l.unknownTitle }),
2015
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc pl-5 text-muted-foreground", children: report.unknown.map((name) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: name }, name)) })
2016
- ] })
2017
- ] }),
2018
- /* @__PURE__ */ jsxRuntime.jsx(ui.DialogFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "outline", onClick: () => setReport(null), children: l.close }) })
2019
- ]
2020
- }
2021
- ) })
1854
+ )
2022
1855
  ] });
2023
1856
  }
2024
1857
  function DefinedTermMenu({
@@ -2355,6 +2188,241 @@ function HouseTemplateProvider({
2355
2188
  );
2356
2189
  return /* @__PURE__ */ jsxRuntime.jsx(HouseTemplateContext.Provider, { value: enabled ? value : null, children });
2357
2190
  }
2191
+ function RibbonMenu({
2192
+ label,
2193
+ icon,
2194
+ children,
2195
+ className,
2196
+ testId,
2197
+ title
2198
+ }) {
2199
+ const [open, setOpen] = React3.useState(false);
2200
+ const element = React3.useRef(null);
2201
+ React3.useEffect(() => {
2202
+ if (!open) return;
2203
+ const dismiss = (event) => {
2204
+ if (!element.current?.contains(event.target)) setOpen(false);
2205
+ };
2206
+ const escape = (event) => {
2207
+ if (event.key === "Escape") setOpen(false);
2208
+ };
2209
+ document.addEventListener("pointerdown", dismiss);
2210
+ document.addEventListener("keydown", escape);
2211
+ return () => {
2212
+ document.removeEventListener("pointerdown", dismiss);
2213
+ document.removeEventListener("keydown", escape);
2214
+ };
2215
+ }, [open]);
2216
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
2217
+ /* @__PURE__ */ jsxRuntime.jsx(
2218
+ "button",
2219
+ {
2220
+ type: "button",
2221
+ className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
2222
+ "aria-label": label,
2223
+ title: title ?? label,
2224
+ "aria-expanded": open,
2225
+ "data-testid": testId,
2226
+ onMouseDown: (event) => event.preventDefault(),
2227
+ onClick: () => setOpen((value) => !value),
2228
+ children: icon
2229
+ }
2230
+ ),
2231
+ open && /* @__PURE__ */ jsxRuntime.jsx(
2232
+ "div",
2233
+ {
2234
+ className: "native-docx-ribbon-popover",
2235
+ role: "group",
2236
+ "aria-label": label,
2237
+ onMouseDown: (event) => {
2238
+ if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
2239
+ event.preventDefault();
2240
+ },
2241
+ children: typeof children === "function" ? children(() => setOpen(false)) : children
2242
+ }
2243
+ )
2244
+ ] });
2245
+ }
2246
+ var titleOf = (prompt) => {
2247
+ const line = prompt.trim().split("\n")[0];
2248
+ return line.length > 80 ? `${line.slice(0, 79)}\u2026` : line;
2249
+ };
2250
+ function rememberCaret(editor) {
2251
+ const surface = editor.surface;
2252
+ const head = surface?.state().selection?.head;
2253
+ if (!surface || !head) return null;
2254
+ return { paraId: surface.session.paraIdOf(head.paragraphId), ...head };
2255
+ }
2256
+ var mounted = [];
2257
+ var running = 0;
2258
+ var listeners = /* @__PURE__ */ new Set();
2259
+ var setRunning = (delta) => {
2260
+ running += delta;
2261
+ listeners.forEach((listener) => listener());
2262
+ };
2263
+ var subscribe = (listener) => {
2264
+ listeners.add(listener);
2265
+ return () => void listeners.delete(listener);
2266
+ };
2267
+ function insertionTarget(origin, caret, editors = mounted) {
2268
+ if (editors.includes(origin) && origin.surface) return origin;
2269
+ const paraId = caret?.paraId;
2270
+ if (!paraId) return null;
2271
+ for (let i = editors.length - 1; i >= 0; i--) {
2272
+ const session = editors[i].surface?.session;
2273
+ if (session?.nodeIdOf(paraId)) return editors[i];
2274
+ }
2275
+ return null;
2276
+ }
2277
+ function restoreCaret(editor, caret) {
2278
+ const surface = editor.surface;
2279
+ if (!surface || !caret) return;
2280
+ const paragraphId = caret.paraId && surface.session.nodeIdOf(caret.paraId) || caret.paragraphId;
2281
+ if (!surface.session.paragraphIds().includes(paragraphId)) return;
2282
+ const point = { paragraphId, offset: caret.offset };
2283
+ surface.setSelection({ anchor: point, head: point });
2284
+ }
2285
+ async function insertGeneratedImage(editor$1, bytes, prompt, caret) {
2286
+ const image = react.normalizeImageBytes(bytes);
2287
+ if (!image.ok) throw new Error("The generated image is not in a format Word can embed.");
2288
+ let result;
2289
+ for (let attempt = 0; attempt < 3 && !result?.ok; attempt++) {
2290
+ editor$1.focus();
2291
+ restoreCaret(editor$1, caret);
2292
+ result = await editor.executeImageCommand(editor$1, {
2293
+ type: "insertImage",
2294
+ data: image.bytes,
2295
+ mime: image.mime,
2296
+ widthPoints: image.widthPoints,
2297
+ heightPoints: image.heightPoints,
2298
+ description: prompt.trim(),
2299
+ title: titleOf(prompt)
2300
+ });
2301
+ if (!result.ok && result.reason !== "stale drawing selection") break;
2302
+ }
2303
+ if (!result?.ok) throw new Error(result?.reason || "The picture could not be inserted here.");
2304
+ }
2305
+ var AUTO = "";
2306
+ function useGenerateImageTarget() {
2307
+ const editor = react.useDocxEditor();
2308
+ const loaded2 = react.useEditorState((snapshot) => !snapshot.isLoading);
2309
+ React3.useEffect(() => {
2310
+ if (!editor || !loaded2) return;
2311
+ mounted.push(editor);
2312
+ return () => void mounted.splice(mounted.indexOf(editor), 1);
2313
+ }, [editor, loaded2]);
2314
+ }
2315
+ function GenerateImageButton({ generation }) {
2316
+ const editor = react.useDocxEditor();
2317
+ const [prompt, setPrompt] = React3.useState("");
2318
+ const [aspectRatio, setAspectRatio] = React3.useState(AUTO);
2319
+ const [busy, setBusy] = React3.useState(false);
2320
+ const [error, setError] = React3.useState("");
2321
+ const abort = React3.useRef(null);
2322
+ const close = React3.useRef(() => {
2323
+ });
2324
+ const editable = react.useEditorState((snapshot) => snapshot.editable);
2325
+ const generating = React3.useSyncExternalStore(subscribe, () => running > 0);
2326
+ async function run() {
2327
+ if (!editor || !prompt.trim() || busy) return;
2328
+ const caret = rememberCaret(editor);
2329
+ const controller = new AbortController();
2330
+ abort.current = controller;
2331
+ setBusy(true);
2332
+ setRunning(1);
2333
+ setError("");
2334
+ try {
2335
+ const bytes = await generation.generate(prompt.trim(), {
2336
+ aspectRatio: aspectRatio || void 0,
2337
+ signal: controller.signal
2338
+ });
2339
+ if (controller.signal.aborted) return;
2340
+ const target = insertionTarget(editor, caret);
2341
+ if (!target) throw new Error("The document was closed before the generated image was ready.");
2342
+ await insertGeneratedImage(target, bytes, prompt, caret);
2343
+ setPrompt("");
2344
+ close.current();
2345
+ generation.notify?.("Generated image inserted", "success");
2346
+ } catch (failure) {
2347
+ if (controller.signal.aborted) return;
2348
+ const message = failure instanceof Error ? failure.message : String(failure);
2349
+ setError(message);
2350
+ generation.notify?.(message, "error");
2351
+ } finally {
2352
+ abort.current = null;
2353
+ setRunning(-1);
2354
+ setBusy(false);
2355
+ }
2356
+ }
2357
+ if (!editable && !generating) return null;
2358
+ return /* @__PURE__ */ jsxRuntime.jsx(
2359
+ RibbonMenu,
2360
+ {
2361
+ label: generating ? "Generating image\u2026" : "Generate image",
2362
+ icon: generating ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "native-docx-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 16 }),
2363
+ testId: "docx-generate-image",
2364
+ children: (dismiss) => {
2365
+ close.current = dismiss;
2366
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2367
+ "form",
2368
+ {
2369
+ className: "native-docx-form native-docx-generate-image",
2370
+ onSubmit: (event) => {
2371
+ event.preventDefault();
2372
+ void run();
2373
+ },
2374
+ children: [
2375
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
2376
+ "Describe the image",
2377
+ /* @__PURE__ */ jsxRuntime.jsx(
2378
+ "textarea",
2379
+ {
2380
+ autoFocus: true,
2381
+ rows: 3,
2382
+ value: prompt,
2383
+ disabled: busy,
2384
+ placeholder: "A watercolour of a lighthouse at dawn",
2385
+ "data-testid": "docx-generate-image-prompt",
2386
+ onChange: (event) => setPrompt(event.target.value),
2387
+ onKeyDown: (event) => {
2388
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) void run();
2389
+ }
2390
+ }
2391
+ )
2392
+ ] }),
2393
+ generation.aspectRatios && generation.aspectRatios.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
2394
+ "Aspect ratio",
2395
+ /* @__PURE__ */ jsxRuntime.jsxs(
2396
+ "select",
2397
+ {
2398
+ value: aspectRatio,
2399
+ disabled: busy,
2400
+ onChange: (event) => setAspectRatio(event.target.value),
2401
+ children: [
2402
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: AUTO, children: "Auto" }),
2403
+ generation.aspectRatios.map((ratio) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: ratio.value, children: ratio.label }, ratio.value))
2404
+ ]
2405
+ }
2406
+ )
2407
+ ] }),
2408
+ busy && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", "data-testid": "docx-generate-image-status", children: "Generating\u2026 the picture is inserted where your cursor was. You can keep writing." }),
2409
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: error }),
2410
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-form-actions", children: busy ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => abort.current?.abort(), children: "Cancel" }) : /* @__PURE__ */ jsxRuntime.jsx(
2411
+ "button",
2412
+ {
2413
+ type: "submit",
2414
+ disabled: !prompt.trim(),
2415
+ "data-testid": "docx-generate-image-submit",
2416
+ children: "Generate"
2417
+ }
2418
+ ) })
2419
+ ]
2420
+ }
2421
+ );
2422
+ }
2423
+ }
2424
+ );
2425
+ }
2358
2426
  function wordDraftHtml(markdown) {
2359
2427
  const template = document.createElement("template");
2360
2428
  template.innerHTML = server.renderToStaticMarkup(/* @__PURE__ */ jsxRuntime.jsx(ui.AiMarkdown, { children: markdown }));
@@ -2442,61 +2510,6 @@ var WordDraftStream = class {
2442
2510
  }
2443
2511
  }
2444
2512
  };
2445
- function RibbonMenu({
2446
- label,
2447
- icon,
2448
- children,
2449
- className,
2450
- testId,
2451
- title
2452
- }) {
2453
- const [open, setOpen] = React3.useState(false);
2454
- const element = React3.useRef(null);
2455
- React3.useEffect(() => {
2456
- if (!open) return;
2457
- const dismiss = (event) => {
2458
- if (!element.current?.contains(event.target)) setOpen(false);
2459
- };
2460
- const escape = (event) => {
2461
- if (event.key === "Escape") setOpen(false);
2462
- };
2463
- document.addEventListener("pointerdown", dismiss);
2464
- document.addEventListener("keydown", escape);
2465
- return () => {
2466
- document.removeEventListener("pointerdown", dismiss);
2467
- document.removeEventListener("keydown", escape);
2468
- };
2469
- }, [open]);
2470
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-ribbon-menu", ref: element, children: [
2471
- /* @__PURE__ */ jsxRuntime.jsx(
2472
- "button",
2473
- {
2474
- type: "button",
2475
- className: ["native-docx-ribbon-trigger", className].filter(Boolean).join(" "),
2476
- "aria-label": label,
2477
- title: title ?? label,
2478
- "aria-expanded": open,
2479
- "data-testid": testId,
2480
- onMouseDown: (event) => event.preventDefault(),
2481
- onClick: () => setOpen((value) => !value),
2482
- children: icon
2483
- }
2484
- ),
2485
- open && /* @__PURE__ */ jsxRuntime.jsx(
2486
- "div",
2487
- {
2488
- className: "native-docx-ribbon-popover",
2489
- role: "group",
2490
- "aria-label": label,
2491
- onMouseDown: (event) => {
2492
- if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
2493
- event.preventDefault();
2494
- },
2495
- children: typeof children === "function" ? children(() => setOpen(false)) : children
2496
- }
2497
- )
2498
- ] });
2499
- }
2500
2513
 
2501
2514
  // src/docx/wordStyles.ts
2502
2515
  var QUICK_STYLES = ["Normal", "Title", "heading 1", "heading 2", "heading 3"];
@@ -2786,6 +2799,679 @@ function WordShortcuts({ scope }) {
2786
2799
  }, [scope]);
2787
2800
  return null;
2788
2801
  }
2802
+
2803
+ // src/docx/layout/presets.ts
2804
+ var TWIPS_PER_INCH = 1440;
2805
+ var MARGIN_PRESETS = [
2806
+ { id: "normal", label: "Normal", top: 1440, right: 1440, bottom: 1440, left: 1440 },
2807
+ { id: "narrow", label: "Narrow", top: 720, right: 720, bottom: 720, left: 720 },
2808
+ { id: "moderate", label: "Moderate", top: 1440, right: 1080, bottom: 1440, left: 1080 },
2809
+ { id: "wide", label: "Wide", top: 1440, right: 2880, bottom: 1440, left: 2880 }
2810
+ ];
2811
+ var PAPER_SIZES = [
2812
+ { id: "letter", label: "Letter", width: 12240, height: 15840 },
2813
+ { id: "legal", label: "Legal", width: 12240, height: 20160 },
2814
+ { id: "a4", label: "A4", width: 11906, height: 16838 },
2815
+ { id: "a5", label: "A5", width: 8391, height: 11906 },
2816
+ { id: "executive", label: "Executive", width: 10440, height: 15120 }
2817
+ ];
2818
+ var TOLERANCE = 20;
2819
+ var near = (a, b) => Math.abs(a - b) <= TOLERANCE;
2820
+ function matchMargins(setup) {
2821
+ if (!setup) return null;
2822
+ const m = setup.marginsTwips;
2823
+ return MARGIN_PRESETS.find(
2824
+ (p) => near(p.top, m.top) && near(p.right, m.right) && near(p.bottom, m.bottom) && near(p.left, m.left)
2825
+ )?.id ?? null;
2826
+ }
2827
+ function matchPaper(setup) {
2828
+ if (!setup) return null;
2829
+ const short = Math.min(setup.pageWidthTwips, setup.pageHeightTwips);
2830
+ const long = Math.max(setup.pageWidthTwips, setup.pageHeightTwips);
2831
+ return PAPER_SIZES.find((p) => near(p.width, short) && near(p.height, long))?.id ?? null;
2832
+ }
2833
+ function orientedSize(paper, orientation) {
2834
+ return orientation === "landscape" ? { pageWidthTwips: paper.height, pageHeightTwips: paper.width } : { pageWidthTwips: paper.width, pageHeightTwips: paper.height };
2835
+ }
2836
+ function inches(twips) {
2837
+ return `${Number((twips / TWIPS_PER_INCH).toFixed(2))}"`;
2838
+ }
2839
+ function paperDescription(paper) {
2840
+ return `${inches(paper.width)} \xD7 ${inches(paper.height)}`;
2841
+ }
2842
+ var COLUMNS_UNSUPPORTED = "Text columns are not available in this editor yet. Documents that already use columns display and save them unchanged.";
2843
+ function RibbonGallery({
2844
+ label,
2845
+ choices,
2846
+ current,
2847
+ onPick,
2848
+ close,
2849
+ testId
2850
+ }) {
2851
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "native-docx-gallery", role: "menu", "aria-label": label, "data-testid": testId, children: choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsxs(
2852
+ "button",
2853
+ {
2854
+ type: "button",
2855
+ role: "menuitemradio",
2856
+ "aria-checked": current === choice.id,
2857
+ disabled: choice.disabled,
2858
+ title: choice.title,
2859
+ "data-choice": choice.id,
2860
+ onClick: () => {
2861
+ onPick(choice.id);
2862
+ close();
2863
+ },
2864
+ children: [
2865
+ choice.icon,
2866
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "native-docx-gallery-text", children: [
2867
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-gallery-label", children: choice.label }),
2868
+ choice.detail && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "native-docx-gallery-detail", children: choice.detail })
2869
+ ] })
2870
+ ]
2871
+ },
2872
+ choice.id
2873
+ )) });
2874
+ }
2875
+ var marginDetail = (p) => `Top ${inches(p.top)} \xB7 Bottom ${inches(p.bottom)} \xB7 Left ${inches(p.left)} \xB7 Right ${inches(p.right)}`;
2876
+ function Margins({ setup, apply, openDialog }) {
2877
+ return /* @__PURE__ */ jsxRuntime.jsx(RibbonMenu, { label: "Margins", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Scaling, { size: 16 }), testId: "docx-layout-margins", children: (close) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2878
+ /* @__PURE__ */ jsxRuntime.jsx(
2879
+ RibbonGallery,
2880
+ {
2881
+ label: "Margins",
2882
+ current: matchMargins(setup),
2883
+ close,
2884
+ choices: MARGIN_PRESETS.map((p) => ({
2885
+ id: p.id,
2886
+ label: p.label,
2887
+ detail: marginDetail(p)
2888
+ })),
2889
+ onPick: (id) => {
2890
+ const p = MARGIN_PRESETS.find((m) => m.id === id);
2891
+ apply({
2892
+ marginTopTwips: p.top,
2893
+ marginRightTwips: p.right,
2894
+ marginBottomTwips: p.bottom,
2895
+ marginLeftTwips: p.left,
2896
+ scope: "section"
2897
+ });
2898
+ }
2899
+ }
2900
+ ),
2901
+ /* @__PURE__ */ jsxRuntime.jsx(
2902
+ "button",
2903
+ {
2904
+ type: "button",
2905
+ className: "native-docx-gallery-more",
2906
+ onClick: () => {
2907
+ close();
2908
+ openDialog();
2909
+ },
2910
+ children: "Custom margins\u2026"
2911
+ }
2912
+ )
2913
+ ] }) });
2914
+ }
2915
+ function Orientation({ setup, apply }) {
2916
+ return /* @__PURE__ */ jsxRuntime.jsx(
2917
+ RibbonMenu,
2918
+ {
2919
+ label: "Orientation",
2920
+ icon: setup?.orientation === "landscape" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RectangleHorizontal, { size: 16 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RectangleVertical, { size: 16 }),
2921
+ testId: "docx-layout-orientation",
2922
+ children: (close) => /* @__PURE__ */ jsxRuntime.jsx(
2923
+ RibbonGallery,
2924
+ {
2925
+ label: "Orientation",
2926
+ current: setup?.orientation,
2927
+ close,
2928
+ choices: [
2929
+ { id: "portrait", label: "Portrait", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RectangleVertical, { size: 16 }) },
2930
+ { id: "landscape", label: "Landscape", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RectangleHorizontal, { size: 16 }) }
2931
+ ],
2932
+ onPick: (id) => apply({ orientation: id, scope: "section" })
2933
+ }
2934
+ )
2935
+ }
2936
+ );
2937
+ }
2938
+ function Size({ setup, apply, openDialog }) {
2939
+ return /* @__PURE__ */ jsxRuntime.jsx(RibbonMenu, { label: "Size", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 16 }), testId: "docx-layout-size", children: (close) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2940
+ /* @__PURE__ */ jsxRuntime.jsx(
2941
+ RibbonGallery,
2942
+ {
2943
+ label: "Size",
2944
+ current: matchPaper(setup),
2945
+ close,
2946
+ choices: PAPER_SIZES.map((p) => ({
2947
+ id: p.id,
2948
+ label: p.label,
2949
+ detail: paperDescription(p)
2950
+ })),
2951
+ onPick: (id) => apply({
2952
+ ...orientedSize(
2953
+ PAPER_SIZES.find((p) => p.id === id),
2954
+ setup?.orientation
2955
+ ),
2956
+ scope: "section"
2957
+ })
2958
+ }
2959
+ ),
2960
+ /* @__PURE__ */ jsxRuntime.jsx(
2961
+ "button",
2962
+ {
2963
+ type: "button",
2964
+ className: "native-docx-gallery-more",
2965
+ onClick: () => {
2966
+ close();
2967
+ openDialog();
2968
+ },
2969
+ children: "More paper sizes\u2026"
2970
+ }
2971
+ )
2972
+ ] }) });
2973
+ }
2974
+ var COLUMN_BREAK = { type: "insertBreak", kind: "column" };
2975
+ function Breaks() {
2976
+ const page = react.useEditorCommand("insert.pageBreak");
2977
+ const nextPage = react.useEditorCommand("insert.sectionBreakNextPage");
2978
+ const continuous = react.useEditorCommand("insert.sectionBreakContinuous");
2979
+ const column = react.useEditorCommand(COLUMN_BREAK);
2980
+ const rows = [
2981
+ { id: "page", label: "Page", detail: "Start the next page here", command: page },
2982
+ { id: "column", label: "Column", detail: "Start the next column here", command: column },
2983
+ {
2984
+ id: "nextPage",
2985
+ label: "Next Page",
2986
+ detail: "Section break: new section on the next page",
2987
+ command: nextPage
2988
+ },
2989
+ {
2990
+ id: "continuous",
2991
+ label: "Continuous",
2992
+ detail: "Section break: new section on the same page",
2993
+ command: continuous
2994
+ }
2995
+ ];
2996
+ return /* @__PURE__ */ jsxRuntime.jsx(RibbonMenu, { label: "Breaks", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SeparatorHorizontal, { size: 16 }), testId: "docx-layout-breaks", children: (close) => /* @__PURE__ */ jsxRuntime.jsx(
2997
+ RibbonGallery,
2998
+ {
2999
+ label: "Breaks",
3000
+ close,
3001
+ choices: rows.map((row) => ({
3002
+ id: row.id,
3003
+ label: row.label,
3004
+ detail: row.detail,
3005
+ disabled: !row.command.isEnabled,
3006
+ title: row.command.disabledReason ?? void 0
3007
+ })),
3008
+ onPick: (id) => void rows.find((row) => row.id === id)?.command.execute()
3009
+ }
3010
+ ) });
3011
+ }
3012
+ function LayoutButtons() {
3013
+ const { pageSetup, apply } = react.usePageSetup();
3014
+ const [dialog, setDialog] = React3.useState(false);
3015
+ const props = { setup: pageSetup, apply, openDialog: () => setDialog(true) };
3016
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3017
+ /* @__PURE__ */ jsxRuntime.jsx(Margins, { ...props }),
3018
+ /* @__PURE__ */ jsxRuntime.jsx(Orientation, { ...props }),
3019
+ /* @__PURE__ */ jsxRuntime.jsx(Size, { ...props }),
3020
+ /* @__PURE__ */ jsxRuntime.jsx(
3021
+ react.DocxEditor.Toolbar.Action,
3022
+ {
3023
+ label: "Columns",
3024
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Columns2, { size: 16 }),
3025
+ disabled: true,
3026
+ disabledReason: COLUMNS_UNSUPPORTED,
3027
+ className: "native-docx-layout-columns"
3028
+ }
3029
+ ),
3030
+ /* @__PURE__ */ jsxRuntime.jsx(Breaks, {}),
3031
+ /* @__PURE__ */ jsxRuntime.jsx(
3032
+ react.DocxEditor.Toolbar.Action,
3033
+ {
3034
+ label: "Page Setup\u2026",
3035
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, { size: 16 }),
3036
+ onSelect: () => setDialog(true),
3037
+ className: "native-docx-layout-page-setup"
3038
+ }
3039
+ ),
3040
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.PageSetupDialog, { open: dialog, onClose: () => setDialog(false) })
3041
+ ] });
3042
+ }
3043
+ var ROW_ABOVE = { type: "insertRow", where: "above" };
3044
+ var ROW_BELOW = { type: "insertRow", where: "below" };
3045
+ var COLUMN_LEFT = { type: "insertColumn", where: "left" };
3046
+ var COLUMN_RIGHT = { type: "insertColumn", where: "right" };
3047
+ var MERGE = { type: "mergeCells" };
3048
+ var SPLIT = { type: "splitCell", rows: 1, cols: 2 };
3049
+ var HEADER_ROW = { type: "toggleHeaderRow" };
3050
+ var DELETE_ROW = { type: "deleteRow" };
3051
+ var DELETE_COLUMN = { type: "deleteColumn" };
3052
+ var DELETE_TABLE = { type: "deleteTable" };
3053
+ var ALIGN_TOP = { type: "setTableCellVerticalAlignment", alignment: "top" };
3054
+ var ALIGN_CENTER = { type: "setTableCellVerticalAlignment", alignment: "center" };
3055
+ var ALIGN_BOTTOM = { type: "setTableCellVerticalAlignment", alignment: "bottom" };
3056
+ function useRows(rows) {
3057
+ return rows.map((row) => ({ ...row, state: react.useEditorCommand(row.command) }));
3058
+ }
3059
+ function CommandGallery({
3060
+ label,
3061
+ icon,
3062
+ testId,
3063
+ rows
3064
+ }) {
3065
+ const bound = useRows(rows);
3066
+ return /* @__PURE__ */ jsxRuntime.jsx(RibbonMenu, { label, icon, testId, children: (close) => /* @__PURE__ */ jsxRuntime.jsx(
3067
+ RibbonGallery,
3068
+ {
3069
+ label,
3070
+ close,
3071
+ choices: bound.map((row) => ({
3072
+ id: row.id,
3073
+ label: row.label,
3074
+ icon: row.icon,
3075
+ disabled: !row.state.isEnabled,
3076
+ title: row.state.disabledReason ?? void 0
3077
+ })),
3078
+ onPick: (id) => void bound.find((row) => row.id === id)?.state.execute()
3079
+ }
3080
+ ) });
3081
+ }
3082
+ function CommandAction({
3083
+ command,
3084
+ label,
3085
+ icon,
3086
+ testId
3087
+ }) {
3088
+ const state = react.useEditorCommand(command);
3089
+ return /* @__PURE__ */ jsxRuntime.jsx(
3090
+ react.DocxEditor.Toolbar.Action,
3091
+ {
3092
+ label,
3093
+ icon,
3094
+ active: state.isActive,
3095
+ disabled: !state.isEnabled,
3096
+ disabledReason: state.disabledReason ?? void 0,
3097
+ onSelect: () => void state.execute(),
3098
+ className: `native-docx-table-${testId}`
3099
+ }
3100
+ );
3101
+ }
3102
+ function TableButtons() {
3103
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3104
+ /* @__PURE__ */ jsxRuntime.jsx(
3105
+ CommandAction,
3106
+ {
3107
+ command: ROW_ABOVE,
3108
+ label: "Insert row above",
3109
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BetweenHorizontalStart, { size: 16 }),
3110
+ testId: "row-above"
3111
+ }
3112
+ ),
3113
+ /* @__PURE__ */ jsxRuntime.jsx(
3114
+ CommandAction,
3115
+ {
3116
+ command: ROW_BELOW,
3117
+ label: "Insert row below",
3118
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BetweenHorizontalEnd, { size: 16 }),
3119
+ testId: "row-below"
3120
+ }
3121
+ ),
3122
+ /* @__PURE__ */ jsxRuntime.jsx(
3123
+ CommandAction,
3124
+ {
3125
+ command: COLUMN_LEFT,
3126
+ label: "Insert column left",
3127
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BetweenVerticalStart, { size: 16 }),
3128
+ testId: "column-left"
3129
+ }
3130
+ ),
3131
+ /* @__PURE__ */ jsxRuntime.jsx(
3132
+ CommandAction,
3133
+ {
3134
+ command: COLUMN_RIGHT,
3135
+ label: "Insert column right",
3136
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BetweenVerticalEnd, { size: 16 }),
3137
+ testId: "column-right"
3138
+ }
3139
+ ),
3140
+ /* @__PURE__ */ jsxRuntime.jsx(
3141
+ CommandGallery,
3142
+ {
3143
+ label: "Delete",
3144
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 16 }),
3145
+ testId: "docx-table-delete",
3146
+ rows: [
3147
+ { id: "row", label: "Delete row", command: DELETE_ROW },
3148
+ { id: "column", label: "Delete column", command: DELETE_COLUMN },
3149
+ { id: "table", label: "Delete table", command: DELETE_TABLE }
3150
+ ]
3151
+ }
3152
+ ),
3153
+ /* @__PURE__ */ jsxRuntime.jsx(
3154
+ CommandAction,
3155
+ {
3156
+ command: MERGE,
3157
+ label: "Merge cells",
3158
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Merge, { size: 16 }),
3159
+ testId: "merge"
3160
+ }
3161
+ ),
3162
+ /* @__PURE__ */ jsxRuntime.jsx(CommandAction, { command: SPLIT, label: "Split cell", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Split, { size: 16 }), testId: "split" }),
3163
+ /* @__PURE__ */ jsxRuntime.jsx(
3164
+ CommandAction,
3165
+ {
3166
+ command: HEADER_ROW,
3167
+ label: "Repeat header row",
3168
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Heading, { size: 16 }),
3169
+ testId: "header-row"
3170
+ }
3171
+ ),
3172
+ /* @__PURE__ */ jsxRuntime.jsx(
3173
+ CommandGallery,
3174
+ {
3175
+ label: "Cell alignment",
3176
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignVerticalJustifyCenter, { size: 16 }),
3177
+ testId: "docx-table-align",
3178
+ rows: [
3179
+ {
3180
+ id: "top",
3181
+ label: "Top",
3182
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignVerticalJustifyStart, { size: 16 }),
3183
+ command: ALIGN_TOP
3184
+ },
3185
+ {
3186
+ id: "center",
3187
+ label: "Center",
3188
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignVerticalJustifyCenter, { size: 16 }),
3189
+ command: ALIGN_CENTER
3190
+ },
3191
+ {
3192
+ id: "bottom",
3193
+ label: "Bottom",
3194
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlignVerticalJustifyEnd, { size: 16 }),
3195
+ command: ALIGN_BOTTOM
3196
+ }
3197
+ ]
3198
+ }
3199
+ ),
3200
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableBorderTarget, {}),
3201
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableBorderStyle, {}),
3202
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableBorderWidth, {}),
3203
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableBorderColor, {}),
3204
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableCellFill, {})
3205
+ ] });
3206
+ }
3207
+ var CAPTION_LABELS = ["Figure", "Table", "Equation"];
3208
+ var CAPTION_STYLES = chunkAOFATOT3_cjs.stylesXml(
3209
+ "",
3210
+ '<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style><w:style w:type="paragraph" w:styleId="Caption"><w:name w:val="caption"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:uiPriority w:val="35"/><w:unhideWhenUsed/><w:qFormat/><w:pPr><w:spacing w:after="200" w:line="240" w:lineRule="auto"/></w:pPr><w:rPr><w:i/><w:iCs/><w:color w:val="44546A"/><w:sz w:val="18"/><w:szCs w:val="18"/></w:rPr></w:style>'
3211
+ );
3212
+ function seqIdentifier(instruction) {
3213
+ const match = /^\s*SEQ\s+("[^"]+"|\S+)/i.exec(instruction);
3214
+ return match ? match[1].replace(/"/g, "") : null;
3215
+ }
3216
+ function countSeqFields(root, label, throughId) {
3217
+ let count = 0;
3218
+ let done = false;
3219
+ const target = label.toLowerCase();
3220
+ const visit = (node) => {
3221
+ if (done) return;
3222
+ if (node.localName === "fldSimple") {
3223
+ const instr = node.attributes?.find((a) => a.localName === "instr")?.value ?? "";
3224
+ if (seqIdentifier(instr)?.toLowerCase() === target) count++;
3225
+ } else if (node.localName === "instrText") {
3226
+ const text = (node.children ?? []).map((c) => c.value ?? "").join("");
3227
+ if (seqIdentifier(text)?.toLowerCase() === target) count++;
3228
+ }
3229
+ node.children?.forEach(visit);
3230
+ if (throughId && node.id === throughId) done = true;
3231
+ };
3232
+ visit(root);
3233
+ return count;
3234
+ }
3235
+ function captionParagraphXml(label, number, text) {
3236
+ const run = (value) => `<w:r><w:t xml:space="preserve">${chunkAOFATOT3_cjs.esc(value)}</w:t></w:r>`;
3237
+ const rest = text.trim() ? `: ${text.trim()}` : "";
3238
+ return `<w:p><w:pPr><w:pStyle w:val="Caption"/></w:pPr>${run(`${label} `)}<w:fldSimple w:instr=" SEQ ${chunkAOFATOT3_cjs.esc(label)} \\* ARABIC "><w:r><w:t>${number}</w:t></w:r></w:fldSimple>${rest ? run(rest) : ""}</w:p>`;
3239
+ }
3240
+ function base642(bytes) {
3241
+ let binary = "";
3242
+ for (let i = 0; i < bytes.length; i += 32768)
3243
+ binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
3244
+ return btoa(binary);
3245
+ }
3246
+ function captionFragmentHtml(label, number, text, revision) {
3247
+ const untracked = chunkAOFATOT3_cjs.docxPackage({
3248
+ document: chunkAOFATOT3_cjs.documentXml(captionParagraphXml(label, number, text)),
3249
+ styles: CAPTION_STYLES,
3250
+ title: "Caption"
3251
+ });
3252
+ const bytes = revision ? template.trackInsertion(untracked, revision, true) : untracked;
3253
+ return `<div data-docx-fragment="${base642(bytes)}" data-docx-fragment-end="covered"></div>`;
3254
+ }
3255
+ var TRANSFORMS = Object.fromEntries(
3256
+ ["rotateCW", "rotateCCW", "flipH", "flipV"].map((action) => [
3257
+ action,
3258
+ { type: "transformImage", action }
3259
+ ])
3260
+ );
3261
+ function TransformAction({
3262
+ action,
3263
+ label,
3264
+ icon
3265
+ }) {
3266
+ const state = react.useEditorCommand(TRANSFORMS[action]);
3267
+ return /* @__PURE__ */ jsxRuntime.jsx(
3268
+ react.DocxEditor.Toolbar.Action,
3269
+ {
3270
+ label,
3271
+ icon,
3272
+ disabled: !state.isEnabled,
3273
+ disabledReason: state.disabledReason ?? void 0,
3274
+ onSelect: () => void state.execute(),
3275
+ className: `native-docx-picture-${action}`
3276
+ }
3277
+ );
3278
+ }
3279
+ function insertCaption(editor, label, text, track) {
3280
+ const surface = editor.surface;
3281
+ const selection = surface?.state().selection;
3282
+ if (!surface || !selection) return null;
3283
+ const { paragraphId } = selection.head;
3284
+ const number = countSeqFields(
3285
+ surface.session.part().root,
3286
+ label,
3287
+ paragraphId
3288
+ ) + 1;
3289
+ const ids = surface.session.paragraphIds();
3290
+ const next = ids[ids.indexOf(paragraphId) + 1];
3291
+ const caption = `${label} ${number}${text.trim() ? `: ${text.trim()}` : ""}`;
3292
+ const outcome = track({
3293
+ label: `Inserted caption: ${caption}`,
3294
+ inserted: (inserted) => inserted !== null,
3295
+ insert: (revision) => {
3296
+ if (next) {
3297
+ surface.setSelection({
3298
+ anchor: { paragraphId: next, offset: 0 },
3299
+ head: { paragraphId: next, offset: 0 }
3300
+ });
3301
+ } else {
3302
+ editor.exec({ type: "selectAll" });
3303
+ const end = surface.state().selection?.head;
3304
+ if (!end) return null;
3305
+ surface.setSelection({ anchor: end, head: end });
3306
+ surface.splitParagraph();
3307
+ }
3308
+ const ok2 = editor.exec({
3309
+ type: "paste",
3310
+ text: caption,
3311
+ html: captionFragmentHtml(label, number, text, revision)
3312
+ }).ok;
3313
+ return ok2 ? number : null;
3314
+ }
3315
+ });
3316
+ return outcome?.result ?? null;
3317
+ }
3318
+ function CaptionButton({ defaultLabel = "Figure" }) {
3319
+ const editor = react.useDocxEditor();
3320
+ const track = useTrackedOrNoted();
3321
+ const [label, setLabel] = React3.useState(defaultLabel);
3322
+ const [text, setText] = React3.useState("");
3323
+ const [error, setError] = React3.useState("");
3324
+ return /* @__PURE__ */ jsxRuntime.jsx(RibbonMenu, { label: "Insert caption", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Captions, { size: 16 }), testId: "docx-insert-caption", children: (close) => /* @__PURE__ */ jsxRuntime.jsxs(
3325
+ "form",
3326
+ {
3327
+ className: "native-docx-form",
3328
+ onSubmit: (event) => {
3329
+ event.preventDefault();
3330
+ if (!editor) return;
3331
+ try {
3332
+ if (insertCaption(editor, label, text, track) === null)
3333
+ return setError("The caption could not be inserted here.");
3334
+ setText("");
3335
+ setError("");
3336
+ close();
3337
+ editor.focus();
3338
+ } catch (failure) {
3339
+ setError(failure instanceof Error ? failure.message : String(failure));
3340
+ }
3341
+ },
3342
+ children: [
3343
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
3344
+ "Label",
3345
+ /* @__PURE__ */ jsxRuntime.jsx(
3346
+ "select",
3347
+ {
3348
+ value: label,
3349
+ onChange: (event) => setLabel(event.target.value),
3350
+ children: CAPTION_LABELS.map((value) => /* @__PURE__ */ jsxRuntime.jsx("option", { children: value }, value))
3351
+ }
3352
+ )
3353
+ ] }),
3354
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
3355
+ "Caption",
3356
+ /* @__PURE__ */ jsxRuntime.jsx(
3357
+ "input",
3358
+ {
3359
+ autoFocus: true,
3360
+ value: text,
3361
+ placeholder: "Optional text after the number",
3362
+ "data-testid": "docx-caption-text",
3363
+ onChange: (event) => setText(event.target.value)
3364
+ }
3365
+ )
3366
+ ] }),
3367
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: error }),
3368
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", "data-testid": "docx-caption-insert", children: "Insert" })
3369
+ ]
3370
+ }
3371
+ ) });
3372
+ }
3373
+ function PictureButtons() {
3374
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3375
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.ImageWrap, {}),
3376
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.ImageProperties, {}),
3377
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.ImageAltText, {}),
3378
+ /* @__PURE__ */ jsxRuntime.jsx(TransformAction, { action: "rotateCW", label: "Rotate right 90\xB0", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCw, { size: 16 }) }),
3379
+ /* @__PURE__ */ jsxRuntime.jsx(TransformAction, { action: "rotateCCW", label: "Rotate left 90\xB0", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { size: 16 }) }),
3380
+ /* @__PURE__ */ jsxRuntime.jsx(
3381
+ TransformAction,
3382
+ {
3383
+ action: "flipH",
3384
+ label: "Flip horizontal",
3385
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FlipHorizontal2, { size: 16 })
3386
+ }
3387
+ ),
3388
+ /* @__PURE__ */ jsxRuntime.jsx(TransformAction, { action: "flipV", label: "Flip vertical", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FlipVertical2, { size: 16 }) }),
3389
+ /* @__PURE__ */ jsxRuntime.jsx(CaptionButton, {})
3390
+ ] });
3391
+ }
3392
+ var LAYOUT_TAB = "Layout";
3393
+ var TABLE_TAB = "Table";
3394
+ var PICTURE_TAB = "Picture";
3395
+ function useContextualTabs(tab) {
3396
+ const inTable = react.useEditorState((snapshot) => snapshot.table !== null);
3397
+ const onPicture = react.useEditorState((snapshot) => snapshot.image !== null);
3398
+ const tabs = [...inTable ? [TABLE_TAB] : [], ...onPicture ? [PICTURE_TAB] : []];
3399
+ const contextual = tab === TABLE_TAB || tab === PICTURE_TAB;
3400
+ return { tabs, shown: contextual && !tabs.includes(tab) ? "Home" : tab };
3401
+ }
3402
+ function LayoutTabContent({ tab }) {
3403
+ if (tab === LAYOUT_TAB) return /* @__PURE__ */ jsxRuntime.jsx(LayoutButtons, {});
3404
+ if (tab === TABLE_TAB) return /* @__PURE__ */ jsxRuntime.jsx(TableButtons, {});
3405
+ if (tab === PICTURE_TAB) return /* @__PURE__ */ jsxRuntime.jsx(PictureButtons, {});
3406
+ return null;
3407
+ }
3408
+ var contains = (node, id) => node.id === id || (node.children ?? []).some((child) => contains(child, id));
3409
+ function firstParagraph(node) {
3410
+ for (const child of node.children ?? []) {
3411
+ if (child.kind === "paragraph" && child.id) return child.id;
3412
+ if (child.localName === "tbl") continue;
3413
+ const nested = firstParagraph(child);
3414
+ if (nested) return nested;
3415
+ }
3416
+ return null;
3417
+ }
3418
+ function tableCells(root, paragraphId) {
3419
+ let table = null;
3420
+ const find = (node) => {
3421
+ if (node.localName === "tbl" && contains(node, paragraphId)) table = node;
3422
+ for (const child of node.children ?? []) if (contains(child, paragraphId)) find(child);
3423
+ };
3424
+ find(root);
3425
+ if (!table) return null;
3426
+ const cells = [];
3427
+ let index = -1;
3428
+ const rows = (node) => (node.children ?? []).flatMap(
3429
+ (child) => child.localName === "tr" ? [child] : child.localName === "tbl" ? [] : rows(child)
3430
+ );
3431
+ for (const row of rows(table))
3432
+ for (const cell of (row.children ?? []).filter((child) => child.localName === "tc")) {
3433
+ const first = firstParagraph(cell);
3434
+ if (!first) continue;
3435
+ if (contains(cell, paragraphId)) index = cells.length;
3436
+ cells.push(first);
3437
+ }
3438
+ return index < 0 ? null : { cells, index };
3439
+ }
3440
+ function TableTabKeys({ scope }) {
3441
+ const editor = react.useDocxEditor();
3442
+ const latest = React3.useRef(editor);
3443
+ latest.current = editor;
3444
+ React3.useEffect(() => {
3445
+ const element = scope.current;
3446
+ if (!element) return;
3447
+ const onKeyDown = (event) => {
3448
+ if (event.key !== "Tab" || event.ctrlKey || event.metaKey || event.altKey) return;
3449
+ const editor2 = latest.current;
3450
+ const surface = editor2?.surface;
3451
+ const head = surface?.state().selection?.head;
3452
+ if (!editor2 || !surface || !head) return;
3453
+ const root = () => surface.session.part().root;
3454
+ const table = tableCells(root(), head.paragraphId);
3455
+ if (!table) return;
3456
+ event.preventDefault();
3457
+ event.stopPropagation();
3458
+ let target = table.cells[table.index + (event.shiftKey ? -1 : 1)];
3459
+ if (!target && !event.shiftKey && editor2.exec({ type: "insertRow", where: "below" }).ok) {
3460
+ const grown = tableCells(
3461
+ root(),
3462
+ surface.state().selection?.head.paragraphId ?? head.paragraphId
3463
+ );
3464
+ target = grown?.cells[table.cells.length] ?? "";
3465
+ }
3466
+ if (!target) return;
3467
+ const point = { paragraphId: target, offset: 0 };
3468
+ surface.setSelection({ anchor: point, head: point });
3469
+ };
3470
+ element.addEventListener("keydown", onKeyDown, true);
3471
+ return () => element.removeEventListener("keydown", onKeyDown, true);
3472
+ }, [scope]);
3473
+ return null;
3474
+ }
2789
3475
  function QuickParts() {
2790
3476
  const house = useHouseTemplate();
2791
3477
  if (!house || house.blocks.length === 0) return null;
@@ -2925,48 +3611,39 @@ function DocxParticipants({ session }) {
2925
3611
  function DefinedTermButtons() {
2926
3612
  const terms = useDefinedTerms();
2927
3613
  if (!terms) return null;
2928
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2929
- /* @__PURE__ */ jsxRuntime.jsx(
2930
- "button",
2931
- {
2932
- type: "button",
2933
- className: "native-docx-ribbon-trigger",
2934
- "aria-label": terms.labels.insert,
2935
- title: `${terms.labels.insert} (type @@)`,
2936
- "data-testid": "docx-insert-defined-term",
2937
- onMouseDown: (event) => event.preventDefault(),
2938
- onClick: (event) => terms.openPicker(event.currentTarget),
2939
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookA, { size: 16 })
2940
- }
2941
- ),
2942
- /* @__PURE__ */ jsxRuntime.jsx(
2943
- "button",
2944
- {
2945
- type: "button",
2946
- className: "native-docx-ribbon-trigger",
2947
- "aria-label": terms.labels.update,
2948
- title: terms.labels.update,
2949
- "data-testid": "docx-update-definitions",
2950
- onMouseDown: (event) => event.preventDefault(),
2951
- onClick: () => terms.update(),
2952
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListRestart, { size: 16 })
2953
- }
2954
- )
2955
- ] });
3614
+ return /* @__PURE__ */ jsxRuntime.jsx(
3615
+ "button",
3616
+ {
3617
+ type: "button",
3618
+ className: "native-docx-ribbon-trigger",
3619
+ "aria-label": terms.labels.insert,
3620
+ title: `${terms.labels.insert} (type @@)`,
3621
+ "data-testid": "docx-insert-defined-term",
3622
+ onMouseDown: (event) => event.preventDefault(),
3623
+ onClick: (event) => terms.openPicker(event.currentTarget),
3624
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookA, { size: 16 })
3625
+ }
3626
+ );
2956
3627
  }
2957
3628
  function DocxRibbon({
2958
3629
  onSave,
2959
- badge
3630
+ badge,
3631
+ imageGeneration
2960
3632
  }) {
2961
- const [tab, setTab] = React3.useState("Home");
3633
+ const [selected, setTab] = React3.useState("Home");
3634
+ const contextual = useContextualTabs(selected);
3635
+ useGenerateImageTarget();
3636
+ const tab = contextual.shown;
2962
3637
  const t = react.useChromeTranslate();
2963
3638
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "native-docx-ribbon", children: [
2964
3639
  /* @__PURE__ */ jsxRuntime.jsxs("select", { "aria-label": "Ribbon tab", value: tab, onChange: (event) => setTab(event.target.value), children: [
2965
3640
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Home" }),
2966
3641
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Insert" }),
2967
3642
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Design" }),
3643
+ /* @__PURE__ */ jsxRuntime.jsx("option", { children: LAYOUT_TAB }),
2968
3644
  /* @__PURE__ */ jsxRuntime.jsx("option", { children: "References" }),
2969
- /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Review" })
3645
+ /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Review" }),
3646
+ contextual.tabs.map((name) => /* @__PURE__ */ jsxRuntime.jsx("option", { children: name }, name))
2970
3647
  ] }),
2971
3648
  /* @__PURE__ */ jsxRuntime.jsxs(react.DocxEditor.Toolbar, { t, preset: false, onSave, children: [
2972
3649
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Undo, {}),
@@ -2997,10 +3674,14 @@ function DocxRibbon({
2997
3674
  tab === "Insert" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2998
3675
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableInsert, {}),
2999
3676
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.ImageInsert, {}),
3677
+ imageGeneration && /* @__PURE__ */ jsxRuntime.jsx(GenerateImageButton, { generation: imageGeneration }),
3678
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Button, { slotId: "insert.pageBreak" }),
3000
3679
  /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.Link, {}),
3001
3680
  /* @__PURE__ */ jsxRuntime.jsx(DefinedTermButtons, {}),
3002
3681
  /* @__PURE__ */ jsxRuntime.jsx(QuickParts, {})
3003
3682
  ] }),
3683
+ /* @__PURE__ */ jsxRuntime.jsx(react.DocxEditor.Toolbar.TableCellFill, { hidden: true }),
3684
+ /* @__PURE__ */ jsxRuntime.jsx(LayoutTabContent, { tab }),
3004
3685
  tab === "Design" && /* @__PURE__ */ jsxRuntime.jsx(HouseTemplatePanel, {}),
3005
3686
  tab === "References" && /* @__PURE__ */ jsxRuntime.jsx(NoteButtons, {}),
3006
3687
  tab === "Review" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -3376,6 +4057,10 @@ function NativeDocxEditor({
3376
4057
  DocxRibbon,
3377
4058
  {
3378
4059
  onSave: () => void saveVersion(),
4060
+ imageGeneration: adapter.imageGeneration && {
4061
+ notify: adapter.notify,
4062
+ ...adapter.imageGeneration
4063
+ },
3379
4064
  badge: (
3380
4065
  // Says the document is shared live, in place of a title row above the ribbon.
3381
4066
  ready.room && ready.canEdit && !snapshot ? /* @__PURE__ */ jsxRuntime.jsx("span", { title: "Edits sync live with everyone who has this document open", children: "Co-edit mode" }) : void 0
@@ -3383,6 +4068,7 @@ function NativeDocxEditor({
3383
4068
  }
3384
4069
  ),
3385
4070
  !stream && /* @__PURE__ */ jsxRuntime.jsx(WordShortcuts, { scope: stageRef }),
4071
+ !stream && /* @__PURE__ */ jsxRuntime.jsx(TableTabKeys, { scope: stageRef }),
3386
4072
  /* @__PURE__ */ jsxRuntime.jsxs(
3387
4073
  "div",
3388
4074
  {
@@ -3525,6 +4211,7 @@ exports.DocxEditorReview = DocxEditorReview;
3525
4211
  exports.DocxMentions = DocxMentions;
3526
4212
  exports.DocxParticipants = DocxParticipants;
3527
4213
  exports.DocxRibbon = DocxRibbon;
4214
+ exports.GenerateImageButton = GenerateImageButton;
3528
4215
  exports.HouseTemplateProvider = HouseTemplateProvider;
3529
4216
  exports.NativeDocxEditor = NativeDocxEditor;
3530
4217
  exports.builtinHouseTemplate = builtinHouseTemplate;
@@ -3539,12 +4226,11 @@ exports.reviewModule = reviewModule;
3539
4226
  exports.structuralPort = structuralPort;
3540
4227
  exports.templateFileName = templateFileName;
3541
4228
  exports.trackedOrNoted = trackedOrNoted;
3542
- exports.updateDefinitions = updateDefinitions;
3543
4229
  exports.useDefinedTerms = useDefinedTerms;
3544
4230
  exports.useHouseTemplate = useHouseTemplate;
3545
4231
  exports.useReview = useReview;
3546
4232
  exports.useReviewItem = useReviewItem;
3547
4233
  exports.useTrackedOrNoted = useTrackedOrNoted;
3548
4234
  exports.withDirectEditing = withDirectEditing;
3549
- //# sourceMappingURL=chunk-44TPPPRJ.cjs.map
3550
- //# sourceMappingURL=chunk-44TPPPRJ.cjs.map
4235
+ //# sourceMappingURL=chunk-FAGQTB3D.cjs.map
4236
+ //# sourceMappingURL=chunk-FAGQTB3D.cjs.map