bun-docx 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +11 -2
  2. package/dist/index.js +1099 -346
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22198,15 +22198,12 @@ class CommentsView {
22198
22198
  }
22199
22199
  paraIdFor(commentId) {
22200
22200
  const numericId = commentId.startsWith("c") ? commentId.slice(1) : commentId;
22201
- const comment = this.findById(numericId);
22202
- const paragraph = comment?.findChild("w:p");
22203
- return paragraph?.getAttribute("w14:paraId");
22201
+ return lastParagraph(this.findById(numericId))?.getAttribute("w14:paraId");
22204
22202
  }
22205
22203
  ensureParaId(commentId) {
22206
22204
  const numericId = commentId.startsWith("c") ? commentId.slice(1) : commentId;
22207
22205
  const root = XmlNode2.findRoot(this.tree, "w:comments");
22208
- const comment = this.findById(numericId);
22209
- const paragraph = comment?.findChild("w:p");
22206
+ const paragraph = lastParagraph(this.findById(numericId));
22210
22207
  if (!root || !paragraph)
22211
22208
  return;
22212
22209
  const existing = paragraph.getAttribute("w14:paraId");
@@ -22236,6 +22233,96 @@ class CommentsView {
22236
22233
  }
22237
22234
  return String(highest + 1);
22238
22235
  }
22236
+ threadRootId(commentId) {
22237
+ const root = XmlNode2.findRoot(this.tree, "w:comments");
22238
+ const numericId = commentId.startsWith("c") ? commentId.slice(1) : commentId;
22239
+ if (!root)
22240
+ return numericId;
22241
+ const extended = this.#readExtended();
22242
+ const idByParaId = new Map;
22243
+ for (const child2 of root.children) {
22244
+ if (child2.tag !== "w:comment")
22245
+ continue;
22246
+ const childId = child2.getAttribute("w:id");
22247
+ if (childId == null)
22248
+ continue;
22249
+ const paraId = lastParagraph(child2)?.getAttribute("w14:paraId");
22250
+ if (paraId)
22251
+ idByParaId.set(paraId, childId);
22252
+ }
22253
+ let currentId = numericId;
22254
+ const seen = new Set([currentId]);
22255
+ for (;; ) {
22256
+ const paraId = lastParagraph(this.findById(currentId))?.getAttribute("w14:paraId");
22257
+ const parentParaId = paraId ? extended.get(paraId)?.parentParaId : undefined;
22258
+ const parentId = parentParaId ? idByParaId.get(parentParaId) : undefined;
22259
+ if (!parentId || seen.has(parentId))
22260
+ return currentId;
22261
+ seen.add(parentId);
22262
+ currentId = parentId;
22263
+ }
22264
+ }
22265
+ descendantReplyIds(commentId) {
22266
+ const root = XmlNode2.findRoot(this.tree, "w:comments");
22267
+ if (!root)
22268
+ return [];
22269
+ const numericId = commentId.startsWith("c") ? commentId.slice(1) : commentId;
22270
+ const startParaId = lastParagraph(this.findById(numericId))?.getAttribute("w14:paraId");
22271
+ if (!startParaId)
22272
+ return [];
22273
+ const extended = this.#readExtended();
22274
+ const idByParaId = new Map;
22275
+ const childParaIdsByParent = new Map;
22276
+ for (const child2 of root.children) {
22277
+ if (child2.tag !== "w:comment")
22278
+ continue;
22279
+ const childId = child2.getAttribute("w:id");
22280
+ if (childId == null)
22281
+ continue;
22282
+ const paraId = lastParagraph(child2)?.getAttribute("w14:paraId");
22283
+ if (!paraId)
22284
+ continue;
22285
+ idByParaId.set(paraId, childId);
22286
+ const parentParaId = extended.get(paraId)?.parentParaId;
22287
+ if (!parentParaId)
22288
+ continue;
22289
+ const siblings = childParaIdsByParent.get(parentParaId) ?? [];
22290
+ siblings.push(paraId);
22291
+ childParaIdsByParent.set(parentParaId, siblings);
22292
+ }
22293
+ const descendants = [];
22294
+ const queue = [startParaId];
22295
+ while (queue.length > 0) {
22296
+ const paraId = queue.pop();
22297
+ if (!paraId)
22298
+ break;
22299
+ for (const childParaId of childParaIdsByParent.get(paraId) ?? []) {
22300
+ const childId = idByParaId.get(childParaId);
22301
+ if (childId)
22302
+ descendants.push(childId);
22303
+ queue.push(childParaId);
22304
+ }
22305
+ }
22306
+ return descendants;
22307
+ }
22308
+ insertReplyAfter(parentNumericId, comment) {
22309
+ const root = XmlNode2.findRoot(this.tree, "w:comments");
22310
+ if (!root)
22311
+ throw new Error("expected <w:comments> root");
22312
+ const threadIds = new Set([
22313
+ parentNumericId,
22314
+ ...this.descendantReplyIds(parentNumericId)
22315
+ ]);
22316
+ let insertIndex = root.children.length;
22317
+ for (const [index, child2] of root.children.entries()) {
22318
+ if (child2.tag !== "w:comment")
22319
+ continue;
22320
+ const id = child2.getAttribute("w:id");
22321
+ if (id != null && threadIds.has(id))
22322
+ insertIndex = index + 1;
22323
+ }
22324
+ root.children.splice(insertIndex, 0, comment);
22325
+ }
22239
22326
  toComments(anchors) {
22240
22327
  const root = XmlNode2.findRoot(this.tree, "w:comments");
22241
22328
  if (!root)
@@ -22247,8 +22334,7 @@ class CommentsView {
22247
22334
  const numericId = child2.getAttribute("w:id");
22248
22335
  if (numericId == null)
22249
22336
  continue;
22250
- const paragraph = child2.findChild("w:p");
22251
- const paraId = paragraph?.getAttribute("w14:paraId");
22337
+ const paraId = lastParagraph(child2)?.getAttribute("w14:paraId");
22252
22338
  if (paraId)
22253
22339
  commentIdByParaId.set(paraId, `c${numericId}`);
22254
22340
  }
@@ -22271,8 +22357,7 @@ class CommentsView {
22271
22357
  endBlockId: "",
22272
22358
  endOffset: 0
22273
22359
  };
22274
- const paragraph = child2.findChild("w:p");
22275
- const paraId = paragraph?.getAttribute("w14:paraId");
22360
+ const paraId = lastParagraph(child2)?.getAttribute("w14:paraId");
22276
22361
  const meta = paraId ? extendedByParaId.get(paraId) ?? {} : {};
22277
22362
  const parentCommentId = meta.parentParaId ? commentIdByParaId.get(meta.parentParaId) : undefined;
22278
22363
  comments.push({
@@ -22331,12 +22416,21 @@ function CommentsRoot() {
22331
22416
  "xmlns:w14": NS_W14
22332
22417
  }, undefined, false, undefined, this);
22333
22418
  }
22419
+ function lastParagraph(comment) {
22420
+ return comment?.findChildren("w:p").at(-1);
22421
+ }
22334
22422
  function generateParaId() {
22423
+ const seed = Bun.env.DOCX_CLI_PARA_ID_SEED;
22424
+ if (seed) {
22425
+ const minted = (Number.parseInt(seed, 16) + seededParaIds++) % 2147483648;
22426
+ return (minted || 1).toString(16).padStart(8, "0").toUpperCase();
22427
+ }
22335
22428
  const bytes = new Uint8Array(4);
22336
22429
  crypto.getRandomValues(bytes);
22337
- let hex = "";
22338
- for (const byte of bytes)
22339
- hex += byte.toString(16).padStart(2, "0");
22430
+ bytes[0] = (bytes[0] ?? 0) & 127;
22431
+ const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
22432
+ if (hex === "00000000")
22433
+ return "00000001";
22340
22434
  return hex.toUpperCase();
22341
22435
  }
22342
22436
  function CommentsExRoot() {
@@ -22346,7 +22440,7 @@ function CommentsExRoot() {
22346
22440
  "mc:Ignorable": "w15"
22347
22441
  }, undefined, false, undefined, this);
22348
22442
  }
22349
- var COMMENTS_PART_NAME = "word/comments.xml", COMMENTS_EXT_PART_NAME = "word/commentsExtended.xml", COMMENTS_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", COMMENTS_EXT_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/office/2011/relationships/commentsExtended", COMMENTS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", COMMENTS_EXT_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml", NS_W2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", NS_W14 = "http://schemas.microsoft.com/office/word/2010/wordml", NS_W15 = "http://schemas.microsoft.com/office/word/2012/wordml";
22443
+ var COMMENTS_PART_NAME = "word/comments.xml", COMMENTS_EXT_PART_NAME = "word/commentsExtended.xml", COMMENTS_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", COMMENTS_EXT_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/office/2011/relationships/commentsExtended", COMMENTS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", COMMENTS_EXT_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml", NS_W2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", NS_W14 = "http://schemas.microsoft.com/office/word/2010/wordml", NS_W15 = "http://schemas.microsoft.com/office/word/2012/wordml", seededParaIds = 0;
22350
22444
  var init_comments = __esm(() => {
22351
22445
  init_jsx();
22352
22446
  init_parser();
@@ -32436,10 +32530,349 @@ var init_settings = __esm(() => {
32436
32530
  ]);
32437
32531
  });
32438
32532
 
32533
+ // src/core/blocks.tsx
32534
+ function Paragraph({
32535
+ style,
32536
+ alignment,
32537
+ list,
32538
+ taskState,
32539
+ tabs,
32540
+ text: text2,
32541
+ runs,
32542
+ ...formatting
32543
+ }) {
32544
+ const resolvedRuns = runs ?? textToRuns(text2 ?? "", formatting);
32545
+ return /* @__PURE__ */ jsxDEV(w.p, {
32546
+ children: [
32547
+ /* @__PURE__ */ jsxDEV(ParagraphProperties, {
32548
+ options: { style, alignment, list, tabs }
32549
+ }, undefined, false, undefined, this),
32550
+ taskState && /* @__PURE__ */ jsxDEV(TaskCheckbox, {
32551
+ checked: taskState === "checked"
32552
+ }, undefined, false, undefined, this),
32553
+ taskState && /* @__PURE__ */ jsxDEV(w.r, {
32554
+ children: /* @__PURE__ */ jsxDEV(w.t, {
32555
+ "xml:space": "preserve",
32556
+ children: " "
32557
+ }, undefined, false, undefined, this)
32558
+ }, undefined, false, undefined, this),
32559
+ resolvedRuns.map((run) => /* @__PURE__ */ jsxDEV(RunElement, {
32560
+ run
32561
+ }, undefined, false, undefined, this))
32562
+ ]
32563
+ }, undefined, true, undefined, this);
32564
+ }
32565
+ function textToRuns(text2, formatting) {
32566
+ if (!text2.includes(`
32567
+ `) && !text2.includes("\t")) {
32568
+ return [{ type: "text", text: text2, ...formatting }];
32569
+ }
32570
+ const runs = [];
32571
+ for (const segment of text2.split(/(\n|\t)/)) {
32572
+ if (segment === "")
32573
+ continue;
32574
+ if (segment === `
32575
+ `)
32576
+ runs.push({ type: "break", kind: "line" });
32577
+ else if (segment === "\t")
32578
+ runs.push({ type: "tab" });
32579
+ else
32580
+ runs.push({ type: "text", text: segment, ...formatting });
32581
+ }
32582
+ return runs;
32583
+ }
32584
+ function RunElement({ run }) {
32585
+ if (run.type === "text")
32586
+ return /* @__PURE__ */ jsxDEV(TextRunElement, {
32587
+ run
32588
+ }, undefined, false, undefined, this);
32589
+ if (run.type === "break") {
32590
+ return /* @__PURE__ */ jsxDEV(w.r, {
32591
+ children: /* @__PURE__ */ jsxDEV(w.br, {
32592
+ "w-type": run.kind === "line" ? undefined : run.kind
32593
+ }, undefined, false, undefined, this)
32594
+ }, undefined, false, undefined, this);
32595
+ }
32596
+ if (run.type === "tab") {
32597
+ return /* @__PURE__ */ jsxDEV(w.r, {
32598
+ children: /* @__PURE__ */ jsxDEV(w.tab, {}, undefined, false, undefined, this)
32599
+ }, undefined, false, undefined, this);
32600
+ }
32601
+ return null;
32602
+ }
32603
+ function applyParagraphOptionsInPlace(rebuilt, options) {
32604
+ if (!options.style && !options.alignment && !options.tabs)
32605
+ return;
32606
+ let pPr = rebuilt.find((child2) => child2.tag === "w:pPr");
32607
+ if (!pPr) {
32608
+ pPr = new XmlNode2("w:pPr");
32609
+ rebuilt.unshift(pPr);
32610
+ }
32611
+ if (options.tabs) {
32612
+ pPr.children = pPr.children.filter((child2) => child2.tag !== "w:tabs");
32613
+ if (options.tabs.length > 0) {
32614
+ const tabsNode = new XmlNode2("w:tabs");
32615
+ for (const stop of options.tabs) {
32616
+ tabsNode.children.push(new XmlNode2("w:tab", {
32617
+ "w:val": stop.align,
32618
+ "w:pos": String(Math.round(stop.pos))
32619
+ }));
32620
+ }
32621
+ insertPprChildInOrder(pPr, tabsNode);
32622
+ }
32623
+ }
32624
+ if (options.style) {
32625
+ const existingStyle = pPr.findChild("w:pStyle");
32626
+ if (existingStyle) {
32627
+ existingStyle.setAttribute("w:val", options.style);
32628
+ } else {
32629
+ insertPprChildInOrder(pPr, new XmlNode2("w:pStyle", { "w:val": options.style }));
32630
+ }
32631
+ }
32632
+ if (options.alignment) {
32633
+ const existingJc = pPr.findChild("w:jc");
32634
+ if (existingJc) {
32635
+ existingJc.setAttribute("w:val", options.alignment);
32636
+ } else {
32637
+ insertPprChildInOrder(pPr, new XmlNode2("w:jc", { "w:val": options.alignment }));
32638
+ }
32639
+ }
32640
+ }
32641
+ function pprChildRank(tag) {
32642
+ const index = PPR_CHILD_ORDER.indexOf(tag);
32643
+ if (index >= 0)
32644
+ return index;
32645
+ return PPR_CHILD_ORDER.indexOf("w:rPr") - 0.5;
32646
+ }
32647
+ function insertPprChildInOrder(pPr, child2) {
32648
+ const rank = pprChildRank(child2.tag);
32649
+ const at = pPr.children.findIndex((existing) => pprChildRank(existing.tag) > rank);
32650
+ if (at < 0)
32651
+ pPr.children.push(child2);
32652
+ else
32653
+ pPr.children.splice(at, 0, child2);
32654
+ }
32655
+ function rprChildRank(tag) {
32656
+ const index = RPR_CHILD_ORDER.indexOf(tag);
32657
+ return index >= 0 ? index : RPR_CHILD_ORDER.length;
32658
+ }
32659
+ function insertRprChildInOrder(rPr, child2) {
32660
+ const rank = rprChildRank(child2.tag);
32661
+ const at = rPr.children.findIndex((existing) => rprChildRank(existing.tag) > rank);
32662
+ if (at < 0)
32663
+ rPr.children.push(child2);
32664
+ else
32665
+ rPr.children.splice(at, 0, child2);
32666
+ }
32667
+ function HorizontalRule() {
32668
+ return /* @__PURE__ */ jsxDEV(w.p, {
32669
+ children: /* @__PURE__ */ jsxDEV(w.pPr, {
32670
+ children: /* @__PURE__ */ jsxDEV(w.pBdr, {
32671
+ children: /* @__PURE__ */ jsxDEV(w.bottom, {
32672
+ "w-val": "single",
32673
+ "w-sz": "6",
32674
+ "w-space": "1",
32675
+ "w-color": "auto"
32676
+ }, undefined, false, undefined, this)
32677
+ }, undefined, false, undefined, this)
32678
+ }, undefined, false, undefined, this)
32679
+ }, undefined, false, undefined, this);
32680
+ }
32681
+ function TextRunElement({ run }) {
32682
+ return /* @__PURE__ */ jsxDEV(w.r, {
32683
+ children: [
32684
+ /* @__PURE__ */ jsxDEV(RunProperties, {
32685
+ run
32686
+ }, undefined, false, undefined, this),
32687
+ /* @__PURE__ */ jsxDEV(w.t, {
32688
+ "xml:space": "preserve",
32689
+ children: run.text
32690
+ }, undefined, false, undefined, this)
32691
+ ]
32692
+ }, undefined, true, undefined, this);
32693
+ }
32694
+ function RunProperties({ run }) {
32695
+ const isEmpty = FORMATTING_KEYS.every((key) => run[key] == null);
32696
+ if (isEmpty)
32697
+ return null;
32698
+ return /* @__PURE__ */ jsxDEV(w.rPr, {
32699
+ children: [
32700
+ run.runStyle && /* @__PURE__ */ jsxDEV(w.rStyle, {
32701
+ "w-val": run.runStyle
32702
+ }, undefined, false, undefined, this),
32703
+ run.font && /* @__PURE__ */ jsxDEV(w.rFonts, {
32704
+ "w-ascii": run.font,
32705
+ "w-hAnsi": run.font
32706
+ }, undefined, false, undefined, this),
32707
+ run.bold && /* @__PURE__ */ jsxDEV(w.b, {}, undefined, false, undefined, this),
32708
+ run.italic && /* @__PURE__ */ jsxDEV(w.i, {}, undefined, false, undefined, this),
32709
+ run.allCaps && /* @__PURE__ */ jsxDEV(w.caps, {}, undefined, false, undefined, this),
32710
+ run.smallCaps && /* @__PURE__ */ jsxDEV(w.smallCaps, {}, undefined, false, undefined, this),
32711
+ run.strike && /* @__PURE__ */ jsxDEV(w.strike, {}, undefined, false, undefined, this),
32712
+ (run.color || run.colorTheme) && /* @__PURE__ */ jsxDEV(w.color, {
32713
+ "w-val": run.color ?? "auto",
32714
+ "w-themeColor": run.colorTheme,
32715
+ "w-themeTint": run.colorThemeTint,
32716
+ "w-themeShade": run.colorThemeShade
32717
+ }, undefined, false, undefined, this),
32718
+ run.sizeHalfPoints !== undefined && /* @__PURE__ */ jsxDEV(w.sz, {
32719
+ "w-val": String(run.sizeHalfPoints)
32720
+ }, undefined, false, undefined, this),
32721
+ run.highlight && /* @__PURE__ */ jsxDEV(w.highlight, {
32722
+ "w-val": run.highlight
32723
+ }, undefined, false, undefined, this),
32724
+ run.underline && /* @__PURE__ */ jsxDEV(w.u, {
32725
+ "w-val": run.underline,
32726
+ "w-color": run.underlineColor
32727
+ }, undefined, false, undefined, this),
32728
+ run.shade && /* @__PURE__ */ jsxDEV(w.shd, {
32729
+ "w-val": "clear",
32730
+ "w-color": "auto",
32731
+ "w-fill": run.shade
32732
+ }, undefined, false, undefined, this),
32733
+ run.vertAlign && /* @__PURE__ */ jsxDEV(w.vertAlign, {
32734
+ "w-val": run.vertAlign
32735
+ }, undefined, false, undefined, this)
32736
+ ]
32737
+ }, undefined, true, undefined, this);
32738
+ }
32739
+ function ParagraphProperties({
32740
+ options
32741
+ }) {
32742
+ const hasTabs = options.tabs !== undefined && options.tabs.length > 0;
32743
+ if (!options.style && !options.alignment && !options.list && !hasTabs)
32744
+ return null;
32745
+ return /* @__PURE__ */ jsxDEV(w.pPr, {
32746
+ children: [
32747
+ options.style && /* @__PURE__ */ jsxDEV(w.pStyle, {
32748
+ "w-val": options.style
32749
+ }, undefined, false, undefined, this),
32750
+ options.list && /* @__PURE__ */ jsxDEV(w.numPr, {
32751
+ children: [
32752
+ /* @__PURE__ */ jsxDEV(w.ilvl, {
32753
+ "w-val": String(options.list.level)
32754
+ }, undefined, false, undefined, this),
32755
+ /* @__PURE__ */ jsxDEV(w.numId, {
32756
+ "w-val": String(options.list.numId)
32757
+ }, undefined, false, undefined, this)
32758
+ ]
32759
+ }, undefined, true, undefined, this),
32760
+ hasTabs && /* @__PURE__ */ jsxDEV(w.tabs, {
32761
+ children: options.tabs?.map((stop) => /* @__PURE__ */ jsxDEV(w.tab, {
32762
+ "w-val": stop.align,
32763
+ "w-pos": String(Math.round(stop.pos))
32764
+ }, undefined, false, undefined, this))
32765
+ }, undefined, false, undefined, this),
32766
+ options.alignment && /* @__PURE__ */ jsxDEV(w.jc, {
32767
+ "w-val": options.alignment
32768
+ }, undefined, false, undefined, this)
32769
+ ]
32770
+ }, undefined, true, undefined, this);
32771
+ }
32772
+ var PPR_CHILD_ORDER, RPR_CHILD_ORDER, FORMATTING_KEYS;
32773
+ var init_blocks = __esm(() => {
32774
+ init_jsx();
32775
+ init_parser();
32776
+ init_task_list();
32777
+ init_jsx_dev_runtime();
32778
+ PPR_CHILD_ORDER = [
32779
+ "w:pStyle",
32780
+ "w:keepNext",
32781
+ "w:keepLines",
32782
+ "w:pageBreakBefore",
32783
+ "w:framePr",
32784
+ "w:widowControl",
32785
+ "w:numPr",
32786
+ "w:suppressLineNumbers",
32787
+ "w:pBdr",
32788
+ "w:shd",
32789
+ "w:tabs",
32790
+ "w:suppressAutoHyphens",
32791
+ "w:bidi",
32792
+ "w:adjustRightInd",
32793
+ "w:snapToGrid",
32794
+ "w:spacing",
32795
+ "w:ind",
32796
+ "w:contextualSpacing",
32797
+ "w:mirrorIndents",
32798
+ "w:suppressOverlap",
32799
+ "w:jc",
32800
+ "w:textDirection",
32801
+ "w:textAlignment",
32802
+ "w:textboxTightWrap",
32803
+ "w:outlineLvl",
32804
+ "w:divId",
32805
+ "w:cnfStyle",
32806
+ "w:rPr",
32807
+ "w:sectPr",
32808
+ "w:pPrChange"
32809
+ ];
32810
+ RPR_CHILD_ORDER = [
32811
+ "w:rStyle",
32812
+ "w:rFonts",
32813
+ "w:b",
32814
+ "w:bCs",
32815
+ "w:i",
32816
+ "w:iCs",
32817
+ "w:caps",
32818
+ "w:smallCaps",
32819
+ "w:strike",
32820
+ "w:dstrike",
32821
+ "w:outline",
32822
+ "w:shadow",
32823
+ "w:emboss",
32824
+ "w:imprint",
32825
+ "w:noProof",
32826
+ "w:snapToGrid",
32827
+ "w:vanish",
32828
+ "w:webHidden",
32829
+ "w:color",
32830
+ "w:spacing",
32831
+ "w:w",
32832
+ "w:kern",
32833
+ "w:position",
32834
+ "w:sz",
32835
+ "w:szCs",
32836
+ "w:highlight",
32837
+ "w:u",
32838
+ "w:effect",
32839
+ "w:bdr",
32840
+ "w:shd",
32841
+ "w:fitText",
32842
+ "w:vertAlign",
32843
+ "w:rtl",
32844
+ "w:cs",
32845
+ "w:em",
32846
+ "w:lang",
32847
+ "w:eastAsianLayout",
32848
+ "w:specVanish",
32849
+ "w:oMath"
32850
+ ];
32851
+ FORMATTING_KEYS = [
32852
+ "runStyle",
32853
+ "font",
32854
+ "bold",
32855
+ "italic",
32856
+ "allCaps",
32857
+ "smallCaps",
32858
+ "strike",
32859
+ "color",
32860
+ "colorTheme",
32861
+ "sizeHalfPoints",
32862
+ "highlight",
32863
+ "underline",
32864
+ "shade",
32865
+ "vertAlign"
32866
+ ];
32867
+ });
32868
+
32439
32869
  // src/core/ast/document/styles.tsx
32440
32870
  function isBaselineStyle(styleId) {
32441
32871
  return Object.hasOwn(BASELINE, styleId);
32442
32872
  }
32873
+ function baselineCatalog() {
32874
+ return Object.keys(BASELINE).map((id) => BASELINE[id]());
32875
+ }
32443
32876
 
32444
32877
  class StylesView {
32445
32878
  tree;
@@ -32521,6 +32954,79 @@ class StylesView {
32521
32954
  return;
32522
32955
  root.children.push(build());
32523
32956
  }
32957
+ setDefaultFont(fontName) {
32958
+ const rPr = this.#ensureDocDefaultsRPr();
32959
+ let rFonts = rPr.findChild("w:rFonts");
32960
+ if (!rFonts) {
32961
+ rFonts = XmlNode2.element("w:rFonts");
32962
+ insertRprChildInOrder(rPr, rFonts);
32963
+ }
32964
+ applyRunFont(rFonts, fontName);
32965
+ }
32966
+ setDefaultSizeHalfPoints(halfPoints) {
32967
+ const rPr = this.#ensureDocDefaultsRPr();
32968
+ const value = String(halfPoints);
32969
+ let sz = rPr.findChild("w:sz");
32970
+ if (!sz) {
32971
+ sz = XmlNode2.element("w:sz");
32972
+ insertRprChildInOrder(rPr, sz);
32973
+ }
32974
+ sz.setAttribute("w:val", value);
32975
+ let szCs = rPr.findChild("w:szCs");
32976
+ if (!szCs) {
32977
+ szCs = XmlNode2.element("w:szCs");
32978
+ insertRprChildInOrder(rPr, szCs);
32979
+ }
32980
+ szCs.setAttribute("w:val", value);
32981
+ }
32982
+ explicitFontStyleIds() {
32983
+ const root = XmlNode2.findRoot(this.tree, "w:styles");
32984
+ if (!root)
32985
+ return [];
32986
+ const out = [];
32987
+ for (const style of root.findChildren("w:style")) {
32988
+ const ascii = style.findChild("w:rPr")?.findChild("w:rFonts")?.getAttribute("w:ascii");
32989
+ if (!ascii)
32990
+ continue;
32991
+ const id = style.getAttribute("w:styleId");
32992
+ if (id)
32993
+ out.push(id);
32994
+ }
32995
+ return out;
32996
+ }
32997
+ overrideStyleFonts(fontName) {
32998
+ const root = XmlNode2.findRoot(this.tree, "w:styles");
32999
+ if (!root)
33000
+ return 0;
33001
+ let count = 0;
33002
+ for (const style of root.findChildren("w:style")) {
33003
+ const rFonts = style.findChild("w:rPr")?.findChild("w:rFonts");
33004
+ if (!rFonts)
33005
+ continue;
33006
+ applyRunFont(rFonts, fontName);
33007
+ count++;
33008
+ }
33009
+ return count;
33010
+ }
33011
+ #ensureDocDefaultsRPr() {
33012
+ const root = this.ensureStylesRoot();
33013
+ let docDefaults = root.findChild("w:docDefaults");
33014
+ if (!docDefaults) {
33015
+ docDefaults = XmlNode2.element("w:docDefaults");
33016
+ root.children.unshift(docDefaults);
33017
+ }
33018
+ let rPrDefault = docDefaults.findChild("w:rPrDefault");
33019
+ if (!rPrDefault) {
33020
+ rPrDefault = XmlNode2.element("w:rPrDefault");
33021
+ docDefaults.children.unshift(rPrDefault);
33022
+ }
33023
+ let rPr = rPrDefault.findChild("w:rPr");
33024
+ if (!rPr) {
33025
+ rPr = XmlNode2.element("w:rPr");
33026
+ rPrDefault.children.push(rPr);
33027
+ }
33028
+ return rPr;
33029
+ }
32524
33030
  ensureStylesRoot() {
32525
33031
  const root = XmlNode2.findRoot(this.tree, "w:styles");
32526
33032
  if (!root)
@@ -32534,6 +33040,14 @@ class StylesView {
32534
33040
  stylesRoot.children.push(BASELINE[styleId]());
32535
33041
  }
32536
33042
  }
33043
+ function applyRunFont(rFonts, fontName) {
33044
+ rFonts.setAttribute("w:ascii", fontName);
33045
+ rFonts.setAttribute("w:hAnsi", fontName);
33046
+ rFonts.setAttribute("w:cs", fontName);
33047
+ delete rFonts.attributes["w:asciiTheme"];
33048
+ delete rFonts.attributes["w:hAnsiTheme"];
33049
+ delete rFonts.attributes["w:cstheme"];
33050
+ }
32537
33051
  function NormalStyle() {
32538
33052
  return /* @__PURE__ */ jsxDEV(w.style, {
32539
33053
  "w-type": "paragraph",
@@ -32547,6 +33061,80 @@ function NormalStyle() {
32547
33061
  ]
32548
33062
  }, undefined, true, undefined, this);
32549
33063
  }
33064
+ function TitleStyle() {
33065
+ return /* @__PURE__ */ jsxDEV(w.style, {
33066
+ "w-type": "paragraph",
33067
+ "w-styleId": "Title",
33068
+ children: [
33069
+ /* @__PURE__ */ jsxDEV(w.name, {
33070
+ "w-val": "Title"
33071
+ }, undefined, false, undefined, this),
33072
+ /* @__PURE__ */ jsxDEV(w.basedOn, {
33073
+ "w-val": "Normal"
33074
+ }, undefined, false, undefined, this),
33075
+ /* @__PURE__ */ jsxDEV(w.next, {
33076
+ "w-val": "Normal"
33077
+ }, undefined, false, undefined, this),
33078
+ /* @__PURE__ */ jsxDEV(w.qFormat, {}, undefined, false, undefined, this),
33079
+ /* @__PURE__ */ jsxDEV(w.pPr, {
33080
+ children: /* @__PURE__ */ jsxDEV(w.spacing, {
33081
+ "w-after": "60"
33082
+ }, undefined, false, undefined, this)
33083
+ }, undefined, false, undefined, this),
33084
+ /* @__PURE__ */ jsxDEV(w.rPr, {
33085
+ children: [
33086
+ /* @__PURE__ */ jsxDEV(w.rFonts, {
33087
+ "w-ascii": "Calibri Light",
33088
+ "w-hAnsi": "Calibri Light"
33089
+ }, undefined, false, undefined, this),
33090
+ /* @__PURE__ */ jsxDEV(w.color, {
33091
+ "w-val": "1F3864"
33092
+ }, undefined, false, undefined, this),
33093
+ /* @__PURE__ */ jsxDEV(w.sz, {
33094
+ "w-val": "56"
33095
+ }, undefined, false, undefined, this)
33096
+ ]
33097
+ }, undefined, true, undefined, this)
33098
+ ]
33099
+ }, undefined, true, undefined, this);
33100
+ }
33101
+ function SubtitleStyle() {
33102
+ return /* @__PURE__ */ jsxDEV(w.style, {
33103
+ "w-type": "paragraph",
33104
+ "w-styleId": "Subtitle",
33105
+ children: [
33106
+ /* @__PURE__ */ jsxDEV(w.name, {
33107
+ "w-val": "Subtitle"
33108
+ }, undefined, false, undefined, this),
33109
+ /* @__PURE__ */ jsxDEV(w.basedOn, {
33110
+ "w-val": "Normal"
33111
+ }, undefined, false, undefined, this),
33112
+ /* @__PURE__ */ jsxDEV(w.next, {
33113
+ "w-val": "Normal"
33114
+ }, undefined, false, undefined, this),
33115
+ /* @__PURE__ */ jsxDEV(w.qFormat, {}, undefined, false, undefined, this),
33116
+ /* @__PURE__ */ jsxDEV(w.pPr, {
33117
+ children: /* @__PURE__ */ jsxDEV(w.spacing, {
33118
+ "w-after": "160"
33119
+ }, undefined, false, undefined, this)
33120
+ }, undefined, false, undefined, this),
33121
+ /* @__PURE__ */ jsxDEV(w.rPr, {
33122
+ children: [
33123
+ /* @__PURE__ */ jsxDEV(w.rFonts, {
33124
+ "w-ascii": "Calibri Light",
33125
+ "w-hAnsi": "Calibri Light"
33126
+ }, undefined, false, undefined, this),
33127
+ /* @__PURE__ */ jsxDEV(w.color, {
33128
+ "w-val": "5A5A5A"
33129
+ }, undefined, false, undefined, this),
33130
+ /* @__PURE__ */ jsxDEV(w.sz, {
33131
+ "w-val": "28"
33132
+ }, undefined, false, undefined, this)
33133
+ ]
33134
+ }, undefined, true, undefined, this)
33135
+ ]
33136
+ }, undefined, true, undefined, this);
33137
+ }
32550
33138
  function HeadingStyle({
32551
33139
  styleId,
32552
33140
  displayName,
@@ -32915,11 +33503,14 @@ function EndnoteTextStyle() {
32915
33503
  var STYLES_PART_NAME = "word/styles.xml", STYLES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", STYLES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", EMPTY_STYLES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
32916
33504
  <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>`, BASELINE;
32917
33505
  var init_styles = __esm(() => {
33506
+ init_blocks();
32918
33507
  init_jsx();
32919
33508
  init_parser();
32920
33509
  init_jsx_dev_runtime();
32921
33510
  BASELINE = {
32922
33511
  Normal: () => /* @__PURE__ */ jsxDEV(NormalStyle, {}, undefined, false, undefined, this),
33512
+ Title: () => /* @__PURE__ */ jsxDEV(TitleStyle, {}, undefined, false, undefined, this),
33513
+ Subtitle: () => /* @__PURE__ */ jsxDEV(SubtitleStyle, {}, undefined, false, undefined, this),
32923
33514
  Heading1: () => /* @__PURE__ */ jsxDEV(HeadingStyle, {
32924
33515
  styleId: "Heading1",
32925
33516
  displayName: "heading 1",
@@ -32968,6 +33559,28 @@ var init_styles = __esm(() => {
32968
33559
  italic: true,
32969
33560
  color: "1F4D78"
32970
33561
  }, undefined, false, undefined, this),
33562
+ Heading7: () => /* @__PURE__ */ jsxDEV(HeadingStyle, {
33563
+ styleId: "Heading7",
33564
+ displayName: "heading 7",
33565
+ outlineLevel: 6,
33566
+ sizeHalfPoints: 22,
33567
+ color: "2E74B5"
33568
+ }, undefined, false, undefined, this),
33569
+ Heading8: () => /* @__PURE__ */ jsxDEV(HeadingStyle, {
33570
+ styleId: "Heading8",
33571
+ displayName: "heading 8",
33572
+ outlineLevel: 7,
33573
+ sizeHalfPoints: 22,
33574
+ italic: true,
33575
+ color: "272727"
33576
+ }, undefined, false, undefined, this),
33577
+ Heading9: () => /* @__PURE__ */ jsxDEV(HeadingStyle, {
33578
+ styleId: "Heading9",
33579
+ displayName: "heading 9",
33580
+ outlineLevel: 8,
33581
+ sizeHalfPoints: 22,
33582
+ color: "272727"
33583
+ }, undefined, false, undefined, this),
32971
33584
  Quote: () => /* @__PURE__ */ jsxDEV(QuoteStyle, {}, undefined, false, undefined, this),
32972
33585
  IntenseQuote: () => /* @__PURE__ */ jsxDEV(IntenseQuoteStyle, {}, undefined, false, undefined, this),
32973
33586
  Code: () => /* @__PURE__ */ jsxDEV(CodeStyle, {}, undefined, false, undefined, this),
@@ -33131,6 +33744,7 @@ var init_document = __esm(() => {
33131
33744
  var init_ast = __esm(() => {
33132
33745
  init_document();
33133
33746
  init_body();
33747
+ init_styles();
33134
33748
  init_text();
33135
33749
  });
33136
33750
 
@@ -33826,6 +34440,38 @@ function CommentBody({
33826
34440
  }, undefined, false, undefined, this)
33827
34441
  }, undefined, false, undefined, this);
33828
34442
  }
34443
+ function addReplyCommentMarkers(documentTree, threadNumericIds, replyNumericId) {
34444
+ const document2 = XmlNode2.findRoot(documentTree, "w:document");
34445
+ if (!document2)
34446
+ return false;
34447
+ const threadIds = new Set(threadNumericIds);
34448
+ const referenceRun = findLastDescendant(document2, (node) => node.tag === "w:r" && node.children.some((child2) => child2.tag === "w:commentReference" && threadIds.has(child2.getAttribute("w:id") ?? "")));
34449
+ if (!referenceRun)
34450
+ return false;
34451
+ const rangeStart = findLastDescendant(document2, (node) => node.tag === "w:commentRangeStart" && threadIds.has(node.getAttribute("w:id") ?? ""));
34452
+ if (!rangeStart) {
34453
+ spliceAfter(referenceRun, commentReferenceRun(replyNumericId));
34454
+ return true;
34455
+ }
34456
+ spliceAfter(rangeStart, commentRangeStartMarker(replyNumericId));
34457
+ spliceAfter(referenceRun, commentRangeEndMarker(replyNumericId), commentReferenceRun(replyNumericId));
34458
+ return true;
34459
+ }
34460
+ function findLastDescendant(root, matches) {
34461
+ let last;
34462
+ for (const child2 of root.children) {
34463
+ if (matches(child2))
34464
+ last = { node: child2, siblings: root.children };
34465
+ const found = findLastDescendant(child2, matches);
34466
+ if (found)
34467
+ last = found;
34468
+ }
34469
+ return last;
34470
+ }
34471
+ function spliceAfter(target, ...nodes) {
34472
+ const index = target.siblings.indexOf(target.node);
34473
+ target.siblings.splice(index + 1, 0, ...nodes);
34474
+ }
33829
34475
  function removeCommentMarkers(documentTree, numericId) {
33830
34476
  const document2 = XmlNode2.findRoot(documentTree, "w:document");
33831
34477
  if (!document2)
@@ -33886,7 +34532,7 @@ class Comments {
33886
34532
  return comments;
33887
34533
  }
33888
34534
  add(anchor, options) {
33889
- const date = options.date ?? new Date().toISOString();
34535
+ const date = options.date ?? resolveDate();
33890
34536
  const numericId = this.document.comments?.nextId() ?? "0";
33891
34537
  const paraId = generateParaId();
33892
34538
  try {
@@ -33913,21 +34559,47 @@ class Comments {
33913
34559
  if (!view?.findById(parentNumericId)) {
33914
34560
  throw new CommentsError("COMMENT_NOT_FOUND", `Parent comment not found: c${parentNumericId}`);
33915
34561
  }
33916
- const parentParaId = view.ensureParaId(parentNumericId);
33917
- if (!parentParaId) {
33918
- throw new CommentsError("COMMENT_NOT_FOUND", `Parent comment c${parentNumericId} could not be assigned a w14:paraId.`);
34562
+ const rootNumericId = view.threadRootId(parentNumericId);
34563
+ const rootParaId = view.ensureParaId(rootNumericId);
34564
+ if (!rootParaId) {
34565
+ throw new CommentsError("COMMENT_NOT_FOUND", `Parent comment c${rootNumericId} could not be assigned a w14:paraId.`);
33919
34566
  }
33920
- const date = options.date ?? new Date().toISOString();
34567
+ const date = options.date ?? resolveDate();
33921
34568
  const numericId = view.nextId();
33922
34569
  const replyParaId = generateParaId();
33923
- this.#appendCommentBody(numericId, replyParaId, body, options.author, date);
34570
+ const anchored = addReplyCommentMarkers(this.document.documentTree, [rootNumericId, ...view.descendantReplyIds(rootNumericId)], numericId);
34571
+ if (!anchored) {
34572
+ throw new CommentsError("COMMENT_NOT_FOUND", `Parent comment c${parentNumericId} has no anchor in the document body; cannot anchor a reply to it.`);
34573
+ }
34574
+ this.#appendCommentBody(numericId, replyParaId, body, options.author, date, {
34575
+ threadParentNumericId: rootNumericId
34576
+ });
33924
34577
  const extView = this.document.ensureCommentsExtended();
33925
34578
  const extRoot = extView.extendedTree ? XmlNode2.findRoot(extView.extendedTree, "w15:commentsEx") : undefined;
33926
34579
  if (!extRoot)
33927
34580
  throw new Error("expected <w15:commentsEx> root");
33928
- extRoot.children.push(new XmlNode2("w15:commentEx", {
34581
+ const threadParaIds = new Set([
34582
+ rootParaId,
34583
+ ...view.descendantReplyIds(rootNumericId).map((replyId) => view.paraIdFor(replyId))
34584
+ ]);
34585
+ const hasRootEntry = extRoot.children.some((child2) => child2.tag === "w15:commentEx" && child2.getAttribute("w15:paraId") === rootParaId);
34586
+ if (!hasRootEntry) {
34587
+ extRoot.children.push(new XmlNode2("w15:commentEx", {
34588
+ "w15:paraId": rootParaId,
34589
+ "w15:done": "0"
34590
+ }));
34591
+ }
34592
+ let insertIndex = extRoot.children.length;
34593
+ for (const [index, child2] of extRoot.children.entries()) {
34594
+ if (child2.tag !== "w15:commentEx")
34595
+ continue;
34596
+ const paraId = child2.getAttribute("w15:paraId");
34597
+ if (paraId && threadParaIds.has(paraId))
34598
+ insertIndex = index + 1;
34599
+ }
34600
+ extRoot.children.splice(insertIndex, 0, new XmlNode2("w15:commentEx", {
33929
34601
  "w15:paraId": replyParaId,
33930
- "w15:paraIdParent": parentParaId,
34602
+ "w15:paraIdParent": rootParaId,
33931
34603
  "w15:done": "0"
33932
34604
  }));
33933
34605
  return numericId;
@@ -33977,10 +34649,16 @@ class Comments {
33977
34649
  }
33978
34650
  if (!view)
33979
34651
  return;
34652
+ const cascaded = new Set(normalized);
34653
+ for (const commentId of normalized) {
34654
+ for (const replyId of view.descendantReplyIds(commentId)) {
34655
+ cascaded.add(`c${replyId}`);
34656
+ }
34657
+ }
33980
34658
  const root = XmlNode2.findRoot(view.tree, "w:comments");
33981
34659
  if (!root)
33982
34660
  return;
33983
- for (const commentId of normalized) {
34661
+ for (const commentId of cascaded) {
33984
34662
  const numericId = commentId.slice(1);
33985
34663
  const node = view.findById(numericId);
33986
34664
  if (!node)
@@ -34009,12 +34687,12 @@ class Comments {
34009
34687
  this.#appendCommentBody(numericId, paraId, options.body, options.author, options.date);
34010
34688
  return numericId;
34011
34689
  }
34012
- #appendCommentBody(numericId, paraId, text2, author, date) {
34690
+ #appendCommentBody(numericId, paraId, text2, author, date, options) {
34013
34691
  const commentsView = this.document.ensureComments();
34014
34692
  const commentsRoot = XmlNode2.findRoot(commentsView.tree, "w:comments");
34015
34693
  if (!commentsRoot)
34016
34694
  throw new Error("expected <w:comments> root");
34017
- commentsRoot.children.push(/* @__PURE__ */ jsxDEV(CommentBody, {
34695
+ const comment = /* @__PURE__ */ jsxDEV(CommentBody, {
34018
34696
  options: {
34019
34697
  id: numericId,
34020
34698
  author,
@@ -34023,7 +34701,12 @@ class Comments {
34023
34701
  paraId,
34024
34702
  text: text2
34025
34703
  }
34026
- }, undefined, false, undefined, this));
34704
+ }, undefined, false, undefined, this);
34705
+ if (options?.threadParentNumericId) {
34706
+ commentsView.insertReplyAfter(options.threadParentNumericId, comment);
34707
+ return;
34708
+ }
34709
+ commentsRoot.children.push(comment);
34027
34710
  }
34028
34711
  #resolveBlock(blockId) {
34029
34712
  try {
@@ -34058,6 +34741,7 @@ var init_comments2 = __esm(() => {
34058
34741
  init_parser();
34059
34742
  init_markers();
34060
34743
  init_markers();
34744
+ init_track_changes();
34061
34745
  init_jsx_dev_runtime();
34062
34746
  CommentsError = class CommentsError extends Error {
34063
34747
  code;
@@ -34071,289 +34755,6 @@ var init_comments2 = __esm(() => {
34071
34755
  };
34072
34756
  });
34073
34757
 
34074
- // src/core/blocks.tsx
34075
- function Paragraph({
34076
- style,
34077
- alignment,
34078
- list,
34079
- taskState,
34080
- tabs,
34081
- text: text2,
34082
- runs,
34083
- ...formatting
34084
- }) {
34085
- const resolvedRuns = runs ?? textToRuns(text2 ?? "", formatting);
34086
- return /* @__PURE__ */ jsxDEV(w.p, {
34087
- children: [
34088
- /* @__PURE__ */ jsxDEV(ParagraphProperties, {
34089
- options: { style, alignment, list, tabs }
34090
- }, undefined, false, undefined, this),
34091
- taskState && /* @__PURE__ */ jsxDEV(TaskCheckbox, {
34092
- checked: taskState === "checked"
34093
- }, undefined, false, undefined, this),
34094
- taskState && /* @__PURE__ */ jsxDEV(w.r, {
34095
- children: /* @__PURE__ */ jsxDEV(w.t, {
34096
- "xml:space": "preserve",
34097
- children: " "
34098
- }, undefined, false, undefined, this)
34099
- }, undefined, false, undefined, this),
34100
- resolvedRuns.map((run) => /* @__PURE__ */ jsxDEV(RunElement, {
34101
- run
34102
- }, undefined, false, undefined, this))
34103
- ]
34104
- }, undefined, true, undefined, this);
34105
- }
34106
- function textToRuns(text2, formatting) {
34107
- if (!text2.includes(`
34108
- `) && !text2.includes("\t")) {
34109
- return [{ type: "text", text: text2, ...formatting }];
34110
- }
34111
- const runs = [];
34112
- for (const segment of text2.split(/(\n|\t)/)) {
34113
- if (segment === "")
34114
- continue;
34115
- if (segment === `
34116
- `)
34117
- runs.push({ type: "break", kind: "line" });
34118
- else if (segment === "\t")
34119
- runs.push({ type: "tab" });
34120
- else
34121
- runs.push({ type: "text", text: segment, ...formatting });
34122
- }
34123
- return runs;
34124
- }
34125
- function RunElement({ run }) {
34126
- if (run.type === "text")
34127
- return /* @__PURE__ */ jsxDEV(TextRunElement, {
34128
- run
34129
- }, undefined, false, undefined, this);
34130
- if (run.type === "break") {
34131
- return /* @__PURE__ */ jsxDEV(w.r, {
34132
- children: /* @__PURE__ */ jsxDEV(w.br, {
34133
- "w-type": run.kind === "line" ? undefined : run.kind
34134
- }, undefined, false, undefined, this)
34135
- }, undefined, false, undefined, this);
34136
- }
34137
- if (run.type === "tab") {
34138
- return /* @__PURE__ */ jsxDEV(w.r, {
34139
- children: /* @__PURE__ */ jsxDEV(w.tab, {}, undefined, false, undefined, this)
34140
- }, undefined, false, undefined, this);
34141
- }
34142
- return null;
34143
- }
34144
- function applyParagraphOptionsInPlace(rebuilt, options) {
34145
- if (!options.style && !options.alignment && !options.tabs)
34146
- return;
34147
- let pPr = rebuilt.find((child2) => child2.tag === "w:pPr");
34148
- if (!pPr) {
34149
- pPr = new XmlNode2("w:pPr");
34150
- rebuilt.unshift(pPr);
34151
- }
34152
- if (options.tabs) {
34153
- pPr.children = pPr.children.filter((child2) => child2.tag !== "w:tabs");
34154
- if (options.tabs.length > 0) {
34155
- const tabsNode = new XmlNode2("w:tabs");
34156
- for (const stop of options.tabs) {
34157
- tabsNode.children.push(new XmlNode2("w:tab", {
34158
- "w:val": stop.align,
34159
- "w:pos": String(Math.round(stop.pos))
34160
- }));
34161
- }
34162
- insertPprChildInOrder(pPr, tabsNode);
34163
- }
34164
- }
34165
- if (options.style) {
34166
- const existingStyle = pPr.findChild("w:pStyle");
34167
- if (existingStyle) {
34168
- existingStyle.setAttribute("w:val", options.style);
34169
- } else {
34170
- insertPprChildInOrder(pPr, new XmlNode2("w:pStyle", { "w:val": options.style }));
34171
- }
34172
- }
34173
- if (options.alignment) {
34174
- const existingJc = pPr.findChild("w:jc");
34175
- if (existingJc) {
34176
- existingJc.setAttribute("w:val", options.alignment);
34177
- } else {
34178
- insertPprChildInOrder(pPr, new XmlNode2("w:jc", { "w:val": options.alignment }));
34179
- }
34180
- }
34181
- }
34182
- function pprChildRank(tag) {
34183
- const index = PPR_CHILD_ORDER.indexOf(tag);
34184
- if (index >= 0)
34185
- return index;
34186
- return PPR_CHILD_ORDER.indexOf("w:rPr") - 0.5;
34187
- }
34188
- function insertPprChildInOrder(pPr, child2) {
34189
- const rank = pprChildRank(child2.tag);
34190
- const at = pPr.children.findIndex((existing) => pprChildRank(existing.tag) > rank);
34191
- if (at < 0)
34192
- pPr.children.push(child2);
34193
- else
34194
- pPr.children.splice(at, 0, child2);
34195
- }
34196
- function HorizontalRule() {
34197
- return /* @__PURE__ */ jsxDEV(w.p, {
34198
- children: /* @__PURE__ */ jsxDEV(w.pPr, {
34199
- children: /* @__PURE__ */ jsxDEV(w.pBdr, {
34200
- children: /* @__PURE__ */ jsxDEV(w.bottom, {
34201
- "w-val": "single",
34202
- "w-sz": "6",
34203
- "w-space": "1",
34204
- "w-color": "auto"
34205
- }, undefined, false, undefined, this)
34206
- }, undefined, false, undefined, this)
34207
- }, undefined, false, undefined, this)
34208
- }, undefined, false, undefined, this);
34209
- }
34210
- function TextRunElement({ run }) {
34211
- return /* @__PURE__ */ jsxDEV(w.r, {
34212
- children: [
34213
- /* @__PURE__ */ jsxDEV(RunProperties, {
34214
- run
34215
- }, undefined, false, undefined, this),
34216
- /* @__PURE__ */ jsxDEV(w.t, {
34217
- "xml:space": "preserve",
34218
- children: run.text
34219
- }, undefined, false, undefined, this)
34220
- ]
34221
- }, undefined, true, undefined, this);
34222
- }
34223
- function RunProperties({ run }) {
34224
- const isEmpty = FORMATTING_KEYS.every((key) => run[key] == null);
34225
- if (isEmpty)
34226
- return null;
34227
- return /* @__PURE__ */ jsxDEV(w.rPr, {
34228
- children: [
34229
- run.runStyle && /* @__PURE__ */ jsxDEV(w.rStyle, {
34230
- "w-val": run.runStyle
34231
- }, undefined, false, undefined, this),
34232
- run.font && /* @__PURE__ */ jsxDEV(w.rFonts, {
34233
- "w-ascii": run.font,
34234
- "w-hAnsi": run.font
34235
- }, undefined, false, undefined, this),
34236
- run.bold && /* @__PURE__ */ jsxDEV(w.b, {}, undefined, false, undefined, this),
34237
- run.italic && /* @__PURE__ */ jsxDEV(w.i, {}, undefined, false, undefined, this),
34238
- run.allCaps && /* @__PURE__ */ jsxDEV(w.caps, {}, undefined, false, undefined, this),
34239
- run.smallCaps && /* @__PURE__ */ jsxDEV(w.smallCaps, {}, undefined, false, undefined, this),
34240
- run.strike && /* @__PURE__ */ jsxDEV(w.strike, {}, undefined, false, undefined, this),
34241
- (run.color || run.colorTheme) && /* @__PURE__ */ jsxDEV(w.color, {
34242
- "w-val": run.color ?? "auto",
34243
- "w-themeColor": run.colorTheme,
34244
- "w-themeTint": run.colorThemeTint,
34245
- "w-themeShade": run.colorThemeShade
34246
- }, undefined, false, undefined, this),
34247
- run.sizeHalfPoints !== undefined && /* @__PURE__ */ jsxDEV(w.sz, {
34248
- "w-val": String(run.sizeHalfPoints)
34249
- }, undefined, false, undefined, this),
34250
- run.highlight && /* @__PURE__ */ jsxDEV(w.highlight, {
34251
- "w-val": run.highlight
34252
- }, undefined, false, undefined, this),
34253
- run.underline && /* @__PURE__ */ jsxDEV(w.u, {
34254
- "w-val": run.underline,
34255
- "w-color": run.underlineColor
34256
- }, undefined, false, undefined, this),
34257
- run.shade && /* @__PURE__ */ jsxDEV(w.shd, {
34258
- "w-val": "clear",
34259
- "w-color": "auto",
34260
- "w-fill": run.shade
34261
- }, undefined, false, undefined, this),
34262
- run.vertAlign && /* @__PURE__ */ jsxDEV(w.vertAlign, {
34263
- "w-val": run.vertAlign
34264
- }, undefined, false, undefined, this)
34265
- ]
34266
- }, undefined, true, undefined, this);
34267
- }
34268
- function ParagraphProperties({
34269
- options
34270
- }) {
34271
- const hasTabs = options.tabs !== undefined && options.tabs.length > 0;
34272
- if (!options.style && !options.alignment && !options.list && !hasTabs)
34273
- return null;
34274
- return /* @__PURE__ */ jsxDEV(w.pPr, {
34275
- children: [
34276
- options.style && /* @__PURE__ */ jsxDEV(w.pStyle, {
34277
- "w-val": options.style
34278
- }, undefined, false, undefined, this),
34279
- options.list && /* @__PURE__ */ jsxDEV(w.numPr, {
34280
- children: [
34281
- /* @__PURE__ */ jsxDEV(w.ilvl, {
34282
- "w-val": String(options.list.level)
34283
- }, undefined, false, undefined, this),
34284
- /* @__PURE__ */ jsxDEV(w.numId, {
34285
- "w-val": String(options.list.numId)
34286
- }, undefined, false, undefined, this)
34287
- ]
34288
- }, undefined, true, undefined, this),
34289
- hasTabs && /* @__PURE__ */ jsxDEV(w.tabs, {
34290
- children: options.tabs?.map((stop) => /* @__PURE__ */ jsxDEV(w.tab, {
34291
- "w-val": stop.align,
34292
- "w-pos": String(Math.round(stop.pos))
34293
- }, undefined, false, undefined, this))
34294
- }, undefined, false, undefined, this),
34295
- options.alignment && /* @__PURE__ */ jsxDEV(w.jc, {
34296
- "w-val": options.alignment
34297
- }, undefined, false, undefined, this)
34298
- ]
34299
- }, undefined, true, undefined, this);
34300
- }
34301
- var PPR_CHILD_ORDER, FORMATTING_KEYS;
34302
- var init_blocks = __esm(() => {
34303
- init_jsx();
34304
- init_parser();
34305
- init_task_list();
34306
- init_jsx_dev_runtime();
34307
- PPR_CHILD_ORDER = [
34308
- "w:pStyle",
34309
- "w:keepNext",
34310
- "w:keepLines",
34311
- "w:pageBreakBefore",
34312
- "w:framePr",
34313
- "w:widowControl",
34314
- "w:numPr",
34315
- "w:suppressLineNumbers",
34316
- "w:pBdr",
34317
- "w:shd",
34318
- "w:tabs",
34319
- "w:suppressAutoHyphens",
34320
- "w:bidi",
34321
- "w:adjustRightInd",
34322
- "w:snapToGrid",
34323
- "w:spacing",
34324
- "w:ind",
34325
- "w:contextualSpacing",
34326
- "w:mirrorIndents",
34327
- "w:suppressOverlap",
34328
- "w:jc",
34329
- "w:textDirection",
34330
- "w:textAlignment",
34331
- "w:textboxTightWrap",
34332
- "w:outlineLvl",
34333
- "w:divId",
34334
- "w:cnfStyle",
34335
- "w:rPr",
34336
- "w:sectPr",
34337
- "w:pPrChange"
34338
- ];
34339
- FORMATTING_KEYS = [
34340
- "runStyle",
34341
- "font",
34342
- "bold",
34343
- "italic",
34344
- "allCaps",
34345
- "smallCaps",
34346
- "strike",
34347
- "color",
34348
- "colorTheme",
34349
- "sizeHalfPoints",
34350
- "highlight",
34351
- "underline",
34352
- "shade",
34353
- "vertAlign"
34354
- ];
34355
- });
34356
-
34357
34758
  // src/core/code-block/style.tsx
34358
34759
  function ensureCodeBlockStyles(document2, language) {
34359
34760
  document2.ensureStyles().ensureStyle("Code");
@@ -50188,6 +50589,88 @@ var init_edit = __esm(() => {
50188
50589
  };
50189
50590
  });
50190
50591
 
50592
+ // src/core/fonts/index.ts
50593
+ class Fonts {
50594
+ document;
50595
+ constructor(document2) {
50596
+ this.document = document2;
50597
+ }
50598
+ async setDefault(fontName, opts = {}) {
50599
+ const styles = this.document.ensureStyles();
50600
+ styles.setDefaultFont(fontName);
50601
+ if (opts.sizeHalfPoints !== undefined) {
50602
+ styles.setDefaultSizeHalfPoints(opts.sizeHalfPoints);
50603
+ }
50604
+ const themeUpdated = await applyThemeLatinFonts(this.document.pkg, themePartName(this.document.relationships), fontName);
50605
+ const explicitStyles = styles.explicitFontStyleIds();
50606
+ let repointed = 0;
50607
+ if (opts.all) {
50608
+ repointed += styles.overrideStyleFonts(fontName);
50609
+ repointed += repointTreeFonts(this.document.documentTree, fontName);
50610
+ if (this.document.footnotes) {
50611
+ repointed += repointTreeFonts(this.document.footnotes.tree, fontName);
50612
+ }
50613
+ if (this.document.endnotes) {
50614
+ repointed += repointTreeFonts(this.document.endnotes.tree, fontName);
50615
+ }
50616
+ }
50617
+ return {
50618
+ font: fontName,
50619
+ sizeHalfPoints: opts.sizeHalfPoints,
50620
+ themeUpdated,
50621
+ explicitStyles,
50622
+ repointed,
50623
+ all: Boolean(opts.all)
50624
+ };
50625
+ }
50626
+ }
50627
+ function themePartName(relationships) {
50628
+ const rel2 = relationships.list().find((info) => info.type === THEME_RELATIONSHIP_TYPE);
50629
+ if (!rel2)
50630
+ return "word/theme/theme1.xml";
50631
+ return rel2.target.startsWith("/") ? rel2.target.slice(1) : `word/${rel2.target}`;
50632
+ }
50633
+ async function applyThemeLatinFonts(pkg, partName, fontName) {
50634
+ const tree = await pkg.readPart(partName);
50635
+ if (!tree)
50636
+ return false;
50637
+ const fontScheme = XmlNode2.findRoot(tree, "a:theme")?.findChild("a:themeElements")?.findChild("a:fontScheme");
50638
+ if (!fontScheme)
50639
+ return false;
50640
+ let changed = false;
50641
+ for (const slot of ["a:majorFont", "a:minorFont"]) {
50642
+ const latin = fontScheme.findChild(slot)?.findChild("a:latin");
50643
+ if (!latin)
50644
+ continue;
50645
+ latin.setAttribute("typeface", fontName);
50646
+ delete latin.attributes.panose;
50647
+ changed = true;
50648
+ }
50649
+ if (changed)
50650
+ pkg.writeText(partName, XmlNode2.serialize(tree));
50651
+ return changed;
50652
+ }
50653
+ function repointTreeFonts(tree, fontName) {
50654
+ let count = 0;
50655
+ const visit = (node) => {
50656
+ if (node.tag === "w:rFonts") {
50657
+ applyRunFont(node, fontName);
50658
+ count++;
50659
+ return;
50660
+ }
50661
+ for (const child2 of node.children)
50662
+ visit(child2);
50663
+ };
50664
+ for (const node of tree)
50665
+ visit(node);
50666
+ return count;
50667
+ }
50668
+ var THEME_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
50669
+ var init_fonts = __esm(() => {
50670
+ init_styles();
50671
+ init_parser();
50672
+ });
50673
+
50191
50674
  // src/core/image/drawing.tsx
50192
50675
  class Images {
50193
50676
  document;
@@ -57566,6 +58049,26 @@ var init_image = __esm(() => {
57566
58049
  init_source();
57567
58050
  });
57568
58051
 
58052
+ // src/core/literal-text.tsx
58053
+ function literalParagraphs(text2, options = {}) {
58054
+ return splitLiteralLines(text2).map((line) => /* @__PURE__ */ jsxDEV(Paragraph, {
58055
+ text: line,
58056
+ ...options
58057
+ }, undefined, false, undefined, this));
58058
+ }
58059
+ function splitLiteralLines(text2) {
58060
+ const normalized = text2.replace(/\r\n?/g, `
58061
+ `);
58062
+ const trimmed = normalized.endsWith(`
58063
+ `) ? normalized.slice(0, -1) : normalized;
58064
+ return trimmed.split(`
58065
+ `);
58066
+ }
58067
+ var init_literal_text = __esm(() => {
58068
+ init_blocks();
58069
+ init_jsx_dev_runtime();
58070
+ });
58071
+
57569
58072
  // src/core/markdown/errors.ts
57570
58073
  var MarkdownImportError;
57571
58074
  var init_errors = __esm(() => {
@@ -72083,7 +72586,7 @@ class Insert {
72083
72586
  ensureCodeBlockStyles(this.document, spec.language);
72084
72587
  }
72085
72588
  const blocks = Array.isArray(built) ? built : [built];
72086
- if (spec.kind === "text" || spec.kind === "runs" || spec.kind === "markdown") {
72589
+ if (spec.kind === "text" || spec.kind === "runs" || spec.kind === "markdown" || spec.kind === "literal") {
72087
72590
  inheritFormattingFromAnchor(blocks, blockRef.node);
72088
72591
  }
72089
72592
  if (opts.track ?? this.document.isTrackChangesEnabled()) {
@@ -72137,6 +72640,8 @@ async function buildInsertedParagraph(document4, spec, paragraphOptions) {
72137
72640
  return buildEquationParagraph(spec, paragraphOptions);
72138
72641
  case "markdown":
72139
72642
  return buildMarkdownBlocks(document4, spec);
72643
+ case "literal":
72644
+ return literalParagraphs(spec.text, paragraphOptions);
72140
72645
  }
72141
72646
  }
72142
72647
  async function buildMarkdownBlocks(document4, spec) {
@@ -72316,6 +72821,7 @@ var init_insert = __esm(() => {
72316
72821
  init_equation();
72317
72822
  init_image();
72318
72823
  init_jsx();
72824
+ init_literal_text();
72319
72825
  init_markdown2();
72320
72826
  init_sections();
72321
72827
  init_table();
@@ -80406,7 +80912,9 @@ var init_core2 = __esm(() => {
80406
80912
  init_package();
80407
80913
  init_comments2();
80408
80914
  init_edit();
80915
+ init_fonts();
80409
80916
  init_insert();
80917
+ init_literal_text();
80410
80918
  init_locators();
80411
80919
  init_markdown2();
80412
80920
  init_parser();
@@ -80486,6 +80994,8 @@ function ackTarget(ack) {
80486
80994
  return plural(applied, "change");
80487
80995
  if (str(ack.mode))
80488
80996
  return `tracking ${ack.mode}`;
80997
+ if (str(ack.font))
80998
+ return str(ack.font);
80489
80999
  if (Array.isArray(ack.batch))
80490
81000
  return plural(ack.batch.length, "change");
80491
81001
  if (str(ack.path))
@@ -81196,7 +81706,8 @@ Target (one required, mutually exclusive):
81196
81706
  --at cN Comment id (e.g., c0). Repeat for multiple ids:
81197
81707
  --at c1 --at c3 --at c5. All ids are validated against
81198
81708
  the pre-mutation tree, so the batch is atomic. The "c"
81199
- prefix is optional.
81709
+ prefix is optional. Deleting a thread parent also
81710
+ deletes its replies.
81200
81711
  --batch PATH JSONL with one {"id": "cN"} per line. Use - for stdin.
81201
81712
 
81202
81713
  Optional:
@@ -81320,7 +81831,7 @@ async function run4(args) {
81320
81831
  dryRun: true,
81321
81832
  path: path2,
81322
81833
  commentId: `c${nextId}`,
81323
- parentId: `c${parentNumericId}`,
81834
+ parentId: `c${document4.comments.threadRootId(parentNumericId)}`,
81324
81835
  ...outputPath ? { output: outputPath } : {}
81325
81836
  });
81326
81837
  return EXIT2.OK;
@@ -81340,7 +81851,7 @@ async function run4(args) {
81340
81851
  operation: "comments.reply",
81341
81852
  path: outputPath ?? path2,
81342
81853
  commentId: `c${numericId}`,
81343
- parentId: `c${parentNumericId}`
81854
+ parentId: `c${document4.comments?.threadRootId(parentNumericId) ?? parentNumericId}`
81344
81855
  });
81345
81856
  return EXIT2.OK;
81346
81857
  }
@@ -81351,6 +81862,8 @@ Usage:
81351
81862
 
81352
81863
  Required:
81353
81864
  --at cN Parent comment id (e.g., c0). The "c" prefix is optional.
81865
+ Replying to a reply attaches to the thread root (Word
81866
+ threads are single-level); the ack's parentId reports it.
81354
81867
  --text TEXT Reply body
81355
81868
 
81356
81869
  Optional:
@@ -81843,6 +82356,7 @@ async function run7(args) {
81843
82356
  title: { type: "string" },
81844
82357
  author: { type: "string" },
81845
82358
  text: { type: "string" },
82359
+ "text-file": { type: "string" },
81846
82360
  from: { type: "string" },
81847
82361
  force: { type: "boolean" },
81848
82362
  "dry-run": { type: "boolean" },
@@ -81862,8 +82376,10 @@ async function run7(args) {
81862
82376
  }
81863
82377
  const text6 = parsed.values.text;
81864
82378
  const fromPath = parsed.values.from;
81865
- if (text6 !== undefined && fromPath !== undefined) {
81866
- return fail("USAGE", "Pass either --text or --from, not both", "--text seeds a single paragraph; --from parses a markdown file into the body.");
82379
+ const textFilePath = parsed.values["text-file"];
82380
+ const contentSources = [text6, fromPath, textFilePath].filter((value) => value !== undefined);
82381
+ if (contentSources.length > 1) {
82382
+ return fail("USAGE", "Pass at most one of --text, --text-file, --from", "--text seeds one paragraph; --text-file seeds literal multi-paragraph text; --from parses a markdown file into the body.");
81867
82383
  }
81868
82384
  const dryRun = Boolean(parsed.values["dry-run"]);
81869
82385
  const destination = path2;
@@ -81876,6 +82392,7 @@ async function run7(args) {
81876
82392
  dryRun: true,
81877
82393
  path: destination,
81878
82394
  ...text6 !== undefined ? { text: text6 } : {},
82395
+ ...textFilePath !== undefined ? { textFile: textFilePath } : {},
81879
82396
  ...fromPath !== undefined ? { from: fromPath } : {}
81880
82397
  });
81881
82398
  return EXIT2.OK;
@@ -81890,6 +82407,11 @@ async function run7(args) {
81890
82407
  if (typeof applied === "number")
81891
82408
  return applied;
81892
82409
  blockCount = applied.blockCount;
82410
+ } else if (textFilePath !== undefined) {
82411
+ const applied = await applyLiteralToBody(destination, textFilePath);
82412
+ if (typeof applied === "number")
82413
+ return applied;
82414
+ blockCount = applied.blockCount;
81893
82415
  }
81894
82416
  await respondAck({
81895
82417
  ok: true,
@@ -81918,9 +82440,22 @@ async function applyMarkdownToBody(docxPath, markdownPath) {
81918
82440
  }
81919
82441
  throw error;
81920
82442
  }
81921
- if (blocks.length === 0) {
81922
- return { blockCount: 1 };
82443
+ return replacePlaceholderAndSave(document4, blocks);
82444
+ }
82445
+ async function applyLiteralToBody(docxPath, textFilePath) {
82446
+ let source2;
82447
+ try {
82448
+ source2 = textFilePath === "-" ? await new Response(Bun.stdin.stream()).text() : await Bun.file(textFilePath).text();
82449
+ } catch (error) {
82450
+ const message = error instanceof Error ? error.message : String(error);
82451
+ return fail("FILE_NOT_FOUND", `Failed to read --text-file ${textFilePath}: ${message}`);
81923
82452
  }
82453
+ const document4 = await Document.open(docxPath);
82454
+ return replacePlaceholderAndSave(document4, literalParagraphs(source2));
82455
+ }
82456
+ async function replacePlaceholderAndSave(document4, blocks) {
82457
+ if (blocks.length === 0)
82458
+ return { blockCount: 1 };
81924
82459
  const body = document4.body.body;
81925
82460
  const placeholderIndex = body.children.findIndex((child2) => child2.tag === "w:p");
81926
82461
  if (placeholderIndex === -1) {
@@ -81938,14 +82473,21 @@ Usage:
81938
82473
  Options:
81939
82474
  --title TEXT Document title
81940
82475
  --author TEXT Document author (default: $DOCX_AUTHOR)
81941
- --text TEXT Seed first paragraph with this text. Mutex with --from.
82476
+ --text TEXT Seed first paragraph with this text. One content source
82477
+ only (mutex with --text-file / --from).
82478
+ --text-file PATH Seed the body with LITERAL multi-paragraph text from PATH
82479
+ (use "-" for stdin), NOT parsed as markdown \u2014 every
82480
+ character lands verbatim, each newline starts a new
82481
+ paragraph. Use for prose GFM would corrupt ("3. note"
82482
+ stays "3.", *x* / [t](u) / bare URLs / {++x++} untouched).
82483
+ One content source only.
81942
82484
  --from PATH Seed the body with parsed markdown from PATH (use "-" for
81943
- stdin). Mutex with --text. Uses the same markdown dialect
81944
- as 'docx insert --markdown'. This is the canonical way to
81945
- build a whole .docx from a markdown file. Footnote/endnote
81946
- bodies keep bold/italic + hyperlinks; footnote labels
81947
- renumber to [^fnN] on import. (Under track-changes, note
81948
- bodies flatten to plain text.)
82485
+ stdin). One content source only. Uses the same markdown
82486
+ dialect as 'docx insert --markdown'. This is the canonical
82487
+ way to build a whole .docx from a markdown file.
82488
+ Footnote/endnote bodies keep bold/italic + hyperlinks;
82489
+ footnote labels renumber to [^fnN] on import. (Under
82490
+ track-changes, note bodies flatten to plain text.)
81949
82491
  --force Overwrite if FILE already exists
81950
82492
  --dry-run Print what would be created; do not write the file
81951
82493
  -v, --verbose Print the success ack JSON (default: a one-line confirmation)
@@ -81961,6 +82503,8 @@ Examples:
81961
82503
  docx create out.docx --title "Spec" --author "Claude" --text "First paragraph."
81962
82504
  docx create out.docx --from draft.md
81963
82505
  cat draft.md | docx create out.docx --from -
82506
+ docx create out.docx --text-file reviewer-notes.txt
82507
+ cat notes.txt | docx create out.docx --text-file -
81964
82508
 
81965
82509
  For a doc that opens with a code block, chain create with insert:
81966
82510
  docx create out.docx
@@ -82928,7 +83472,7 @@ async function commitRangeProps(document4, opts, options) {
82928
83472
  throw error;
82929
83473
  }
82930
83474
  if (applied === 0) {
82931
- return fail("BLOCK_NOT_FOUND", options.tabs !== undefined ? `No paragraphs with tab stops in ${opts.locator} \u2014 --tabs only adjusts tab-using lines (the ones \`read\` flags with docx:layout).` : `No paragraphs in ${opts.locator} to restyle.`);
83475
+ return fail("BLOCK_NOT_FOUND", options.tabs !== undefined ? `No non-list paragraphs with tab stops in ${opts.locator} \u2014 --tabs only adjusts tab-using lines (the ones \`read\` flags with docx:layout), and skips bullets.` : `No paragraphs in ${opts.locator} to restyle.`);
82932
83476
  }
82933
83477
  await document4.save(opts.outputPath);
82934
83478
  return emitEditAck(opts);
@@ -82939,7 +83483,7 @@ function scopeRangeProps(node2, options) {
82939
83483
  out.style = options.style;
82940
83484
  if (options.alignment !== undefined)
82941
83485
  out.alignment = options.alignment;
82942
- if (options.tabs !== undefined && paragraphHasTabStops(node2)) {
83486
+ if (options.tabs !== undefined && paragraphHasTabStops(node2) && !isListParagraph(node2)) {
82943
83487
  out.tabs = options.tabs;
82944
83488
  }
82945
83489
  if (out.style === undefined && out.alignment === undefined && out.tabs === undefined)
@@ -82949,6 +83493,9 @@ function scopeRangeProps(node2, options) {
82949
83493
  function paragraphHasTabStops(node2) {
82950
83494
  return node2.findChild("w:pPr")?.findChild("w:tabs") !== undefined;
82951
83495
  }
83496
+ function isListParagraph(node2) {
83497
+ return node2.findChild("w:pPr")?.findChild("w:numPr") !== undefined;
83498
+ }
82952
83499
  async function commitEquationEdit(document4, spec, opts) {
82953
83500
  if (spec.latex === undefined && spec.display === undefined) {
82954
83501
  return fail("USAGE", "--equation requires --equation NEW_LATEX, --display, or --inline");
@@ -85259,6 +85806,9 @@ async function runInsertBatch(filePath, batchSource, values2) {
85259
85806
  const placement = await parseTargetPlacement(entryValues);
85260
85807
  if (typeof placement === "number")
85261
85808
  return placement;
85809
+ if ("boundary" in placement) {
85810
+ return fail("USAGE", `entry ${index2}: --at-start/--at-end aren't supported in --batch (use --after/--before with a locator)`);
85811
+ }
85262
85812
  const spec = await chooseContentSpec(entryValues);
85263
85813
  if (typeof spec === "number")
85264
85814
  return spec;
@@ -85379,7 +85929,10 @@ var init_batch3 = __esm(() => {
85379
85929
  SINGLE_SHOT_FLAGS2 = [
85380
85930
  "after",
85381
85931
  "before",
85932
+ "at-start",
85933
+ "at-end",
85382
85934
  "text",
85935
+ "text-file",
85383
85936
  "runs",
85384
85937
  "page-break",
85385
85938
  "column-break",
@@ -85448,13 +86001,14 @@ async function run22(args) {
85448
86001
  const document4 = await openOrFail(opts.filePath);
85449
86002
  if (typeof document4 === "number")
85450
86003
  return document4;
85451
- const blockRef = await resolveBlockOrFail(document4, opts.placement.locator);
85452
- if (typeof blockRef === "number")
85453
- return blockRef;
86004
+ const resolved = await resolvePlacement(document4, opts.placement);
86005
+ if (typeof resolved === "number")
86006
+ return resolved;
86007
+ const { blockRef, mode, locator } = resolved;
85454
86008
  let blocks;
85455
86009
  try {
85456
86010
  blocks = await new Insert(document4).paragraph(blockRef, opts.spec, opts.paragraphOptions, {
85457
- placement: opts.placement.mode,
86011
+ placement: mode,
85458
86012
  authorFlag: opts.authorFlag,
85459
86013
  track: resolveTracked(document4, opts.trackFlag)
85460
86014
  });
@@ -85464,16 +86018,16 @@ async function run22(args) {
85464
86018
  }
85465
86019
  throw error;
85466
86020
  }
85467
- return commitInsert(document4, blockRef, blocks, opts);
86021
+ return commitInsert(document4, blockRef, blocks, opts, mode, locator);
85468
86022
  }
85469
- async function commitInsert(document4, blockRef, blocks, opts) {
86023
+ async function commitInsert(document4, blockRef, blocks, opts, mode, anchorLocator) {
85470
86024
  if (opts.dryRun) {
85471
86025
  await respond({
85472
86026
  operation: "insert",
85473
86027
  dryRun: true,
85474
86028
  path: opts.filePath,
85475
- anchor: opts.placement.locator,
85476
- placement: opts.placement.mode,
86029
+ anchor: anchorLocator,
86030
+ placement: mode,
85477
86031
  ...opts.outputPath ? { output: opts.outputPath } : {}
85478
86032
  });
85479
86033
  return EXIT2.OK;
@@ -85482,7 +86036,7 @@ async function commitInsert(document4, blockRef, blocks, opts) {
85482
86036
  if (targetIndex === -1) {
85483
86037
  return fail("BLOCK_NOT_FOUND", "Block reference is stale (parent does not contain it)");
85484
86038
  }
85485
- const insertIndex = opts.placement.mode === "after" ? targetIndex + 1 : targetIndex;
86039
+ const insertIndex = mode === "after" ? targetIndex + 1 : targetIndex;
85486
86040
  blockRef.parent.splice(insertIndex, 0, ...blocks);
85487
86041
  await document4.save(opts.outputPath);
85488
86042
  document4.reread();
@@ -85498,8 +86052,8 @@ async function commitInsert(document4, blockRef, blocks, opts) {
85498
86052
  operation: "insert",
85499
86053
  path: destination,
85500
86054
  locators,
85501
- anchor: opts.placement.locator,
85502
- placement: opts.placement.mode
86055
+ anchor: anchorLocator,
86056
+ placement: mode
85503
86057
  }, isLayoutAffecting(opts.spec) ? renderVerifyHint(destination) : undefined);
85504
86058
  return EXIT2.OK;
85505
86059
  }
@@ -85544,16 +86098,61 @@ async function buildSingleShotOptions(filePath, values2) {
85544
86098
  async function parseTargetPlacement(values2) {
85545
86099
  const after = values2.after;
85546
86100
  const before = values2.before;
85547
- if (!after && !before) {
85548
- return fail("USAGE", "Missing locator: pass --after or --before", HELP18);
85549
- }
85550
- if (after && before) {
85551
- return fail("USAGE", "Pass either --after or --before, not both", HELP18);
85552
- }
86101
+ const atStart = Boolean(values2["at-start"]);
86102
+ const atEnd = Boolean(values2["at-end"]);
86103
+ const chosen = [
86104
+ after !== undefined ? "--after" : null,
86105
+ before !== undefined ? "--before" : null,
86106
+ atStart ? "--at-start" : null,
86107
+ atEnd ? "--at-end" : null
86108
+ ].filter((flag) => flag !== null);
86109
+ if (chosen.length === 0) {
86110
+ return fail("USAGE", "Missing placement: pass --after, --before, --at-start, or --at-end", HELP18);
86111
+ }
86112
+ if (chosen.length > 1) {
86113
+ return fail("USAGE", `Pass exactly one placement, got ${chosen.join(" + ")}`, HELP18);
86114
+ }
86115
+ if (atStart)
86116
+ return { boundary: "start" };
86117
+ if (atEnd)
86118
+ return { boundary: "end" };
85553
86119
  if (after !== undefined)
85554
86120
  return { mode: "after", locator: after };
85555
86121
  return { mode: "before", locator: before };
85556
86122
  }
86123
+ async function resolvePlacement(document4, placement) {
86124
+ if ("boundary" in placement) {
86125
+ return resolveBoundaryAnchor(document4, placement.boundary);
86126
+ }
86127
+ const blockRef = await resolveBlockOrFail(document4, placement.locator);
86128
+ if (typeof blockRef === "number")
86129
+ return blockRef;
86130
+ return { blockRef, mode: placement.mode, locator: placement.locator };
86131
+ }
86132
+ async function resolveBoundaryAnchor(document4, boundary) {
86133
+ const bodyChildren = document4.body.body.children;
86134
+ const refs = [...document4.body.blockReferences.entries()].filter(([, ref]) => ref.parent === bodyChildren && (ref.node.tag === "w:p" || ref.node.tag === "w:tbl"));
86135
+ if (refs.length > 0) {
86136
+ const entry = boundary === "start" ? refs[0] : refs[refs.length - 1];
86137
+ if (entry) {
86138
+ const [locator, blockRef] = entry;
86139
+ return {
86140
+ blockRef,
86141
+ mode: boundary === "start" ? "before" : "after",
86142
+ locator
86143
+ };
86144
+ }
86145
+ }
86146
+ const sectPr = bodyChildren.find((child2) => child2.tag === "w:sectPr");
86147
+ if (!sectPr) {
86148
+ return fail("BLOCK_NOT_FOUND", "Document body has no blocks to anchor against");
86149
+ }
86150
+ return {
86151
+ blockRef: { node: sectPr, parent: bodyChildren },
86152
+ mode: "before",
86153
+ locator: "start"
86154
+ };
86155
+ }
85557
86156
  async function chooseContentSpec(values2) {
85558
86157
  if (values2.section !== undefined || values2.columns !== undefined || values2.type !== undefined) {
85559
86158
  return fail("USAGE", "insert no longer creates section/column layout \u2014 use `docx sections`", "To put paragraphs pN\u2026pM in N columns: `docx sections --at pN-pM --columns N`. To recount an existing section: `docx sections --at sN --columns N`.");
@@ -85578,6 +86177,8 @@ async function chooseContentSpec(values2) {
85578
86177
  switch (chosen.flag) {
85579
86178
  case "text":
85580
86179
  return buildTextSpec(values2);
86180
+ case "text-file":
86181
+ return resolveLiteralSpec(values2);
85581
86182
  case "runs": {
85582
86183
  const runs = await parseRunsArg(values2.runs);
85583
86184
  return typeof runs === "number" ? runs : { kind: "runs", runs };
@@ -85622,6 +86223,16 @@ async function resolveMarkdownSpec(values2, flag) {
85622
86223
  return fail("FILE_NOT_FOUND", `Failed to read --markdown-file ${path2}: ${message}`);
85623
86224
  }
85624
86225
  }
86226
+ async function resolveLiteralSpec(values2) {
86227
+ const path2 = values2["text-file"];
86228
+ try {
86229
+ const text6 = path2 === "-" ? await new Response(Bun.stdin.stream()).text() : await Bun.file(path2).text();
86230
+ return { kind: "literal", text: text6 };
86231
+ } catch (error) {
86232
+ const message = error instanceof Error ? error.message : String(error);
86233
+ return fail("FILE_NOT_FOUND", `Failed to read --text-file ${path2}: ${message}`);
86234
+ }
86235
+ }
85625
86236
  async function resolveCodeSpec(values2, flag) {
85626
86237
  const language = values2.language;
85627
86238
  if (flag === "code") {
@@ -85798,18 +86409,31 @@ var init_insert2 = __esm(() => {
85798
86409
 
85799
86410
  Usage:
85800
86411
  docx insert FILE (--after | --before) LOCATOR <content> [options]
86412
+ docx insert FILE (--at-start | --at-end) <content> [options]
85801
86413
  docx insert FILE --batch FILE.jsonl [options] # many inserts, one read
85802
86414
  docx insert FILE --batch - [options] # read JSONL from stdin
85803
86415
 
85804
- Locator (one required) \u2014 where to place the new block, relative to:
86416
+ Placement (exactly one required) \u2014 where to put the new block:
85805
86417
  --after LOCATOR Insert after the block at LOCATOR
85806
86418
  --before LOCATOR Insert before the block at LOCATOR
85807
86419
  LOCATOR is one of:
85808
86420
  ${ANCHOR_FORMS2}
85809
86421
  See \`docx info locators\`.
86422
+ --at-start Insert at the very top of the document (before the first
86423
+ paragraph/table) \u2014 no locator needed, works on a fresh doc.
86424
+ --at-end Insert at the very end (after the last paragraph/table, but
86425
+ before the trailing section properties). No locator needed.
86426
+ (--at-start/--at-end are single-shot only, not --batch.)
85810
86427
 
85811
86428
  Content (one required):
85812
86429
  --text TEXT Insert a paragraph with this text
86430
+ --text-file PATH Insert literal multi-paragraph text from PATH (use "-" for
86431
+ stdin), NOT parsed as markdown \u2014 every character lands
86432
+ verbatim. Each newline starts a new paragraph (blank lines
86433
+ become empty paragraphs). Use this for prose that must stay
86434
+ untouched: "3. note" stays "3." (no list renumber), *x* /
86435
+ [t](u) / bare URLs / {++x++} are NOT interpreted. (--text /
86436
+ --markdown go through GFM and would corrupt such content.)
85813
86437
  --runs JSON Insert a paragraph with custom runs (Run[] JSON)
85814
86438
  --page-break Insert an empty paragraph containing a page break
85815
86439
  --column-break Insert an empty paragraph containing a column break
@@ -85930,18 +86554,27 @@ Examples:
85930
86554
  docx insert doc.docx --after p3 --markdown $'# Heading\\n\\n- a\\n- b'
85931
86555
  docx insert doc.docx --after p3 --markdown-file README.md
85932
86556
  cat draft.md | docx insert doc.docx --after p3 --markdown-file -
86557
+ docx insert doc.docx --at-start --text "Title" --style Title
86558
+ docx insert doc.docx --after p3 --text-file reviewer-notes.txt
86559
+ cat notes.txt | docx insert doc.docx --at-end --text-file -
85933
86560
  docx insert doc.docx --batch additions.jsonl
85934
86561
 
85935
86562
  Batch JSONL example (keys mirror the flags; one insert per line):
85936
86563
  {"after": "p3", "text": "New clause.", "style": "Heading2"}
85937
86564
  {"before": "p0", "text": "ALERT", "color": "CC0000", "bold": true}
85938
86565
  {"after": "p5", "markdown": "## Summary\\n\\n- point a\\n- point b"}
86566
+ Ordering guarantee: entries apply in file order, and several entries anchored
86567
+ after the SAME block stack in that order \u2014 so three lines all "after": "p0"
86568
+ land as p1, p2, p3 in the order written (not reversed).
85939
86569
  `;
85940
86570
  OPTION_SPEC3 = {
85941
86571
  after: { type: "string" },
85942
86572
  before: { type: "string" },
86573
+ "at-start": { type: "boolean" },
86574
+ "at-end": { type: "boolean" },
85943
86575
  batch: { type: "string" },
85944
86576
  text: { type: "string" },
86577
+ "text-file": { type: "string" },
85945
86578
  runs: { type: "string" },
85946
86579
  "page-break": { type: "boolean" },
85947
86580
  "column-break": { type: "boolean" },
@@ -85989,6 +86622,7 @@ Batch JSONL example (keys mirror the flags; one insert per line):
85989
86622
  ];
85990
86623
  CONTENT_KINDS = [
85991
86624
  { flag: "text", subFlags: ["color", "bold", "italic", "url"] },
86625
+ { flag: "text-file", subFlags: [] },
85992
86626
  { flag: "runs", subFlags: [] },
85993
86627
  { flag: "page-break", subFlags: [] },
85994
86628
  { flag: "column-break", subFlags: [] },
@@ -87861,6 +88495,8 @@ function layoutHazardNote(paragraph2, ctx) {
87861
88495
  ]
87862
88496
  ], [paragraph2.id])}`;
87863
88497
  }
88498
+ if (paragraph2.list)
88499
+ return "";
87864
88500
  const tabs = paragraph2.tabStops ?? [];
87865
88501
  if (tabs.some((tab) => tab.align === "right"))
87866
88502
  return "";
@@ -89556,9 +90192,13 @@ __export(exports_styles, {
89556
90192
  run: () => run37
89557
90193
  });
89558
90194
  async function run37(args) {
90195
+ if (args[0] === "set-default-font") {
90196
+ return runSetDefaultFont(args.slice(1));
90197
+ }
89559
90198
  const parsed = await tryParseArgs(args, {
89560
90199
  at: { type: "string" },
89561
90200
  used: { type: "boolean" },
90201
+ catalog: { type: "boolean" },
89562
90202
  json: { type: "boolean" },
89563
90203
  help: { type: "boolean", short: "h" }
89564
90204
  }, HELP33);
@@ -89568,6 +90208,15 @@ async function run37(args) {
89568
90208
  await writeStdout(HELP33);
89569
90209
  return EXIT2.OK;
89570
90210
  }
90211
+ if (parsed.values.catalog) {
90212
+ const metas2 = baselineCatalog().map(styleMeta);
90213
+ if (parsed.values.json) {
90214
+ await respond(metas2);
90215
+ return EXIT2.OK;
90216
+ }
90217
+ await writeStdout(formatList(metas2));
90218
+ return EXIT2.OK;
90219
+ }
89571
90220
  const path2 = parsed.positionals[0];
89572
90221
  if (!path2)
89573
90222
  return fail("USAGE", "Missing FILE argument", HELP33);
@@ -89603,6 +90252,73 @@ async function run37(args) {
89603
90252
  await writeStdout(formatList(metas));
89604
90253
  return EXIT2.OK;
89605
90254
  }
90255
+ async function runSetDefaultFont(args) {
90256
+ const parsed = await tryParseArgs(args, { size: { type: "string" }, all: { type: "boolean" }, ...SAVE_FLAGS }, FONT_HELP);
90257
+ if (typeof parsed === "number")
90258
+ return parsed;
90259
+ if (parsed.values.help) {
90260
+ await writeStdout(FONT_HELP);
90261
+ return EXIT2.OK;
90262
+ }
90263
+ setVerboseAck(Boolean(parsed.values.verbose));
90264
+ const filePath = parsed.positionals[0];
90265
+ if (!filePath)
90266
+ return fail("USAGE", "Missing FILE argument", FONT_HELP);
90267
+ const fontName = parsed.positionals[1];
90268
+ if (!fontName) {
90269
+ return fail("USAGE", 'Missing FONT name (e.g. "Times New Roman")', FONT_HELP);
90270
+ }
90271
+ let sizeHalfPoints;
90272
+ const sizeRaw = parsed.values.size;
90273
+ if (sizeRaw !== undefined) {
90274
+ if (!/^\s*\d+(\.\d+)?\s*$/.test(sizeRaw) || Number.parseFloat(sizeRaw) <= 0) {
90275
+ return fail("USAGE", `--size must be a positive number of points, got "${sizeRaw}"`);
90276
+ }
90277
+ sizeHalfPoints = Math.round(Number.parseFloat(sizeRaw) * 2);
90278
+ }
90279
+ const all2 = Boolean(parsed.values.all);
90280
+ const outputPath = parsed.values.output;
90281
+ const dryRun = Boolean(parsed.values["dry-run"]);
90282
+ const document4 = await openOrFail(filePath);
90283
+ if (typeof document4 === "number")
90284
+ return document4;
90285
+ const result = await new Fonts(document4).setDefault(fontName, {
90286
+ sizeHalfPoints,
90287
+ all: all2
90288
+ });
90289
+ if (dryRun) {
90290
+ await respond({
90291
+ operation: "styles.set-default-font",
90292
+ dryRun: true,
90293
+ path: filePath,
90294
+ font: fontName,
90295
+ ...sizeHalfPoints !== undefined ? { sizePt: sizeHalfPoints / 2 } : {},
90296
+ all: all2,
90297
+ themeUpdated: result.themeUpdated,
90298
+ ...all2 ? { repointed: result.repointed } : { explicitStyles: result.explicitStyles },
90299
+ ...outputPath ? { output: outputPath } : {}
90300
+ });
90301
+ return EXIT2.OK;
90302
+ }
90303
+ await document4.save(outputPath);
90304
+ const count = result.explicitStyles.length;
90305
+ const leftover = !all2 && count > 0 ? `${count === 1 ? "1 style keeps" : `${count} styles keep`} their own font (${formatStyleList(result.explicitStyles)}); pass --all to override them too.` : undefined;
90306
+ await respondAck({
90307
+ ok: true,
90308
+ operation: "styles.set-default-font",
90309
+ path: outputPath ?? filePath,
90310
+ font: fontName,
90311
+ ...sizeHalfPoints !== undefined ? { sizePt: sizeHalfPoints / 2 } : {},
90312
+ themeUpdated: result.themeUpdated,
90313
+ all: all2,
90314
+ ...all2 ? { repointed: result.repointed } : { explicitStyles: result.explicitStyles }
90315
+ }, leftover);
90316
+ return EXIT2.OK;
90317
+ }
90318
+ function formatStyleList(ids) {
90319
+ const shown = ids.slice(0, 5).join(", ");
90320
+ return ids.length > 5 ? `${shown}, \u2026` : shown;
90321
+ }
89606
90322
  function styleMeta(node2) {
89607
90323
  const meta = {
89608
90324
  id: node2.getAttribute("w:styleId") ?? "",
@@ -89702,10 +90418,12 @@ function formatDetail(detail) {
89702
90418
  `)}
89703
90419
  `;
89704
90420
  }
89705
- var HELP33 = `docx styles \u2014 list the styles available to apply, or describe one
90421
+ var HELP33 = `docx styles \u2014 list the styles available to apply, describe one, or set the document font
89706
90422
 
89707
90423
  Usage:
89708
90424
  docx styles FILE [--used] [--at STYLEID] [--json]
90425
+ docx styles --catalog [--json]
90426
+ docx styles set-default-font FILE "Font Name" [--size N] [--all] # set the document-wide font
89709
90427
 
89710
90428
  The style catalog lives in word/styles.xml, not in the document body \u2014 so
89711
90429
  unlike everything else, you can't see it by reading the doc. Use this to learn
@@ -89716,6 +90434,10 @@ Options:
89716
90434
  --at STYLEID Describe one style (id, type, name, basedOn, key formatting)
89717
90435
  --used List only the styles actually applied somewhere in the body
89718
90436
  (paragraph styles + character/run styles)
90437
+ --catalog List the built-in styles docx-cli can apply on demand \u2014 every
90438
+ \`--style NAME\` value that \`insert\`/\`edit\` will auto-provision
90439
+ (Title, Subtitle, Heading1\u20139, Quote, IntenseQuote, Code, \u2026)
90440
+ even when the doc doesn't contain them yet. No FILE needed.
89719
90441
  --json Structured output (a JSON array for the list; an object for --at)
89720
90442
  -h, --help Show this help
89721
90443
 
@@ -89725,10 +90447,41 @@ Output:
89725
90447
  {code, error, hint?} with a nonzero exit.
89726
90448
 
89727
90449
  Examples:
89728
- docx styles report.docx # full catalog
90450
+ docx styles report.docx # styles defined in this doc
89729
90451
  docx styles report.docx --used # only styles the doc uses
90452
+ docx styles --catalog # built-ins you can apply via --style
89730
90453
  docx styles report.docx --at Caption # what does Caption look like?
89731
90454
  docx styles report.docx --json | jq '.[].id'
90455
+ docx styles set-default-font report.docx "Times New Roman" # whole-doc font
90456
+ docx styles set-default-font report.docx "Georgia" --all # incl. explicit fonts
90457
+
90458
+ See \`docx styles set-default-font --help\` for the font-setting details.
90459
+ `, FONT_HELP = `docx styles set-default-font \u2014 set the document-wide default font
90460
+
90461
+ Usage:
90462
+ docx styles set-default-font FILE "Font Name" [--size N] [--all] [options]
90463
+
90464
+ A document font lives in TWO places at once \u2014 word/styles.xml (<w:docDefaults>)
90465
+ and the theme font scheme (word/theme/theme1.xml, major + minor) \u2014 and setting
90466
+ only one silently loses to the other. This sets both, so body text AND
90467
+ theme-following headings adopt the font. Styles/runs that pin their OWN font
90468
+ (e.g. a code block's monospace, a deliberately-Arial run) are preserved; pass
90469
+ --all to repoint those too.
90470
+
90471
+ Options:
90472
+ --size N Also set the default font size, in points (e.g. 12).
90473
+ --all Repoint EVERY explicit font \u2014 styles, body runs, and notes \u2014
90474
+ onto FONT too, for a guaranteed-uniform document (overrides
90475
+ even code monospace and per-run font choices).
90476
+ -o, --output PATH Write to PATH instead of overwriting FILE
90477
+ --dry-run Print what would change; do not write the file
90478
+ -v, --verbose Print the full success ack JSON
90479
+ -h, --help Show this help
90480
+
90481
+ Examples:
90482
+ docx styles set-default-font report.docx "Times New Roman"
90483
+ docx styles set-default-font report.docx "Calibri" --size 11
90484
+ docx styles set-default-font report.docx "Georgia" --all
89732
90485
  `;
89733
90486
  var init_styles2 = __esm(() => {
89734
90487
  init_core2();
@@ -92085,7 +92838,7 @@ Examples:
92085
92838
  // package.json
92086
92839
  var package_default = {
92087
92840
  name: "bun-docx",
92088
- version: "0.14.0",
92841
+ version: "0.15.0",
92089
92842
  description: "CLI for AI agents (Claude, Codex) to read, edit, and comment on .docx files with full format fidelity.",
92090
92843
  keywords: [
92091
92844
  "docx",