@arbidocs/coedit 0.4.111 → 0.4.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/commands.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var store = require('@docx-editor.dev/core/store');
4
4
  var layout = require('@docx-editor.dev/core/layout');
5
+ var fflate = require('fflate');
5
6
 
6
7
  // src/commands/index.ts
7
8
 
@@ -265,6 +266,35 @@ function documentNumbering(source) {
265
266
  }
266
267
  };
267
268
  }
269
+ var W_NAMESPACES = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"';
270
+ var INVALID_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F￾￿]/g;
271
+ var esc = (text) => text.replace(INVALID_XML, "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
272
+ var documentXml = (body) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
273
+ <w:document ${W_NAMESPACES}><w:body>${body}</w:body></w:document>`;
274
+ var stylesXml = (defaults, styles) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
275
+ <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${defaults}${styles}</w:styles>`;
276
+ var CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
277
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`;
278
+ var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
279
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`;
280
+ var documentRels = (more = "") => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
281
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${more}</Relationships>`;
282
+ var hyperlinkRelationship = (id, url) => `<Relationship Id="${esc(id)}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${esc(url)}" TargetMode="External"/>`;
283
+ var coreXml = (title, at) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
284
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${esc(
285
+ title
286
+ )}</dc:title><dcterms:created xsi:type="dcterms:W3CDTF">${at}</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">${at}</dcterms:modified></cp:coreProperties>`;
287
+ function docxPackage(parts) {
288
+ const at = new Date(parts.at ?? Date.now()).toISOString().replace(/\.\d{3}Z$/, "Z");
289
+ return fflate.zipSync({
290
+ "[Content_Types].xml": fflate.strToU8(CONTENT_TYPES),
291
+ "_rels/.rels": fflate.strToU8(ROOT_RELS),
292
+ "docProps/core.xml": fflate.strToU8(coreXml(parts.title, at)),
293
+ "word/document.xml": fflate.strToU8(parts.document),
294
+ "word/styles.xml": fflate.strToU8(parts.styles),
295
+ "word/_rels/document.xml.rels": fflate.strToU8(documentRels(parts.relationships))
296
+ });
297
+ }
268
298
 
269
299
  // src/commands/references.ts
270
300
  var REF_BOOKMARK_PREFIX = "_Ref";
@@ -314,6 +344,126 @@ function brokenCrossReferences(port) {
314
344
  (p) => (p.marks ?? []).filter((m) => m.styleId === BROKEN_CROSS_REFERENCE_STYLE).map((m) => ({ paragraphId: p.id, offset: m.offset, text: m.text }))
315
345
  );
316
346
  }
347
+ function newRefBookmark(taken, random = Math.random) {
348
+ for (; ; ) {
349
+ const name = `${REF_BOOKMARK_PREFIX}${String(Math.floor(random() * 9e8) + 1e8)}`;
350
+ if (!taken.has(name)) return name;
351
+ }
352
+ }
353
+ var styled = (style) => style ? `<w:rPr><w:rStyle w:val="${style}"/></w:rPr>` : "";
354
+ var run = (text, style) => `<w:r>${styled(style)}<w:t xml:space="preserve">${esc(text)}</w:t></w:r>`;
355
+ function refFieldXml(instruction, cached) {
356
+ const r = (inner) => `<w:r>${styled(CROSS_REFERENCE_STYLE)}${inner}</w:r>`;
357
+ return `${r('<w:fldChar w:fldCharType="begin"/>')}${r(
358
+ `<w:instrText xml:space="preserve"> ${esc(instruction)} </w:instrText>`
359
+ )}${r('<w:fldChar w:fldCharType="separate"/>')}${run(cached, CROSS_REFERENCE_STYLE)}${r(
360
+ '<w:fldChar w:fldCharType="end"/>'
361
+ )}`;
362
+ }
363
+ var refTooltip = (value, mode) => mode === "number" ? `Cross-reference to clause ${value}` : `Cross-reference to \u201C${value}\u201D`;
364
+ function crossReferenceXml(bookmark, mode, value) {
365
+ return `<w:hyperlink w:anchor="${esc(bookmark)}" w:tooltip="${esc(
366
+ refTooltip(value, mode)
367
+ )}" w:history="1">${refFieldXml(refInstruction(bookmark, mode), value)}</w:hyperlink>`;
368
+ }
369
+ var refInstruction = (bookmark, mode) => mode === "number" ? `REF ${bookmark} \\r \\h` : `REF ${bookmark} \\h`;
370
+ var refResult = (target, mode) => mode === "number" && target.number ? target.number : target.text;
371
+ var allBookmarks = (port) => new Set(port.paragraphs().flatMap((p) => p.bookmarks));
372
+ function ensureTargetBookmark(port, target, mode) {
373
+ const paragraph = port.paragraphs().find((p) => p.id === target.paragraphId);
374
+ if (!paragraph) throw new Error("That clause is no longer in the document.");
375
+ const existing = paragraph.bookmarks.find((b) => b.startsWith(REF_BOOKMARK_PREFIX));
376
+ if (existing && mode === "number") return existing;
377
+ const name = newRefBookmark(allBookmarks(port));
378
+ const start = `<w:bookmarkStart w:id="0" w:name="${name}"/>`;
379
+ const end = '<w:bookmarkEnd w:id="0"/>';
380
+ if (mode === "number") {
381
+ port.setCaret({ paragraphId: paragraph.id, offset: 0 });
382
+ if (!port.paste(`<w:p>${start}${end}</w:p>`, "", false))
383
+ throw new Error("The cross-reference target could not be marked.");
384
+ return name;
385
+ }
386
+ const runs = port.runsXml(paragraph.id);
387
+ if (!runs) throw new Error("This heading cannot be referred to by its text; use its number.");
388
+ port.deleteRange(
389
+ { paragraphId: paragraph.id, offset: 0 },
390
+ { paragraphId: paragraph.id, offset: paragraph.text.length }
391
+ );
392
+ if (!port.paste(`<w:p>${start}${runs}${end}</w:p>`, paragraph.text, false))
393
+ throw new Error("The cross-reference target could not be marked.");
394
+ return name;
395
+ }
396
+ function pastePoint(port, caret) {
397
+ const paragraph = port.paragraphs().find((p) => p.id === caret.paragraphId);
398
+ if (paragraph?.text[caret.offset - 1] !== FIELD_ATOM) return caret;
399
+ port.setCaret(caret);
400
+ port.typeText(" ");
401
+ return { paragraphId: caret.paragraphId, offset: caret.offset + 1 };
402
+ }
403
+ function markCrossReferenceTarget(port, target, mode) {
404
+ if (mode === "number" && !target.number) mode = "text";
405
+ const bookmark = ensureTargetBookmark(port, target, mode);
406
+ return { bookmark, mode, value: refResult(target, mode) };
407
+ }
408
+ function insertCrossReferenceField(port, { bookmark, mode, value }, caret) {
409
+ caret = pastePoint(port, caret);
410
+ port.setCaret(caret);
411
+ if (!port.paste(`<w:p>${crossReferenceXml(bookmark, mode, value)}</w:p>`, value, false))
412
+ throw new Error("The cross-reference could not be inserted.");
413
+ port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + 1 });
414
+ return { bookmark };
415
+ }
416
+ function insertCrossReference(port, target, mode, caret) {
417
+ return insertCrossReferenceField(port, markCrossReferenceTarget(port, target, mode), caret);
418
+ }
419
+ var documentReferenceTooltip = (text, appName = "ARBI") => `Cross-reference: ${text} \u2014 opens in ${appName}`;
420
+ function insertDocumentReference(port, reference, caret) {
421
+ const text = reference.text.trim();
422
+ if (!text) throw new Error("Enter the reference text.");
423
+ const tooltip = documentReferenceTooltip(text, reference.appName).slice(0, 255);
424
+ caret = pastePoint(port, caret);
425
+ port.setCaret(caret);
426
+ const link = `<w:p><w:hyperlink r:id="rIdRef1"${tooltip ? ` w:tooltip="${esc(tooltip)}"` : ""} w:history="1">${run(text, CROSS_REFERENCE_STYLE)}</w:hyperlink></w:p>`;
427
+ if (!port.paste(link, text, false, hyperlinkRelationship("rIdRef1", reference.url)))
428
+ throw new Error("The reference could not be inserted.");
429
+ port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + text.length });
430
+ }
431
+ function refreshCrossReferences(port) {
432
+ const stale = staleCrossReferences(port).sort(
433
+ (a, b) => a.paragraphId === b.paragraphId ? b.offset - a.offset : 0
434
+ );
435
+ let refreshed = 0;
436
+ for (const ref of stale) {
437
+ const paragraph = port.paragraphs().find((p) => p.id === ref.paragraphId);
438
+ if (!paragraph || paragraph.text[ref.offset - 1] === FIELD_ATOM) continue;
439
+ const at = { paragraphId: ref.paragraphId, offset: ref.offset };
440
+ port.deleteRange(at, { paragraphId: ref.paragraphId, offset: ref.offset + 1 });
441
+ port.setCaret(at);
442
+ const text = ref.value ?? (ref.cached.trim() || ref.bookmark);
443
+ const body = ref.value === null ? run(text, BROKEN_CROSS_REFERENCE_STYLE) : crossReferenceXml(ref.bookmark, ref.mode, ref.value);
444
+ if (port.paste(`<w:p>${body}</w:p>`, text, false) && ref.value !== null) refreshed++;
445
+ }
446
+ return { refreshed, broken: brokenCrossReferences(port) };
447
+ }
448
+ var RUN_PARTS = /* @__PURE__ */ new Set(["rPr", "t", "tab", "br", "lastRenderedPageBreak"]);
449
+ var SKIPPED = /* @__PURE__ */ new Set(["pPr", "bookmarkStart", "bookmarkEnd", "proofErr", "permStart", "permEnd"]);
450
+ function serialize(node) {
451
+ if (node.kind === "textValue") return esc(node.value ?? "");
452
+ const name = `${node.prefix ?? "w"}:${node.localName}`;
453
+ const attrs = (node.attributes ?? []).map((a) => ` ${a.prefix ? `${a.prefix}:` : ""}${a.localName}="${esc(a.value)}"`).join("");
454
+ const children = (node.children ?? []).map((c) => serialize(c)).join("");
455
+ return children ? `<${name}${attrs}>${children}</${name}>` : `<${name}${attrs}/>`;
456
+ }
457
+ function paragraphRunsXml(paragraph) {
458
+ const runs = [];
459
+ for (const child2 of paragraph.children ?? []) {
460
+ if (child2.localName && SKIPPED.has(child2.localName)) continue;
461
+ if (child2.localName !== "r") return null;
462
+ if ((child2.children ?? []).some((c) => !c.localName || !RUN_PARTS.has(c.localName))) return null;
463
+ runs.push(serialize(child2));
464
+ }
465
+ return runs.length ? runs.join("") : null;
466
+ }
317
467
  var AGENT_UNDO_MAP = "agentUndo";
318
468
  var attr2 = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
319
469
  var child = (node, name) => node?.children?.find((c) => c.localName === name);
@@ -477,17 +627,511 @@ function undoOps(root, record) {
477
627
  return ops;
478
628
  }
479
629
 
630
+ // src/commands/terms.ts
631
+ var QUOTES = `"\u201C\u201D\u201E\u201F'\u2018\u2019\u201A\u201B\xAB\xBB`;
632
+ var OPEN_CLOSE = {
633
+ '"': '"',
634
+ "\u201C": "\u201D",
635
+ "\u201E": "\u201C",
636
+ "\u201F": "\u201D",
637
+ "'": "'",
638
+ "\u2018": "\u2019",
639
+ "\u201A": "\u2018",
640
+ "\u201B": "\u2019",
641
+ "\xAB": "\xBB"
642
+ };
643
+ var EMPHASIS = /^(\*\*|__|\*|_)/;
644
+ var CONNECTORS = /^(?:shall\s+(?:have\s+the\s+meaning|mean|include)s?|ha(?:s|ve)\s+the\s+meanings?|means?\s+and\s+includes?|means|mean|includes|include|[:—–]|\s-\s|-\s)/i;
645
+ var MAX_TERM_LENGTH = 80;
646
+ var MAX_TERM_WORDS = 10;
647
+ function plain(text) {
648
+ return text.replace(/\\([\\`*_{}[\]()#+\-.!"'])/g, "$1").replace(/(\*\*|__)(.+?)\1/g, "$2").replace(/\s+/g, " ").trim();
649
+ }
650
+ function termKey(term) {
651
+ return plain(term).replace(new RegExp(`[${QUOTES}*_]`, "g"), "").replace(/\s+/g, " ").trim().toLowerCase();
652
+ }
653
+ var validTerm = (term) => term.length > 0 && term.length <= MAX_TERM_LENGTH && term.split(/\s+/).length <= MAX_TERM_WORDS && /\p{L}/u.test(term);
654
+ function connectorAt(rest) {
655
+ const lead = rest.length - rest.trimStart().length;
656
+ const spaced = /^\s-\s/.exec(rest);
657
+ if (spaced) return spaced[0].length;
658
+ const match = CONNECTORS.exec(rest.slice(lead));
659
+ return match ? lead + match[0].length : 0;
660
+ }
661
+ function parseDefinition(text) {
662
+ let s = plain(text);
663
+ for (let i = 0; i < 3; i++) s = s.replace(EMPHASIS, "").trimStart();
664
+ let term;
665
+ let rest;
666
+ const open = s[0];
667
+ const close = open ? OPEN_CLOSE[open] : void 0;
668
+ if (close) {
669
+ const end = s.indexOf(close, 1);
670
+ if (end < 0) return null;
671
+ term = s.slice(1, end);
672
+ rest = s.slice(end + 1);
673
+ rest = rest.replace(new RegExp(`^(?:\\*\\*|__|\\*|_|[${QUOTES}])+`), "");
674
+ } else {
675
+ const match = /^(.+?)(\s+(?:shall\s+(?:have\s+the\s+meaning|mean|include)s?|ha(?:s|ve)\s+the\s+meanings?|means?\s+and\s+includes?|means|includes)\b|\s*[:—–]|\s+-\s)/i.exec(
676
+ s
677
+ );
678
+ if (!match) return null;
679
+ term = match[1];
680
+ rest = s.slice(match[1].length);
681
+ }
682
+ term = term.replace(/(\*\*|__|\*|_)/g, "").trim();
683
+ const joint = connectorAt(rest);
684
+ if (!joint || !validTerm(term)) return null;
685
+ const meaning = rest.slice(joint).trim();
686
+ if (!meaning) return null;
687
+ return { term, rest: rest.replace(/^\s+/, " ").replace(/^ (?=[:—–])/, ""), meaning };
688
+ }
689
+ var compareTerms = (a, b) => a.localeCompare(b, void 0, { sensitivity: "base", numeric: true });
690
+ var DEFINITION_BOOKMARK_PREFIX = "_dt_";
691
+ var MAX_BOOKMARK_LENGTH = 40;
692
+ function definitionBookmark(factId) {
693
+ return (DEFINITION_BOOKMARK_PREFIX + factId.replace(/[^A-Za-z0-9_]/g, "_")).slice(
694
+ 0,
695
+ MAX_BOOKMARK_LENGTH
696
+ );
697
+ }
698
+ function definitionIdOfBookmark(name) {
699
+ const match = /^_dt_([a-z]{3})_([A-Za-z0-9]+)$/.exec(name);
700
+ return match ? `${match[1]}-${match[2]}` : null;
701
+ }
702
+ var W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
703
+ var TemplateError = class extends Error {
704
+ constructor(message) {
705
+ super(message);
706
+ this.name = "TemplateError";
707
+ }
708
+ };
709
+ function parse(xml, part = "part") {
710
+ const read = store.readXml(xml, { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 });
711
+ if (!read.ok) throw new TemplateError(`${part} is not readable XML (${read.reason})`);
712
+ const convert = (node) => node.type === "text" ? node.value : { name: node.name, attrs: { ...node.attributes }, children: node.children.map(convert) };
713
+ const root = read.nodes.map(convert).find((n) => typeof n !== "string");
714
+ if (!root) throw new TemplateError(`${part} has no root element`);
715
+ const w = root.attrs["xmlns:w"];
716
+ if (w !== void 0 && w !== W_NS) throw new TemplateError(`${part} binds w: to ${w}`);
717
+ return root;
718
+ }
719
+ var escText = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
720
+ var escAttr = (s) => escText(s).replace(/"/g, "&quot;");
721
+ function serialize2(root) {
722
+ const out = ['<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'];
723
+ const walk = (node) => {
724
+ if (typeof node === "string") return void out.push(escText(node));
725
+ out.push("<", node.name);
726
+ for (const [k, v] of Object.entries(node.attrs)) out.push(" ", k, '="', escAttr(v), '"');
727
+ if (node.children.length === 0) return void out.push("/>");
728
+ out.push(">");
729
+ node.children.forEach(walk);
730
+ out.push("</", node.name, ">");
731
+ };
732
+ walk(root);
733
+ return out.join("");
734
+ }
735
+ var el = (name, attrs = {}, children = []) => ({
736
+ name,
737
+ attrs,
738
+ children
739
+ });
740
+ var isEl = (n) => typeof n === "object" && n !== null;
741
+ var kids = (e, name) => e.children.filter((c) => isEl(c) && (name === void 0 || c.name === name));
742
+ var kid = (e, name) => e ? kids(e, name)[0] : void 0;
743
+
744
+ // src/template/pkg.ts
745
+ var REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
746
+ var RT = {
747
+ officeDocument: `${REL}/officeDocument`};
748
+ var Pkg = class _Pkg {
749
+ files;
750
+ types;
751
+ constructor(files) {
752
+ this.files = files;
753
+ const ct = this.find("[Content_Types].xml");
754
+ if (!ct) throw new TemplateError("not a Word package: no [Content_Types].xml");
755
+ this.types = parse(fflate.strFromU8(files.get(ct)), "[Content_Types].xml");
756
+ }
757
+ static read(bytes) {
758
+ let files;
759
+ try {
760
+ files = fflate.unzipSync(bytes);
761
+ } catch {
762
+ throw new TemplateError("not a Word package: not a zip");
763
+ }
764
+ return new _Pkg(new Map(Object.entries(files).filter(([n]) => !n.endsWith("/"))));
765
+ }
766
+ write() {
767
+ this.files.set(
768
+ this.find("[Content_Types].xml") ?? "[Content_Types].xml",
769
+ fflate.strToU8(serialize2(this.types))
770
+ );
771
+ return fflate.zipSync(Object.fromEntries(this.files), { level: 6 });
772
+ }
773
+ /** The stored name of a part, matched case-insensitively as OPC does. */
774
+ find(name) {
775
+ const key = name.replace(/^\//, "").toLowerCase();
776
+ for (const n of this.files.keys()) if (n.toLowerCase() === key) return n;
777
+ return void 0;
778
+ }
779
+ has(name) {
780
+ return this.find(name) !== void 0;
781
+ }
782
+ text(name) {
783
+ const n = this.find(name);
784
+ return n === void 0 ? void 0 : fflate.strFromU8(this.files.get(n));
785
+ }
786
+ xml(name) {
787
+ const t = this.text(name);
788
+ return t === void 0 ? void 0 : parse(t, name);
789
+ }
790
+ setXml(name, root, contentType) {
791
+ this.files.set(this.find(name) ?? name, fflate.strToU8(serialize2(root)));
792
+ if (contentType) this.setContentType(name, contentType);
793
+ }
794
+ setBytes(name, bytes, contentType) {
795
+ this.files.set(this.find(name) ?? name, bytes);
796
+ if (contentType) this.setContentType(name, contentType);
797
+ }
798
+ remove(name) {
799
+ const n = this.find(name);
800
+ if (n === void 0) return;
801
+ this.files.delete(n);
802
+ this.types.children = this.types.children.filter(
803
+ (c) => !(typeof c !== "string" && c.name === "Override" && c.attrs.PartName.toLowerCase() === `/${n.toLowerCase()}`)
804
+ );
805
+ this.files.delete(relsName(n));
806
+ }
807
+ contentType(name) {
808
+ const part = `/${name.replace(/^\//, "")}`.toLowerCase();
809
+ const override = kids(this.types, "Override").find(
810
+ (o) => o.attrs.PartName.toLowerCase() === part
811
+ );
812
+ if (override) return override.attrs.ContentType;
813
+ const ext = part.slice(part.lastIndexOf(".") + 1);
814
+ return kids(this.types, "Default").find((d) => d.attrs.Extension.toLowerCase() === ext)?.attrs.ContentType;
815
+ }
816
+ setContentType(name, type) {
817
+ const part = `/${name.replace(/^\//, "")}`;
818
+ const override = kids(this.types, "Override").find(
819
+ (o) => o.attrs.PartName.toLowerCase() === part.toLowerCase()
820
+ );
821
+ if (override) override.attrs.ContentType = type;
822
+ else this.types.children.push(el("Override", { PartName: part, ContentType: type }));
823
+ }
824
+ /** A `Default` for an extension (media), unless one exists. */
825
+ ensureDefault(ext, type) {
826
+ if (!kids(this.types, "Default").some(
827
+ (d) => d.attrs.Extension.toLowerCase() === ext.toLowerCase()
828
+ ))
829
+ this.types.children.unshift(el("Default", { Extension: ext, ContentType: type }));
830
+ }
831
+ rels(owner) {
832
+ const root = this.xml(relsName(owner));
833
+ if (!root) return [];
834
+ return kids(root, "Relationship").map((r) => ({
835
+ id: r.attrs.Id,
836
+ type: r.attrs.Type,
837
+ target: r.attrs.Target,
838
+ external: r.attrs.TargetMode === "External"
839
+ }));
840
+ }
841
+ setRels(owner, rels) {
842
+ const root = el(
843
+ "Relationships",
844
+ { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" },
845
+ rels.map(
846
+ (r) => el("Relationship", {
847
+ Id: r.id,
848
+ Type: r.type,
849
+ Target: r.target,
850
+ ...r.external ? { TargetMode: "External" } : {}
851
+ })
852
+ )
853
+ );
854
+ this.setXml(relsName(owner), root);
855
+ }
856
+ /** Add a relationship from `owner`; returns its new id. */
857
+ addRel(owner, type, target, external = false) {
858
+ const rels = this.rels(owner);
859
+ const id = freshId(new Set(rels.map((r) => r.id)), "rIdT");
860
+ rels.push({ id, type, target, external });
861
+ this.setRels(owner, rels);
862
+ return id;
863
+ }
864
+ /** The part a relationship of `owner` points at (internal targets only). */
865
+ target(owner, rel) {
866
+ return resolveTarget(owner, rel.target);
867
+ }
868
+ /** The first internal part of a relationship type from `owner`. */
869
+ partOf(owner, type) {
870
+ const rel = this.rels(owner).find((r) => r.type === type && !r.external);
871
+ return rel && this.find(this.target(owner, rel));
872
+ }
873
+ /** The main document part (`word/document.xml` by convention). */
874
+ get main() {
875
+ const main = this.partOf("", RT.officeDocument);
876
+ if (!main) throw new TemplateError("not a Word package: no main document part");
877
+ return main;
878
+ }
879
+ };
880
+ function relsName(owner) {
881
+ const slash = owner.lastIndexOf("/");
882
+ return `${owner.slice(0, slash + 1)}_rels/${owner.slice(slash + 1)}.rels`;
883
+ }
884
+ function resolveTarget(owner, target) {
885
+ if (target.startsWith("/")) return target.slice(1);
886
+ const base = owner.slice(0, owner.lastIndexOf("/") + 1).split("/").filter(Boolean);
887
+ for (const seg of target.split("/")) {
888
+ if (seg === "..") base.pop();
889
+ else if (seg !== "." && seg !== "") base.push(seg);
890
+ }
891
+ return base.join("/");
892
+ }
893
+ function freshId(taken, prefix) {
894
+ for (let i = 1; ; i++) if (!taken.has(`${prefix}${i}`)) return `${prefix}${i}`;
895
+ }
896
+
897
+ // src/template/track.ts
898
+ var TRACKED = /* @__PURE__ */ new Set(["w:ins", "w:del", "w:moveFrom", "w:moveTo"]);
899
+ function trackInsertion(fragment, revision, covered) {
900
+ const pkg = Pkg.read(fragment);
901
+ const main = pkg.main;
902
+ const root = pkg.xml(main);
903
+ const body = kid(root, "w:body");
904
+ if (!root || !body) return fragment;
905
+ const attrs = { "w:id": "1", "w:author": revision.author, "w:date": revision.date };
906
+ const lastParagraph = body.children.filter(isEl).findLast((c) => c.name === "w:p");
907
+ const visit = (node) => {
908
+ node.children = node.children.map((child2) => {
909
+ if (!isEl(child2)) return child2;
910
+ if (child2.name === "w:r") return TRACKED.has(node.name) ? child2 : el("w:ins", attrs, [child2]);
911
+ if (TRACKED.has(child2.name)) return child2;
912
+ if (child2.name === "w:tr") {
913
+ markRow(child2, attrs);
914
+ return child2;
915
+ }
916
+ visit(child2);
917
+ if (child2.name === "w:p" && (covered || child2 !== lastParagraph)) markParagraph(child2, attrs);
918
+ return child2;
919
+ });
920
+ };
921
+ visit(body);
922
+ pkg.setXml(main, root);
923
+ return pkg.write();
924
+ }
925
+ function markParagraph(p, attrs) {
926
+ let pPr = kid(p, "w:pPr");
927
+ if (!pPr) p.children.unshift(pPr = el("w:pPr"));
928
+ let rPr = kid(pPr, "w:rPr");
929
+ if (!rPr) {
930
+ rPr = el("w:rPr");
931
+ const at = pPr.children.findIndex(
932
+ (c) => isEl(c) && (c.name === "w:sectPr" || c.name === "w:pPrChange")
933
+ );
934
+ pPr.children.splice(at < 0 ? pPr.children.length : at, 0, rPr);
935
+ }
936
+ if (!kid(rPr, "w:ins")) rPr.children.unshift(el("w:ins", attrs));
937
+ }
938
+ function markRow(tr, attrs) {
939
+ let trPr = kid(tr, "w:trPr");
940
+ if (!trPr) {
941
+ trPr = el("w:trPr");
942
+ const after = tr.children.findIndex((c) => isEl(c) && c.name === "w:tblPrEx");
943
+ tr.children.splice(after + 1, 0, trPr);
944
+ }
945
+ if (kid(trPr, "w:ins")) return;
946
+ const at = trPr.children.findIndex((c) => isEl(c) && c.name === "w:trPrChange");
947
+ trPr.children.splice(at < 0 ? trPr.children.length : at, 0, el("w:ins", attrs));
948
+ }
949
+
950
+ // src/commands/fragment.ts
951
+ var CHARACTER_STYLES = {
952
+ [DEFINED_TERM_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>`,
953
+ [CROSS_REFERENCE_STYLE]: `<w:style w:type="character" w:customStyle="1" w:styleId="${CROSS_REFERENCE_STYLE}"><w:name w:val="Cross Reference"/><w:rPr><w:color w:val="1F4E79"/><w:u w:val="dotted" w:color="8EA9C1"/></w:rPr></w:style>`,
954
+ [BROKEN_CROSS_REFERENCE_STYLE]: `<w:style w:type="character" w:customStyle="1" w:styleId="${BROKEN_CROSS_REFERENCE_STYLE}"><w:name w:val="Cross Reference Broken"/><w:basedOn w:val="${CROSS_REFERENCE_STYLE}"/><w:rPr><w:color w:val="C00000"/><w:u w:val="wave" w:color="C00000"/></w:rPr></w:style>`
955
+ };
956
+ function stylesUsed(body) {
957
+ return [...new Set([...body.matchAll(/<w:rStyle w:val="([^"]+)"/g)].map((m) => m[1]))];
958
+ }
959
+ var fragmentStyles = (ids) => stylesXml(
960
+ "",
961
+ `<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style>${ids.map((id) => CHARACTER_STYLES[id] ?? "").join("")}`
962
+ );
963
+ var textRun = (text, props = "") => `<w:r>${props ? `<w:rPr>${props}</w:rPr>` : ""}<w:t xml:space="preserve">${esc(text)}</w:t></w:r>`;
964
+ function definitionTooltip(definition) {
965
+ const line = `\u201C${definition.term}\u201D${definition.rest}`.replace(/\s+/g, " ").trim();
966
+ return line.length > 255 ? `${line.slice(0, 254)}\u2026` : line;
967
+ }
968
+ var definitionParagraphText = (definition) => `\u201C${definition.term}\u201D${definition.rest}`;
969
+ function definitionParagraphXml(definition, bookmark, id = 0) {
970
+ return `<w:p><w:bookmarkStart w:id="${id}" w:name="${esc(bookmark)}"/>${textRun("\u201C")}${textRun(
971
+ definition.term,
972
+ "<w:b/>"
973
+ )}${textRun("\u201D")}<w:bookmarkEnd w:id="${id}"/>${textRun(definition.rest)}</w:p>`;
974
+ }
975
+ function headingParagraphXml(text, styleId) {
976
+ 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>`;
977
+ }
978
+ function termLinkXml(term, bookmark, tooltip) {
979
+ return `<w:p><w:hyperlink w:anchor="${esc(bookmark)}" w:tooltip="${esc(tooltip)}" w:history="1">${textRun(
980
+ term,
981
+ `<w:rStyle w:val="${DEFINED_TERM_STYLE}"/>`
982
+ )}</w:hyperlink></w:p>`;
983
+ }
984
+ function fragmentPackage(body, covered, {
985
+ revision,
986
+ relationships,
987
+ styles = stylesUsed(body)
988
+ } = {}) {
989
+ const untracked = docxPackage({
990
+ document: documentXml(body),
991
+ styles: fragmentStyles(styles),
992
+ title: "Defined terms",
993
+ relationships
994
+ });
995
+ return revision ? trackInsertion(untracked, revision, covered) : untracked;
996
+ }
997
+ var attr3 = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
998
+ function documentStyleIds(root) {
999
+ const ids = /* @__PURE__ */ new Set();
1000
+ for (const style of root?.children ?? [])
1001
+ if (style.localName === "style") {
1002
+ const id = attr3(style, "styleId");
1003
+ if (id) ids.add(id);
1004
+ }
1005
+ return ids;
1006
+ }
1007
+
1008
+ // src/commands/definitions.ts
1009
+ var DEFINITIONS_HEADING = "Definitions";
1010
+ var SECTION_TITLE = /^(definitions?(\s*(and|&)\s*interpretations?)?|interpretations?(\s*(and|&)\s*definitions?)?|defined\s+terms|glossary)$/i;
1011
+ var NUMBERING = /^(?:(?:clause|section|article|schedule|part)\s+)?(?:[0-9]+(?:\.[0-9]+)*|[A-Z]|[ivxlc]+)[.):]?\s+(?:[-–—]\s*)?/i;
1012
+ function isDefinitionsTitle(paragraph) {
1013
+ if (!paragraph.topLevel) return false;
1014
+ const title = paragraph.text.trim().replace(NUMBERING, "").replace(/[.:]+$/, "").trim();
1015
+ return SECTION_TITLE.test(title) && (paragraph.heading || title.length <= 40);
1016
+ }
1017
+ function findDefinitionsSection(paragraphs) {
1018
+ const title = paragraphs.findIndex(isDefinitionsTitle);
1019
+ if (title < 0) return null;
1020
+ let end = title + 1;
1021
+ while (end < paragraphs.length && paragraphs[end].topLevel && !paragraphs[end].heading) end++;
1022
+ return { title, start: title + 1, end };
1023
+ }
1024
+ var termOf = (paragraph) => parseDefinition(paragraph.text)?.term ?? null;
1025
+ function insertionIndex(paragraphs, section, term, skip = /* @__PURE__ */ new Set()) {
1026
+ let afterLast = section.start;
1027
+ for (let i = section.start; i < section.end; i++) {
1028
+ if (skip.has(paragraphs[i].id)) continue;
1029
+ const other = termOf(paragraphs[i]);
1030
+ if (other === null) continue;
1031
+ if (compareTerms(other, term) > 0) return i;
1032
+ afterLast = i + 1;
1033
+ }
1034
+ return afterLast;
1035
+ }
1036
+ var headingStyle = (paragraphs) => paragraphs.find((p) => p.topLevel && p.heading && p.styleId)?.styleId;
1037
+ function insertParagraphs(port, index, body, count, keep) {
1038
+ const before = port.paragraphs();
1039
+ const kept = keep ? before.findIndex((p) => p.id === keep.paragraphId) : -1;
1040
+ if (index < before.length) {
1041
+ port.setCaret({ paragraphId: before[index].id, offset: 0 });
1042
+ } else {
1043
+ const last = before[before.length - 1];
1044
+ port.setCaret({ paragraphId: last.id, offset: last.text.length });
1045
+ port.splitParagraph();
1046
+ const opened = port.paragraphs();
1047
+ port.setCaret({ paragraphId: opened[opened.length - 1].id, offset: 0 });
1048
+ }
1049
+ if (!port.paste(body, "", true)) throw new Error("The definition could not be added.");
1050
+ if (!keep || kept < 0) return keep;
1051
+ const after = port.paragraphs();
1052
+ const moved = index <= kept ? kept + count : kept;
1053
+ return { paragraphId: after[moved]?.id ?? keep.paragraphId, offset: keep.offset };
1054
+ }
1055
+ function addDefinition(port, definition, keep) {
1056
+ const paragraphs = port.paragraphs();
1057
+ const section = findDefinitionsSection(paragraphs);
1058
+ const paragraph = definitionParagraphXml(definition, definitionBookmark(definition.id));
1059
+ if (section)
1060
+ return insertParagraphs(
1061
+ port,
1062
+ insertionIndex(paragraphs, section, definition.term),
1063
+ paragraph,
1064
+ 1,
1065
+ keep
1066
+ );
1067
+ return insertParagraphs(
1068
+ port,
1069
+ paragraphs.length,
1070
+ headingParagraphXml(DEFINITIONS_HEADING, headingStyle(paragraphs)) + paragraph,
1071
+ 2,
1072
+ keep
1073
+ );
1074
+ }
1075
+ var hasBookmark = (paragraphs, name) => paragraphs.some((p) => p.bookmarks.includes(name));
1076
+ function insertDefinedTerm(port, definition, options = {}) {
1077
+ const start = port.caret();
1078
+ if (!start) throw new Error("Place the cursor where the term should go.");
1079
+ let caret = start;
1080
+ if (options.replace) {
1081
+ caret = { paragraphId: start.paragraphId, offset: Math.max(0, start.offset - options.replace) };
1082
+ port.deleteRange(caret, start);
1083
+ }
1084
+ const bookmark = definitionBookmark(definition.id);
1085
+ const addedDefinition = !hasBookmark(port.paragraphs(), bookmark);
1086
+ if (addedDefinition) caret = addDefinition(port, definition, caret) ?? caret;
1087
+ port.setCaret(caret);
1088
+ if (!port.paste(
1089
+ termLinkXml(definition.term, bookmark, definitionTooltip(definition)),
1090
+ definition.term,
1091
+ false
1092
+ ))
1093
+ throw new Error("The term could not be inserted.");
1094
+ port.setCaret({ paragraphId: caret.paragraphId, offset: caret.offset + definition.term.length });
1095
+ return { addedDefinition };
1096
+ }
1097
+ function definitionsById(definitions) {
1098
+ return new Map(definitions.map((d) => [d.id, d]));
1099
+ }
1100
+ function definitionOfAnchor(anchor, definitions) {
1101
+ const id = definitionIdOfBookmark(anchor.replace(/^#/, ""));
1102
+ return id ? definitions.get(id) : void 0;
1103
+ }
1104
+
480
1105
  // src/commands/index.ts
481
1106
  function documentReader(view) {
482
1107
  const root = () => view.part().root;
483
1108
  let paragraphs;
484
1109
  let items;
485
1110
  let names;
1111
+ let notes;
1112
+ const readNotes = () => {
1113
+ const out = /* @__PURE__ */ new Map();
1114
+ for (const part of view.currentPackage?.().parts.values() ?? []) {
1115
+ const root2 = part.root;
1116
+ const kind = root2?.localName === "footnotes" ? "footnote" : root2?.localName === "endnotes" ? "endnote" : null;
1117
+ if (!kind) continue;
1118
+ for (const note of root2?.children ?? []) {
1119
+ const id = note.attributes?.find((a) => a.localName === "id")?.value;
1120
+ if (id)
1121
+ out.set(
1122
+ `${kind}:${id}`,
1123
+ readParagraphs(note).map((p) => p.text).join(" ")
1124
+ );
1125
+ }
1126
+ }
1127
+ return out;
1128
+ };
486
1129
  const numbering = documentNumbering({ ...view, root });
487
1130
  return {
488
1131
  paragraphs: () => paragraphs ??= readParagraphs(root()),
489
1132
  listItems: () => items ??= numbering.listItems(),
490
1133
  levelOf: numbering.levelOf,
1134
+ noteText: (kind, id) => (notes ??= readNotes()).get(`${kind}:${id}`),
491
1135
  styleName: (id) => (names ??= styleNames(view.stylesRoot())).get(id) ?? id
492
1136
  };
493
1137
  }
@@ -499,28 +1143,71 @@ function readDocument(bytes) {
499
1143
 
500
1144
  exports.AGENT_UNDO_MAP = AGENT_UNDO_MAP;
501
1145
  exports.BROKEN_CROSS_REFERENCE_STYLE = BROKEN_CROSS_REFERENCE_STYLE;
1146
+ exports.CHARACTER_STYLES = CHARACTER_STYLES;
502
1147
  exports.CROSS_REFERENCE_STYLE = CROSS_REFERENCE_STYLE;
503
1148
  exports.DEFINED_TERM_STYLE = DEFINED_TERM_STYLE;
1149
+ exports.DEFINITIONS_HEADING = DEFINITIONS_HEADING;
1150
+ exports.DEFINITION_BOOKMARK_PREFIX = DEFINITION_BOOKMARK_PREFIX;
504
1151
  exports.FIELD_ATOM = FIELD_ATOM;
505
1152
  exports.REF_BOOKMARK_PREFIX = REF_BOOKMARK_PREFIX;
506
1153
  exports.WATCHED_STYLES = WATCHED_STYLES;
1154
+ exports.W_NAMESPACES = W_NAMESPACES;
1155
+ exports.addDefinition = addDefinition;
507
1156
  exports.brokenCrossReferences = brokenCrossReferences;
1157
+ exports.compareTerms = compareTerms;
1158
+ exports.crossReferenceXml = crossReferenceXml;
508
1159
  exports.crossReferences = crossReferences;
1160
+ exports.definitionBookmark = definitionBookmark;
1161
+ exports.definitionIdOfBookmark = definitionIdOfBookmark;
1162
+ exports.definitionOfAnchor = definitionOfAnchor;
1163
+ exports.definitionParagraphText = definitionParagraphText;
1164
+ exports.definitionParagraphXml = definitionParagraphXml;
1165
+ exports.definitionTooltip = definitionTooltip;
1166
+ exports.definitionsById = definitionsById;
509
1167
  exports.documentNumbering = documentNumbering;
510
1168
  exports.documentReader = documentReader;
1169
+ exports.documentReferenceTooltip = documentReferenceTooltip;
1170
+ exports.documentStyleIds = documentStyleIds;
1171
+ exports.documentXml = documentXml;
1172
+ exports.docxPackage = docxPackage;
1173
+ exports.ensureTargetBookmark = ensureTargetBookmark;
1174
+ exports.esc = esc;
511
1175
  exports.fieldNodeIds = fieldNodeIds;
512
1176
  exports.filterTargets = filterTargets;
1177
+ exports.findDefinitionsSection = findDefinitionsSection;
1178
+ exports.fragmentPackage = fragmentPackage;
1179
+ exports.headingParagraphXml = headingParagraphXml;
1180
+ exports.hyperlinkRelationship = hyperlinkRelationship;
1181
+ exports.insertCrossReference = insertCrossReference;
1182
+ exports.insertCrossReferenceField = insertCrossReferenceField;
1183
+ exports.insertDefinedTerm = insertDefinedTerm;
1184
+ exports.insertDocumentReference = insertDocumentReference;
1185
+ exports.insertionIndex = insertionIndex;
1186
+ exports.isDefinitionsTitle = isDefinitionsTitle;
513
1187
  exports.listTargets = listTargets;
1188
+ exports.markCrossReferenceTarget = markCrossReferenceTarget;
1189
+ exports.newRefBookmark = newRefBookmark;
514
1190
  exports.paragraphIndex = paragraphIndex;
515
1191
  exports.paragraphNumber = paragraphNumber;
1192
+ exports.paragraphRunsXml = paragraphRunsXml;
516
1193
  exports.paragraphState = paragraphState;
1194
+ exports.parseDefinition = parseDefinition;
517
1195
  exports.readDocument = readDocument;
518
1196
  exports.readParagraphs = readParagraphs;
1197
+ exports.refFieldXml = refFieldXml;
1198
+ exports.refInstruction = refInstruction;
1199
+ exports.refResult = refResult;
1200
+ exports.refTooltip = refTooltip;
1201
+ exports.refreshCrossReferences = refreshCrossReferences;
519
1202
  exports.sectionStates = sectionStates;
520
1203
  exports.staleCrossReferences = staleCrossReferences;
521
1204
  exports.styleNames = styleNames;
1205
+ exports.stylesUsed = stylesUsed;
1206
+ exports.stylesXml = stylesXml;
522
1207
  exports.tableOf = tableOf;
523
1208
  exports.tableState = tableState;
1209
+ exports.termKey = termKey;
1210
+ exports.termLinkXml = termLinkXml;
524
1211
  exports.undoOps = undoOps;
525
1212
  //# sourceMappingURL=commands.cjs.map
526
1213
  //# sourceMappingURL=commands.cjs.map