@arbidocs/blocks 0.4.93 → 0.4.95

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 (112) hide show
  1. package/dist/agents/index.cjs +281 -281
  2. package/dist/agents/index.js +5 -5
  3. package/dist/chat/index.cjs +127 -127
  4. package/dist/chat/index.js +4 -4
  5. package/dist/{chunk-HOXKR26S.js → chunk-24CS7JWU.js} +3 -3
  6. package/dist/{chunk-HOXKR26S.js.map → chunk-24CS7JWU.js.map} +1 -1
  7. package/dist/{chunk-6XS5IJ5R.cjs → chunk-3OXWJ3CZ.cjs} +5 -5
  8. package/dist/{chunk-6XS5IJ5R.cjs.map → chunk-3OXWJ3CZ.cjs.map} +1 -1
  9. package/dist/chunk-AF6RDAJF.js +14 -0
  10. package/dist/chunk-AF6RDAJF.js.map +1 -0
  11. package/dist/chunk-AOFATOT3.cjs +196 -0
  12. package/dist/chunk-AOFATOT3.cjs.map +1 -0
  13. package/dist/{chunk-NYBIKDWI.cjs → chunk-CKYA2IG5.cjs} +11 -5
  14. package/dist/chunk-CKYA2IG5.cjs.map +1 -0
  15. package/dist/chunk-D3QPYKZG.js +173 -0
  16. package/dist/chunk-D3QPYKZG.js.map +1 -0
  17. package/dist/{chunk-3WW32OUU.cjs → chunk-DCZGYCN2.cjs} +4 -4
  18. package/dist/{chunk-3WW32OUU.cjs.map → chunk-DCZGYCN2.cjs.map} +1 -1
  19. package/dist/{chunk-2ZDFVRGV.cjs → chunk-E2AAIMXO.cjs} +975 -61
  20. package/dist/chunk-E2AAIMXO.cjs.map +1 -0
  21. package/dist/{chunk-EWSEQEPL.js → chunk-EJAYB7PP.js} +4 -4
  22. package/dist/{chunk-EWSEQEPL.js.map → chunk-EJAYB7PP.js.map} +1 -1
  23. package/dist/{chunk-UO74FLRK.js → chunk-EOEDGJ75.js} +22 -53
  24. package/dist/chunk-EOEDGJ75.js.map +1 -0
  25. package/dist/{chunk-XMBYDLZQ.cjs → chunk-GNHOB2ZB.cjs} +4 -4
  26. package/dist/{chunk-XMBYDLZQ.cjs.map → chunk-GNHOB2ZB.cjs.map} +1 -1
  27. package/dist/{chunk-SF4BDUZS.js → chunk-HUFLBZWI.js} +3 -3
  28. package/dist/{chunk-SF4BDUZS.js.map → chunk-HUFLBZWI.js.map} +1 -1
  29. package/dist/{chunk-B4SRYJLM.cjs → chunk-KBPHLW2R.cjs} +3 -3
  30. package/dist/{chunk-B4SRYJLM.cjs.map → chunk-KBPHLW2R.cjs.map} +1 -1
  31. package/dist/{chunk-IF6VAYIA.cjs → chunk-OYMYNYFV.cjs} +5 -5
  32. package/dist/{chunk-IF6VAYIA.cjs.map → chunk-OYMYNYFV.cjs.map} +1 -1
  33. package/dist/{chunk-ICBAFX5B.js → chunk-PDEHIGJ5.js} +3 -3
  34. package/dist/{chunk-ICBAFX5B.js.map → chunk-PDEHIGJ5.js.map} +1 -1
  35. package/dist/{chunk-SJVUKXS6.js → chunk-QQH2NI5U.js} +6 -6
  36. package/dist/{chunk-SJVUKXS6.js.map → chunk-QQH2NI5U.js.map} +1 -1
  37. package/dist/chunk-RC25WMVB.cjs +16 -0
  38. package/dist/chunk-RC25WMVB.cjs.map +1 -0
  39. package/dist/{chunk-YFPIJUV3.cjs → chunk-RZGVMV6B.cjs} +5 -5
  40. package/dist/{chunk-YFPIJUV3.cjs.map → chunk-RZGVMV6B.cjs.map} +1 -1
  41. package/dist/{chunk-AMC5PMJ3.cjs → chunk-TOVSTC2M.cjs} +36 -68
  42. package/dist/chunk-TOVSTC2M.cjs.map +1 -0
  43. package/dist/{chunk-R3PDNQYT.js → chunk-UMY6Y6BM.js} +11 -5
  44. package/dist/chunk-UMY6Y6BM.js.map +1 -0
  45. package/dist/{chunk-MROSCSPW.cjs → chunk-VQ65UHTV.cjs} +45 -45
  46. package/dist/{chunk-MROSCSPW.cjs.map → chunk-VQ65UHTV.cjs.map} +1 -1
  47. package/dist/{chunk-MP6FK43E.cjs → chunk-VYKMBLDX.cjs} +8 -8
  48. package/dist/{chunk-MP6FK43E.cjs.map → chunk-VYKMBLDX.cjs.map} +1 -1
  49. package/dist/{chunk-W2R7YVFM.js → chunk-WXLKMEMB.js} +970 -65
  50. package/dist/chunk-WXLKMEMB.js.map +1 -0
  51. package/dist/{chunk-XQDM6T6C.js → chunk-YG7DGGCA.js} +3 -3
  52. package/dist/{chunk-XQDM6T6C.js.map → chunk-YG7DGGCA.js.map} +1 -1
  53. package/dist/{chunk-BX62HJZU.js → chunk-YPKSLTL5.js} +3 -3
  54. package/dist/{chunk-BX62HJZU.js.map → chunk-YPKSLTL5.js.map} +1 -1
  55. package/dist/{chunk-UDLMLU6T.js → chunk-ZNDAXSDP.js} +3 -3
  56. package/dist/{chunk-UDLMLU6T.js.map → chunk-ZNDAXSDP.js.map} +1 -1
  57. package/dist/connect/docx.cjs +8 -6
  58. package/dist/connect/docx.cjs.map +1 -1
  59. package/dist/connect/docx.js +6 -4
  60. package/dist/connect/docx.js.map +1 -1
  61. package/dist/connect/index.cjs +47 -45
  62. package/dist/connect/index.cjs.map +1 -1
  63. package/dist/connect/index.js +16 -14
  64. package/dist/connect/index.js.map +1 -1
  65. package/dist/docx/NativeDocxEditor.d.ts +6 -0
  66. package/dist/docx/definitions/DefinedTerms.d.ts +50 -0
  67. package/dist/docx/definitions/commands.d.ts +78 -0
  68. package/dist/docx/definitions/fragment.d.ts +29 -0
  69. package/dist/docx/definitions/port.d.ts +76 -0
  70. package/dist/docx/index.cjs +53 -15
  71. package/dist/docx/index.cjs.map +1 -1
  72. package/dist/docx/index.d.ts +3 -0
  73. package/dist/docx/index.js +5 -3
  74. package/dist/docx/index.js.map +1 -1
  75. package/dist/facts/index.cjs +7 -12
  76. package/dist/facts/index.cjs.map +1 -1
  77. package/dist/facts/index.js +3 -10
  78. package/dist/facts/index.js.map +1 -1
  79. package/dist/home/CreateTile.d.ts +3 -1
  80. package/dist/home/index.cjs +10 -4
  81. package/dist/home/index.cjs.map +1 -1
  82. package/dist/home/index.js +10 -4
  83. package/dist/home/index.js.map +1 -1
  84. package/dist/index.cjs +1207 -1067
  85. package/dist/index.cjs.map +1 -1
  86. package/dist/index.js +102 -15
  87. package/dist/index.js.map +1 -1
  88. package/dist/settings/PanelFrame.d.ts +3 -1
  89. package/dist/settings/index.cjs +12 -4
  90. package/dist/settings/index.cjs.map +1 -1
  91. package/dist/settings/index.js +12 -4
  92. package/dist/settings/index.js.map +1 -1
  93. package/dist/shell/Workspace.d.ts +6 -1
  94. package/dist/shell/index.cjs +67 -67
  95. package/dist/shell/index.js +3 -3
  96. package/dist/studio/index.cjs +7 -7
  97. package/dist/studio/index.js +6 -6
  98. package/dist/tags/definitions.d.ts +92 -0
  99. package/dist/tags/glossary.d.ts +44 -0
  100. package/dist/tags/index.d.ts +2 -0
  101. package/dist/tags/ooxml.d.ts +46 -0
  102. package/dist/tags/tagTableDocx.d.ts +1 -1
  103. package/dist/workflow-canvas/index.cjs +49 -49
  104. package/dist/workflow-canvas/index.js +6 -6
  105. package/package.json +4 -4
  106. package/src/docx/README.md +11 -0
  107. package/dist/chunk-2ZDFVRGV.cjs.map +0 -1
  108. package/dist/chunk-AMC5PMJ3.cjs.map +0 -1
  109. package/dist/chunk-NYBIKDWI.cjs.map +0 -1
  110. package/dist/chunk-R3PDNQYT.js.map +0 -1
  111. package/dist/chunk-UO74FLRK.js.map +0 -1
  112. package/dist/chunk-W2R7YVFM.js.map +0 -1
@@ -1,17 +1,18 @@
1
1
  "use client";
2
- import { filterMentionCandidates, MentionMenu, findMentionQuery, extractMentions } from './chunk-AA3PT6KR.js';
2
+ import { stylesXml, definitionBookmark, DEFINITION_BOOKMARK_PREFIX, compareTerms, groupDefinitions, definitionLabels, filterTermGroups, toDefinition, definitionIdOfBookmark, esc, parseDefinition, docxPackage, documentXml } from './chunk-D3QPYKZG.js';
3
+ import { filterMentionCandidates, MentionMenu, findMentionQuery, listOptionId, extractMentions } from './chunk-AA3PT6KR.js';
3
4
  import { unzipSync, strFromU8, strToU8, zipSync } from 'fflate';
4
5
  import { collectReviewItems, revisionItemsOf } from '@docx-editor.dev/core/store';
5
6
  import React3, { createContext, useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo } from 'react';
6
7
  import { useDocxEditor, ReviewRailContext, useEditorSnapshot, useChromeTranslate, DocxEditor, createFontSource } from '@docx-editor.dev/react';
7
8
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
9
  import { createPortal } from 'react-dom';
9
- import { Dialog, DialogContent, DialogHeader, DialogTitle, splitMarkdownBlocks, AiMarkdown } from '@arbidocs/react/ui';
10
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Button, DialogFooter, cn, splitMarkdownBlocks, AiMarkdown } from '@arbidocs/react/ui';
10
11
  import { renderToStaticMarkup } from 'react-dom/server';
11
12
  import * as Y from 'yjs';
12
13
  import { DOCUMENT_COLLABORATION_VERSIONS, collaborationModule } from '@arbidocs/coedit';
13
14
  import { createHocuspocusCollaboration } from '@arbidocs/coedit/hocuspocus';
14
- import { Type, Ellipsis, RefreshCw, WifiOff, Check, Download, Save } from 'lucide-react';
15
+ import { Type, Ellipsis, RefreshCw, WifiOff, Check, Download, Save, BookA, ListRestart } from 'lucide-react';
15
16
 
16
17
  function normalizeDocxExport(data) {
17
18
  const files = unzipSync(new Uint8Array(data));
@@ -508,18 +509,18 @@ function ReviewCardSlot({ children }) {
508
509
  var DocxEditorReview = Object.assign(DocxEditorReviewRail, { Card: ReviewCardSlot });
509
510
 
510
511
  // src/docx/mentions.ts
511
- function mentionCandidates(mentions) {
512
+ function mentionCandidates(mentions2) {
512
513
  return [
513
514
  { id: "arbi", label: "ARBI", kind: "assistant" },
514
- ...mentions.filter((person) => person.name.trim()).map((person) => ({
515
+ ...mentions2.filter((person) => person.name.trim()).map((person) => ({
515
516
  id: person.id,
516
517
  label: person.name,
517
518
  kind: person.kind === "agent" ? "assistant" : "user"
518
519
  }))
519
520
  ];
520
521
  }
521
- function mentionedDocxRecipients(text, mentions) {
522
- return extractMentions(text, mentionCandidates(mentions)).map((person) => person.id);
522
+ function mentionedDocxRecipients(text, mentions2) {
523
+ return extractMentions(text, mentionCandidates(mentions2)).map((person) => person.id);
523
524
  }
524
525
  var ActivityContext = createContext({});
525
526
  function DocxCommentActivity() {
@@ -602,7 +603,7 @@ function DocxCommentActivity() {
602
603
  }
603
604
  function DocxMentions({
604
605
  children,
605
- mentions = [],
606
+ mentions: mentions2 = [],
606
607
  initialCommentId,
607
608
  requestAgent,
608
609
  getAgentStatus,
@@ -661,7 +662,7 @@ function DocxMentions({
661
662
  const [suggestions, setSuggestions] = useState(null);
662
663
  const [selected, setSelected] = useState(0);
663
664
  const candidates = filterMentionCandidates(
664
- mentionCandidates(mentions),
665
+ mentionCandidates(mentions2),
665
666
  suggestions?.query || ""
666
667
  ).slice(0, 8);
667
668
  useEffect(() => {
@@ -682,7 +683,7 @@ function DocxMentions({
682
683
  }));
683
684
  };
684
685
  const hasAgent = submission.recipientIds.some(
685
- (id) => id === "arbi" || mentions.some((person) => person.id === id && person.kind === "agent")
686
+ (id) => id === "arbi" || mentions2.some((person) => person.id === id && person.kind === "agent")
686
687
  );
687
688
  if (hasAgent) update("delivering");
688
689
  void requestAgent({
@@ -716,7 +717,7 @@ function DocxMentions({
716
717
  "error"
717
718
  );
718
719
  });
719
- }, [review.items, requestAgent, getAgentStatus, mentions, notify]);
720
+ }, [review.items, requestAgent, getAgentStatus, mentions2, notify]);
720
721
  const choose = (name) => {
721
722
  const field = input.current;
722
723
  if (!field || !suggestions) return;
@@ -734,7 +735,7 @@ function DocxMentions({
734
735
  '[data-testid="review-draft-input"], [data-testid="review-reply-input"]'
735
736
  );
736
737
  if (!field || field.readOnly || !requestAgent) return false;
737
- const recipientIds = mentionedDocxRecipients(field.value, mentions);
738
+ const recipientIds = mentionedDocxRecipients(field.value, mentions2);
738
739
  if (!recipientIds.length) return false;
739
740
  pending.current = {
740
741
  ids: new Set(review.items.filter((i) => i.kind === "comment").map((i) => i.item.id)),
@@ -781,7 +782,7 @@ function DocxMentions({
781
782
  (entry) => entry.kind === "comment" && entry.parentId === commentId
782
783
  );
783
784
  const author = replies.at(-1)?.author || target.author;
784
- const agent = mentions.find((person) => person.kind === "agent" && person.name === author);
785
+ const agent = mentions2.find((person) => person.kind === "agent" && person.name === author);
785
786
  if (agent || author === "ARBI" || author === "Assistant")
786
787
  prefixMention(
787
788
  field,
@@ -859,7 +860,7 @@ function DocxMentions({
859
860
  onMouseDown: (event) => event.preventDefault(),
860
861
  onClick: (event) => {
861
862
  event.stopPropagation();
862
- if (!mentionedDocxRecipients(field.value, mentions).includes("arbi"))
863
+ if (!mentionedDocxRecipients(field.value, mentions2).includes("arbi"))
863
864
  prefixMention(field);
864
865
  else field.focus();
865
866
  },
@@ -904,6 +905,869 @@ function DocxMentions({
904
905
  }
905
906
  ) });
906
907
  }
908
+
909
+ // src/docx/definitions/fragment.ts
910
+ var DEFINED_TERM_STYLE = "DefinedTerm";
911
+ var FRAGMENT_STYLES = stylesXml(
912
+ "",
913
+ `<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style><w:style w:type="character" w:customStyle="1" w:styleId="${DEFINED_TERM_STYLE}"><w:name w:val="Defined Term"/><w:rPr><w:u w:val="dotted" w:color="7F7F7F"/></w:rPr></w:style>`
914
+ );
915
+ var textRun = (text, props = "") => `<w:r>${props ? `<w:rPr>${props}</w:rPr>` : ""}<w:t xml:space="preserve">${esc(text)}</w:t></w:r>`;
916
+ function definitionTooltip(definition) {
917
+ const line = `\u201C${definition.term}\u201D${definition.rest}`.replace(/\s+/g, " ").trim();
918
+ return line.length > 255 ? `${line.slice(0, 254)}\u2026` : line;
919
+ }
920
+ var definitionParagraphText = (definition) => `\u201C${definition.term}\u201D${definition.rest}`;
921
+ function definitionParagraphXml(definition, bookmark, id = 0) {
922
+ return `<w:p><w:bookmarkStart w:id="${id}" w:name="${esc(bookmark)}"/>${textRun("\u201C")}${textRun(
923
+ definition.term,
924
+ "<w:b/>"
925
+ )}${textRun("\u201D")}<w:bookmarkEnd w:id="${id}"/>${textRun(definition.rest)}</w:p>`;
926
+ }
927
+ function headingParagraphXml(text, styleId) {
928
+ return styleId ? `<w:p><w:pPr><w:pStyle w:val="${esc(styleId)}"/></w:pPr>${textRun(text)}</w:p>` : `<w:p><w:pPr><w:keepNext/></w:pPr>${textRun(text, "<w:b/>")}</w:p>`;
929
+ }
930
+ function termLinkXml(term, bookmark, tooltip) {
931
+ return `<w:p><w:hyperlink w:anchor="${esc(bookmark)}" w:tooltip="${esc(tooltip)}" w:history="1">${textRun(
932
+ term,
933
+ `<w:rStyle w:val="${DEFINED_TERM_STYLE}"/>`
934
+ )}</w:hyperlink></w:p>`;
935
+ }
936
+ function base64(bytes) {
937
+ let binary = "";
938
+ for (let i = 0; i < bytes.length; i += 32768)
939
+ binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
940
+ return btoa(binary);
941
+ }
942
+ function fragmentHtml(body, covered2) {
943
+ const bytes = docxPackage({
944
+ document: documentXml(body),
945
+ styles: FRAGMENT_STYLES,
946
+ title: "Defined terms"
947
+ });
948
+ return `<div data-docx-fragment="${base64(bytes)}" data-docx-fragment-end="${covered2 ? "covered" : "open"}"></div>`;
949
+ }
950
+
951
+ // src/docx/definitions/commands.ts
952
+ var DEFINITIONS_HEADING = "Definitions";
953
+ var SECTION_TITLE = /^(definitions?(\s*(and|&)\s*interpretations?)?|interpretations?(\s*(and|&)\s*definitions?)?|defined\s+terms|glossary)$/i;
954
+ var NUMBERING = /^(?:(?:clause|section|article|schedule|part)\s+)?(?:[0-9]+(?:\.[0-9]+)*|[A-Z]|[ivxlc]+)[.):]?\s+(?:[-–—]\s*)?/i;
955
+ function isDefinitionsTitle(paragraph) {
956
+ if (!paragraph.topLevel) return false;
957
+ const title = paragraph.text.trim().replace(NUMBERING, "").replace(/[.:]+$/, "").trim();
958
+ return SECTION_TITLE.test(title) && (paragraph.heading || title.length <= 40);
959
+ }
960
+ function findDefinitionsSection(paragraphs) {
961
+ const title = paragraphs.findIndex(isDefinitionsTitle);
962
+ if (title < 0) return null;
963
+ let end = title + 1;
964
+ while (end < paragraphs.length && paragraphs[end].topLevel && !paragraphs[end].heading) end++;
965
+ return { title, start: title + 1, end };
966
+ }
967
+ var termOf = (paragraph) => parseDefinition(paragraph.text)?.term ?? null;
968
+ function insertionIndex(paragraphs, section, term, skip = /* @__PURE__ */ new Set()) {
969
+ let afterLast = section.start;
970
+ for (let i = section.start; i < section.end; i++) {
971
+ if (skip.has(paragraphs[i].id)) continue;
972
+ const other = termOf(paragraphs[i]);
973
+ if (other === null) continue;
974
+ if (compareTerms(other, term) > 0) return i;
975
+ afterLast = i + 1;
976
+ }
977
+ return afterLast;
978
+ }
979
+ var headingStyle = (paragraphs) => paragraphs.find((p) => p.topLevel && p.heading && p.styleId)?.styleId;
980
+ function insertParagraphs(port, index, body, count, keep) {
981
+ const before = port.paragraphs();
982
+ const kept = keep ? before.findIndex((p) => p.id === keep.paragraphId) : -1;
983
+ if (index < before.length) {
984
+ port.setCaret({ paragraphId: before[index].id, offset: 0 });
985
+ } else {
986
+ const last = before[before.length - 1];
987
+ port.setCaret({ paragraphId: last.id, offset: last.text.length });
988
+ port.splitParagraph();
989
+ const opened = port.paragraphs();
990
+ port.setCaret({ paragraphId: opened[opened.length - 1].id, offset: 0 });
991
+ }
992
+ if (!port.paste(body, "", true)) throw new Error("The definition could not be added.");
993
+ if (!keep || kept < 0) return keep;
994
+ const after = port.paragraphs();
995
+ const moved = index <= kept ? kept + count : kept;
996
+ return { paragraphId: after[moved]?.id ?? keep.paragraphId, offset: keep.offset };
997
+ }
998
+ function addDefinition(port, definition, keep) {
999
+ const paragraphs = port.paragraphs();
1000
+ const section = findDefinitionsSection(paragraphs);
1001
+ const paragraph = definitionParagraphXml(definition, definitionBookmark(definition.id));
1002
+ if (section)
1003
+ return insertParagraphs(
1004
+ port,
1005
+ insertionIndex(paragraphs, section, definition.term),
1006
+ paragraph,
1007
+ 1,
1008
+ keep
1009
+ );
1010
+ return insertParagraphs(
1011
+ port,
1012
+ paragraphs.length,
1013
+ headingParagraphXml(DEFINITIONS_HEADING, headingStyle(paragraphs)) + paragraph,
1014
+ 2,
1015
+ keep
1016
+ );
1017
+ }
1018
+ var hasBookmark = (paragraphs, name) => paragraphs.some((p) => p.bookmarks.includes(name));
1019
+ function insertDefinedTerm(port, definition, options = {}) {
1020
+ const start = port.caret();
1021
+ if (!start) throw new Error("Place the cursor where the term should go.");
1022
+ let caret = start;
1023
+ if (options.replace) {
1024
+ caret = { paragraphId: start.paragraphId, offset: Math.max(0, start.offset - options.replace) };
1025
+ port.deleteRange(caret, start);
1026
+ }
1027
+ const bookmark = definitionBookmark(definition.id);
1028
+ const addedDefinition = !hasBookmark(port.paragraphs(), bookmark);
1029
+ if (addedDefinition) caret = addDefinition(port, definition, caret) ?? caret;
1030
+ port.setCaret(caret);
1031
+ if (!port.paste(
1032
+ termLinkXml(definition.term, bookmark, definitionTooltip(definition)),
1033
+ definition.term,
1034
+ false
1035
+ ))
1036
+ throw new Error("The term could not be inserted.");
1037
+ port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + definition.term.length });
1038
+ return { addedDefinition };
1039
+ }
1040
+ var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1041
+ var mentions = (text, term) => new RegExp(`(^|[^\\p{L}\\p{N}])${escapeRegExp(term)}($|[^\\p{L}\\p{N}])`, "iu").test(text);
1042
+ function updateDefinitions(port, definitions) {
1043
+ const result = {
1044
+ added: [],
1045
+ refreshed: [],
1046
+ reordered: [],
1047
+ unused: [],
1048
+ unknown: []
1049
+ };
1050
+ let paragraphs = port.paragraphs();
1051
+ const linked = new Set(
1052
+ paragraphs.flatMap(
1053
+ (p) => p.links.map((l) => l.anchor).filter((a) => a.startsWith(DEFINITION_BOOKMARK_PREFIX))
1054
+ )
1055
+ );
1056
+ const declared = /* @__PURE__ */ new Map();
1057
+ for (const p of paragraphs)
1058
+ for (const b of p.bookmarks)
1059
+ if (b.startsWith(DEFINITION_BOOKMARK_PREFIX) && !declared.has(b)) declared.set(b, p);
1060
+ const factOf = (bookmark) => {
1061
+ const id = definitionIdOfBookmark(bookmark);
1062
+ return id ? definitions.get(id) : void 0;
1063
+ };
1064
+ for (const bookmark of [...linked, ...declared.keys()])
1065
+ if (!factOf(bookmark) && !result.unknown.includes(bookmark)) result.unknown.push(bookmark);
1066
+ const section = findDefinitionsSection(paragraphs);
1067
+ const rewrite = /* @__PURE__ */ new Map();
1068
+ for (const [bookmark, paragraph] of declared) {
1069
+ const fact = factOf(bookmark);
1070
+ if (fact && paragraph.text !== definitionParagraphText(fact)) {
1071
+ rewrite.set(paragraph.id, fact);
1072
+ result.refreshed.push(fact.term);
1073
+ }
1074
+ }
1075
+ if (section) {
1076
+ let previous = null;
1077
+ for (let i = section.start; i < section.end; i++) {
1078
+ const p = paragraphs[i];
1079
+ const term = termOf(p);
1080
+ if (term === null) continue;
1081
+ const bookmark = p.bookmarks.find((b) => b.startsWith(DEFINITION_BOOKMARK_PREFIX));
1082
+ const fact = bookmark ? factOf(bookmark) : void 0;
1083
+ if (previous !== null && compareTerms(term, previous) < 0 && fact) {
1084
+ if (!rewrite.has(p.id)) result.reordered.push(fact.term);
1085
+ rewrite.set(p.id, fact);
1086
+ continue;
1087
+ }
1088
+ previous = term;
1089
+ }
1090
+ }
1091
+ for (const id of [...rewrite.keys()].reverse()) {
1092
+ const index = paragraphs.findIndex((p) => p.id === id);
1093
+ const previous = paragraphs[index - 1];
1094
+ const target = paragraphs[index];
1095
+ if (!previous) continue;
1096
+ port.deleteRange(
1097
+ { paragraphId: previous.id, offset: previous.text.length },
1098
+ { paragraphId: target.id, offset: target.text.length }
1099
+ );
1100
+ paragraphs = port.paragraphs();
1101
+ }
1102
+ const toAdd = [...rewrite.values()];
1103
+ for (const bookmark of linked) {
1104
+ const fact = factOf(bookmark);
1105
+ if (fact && !declared.has(bookmark) && !toAdd.some((d) => d.id === fact.id)) {
1106
+ toAdd.push(fact);
1107
+ result.added.push(fact.term);
1108
+ }
1109
+ }
1110
+ toAdd.sort((a, b) => compareTerms(a.term, b.term));
1111
+ for (const definition of toAdd) addDefinition(port, definition);
1112
+ paragraphs = port.paragraphs();
1113
+ const after = findDefinitionsSection(paragraphs);
1114
+ if (after) {
1115
+ const inSection = (i) => i >= after.title && i < after.end;
1116
+ const outside = paragraphs.filter((_, i) => !inSection(i));
1117
+ const linkedNow = new Set(outside.flatMap((p) => p.links.map((l) => l.anchor)));
1118
+ const outsideText = outside.map((p) => p.text).join("\n");
1119
+ for (let i = after.start; i < after.end; i++) {
1120
+ const p = paragraphs[i];
1121
+ const term = termOf(p);
1122
+ if (term === null) continue;
1123
+ const used = p.bookmarks.some((b) => linkedNow.has(b)) || mentions(outsideText, term);
1124
+ if (!used) result.unused.push({ term, paragraphId: p.id });
1125
+ }
1126
+ }
1127
+ return result;
1128
+ }
1129
+ function definitionOfAnchor(anchor, definitions) {
1130
+ const id = definitionIdOfBookmark(anchor.replace(/^#/, ""));
1131
+ return id ? definitions.get(id) : void 0;
1132
+ }
1133
+
1134
+ // src/docx/definitions/port.ts
1135
+ var attr = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
1136
+ var HEADING_STYLE = /^(heading|berschrift|titre|t[ií]tulo|titolo|kop)\s*\d/i;
1137
+ function readParagraphs(root) {
1138
+ const out = [];
1139
+ const body = root.children?.find((n) => n.kind === "body") ?? root;
1140
+ const visit = (node, topLevel, pending) => {
1141
+ for (const child of node.children ?? []) {
1142
+ if (child.kind === "paragraph") {
1143
+ out.push(readParagraph(child, topLevel, pending.splice(0)));
1144
+ } else if (child.localName === "bookmarkStart") {
1145
+ const name = attr(child, "name");
1146
+ if (name) pending.push(name);
1147
+ } else if (child.localName === "tbl" || child.localName === "tr" || child.localName === "tc") {
1148
+ visit(child, false, pending);
1149
+ } else if (child.localName === "sdt" || child.localName === "sdtContent") {
1150
+ visit(child, topLevel, pending);
1151
+ }
1152
+ }
1153
+ };
1154
+ visit(body, true, []);
1155
+ return out;
1156
+ }
1157
+ function readParagraph(node, topLevel, bookmarks) {
1158
+ let text = "";
1159
+ const links = [];
1160
+ let styleId;
1161
+ let outline = false;
1162
+ const walk = (n, link) => {
1163
+ for (const c of n.children ?? []) {
1164
+ if (c.kind === "paragraphProperties" || c.localName === "pPr") {
1165
+ for (const p of c.children ?? []) {
1166
+ if (p.localName === "pStyle") styleId = attr(p, "val");
1167
+ if (p.localName === "outlineLvl" && Number(attr(p, "val")) < 9) outline = true;
1168
+ }
1169
+ continue;
1170
+ }
1171
+ if (c.localName === "delText" || c.localName === "del" || c.localName === "rPr") continue;
1172
+ if (c.localName === "bookmarkStart") {
1173
+ const name = attr(c, "name");
1174
+ if (name) bookmarks.push(name);
1175
+ continue;
1176
+ }
1177
+ if (c.localName === "hyperlink") {
1178
+ const anchor = attr(c, "anchor");
1179
+ const next = anchor ? { anchor, text: "" } : void 0;
1180
+ walk(c, next ?? link);
1181
+ if (next) links.push(next);
1182
+ continue;
1183
+ }
1184
+ if (c.kind === "textValue") {
1185
+ text += c.value ?? "";
1186
+ if (link) link.text += c.value ?? "";
1187
+ continue;
1188
+ }
1189
+ if (c.localName === "tab") text += " ";
1190
+ walk(c, link);
1191
+ }
1192
+ };
1193
+ walk(node);
1194
+ return {
1195
+ id: node.id,
1196
+ text,
1197
+ heading: outline || !!styleId && HEADING_STYLE.test(styleId),
1198
+ styleId,
1199
+ topLevel,
1200
+ bookmarks,
1201
+ links
1202
+ };
1203
+ }
1204
+ function editorPort(editor) {
1205
+ const surface = () => {
1206
+ const s = editor.surface;
1207
+ if (!s) throw new Error("The document is not open.");
1208
+ return s;
1209
+ };
1210
+ return {
1211
+ paragraphs: () => readParagraphs(surface().session.part().root),
1212
+ caret: () => {
1213
+ const selection = surface().state().selection;
1214
+ if (!selection) return null;
1215
+ const { anchor, head } = selection;
1216
+ return anchor.paragraphId === head.paragraphId && anchor.offset === head.offset ? { paragraphId: anchor.paragraphId, offset: anchor.offset } : null;
1217
+ },
1218
+ setCaret: (point) => surface().setSelection({ anchor: point, head: point }),
1219
+ deleteRange: (from, to) => {
1220
+ surface().setSelection({ anchor: from, head: to });
1221
+ surface().deleteSelection();
1222
+ },
1223
+ paste: (body, text, covered2) => editor.exec({ type: "paste", text, html: fragmentHtml(body, covered2) }).ok,
1224
+ splitParagraph: () => surface().splitParagraph()
1225
+ };
1226
+ }
1227
+ function withDirectEditing(editor, fn) {
1228
+ const mode = editor.getEditingMode();
1229
+ if (mode === "viewing") throw new Error("This document is read only.");
1230
+ if (mode === "suggesting") editor.setEditingMode("editing");
1231
+ try {
1232
+ return fn();
1233
+ } finally {
1234
+ if (mode === "suggesting") editor.setEditingMode("suggesting");
1235
+ }
1236
+ }
1237
+ function holdCaret(editor, timeoutMs = 5e3) {
1238
+ const surface = editor.surface;
1239
+ const selection = surface?.state().selection;
1240
+ if (!surface || !selection) return;
1241
+ const { paragraphId, offset } = selection.head;
1242
+ const paraId = surface.session.paraIdOf(paragraphId);
1243
+ if (!paraId) return;
1244
+ let done = false;
1245
+ const stop = () => {
1246
+ done = true;
1247
+ offChange();
1248
+ offSelection();
1249
+ clearTimeout(timer);
1250
+ };
1251
+ const restore = () => {
1252
+ if (done) return;
1253
+ const now = surface.state().selection;
1254
+ const node = surface.session.nodeIdOf(paraId);
1255
+ if (!now || !node) return;
1256
+ if (now.head.offset !== offset || now.head.paragraphId !== paragraphId) return stop();
1257
+ if (node === paragraphId) return;
1258
+ stop();
1259
+ const point = { paragraphId: node, offset };
1260
+ surface.setSelection({ anchor: point, head: point });
1261
+ };
1262
+ const offChange = editor.on("change", restore);
1263
+ const offSelection = editor.on("selectionChange", restore);
1264
+ const timer = setTimeout(stop, timeoutMs);
1265
+ }
1266
+ var DEFINED_TERM_TRIGGER = "@@";
1267
+ var TRIGGER_QUERY = /(?:^|[\s([{"“‘'])@@([^@\n\t]{0,40})$/;
1268
+ var MAX_GROUPS = 8;
1269
+ var STALE_AFTER_MS = 3e4;
1270
+ var DEFAULT_DEFINED_TERMS_LABELS = {
1271
+ insert: "Defined term",
1272
+ update: "Update definitions",
1273
+ search: "Search defined terms",
1274
+ allAgreements: "All definitions",
1275
+ loading: "Loading definitions\u2026",
1276
+ empty: "No definitions yet. Label a fact \u201CDefinitions\u201D to use it here.",
1277
+ noMatch: "No defined term matches.",
1278
+ hint: "Enter to insert \xB7 Esc to close",
1279
+ preferred: "Preferred",
1280
+ alsoIn: (source) => `Also defined${source ? ` in ${source}` : ""}`,
1281
+ updated: "Definitions updated",
1282
+ updateSummary: (r) => [
1283
+ r.added.length ? `${r.added.length} added` : "",
1284
+ r.refreshed.length ? `${r.refreshed.length} reworded` : "",
1285
+ r.reordered.length ? `${r.reordered.length} reordered` : ""
1286
+ ].filter(Boolean).join(" \xB7 ") || "The Definitions section is up to date.",
1287
+ unusedTitle: "Defined but never used",
1288
+ unusedNone: "Every definition in the section is used.",
1289
+ unknownTitle: "Links to definitions that no longer exist",
1290
+ show: "Show",
1291
+ close: "Close"
1292
+ };
1293
+ var DefinedTermsContext = createContext(null);
1294
+ var useDefinedTerms = () => useContext(DefinedTermsContext);
1295
+ function rowsOf(groups) {
1296
+ return groups.flatMap(
1297
+ (group) => group.definitions.map((definition, i) => ({ group, definition, variant: i > 0 }))
1298
+ );
1299
+ }
1300
+ var sourceOf = (d) => [d.source, ...(d.labels ?? []).map((l) => l.name)].filter(Boolean).join(" \xB7 ");
1301
+ function DefinedTermsProvider({
1302
+ children,
1303
+ load,
1304
+ editable,
1305
+ notify,
1306
+ labels
1307
+ }) {
1308
+ const editor = useDocxEditor();
1309
+ const l = useMemo(() => ({ ...DEFAULT_DEFINED_TERMS_LABELS, ...labels }), [labels]);
1310
+ const [sources, setSources] = useState(null);
1311
+ const loadedAt = useRef(0);
1312
+ const loading = useRef(null);
1313
+ const [labelId, setLabelId] = useState(null);
1314
+ const [picker, setPicker] = useState(null);
1315
+ const [selected, setSelected] = useState(0);
1316
+ const [report, setReport] = useState(null);
1317
+ const marker = useRef(null);
1318
+ const dismissedAt = useRef(null);
1319
+ const refresh = useCallback(
1320
+ (force = false) => {
1321
+ if (!load) return Promise.resolve([]);
1322
+ if (!force && sources && Date.now() - loadedAt.current < STALE_AFTER_MS)
1323
+ return Promise.resolve(sources);
1324
+ if (!loading.current)
1325
+ loading.current = load().then((list) => {
1326
+ loadedAt.current = Date.now();
1327
+ setSources(list);
1328
+ return list;
1329
+ }).catch((error) => {
1330
+ notify?.(
1331
+ error instanceof Error ? error.message : "Could not load definitions.",
1332
+ "error"
1333
+ );
1334
+ setSources((previous) => previous ?? []);
1335
+ return sources ?? [];
1336
+ }).finally(() => {
1337
+ loading.current = null;
1338
+ });
1339
+ return loading.current;
1340
+ },
1341
+ [load, sources, notify]
1342
+ );
1343
+ const all = useMemo(() => groupDefinitions(sources ?? []).groups, [sources]);
1344
+ const byId = useMemo(
1345
+ () => new Map(all.flatMap((g) => g.definitions).map((d) => [d.id, d])),
1346
+ [all]
1347
+ );
1348
+ const agreements = useMemo(() => definitionLabels(sources ?? []), [sources]);
1349
+ const groups = useMemo(
1350
+ () => picker ? filterTermGroups(groupDefinitions(sources ?? [], { labelId }).groups, picker.query).slice(
1351
+ 0,
1352
+ MAX_GROUPS
1353
+ ) : [],
1354
+ [sources, labelId, picker]
1355
+ );
1356
+ const rows = useMemo(() => rowsOf(groups), [groups]);
1357
+ useEffect(() => setSelected(0), [picker?.query, labelId]);
1358
+ const container = () => marker.current?.closest(".native-docx-viewer") ?? document.body;
1359
+ const [viewer, setViewer] = useState(null);
1360
+ useEffect(() => setViewer(container()), []);
1361
+ const refreshRef = useRef(refresh);
1362
+ refreshRef.current = refresh;
1363
+ const firstHover = useCallback(() => void refreshRef.current(), []);
1364
+ const caretRect = () => container().querySelector(".docx-editor-one-surface__caret")?.getBoundingClientRect();
1365
+ const run = useCallback(
1366
+ (fn) => {
1367
+ if (!editor) return void 0;
1368
+ try {
1369
+ return withDirectEditing(editor, fn);
1370
+ } catch (error) {
1371
+ notify?.(error instanceof Error ? error.message : "Could not change the document.", "error");
1372
+ return void 0;
1373
+ }
1374
+ },
1375
+ [editor, notify]
1376
+ );
1377
+ const pick = useCallback(
1378
+ (row) => {
1379
+ if (!editor || !picker || !row) return;
1380
+ const port = editorPort(editor);
1381
+ const result = run(() => {
1382
+ if (picker.mode === "search" && picker.caret) port.setCaret(picker.caret);
1383
+ return insertDefinedTerm(port, row.definition, {
1384
+ replace: picker.mode === "typing" ? DEFINED_TERM_TRIGGER.length + picker.query.length : 0
1385
+ });
1386
+ });
1387
+ setPicker(null);
1388
+ editor.surface?.focus();
1389
+ if (result) holdCaret(editor);
1390
+ if (result?.addedDefinition)
1391
+ notify?.(`\u201C${row.definition.term}\u201D added to the Definitions section.`, "success");
1392
+ },
1393
+ [editor, picker, run, notify]
1394
+ );
1395
+ const openPicker = useCallback(
1396
+ (anchor) => {
1397
+ if (!editor || !editable) return;
1398
+ const caret = editorPort(editor).caret();
1399
+ const rect = caretRect() ?? anchor?.getBoundingClientRect();
1400
+ void refresh();
1401
+ setPicker({
1402
+ mode: "search",
1403
+ query: "",
1404
+ left: rect?.left ?? 120,
1405
+ top: (rect?.bottom ?? 120) + 4,
1406
+ caret
1407
+ });
1408
+ },
1409
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1410
+ [editor, editable, refresh]
1411
+ );
1412
+ useEffect(() => {
1413
+ if (!editor || !editable || !load) return;
1414
+ let frame = 0;
1415
+ const check = () => {
1416
+ cancelAnimationFrame(frame);
1417
+ frame = requestAnimationFrame(() => {
1418
+ const port = editorPort(editor);
1419
+ const caret = port.caret();
1420
+ const paragraph = caret && port.paragraphs().find((p) => p.id === caret.paragraphId);
1421
+ const match = paragraph && TRIGGER_QUERY.exec(paragraph.text.slice(0, caret.offset));
1422
+ setPicker((current) => {
1423
+ if (current?.mode === "search") return current;
1424
+ if (!match || !caret) return null;
1425
+ const key = `${caret.paragraphId}:${caret.offset - match[1].length}`;
1426
+ if (dismissedAt.current === key) return null;
1427
+ const rect = caretRect();
1428
+ return {
1429
+ mode: "typing",
1430
+ query: match[1],
1431
+ left: current?.left ?? rect?.left ?? 120,
1432
+ top: current?.top ?? (rect?.bottom ?? 120) + 4,
1433
+ caret
1434
+ };
1435
+ });
1436
+ if (match) void refresh();
1437
+ });
1438
+ };
1439
+ const offChange = editor.on("change", check);
1440
+ const offSelection = editor.on("selectionChange", check);
1441
+ return () => {
1442
+ cancelAnimationFrame(frame);
1443
+ offChange();
1444
+ offSelection();
1445
+ };
1446
+ }, [editor, editable, load, refresh]);
1447
+ useEffect(() => {
1448
+ if (picker?.mode !== "typing") return;
1449
+ const keys = (event) => {
1450
+ if (event.key === "Escape") {
1451
+ dismissedAt.current = picker.caret ? `${picker.caret.paragraphId}:${picker.caret.offset - picker.query.length}` : null;
1452
+ setPicker(null);
1453
+ } else if (!rows.length) return;
1454
+ else if (event.key === "ArrowDown") setSelected((i) => (i + 1) % rows.length);
1455
+ else if (event.key === "ArrowUp") setSelected((i) => (i - 1 + rows.length) % rows.length);
1456
+ else if (event.key === "Enter" || event.key === "Tab") pick(rows[selected] ?? rows[0]);
1457
+ else return;
1458
+ event.preventDefault();
1459
+ event.stopPropagation();
1460
+ };
1461
+ window.addEventListener("keydown", keys, true);
1462
+ return () => window.removeEventListener("keydown", keys, true);
1463
+ }, [picker, rows, selected, pick]);
1464
+ const update = useCallback(() => {
1465
+ if (!editor || !editable) return;
1466
+ void refresh(true).then((list) => {
1467
+ const definitions = new Map(
1468
+ list.flatMap((s) => {
1469
+ const d = toDefinition(s);
1470
+ return d ? [[d.id, d]] : [];
1471
+ })
1472
+ );
1473
+ const surface = editor.surface;
1474
+ const before = surface?.state().selection?.head;
1475
+ const paraId = before && surface?.session.paraIdOf(before.paragraphId);
1476
+ const result = run(() => updateDefinitions(editorPort(editor), definitions));
1477
+ const node = paraId && surface?.session.nodeIdOf(paraId);
1478
+ if (result && before && node) {
1479
+ const point = { paragraphId: node, offset: before.offset };
1480
+ surface.setSelection({ anchor: point, head: point });
1481
+ holdCaret(editor);
1482
+ }
1483
+ if (result) setReport(result);
1484
+ });
1485
+ }, [editor, editable, refresh, run]);
1486
+ const value = useMemo(
1487
+ () => load && editable ? { openPicker, update, labels: l } : null,
1488
+ [load, editable, openPicker, update, l]
1489
+ );
1490
+ return /* @__PURE__ */ jsxs(DefinedTermsContext.Provider, { value, children: [
1491
+ /* @__PURE__ */ jsx("span", { ref: marker, hidden: true }),
1492
+ children,
1493
+ load && /* @__PURE__ */ jsx(
1494
+ DefinedTermHover,
1495
+ {
1496
+ root: viewer,
1497
+ resolve: (anchor) => definitionOfAnchor(anchor, byId),
1498
+ onFirstHover: firstHover
1499
+ }
1500
+ ),
1501
+ picker && createPortal(
1502
+ /* @__PURE__ */ jsx(
1503
+ DefinedTermMenu,
1504
+ {
1505
+ picker,
1506
+ rows,
1507
+ loading: sources === null,
1508
+ selected,
1509
+ setSelected,
1510
+ agreements,
1511
+ labelId,
1512
+ setLabelId,
1513
+ onQuery: (query) => setPicker((p) => p ? { ...p, query } : p),
1514
+ onPick: pick,
1515
+ onClose: () => {
1516
+ setPicker(null);
1517
+ editor?.surface?.focus();
1518
+ },
1519
+ labels: l
1520
+ }
1521
+ ),
1522
+ document.body
1523
+ ),
1524
+ /* @__PURE__ */ jsx(Dialog, { open: !!report, onOpenChange: (open) => !open && setReport(null), children: /* @__PURE__ */ jsxs(
1525
+ DialogContent,
1526
+ {
1527
+ className: "max-h-[85dvh] overflow-y-auto",
1528
+ "data-testid": "docx-definitions-report",
1529
+ children: [
1530
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
1531
+ /* @__PURE__ */ jsx(DialogTitle, { children: l.updated }),
1532
+ /* @__PURE__ */ jsx(DialogDescription, { children: report && l.updateSummary(report) })
1533
+ ] }),
1534
+ report && /* @__PURE__ */ jsxs("div", { className: "space-y-4 text-sm", children: [
1535
+ /* @__PURE__ */ jsxs("section", { children: [
1536
+ /* @__PURE__ */ jsx("h3", { className: "mb-1 font-medium", children: l.unusedTitle }),
1537
+ report.unused.length ? /* @__PURE__ */ jsx("ul", { className: "space-y-1", "data-testid": "docx-definitions-unused", children: report.unused.map((u) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-2", children: [
1538
+ /* @__PURE__ */ jsx("span", { children: u.term }),
1539
+ /* @__PURE__ */ jsx(
1540
+ Button,
1541
+ {
1542
+ size: "sm",
1543
+ variant: "ghost",
1544
+ onClick: () => {
1545
+ const surface = editor?.surface;
1546
+ surface?.setSelection({
1547
+ anchor: { paragraphId: u.paragraphId, offset: 0 },
1548
+ head: { paragraphId: u.paragraphId, offset: 0 }
1549
+ });
1550
+ surface?.revealParagraph(u.paragraphId);
1551
+ setReport(null);
1552
+ },
1553
+ children: l.show
1554
+ }
1555
+ )
1556
+ ] }, u.paragraphId)) }) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: l.unusedNone })
1557
+ ] }),
1558
+ report.unknown.length > 0 && /* @__PURE__ */ jsxs("section", { children: [
1559
+ /* @__PURE__ */ jsx("h3", { className: "mb-1 font-medium", children: l.unknownTitle }),
1560
+ /* @__PURE__ */ jsx("ul", { className: "list-disc pl-5 text-muted-foreground", children: report.unknown.map((name) => /* @__PURE__ */ jsx("li", { children: name }, name)) })
1561
+ ] })
1562
+ ] }),
1563
+ /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setReport(null), children: l.close }) })
1564
+ ]
1565
+ }
1566
+ ) })
1567
+ ] });
1568
+ }
1569
+ function DefinedTermMenu({
1570
+ picker,
1571
+ rows,
1572
+ loading,
1573
+ selected,
1574
+ setSelected,
1575
+ agreements,
1576
+ labelId,
1577
+ setLabelId,
1578
+ onQuery,
1579
+ onPick,
1580
+ onClose,
1581
+ labels: l
1582
+ }) {
1583
+ const listboxId = React3.useId();
1584
+ const element = useRef(null);
1585
+ const search = picker.mode === "search";
1586
+ useEffect(() => {
1587
+ const dismiss = (event) => {
1588
+ if (!element.current?.contains(event.target)) onClose();
1589
+ };
1590
+ document.addEventListener("pointerdown", dismiss);
1591
+ return () => document.removeEventListener("pointerdown", dismiss);
1592
+ }, [onClose]);
1593
+ useEffect(() => {
1594
+ element.current?.querySelector(`[data-index="${selected}"]`)?.scrollIntoView?.({ block: "nearest" });
1595
+ }, [selected]);
1596
+ const left = Math.max(8, Math.min(picker.left, window.innerWidth - 368));
1597
+ return /* @__PURE__ */ jsxs(
1598
+ "div",
1599
+ {
1600
+ ref: element,
1601
+ className: "native-docx-term-menu rounded-md border border-border bg-popover text-popover-foreground shadow-lg",
1602
+ style: { position: "fixed", left, top: picker.top, width: 360, zIndex: 1e3 },
1603
+ "data-testid": "docx-defined-term-menu",
1604
+ onMouseDown: (event) => {
1605
+ if (!(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement))
1606
+ event.preventDefault();
1607
+ },
1608
+ children: [
1609
+ (search || agreements.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border/50 p-2", children: [
1610
+ search && /* @__PURE__ */ jsx(
1611
+ "input",
1612
+ {
1613
+ autoFocus: true,
1614
+ className: "min-w-0 flex-1 rounded border border-border bg-background px-2 py-1 text-sm",
1615
+ placeholder: l.search,
1616
+ "aria-label": l.search,
1617
+ role: "combobox",
1618
+ "aria-expanded": true,
1619
+ "aria-controls": listboxId,
1620
+ "aria-activedescendant": rows.length ? listOptionId(listboxId, selected) : void 0,
1621
+ value: picker.query,
1622
+ "data-testid": "docx-defined-term-search",
1623
+ onChange: (event) => onQuery(event.target.value),
1624
+ onKeyDown: (event) => {
1625
+ if (event.key === "Escape") onClose();
1626
+ else if (!rows.length) return;
1627
+ else if (event.key === "ArrowDown") setSelected((selected + 1) % rows.length);
1628
+ else if (event.key === "ArrowUp")
1629
+ setSelected((selected - 1 + rows.length) % rows.length);
1630
+ else if (event.key === "Enter") onPick(rows[selected] ?? rows[0]);
1631
+ else return;
1632
+ event.preventDefault();
1633
+ }
1634
+ }
1635
+ ),
1636
+ agreements.length > 0 && /* @__PURE__ */ jsxs(
1637
+ "select",
1638
+ {
1639
+ className: "max-w-[45%] rounded border border-border bg-background px-1 py-1 text-xs",
1640
+ "aria-label": "Narrow to",
1641
+ value: labelId ?? "",
1642
+ "data-testid": "docx-defined-term-label",
1643
+ onChange: (event) => setLabelId(event.target.value || null),
1644
+ children: [
1645
+ /* @__PURE__ */ jsx("option", { value: "", children: l.allAgreements }),
1646
+ agreements.map((a) => /* @__PURE__ */ jsx("option", { value: a.id, children: a.name }, a.id))
1647
+ ]
1648
+ }
1649
+ )
1650
+ ] }),
1651
+ /* @__PURE__ */ jsx("div", { className: "border-b border-border/50 px-3 py-1.5 text-xs text-muted-foreground", children: l.hint }),
1652
+ /* @__PURE__ */ jsxs(
1653
+ "div",
1654
+ {
1655
+ id: listboxId,
1656
+ role: "listbox",
1657
+ "aria-label": l.search,
1658
+ className: "scrollbar-subtle max-h-72 overflow-y-auto py-1",
1659
+ children: [
1660
+ rows.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-sm text-muted-foreground", children: loading ? l.loading : picker.query ? l.noMatch : l.empty }),
1661
+ rows.map((row, index) => /* @__PURE__ */ jsxs(
1662
+ "div",
1663
+ {
1664
+ id: listOptionId(listboxId, index),
1665
+ role: "option",
1666
+ "aria-selected": index === selected,
1667
+ "data-index": index,
1668
+ "data-testid": `docx-defined-term-${row.definition.id}`,
1669
+ className: cn(
1670
+ "cursor-pointer px-3 py-1.5 text-sm",
1671
+ row.variant && "pl-6",
1672
+ index === selected ? "bg-accent text-accent-foreground" : "hover:bg-accent/50"
1673
+ ),
1674
+ onMouseEnter: () => setSelected(index),
1675
+ onClick: () => onPick(row),
1676
+ children: [
1677
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
1678
+ /* @__PURE__ */ jsx("span", { className: cn("shrink-0", row.variant ? "text-xs" : "font-medium"), children: row.variant ? l.alsoIn(sourceOf(row.definition)) : row.group.term }),
1679
+ !row.variant && sourceOf(row.definition) && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: sourceOf(row.definition) }),
1680
+ !row.variant && row.group.definitions.length > 1 && row.definition.confirmed && /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 rounded bg-muted px-1 text-[10px] uppercase text-muted-foreground", children: l.preferred })
1681
+ ] }),
1682
+ /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs text-muted-foreground", children: row.definition.meaning })
1683
+ ]
1684
+ },
1685
+ row.definition.id
1686
+ ))
1687
+ ]
1688
+ }
1689
+ )
1690
+ ]
1691
+ }
1692
+ );
1693
+ }
1694
+ function DefinedTermHover({
1695
+ root,
1696
+ resolve,
1697
+ onFirstHover
1698
+ }) {
1699
+ const [card, setCard] = useState(null);
1700
+ const hovered = useRef(false);
1701
+ useEffect(() => {
1702
+ if (!root) return;
1703
+ const over = (event) => {
1704
+ const link = event.target?.closest?.(
1705
+ 'a.docx-hyperlink[data-docx-link-kind="internal"]'
1706
+ );
1707
+ const href = link?.getAttribute("href") ?? "";
1708
+ if (!link || !href.startsWith("#_dt_")) return;
1709
+ if (!hovered.current) {
1710
+ hovered.current = true;
1711
+ onFirstHover();
1712
+ }
1713
+ const title = link.getAttribute("title");
1714
+ if (title) {
1715
+ link.dataset.definition = title;
1716
+ link.removeAttribute("title");
1717
+ }
1718
+ const rect = link.getBoundingClientRect();
1719
+ setCard({
1720
+ left: rect.left,
1721
+ top: rect.bottom + 6,
1722
+ anchor: href.slice(1),
1723
+ fallback: link.dataset.definition ?? ""
1724
+ });
1725
+ };
1726
+ const out = (event) => {
1727
+ const link = event.target?.closest?.("a.docx-hyperlink");
1728
+ if (link && !link.contains(event.relatedTarget)) setCard(null);
1729
+ };
1730
+ root.addEventListener("mouseover", over);
1731
+ root.addEventListener("mouseout", out);
1732
+ const hide = () => setCard(null);
1733
+ window.addEventListener("keydown", hide, true);
1734
+ window.addEventListener("pointerdown", hide, true);
1735
+ window.addEventListener("scroll", hide, true);
1736
+ return () => {
1737
+ root.removeEventListener("mouseover", over);
1738
+ root.removeEventListener("mouseout", out);
1739
+ window.removeEventListener("keydown", hide, true);
1740
+ window.removeEventListener("pointerdown", hide, true);
1741
+ window.removeEventListener("scroll", hide, true);
1742
+ };
1743
+ }, [root, onFirstHover]);
1744
+ if (!card) return null;
1745
+ const definition = resolve(card.anchor);
1746
+ return createPortal(
1747
+ /* @__PURE__ */ jsx(
1748
+ "div",
1749
+ {
1750
+ role: "tooltip",
1751
+ className: "native-docx-term-card rounded-md border border-border bg-popover p-3 text-sm text-popover-foreground shadow-lg",
1752
+ style: {
1753
+ position: "fixed",
1754
+ left: Math.max(8, Math.min(card.left, window.innerWidth - 348)),
1755
+ top: card.top,
1756
+ width: 340,
1757
+ zIndex: 1e3,
1758
+ pointerEvents: "none"
1759
+ },
1760
+ "data-testid": "docx-defined-term-card",
1761
+ children: definition ? /* @__PURE__ */ jsxs(Fragment, { children: [
1762
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: definition.term }),
1763
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: definition.meaning }),
1764
+ sourceOf(definition) && /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: sourceOf(definition) })
1765
+ ] }) : /* @__PURE__ */ jsx("p", { children: card.fallback })
1766
+ }
1767
+ ),
1768
+ document.body
1769
+ );
1770
+ }
907
1771
  function wordDraftHtml(markdown) {
908
1772
  const template = document.createElement("template");
909
1773
  template.innerHTML = renderToStaticMarkup(/* @__PURE__ */ jsx(AiMarkdown, { children: markdown }));
@@ -1089,6 +1953,38 @@ function RibbonMenu({
1089
1953
  )
1090
1954
  ] });
1091
1955
  }
1956
+ function DefinedTermButtons() {
1957
+ const terms = useDefinedTerms();
1958
+ if (!terms) return null;
1959
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1960
+ /* @__PURE__ */ jsx(
1961
+ "button",
1962
+ {
1963
+ type: "button",
1964
+ className: "native-docx-ribbon-trigger",
1965
+ "aria-label": terms.labels.insert,
1966
+ title: `${terms.labels.insert} (type @@)`,
1967
+ "data-testid": "docx-insert-defined-term",
1968
+ onMouseDown: (event) => event.preventDefault(),
1969
+ onClick: (event) => terms.openPicker(event.currentTarget),
1970
+ children: /* @__PURE__ */ jsx(BookA, { size: 16 })
1971
+ }
1972
+ ),
1973
+ /* @__PURE__ */ jsx(
1974
+ "button",
1975
+ {
1976
+ type: "button",
1977
+ className: "native-docx-ribbon-trigger",
1978
+ "aria-label": terms.labels.update,
1979
+ title: terms.labels.update,
1980
+ "data-testid": "docx-update-definitions",
1981
+ onMouseDown: (event) => event.preventDefault(),
1982
+ onClick: () => terms.update(),
1983
+ children: /* @__PURE__ */ jsx(ListRestart, { size: 16 })
1984
+ }
1985
+ )
1986
+ ] });
1987
+ }
1092
1988
  function DocxRibbon({ onSave }) {
1093
1989
  const [tab, setTab] = useState("Home");
1094
1990
  const t = useChromeTranslate();
@@ -1127,7 +2023,8 @@ function DocxRibbon({ onSave }) {
1127
2023
  tab === "Insert" && /* @__PURE__ */ jsxs(Fragment, { children: [
1128
2024
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.TableInsert, {}),
1129
2025
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.ImageInsert, {}),
1130
- /* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {})
2026
+ /* @__PURE__ */ jsx(DocxEditor.Toolbar.Link, {}),
2027
+ /* @__PURE__ */ jsx(DefinedTermButtons, {})
1131
2028
  ] }),
1132
2029
  tab === "Review" && /* @__PURE__ */ jsxs(Fragment, { children: [
1133
2030
  /* @__PURE__ */ jsx(DocxEditor.Toolbar.EditingMode, {}),
@@ -1449,7 +2346,7 @@ function NativeDocxEditor({
1449
2346
  )
1450
2347
  ] }),
1451
2348
  !ready && !error && /* @__PURE__ */ jsx("div", { className: "native-docx-error", role: "status", children: "Opening Word document\u2026" }),
1452
- ready && /* @__PURE__ */ jsx("div", { className: "native-docx-body docx-editor", children: /* @__PURE__ */ jsx(EditorBoundary, { children: /* @__PURE__ */ jsxs(
2349
+ ready && /* @__PURE__ */ jsx("div", { className: "native-docx-body docx-editor", children: /* @__PURE__ */ jsx(EditorBoundary, { children: /* @__PURE__ */ jsx(
1453
2350
  DocxEditor.Root,
1454
2351
  {
1455
2352
  document: ready.document,
@@ -1464,52 +2361,60 @@ function NativeDocxEditor({
1464
2361
  if (change.source !== "load") changed();
1465
2362
  },
1466
2363
  onFontError: (e) => setError(e.message),
1467
- children: [
1468
- /* @__PURE__ */ jsx(
1469
- DocxEditor.RevisionMarkup,
1470
- {
1471
- insertions: { mark: "underline", color: "blue" },
1472
- deletions: { mark: "strikethrough", color: "red" },
1473
- movedFrom: { mark: "doubleStrikethrough", color: "green" },
1474
- movedTo: { mark: "doubleUnderline", color: "green" },
1475
- trackMoves: true
1476
- }
1477
- ),
1478
- !stream && /* @__PURE__ */ jsx(DocxRibbon, { onSave: () => void saveVersion() }),
1479
- /* @__PURE__ */ jsxs(
1480
- "div",
1481
- {
1482
- className: "native-docx-stage",
1483
- ref: stageRef,
1484
- onMouseDownCapture: (event) => {
1485
- if (event.button === 2 && ref.current?.getSelectionPlacement())
1486
- event.preventDefault();
1487
- },
1488
- children: [
1489
- /* @__PURE__ */ jsxs(DocxEditor.Viewport, { className: "native-docx-viewport", children: [
1490
- /* @__PURE__ */ jsx(DocxEditor.Content, {}),
1491
- /* @__PURE__ */ jsx(
1492
- DocxMentions,
1493
- {
1494
- initialCommentId,
1495
- mentions: adapter.mentions,
1496
- notify: adapter.notify,
1497
- getAgentStatus: adapter.getAgentStatus,
1498
- requestAgent: adapter.requestAgent ? (request) => adapter.requestAgent(documentId, request) : void 0,
1499
- children: /* @__PURE__ */ jsx(DocxEditorReview, { children: () => /* @__PURE__ */ jsx(DocxEditorReview.Card, { children: /* @__PURE__ */ jsx(DocxCommentActivity, {}) }) })
1500
- }
1501
- ),
1502
- /* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
1503
- /* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
1504
- /* @__PURE__ */ jsx(DocxEditor.Equation, {}),
1505
- /* @__PURE__ */ jsx(DocxEditor.HyperLink, {}),
1506
- /* @__PURE__ */ jsx(DocxEditor.ContextMenu, {})
1507
- ] }),
1508
- /* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
1509
- ]
1510
- }
1511
- )
1512
- ]
2364
+ children: /* @__PURE__ */ jsxs(
2365
+ DefinedTermsProvider,
2366
+ {
2367
+ load: adapter.definitions,
2368
+ editable: !stream && !snapshot && ready.canEdit,
2369
+ notify: adapter.notify,
2370
+ children: [
2371
+ /* @__PURE__ */ jsx(
2372
+ DocxEditor.RevisionMarkup,
2373
+ {
2374
+ insertions: { mark: "underline", color: "blue" },
2375
+ deletions: { mark: "strikethrough", color: "red" },
2376
+ movedFrom: { mark: "doubleStrikethrough", color: "green" },
2377
+ movedTo: { mark: "doubleUnderline", color: "green" },
2378
+ trackMoves: true
2379
+ }
2380
+ ),
2381
+ !stream && /* @__PURE__ */ jsx(DocxRibbon, { onSave: () => void saveVersion() }),
2382
+ /* @__PURE__ */ jsxs(
2383
+ "div",
2384
+ {
2385
+ className: "native-docx-stage",
2386
+ ref: stageRef,
2387
+ onMouseDownCapture: (event) => {
2388
+ if (event.button === 2 && ref.current?.getSelectionPlacement())
2389
+ event.preventDefault();
2390
+ },
2391
+ children: [
2392
+ /* @__PURE__ */ jsxs(DocxEditor.Viewport, { className: "native-docx-viewport", children: [
2393
+ /* @__PURE__ */ jsx(DocxEditor.Content, {}),
2394
+ /* @__PURE__ */ jsx(
2395
+ DocxMentions,
2396
+ {
2397
+ initialCommentId,
2398
+ mentions: adapter.mentions,
2399
+ notify: adapter.notify,
2400
+ getAgentStatus: adapter.getAgentStatus,
2401
+ requestAgent: adapter.requestAgent ? (request) => adapter.requestAgent(documentId, request) : void 0,
2402
+ children: /* @__PURE__ */ jsx(DocxEditorReview, { children: () => /* @__PURE__ */ jsx(DocxEditorReview.Card, { children: /* @__PURE__ */ jsx(DocxCommentActivity, {}) }) })
2403
+ }
2404
+ ),
2405
+ /* @__PURE__ */ jsx(DocxEditor.HeaderFooterChrome, {}),
2406
+ /* @__PURE__ */ jsx(DocxEditor.NotesChrome, {}),
2407
+ /* @__PURE__ */ jsx(DocxEditor.Equation, {}),
2408
+ /* @__PURE__ */ jsx(DocxEditor.HyperLink, {}),
2409
+ /* @__PURE__ */ jsx(DocxEditor.ContextMenu, {})
2410
+ ] }),
2411
+ /* @__PURE__ */ jsx(DocxEditor.PageNumber, {})
2412
+ ]
2413
+ }
2414
+ )
2415
+ ]
2416
+ }
2417
+ )
1513
2418
  }
1514
2419
  ) }, retry) }),
1515
2420
  /* @__PURE__ */ jsxs("div", { className: "native-docx-actions", children: [
@@ -1604,6 +2509,6 @@ async function loadFonts(baseUrl) {
1604
2509
  };
1605
2510
  }
1606
2511
 
1607
- export { DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, NativeDocxEditor, loadDocxFonts, normalizeDocxExport, reviewModule, useReview, useReviewItem };
1608
- //# sourceMappingURL=chunk-W2R7YVFM.js.map
1609
- //# sourceMappingURL=chunk-W2R7YVFM.js.map
2512
+ export { DEFAULT_DEFINED_TERMS_LABELS, DEFINED_TERM_TRIGGER, DefinedTermsProvider, DocxCommentActivity, DocxEditorReview, DocxMentions, DocxParticipants, DocxRibbon, NativeDocxEditor, editorPort, findDefinitionsSection, insertDefinedTerm, loadDocxFonts, normalizeDocxExport, reviewModule, updateDefinitions, useDefinedTerms, useReview, useReviewItem, withDirectEditing };
2513
+ //# sourceMappingURL=chunk-WXLKMEMB.js.map
2514
+ //# sourceMappingURL=chunk-WXLKMEMB.js.map