bun-docx 0.19.1 → 0.20.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.
- package/README.md +22 -1
- package/dist/index.js +90 -100
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,27 @@
|
|
|
8
8
|
- Agents address text by **stable locators** with character offsets (`p3:5-20`); humans see normal Word formatting on disk.
|
|
9
9
|
- Custom styles, theme colors, embedded objects — all of it survives. The CLI mutates XML in place rather than re-emitting from a lossy model.
|
|
10
10
|
|
|
11
|
+
## Why docx-cli?
|
|
12
|
+
|
|
13
|
+
The default way agents edit Word docs is to unzip the `.docx` and hand-write the OOXML inside. That takes a strong model to get right, burns tokens, and routinely produces a file Word won't open. `docx-cli` hands the agent plain commands plus an annotated-Markdown read view, so it never has to reason about the XML.
|
|
14
|
+
|
|
15
|
+
We measured it — a controlled A/B bake-off: **six real document tasks** (fill an NDA, fill an invoice, restyle a résumé, redline a contract, finalize a contract, author a journal), the same starting files, and one independent judge grading every result from the **Word-rendered pages**. Three runs per arm at each of two model tiers:
|
|
16
|
+
|
|
17
|
+
| | Haiku (weak, cheap) | | Sonnet (strong) | |
|
|
18
|
+
| :------------------------ | ------------------: | --------------------: | --------------: | --------------------: |
|
|
19
|
+
| | **docx-cli** | default skill | **docx-cli** | default skill |
|
|
20
|
+
| Tasks solved (of 6) | **4.3** (4–5) | 0.7 (0–1) | **6.0** (6–6) | 4.0 (4–4) |
|
|
21
|
+
| Rendered correctly (of 6) | 5.7 | 3.7 | 6.0 | 4.7 |
|
|
22
|
+
| Outright-broken documents | **0** | ~1/run (up to 2) | **0** | 0 |
|
|
23
|
+
| Input tokens | **2.4M** | 6.1M (2.6×) | **1.6M** | 3.6M (2.2×) |
|
|
24
|
+
| Wall-clock | **924 s** | 1,882 s (2.0× slower) | **1,175 s** | 2,029 s (1.7× slower) |
|
|
25
|
+
|
|
26
|
+
- **The correctness gap is widest on the cheap Haiku tier (~6×)**, and a frontier model never closes it — the default skill caps at 4/6, losing the contract redline and the résumé every Sonnet run.
|
|
27
|
+
- **The cost and speed penalties are model-independent** — ~2.2–2.6× more tokens and ~1.7–2× slower at *both* tiers, with token/time ranges that never overlap.
|
|
28
|
+
- **Word couldn't reliably open the default skill's work** — it failed to open 5 of 36 of its outputs; all 36 of docx-cli's opened on the first try.
|
|
29
|
+
|
|
30
|
+
Full methodology, per-task rubric, and side-by-side renders: **[the bake-off writeup](https://kklimuk.github.io/docx-cli/)**.
|
|
31
|
+
|
|
11
32
|
## Install
|
|
12
33
|
|
|
13
34
|
**npm** — the simplest path (requires Bun >= 1.3):
|
|
@@ -366,7 +387,7 @@ The CLI is built for non-interactive agents. **Exit code is the success signal**
|
|
|
366
387
|
| Command class | Default stdout on success | `--verbose` |
|
|
367
388
|
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
368
389
|
| **Mutator that mints a new handle** — `comments add`→`cN`, `comments reply`→`cN`, `footnotes/endnotes add`→`fnN`/`enN`, `hyperlinks add`→`linkN`, `insert`→the new `pN` | the bare locator(s), **one per line** (a multi-block `--markdown` insert prints several) | full `{ok:true,…}` ack |
|
|
369
|
-
| **Mutator with no new handle** — `edit`, `delete`, `replace`, `create`, `comments resolve/delete`, `images replace/delete`, `hyperlinks replace/delete`, `footnotes/endnotes edit/delete`, `headers/footers set/clear`, `tables *`, `track-changes accept/reject` & toggle | **one-line confirmation** — `<operation> <target>` (e.g. `edit t1:r0c1:p0`, `edit 7 changes`, `replace
|
|
390
|
+
| **Mutator with no new handle** — `edit`, `delete`, `replace`, `create`, `comments resolve/delete`, `images replace/delete`, `hyperlinks replace/delete`, `footnotes/endnotes edit/delete`, `headers/footers set/clear`, `tables *`, `track-changes accept/reject` & toggle | **one-line confirmation** — `<operation> <target>` (e.g. `edit t1:r0c1:p0`, `edit 7 changes`, `replace 3 occurrences replaced`) (exit `0`) | full `{ok:true,…}` ack |
|
|
370
391
|
| `find` | matched span locators, one per line (no matches → nothing, exit `0`) | `--json` → `{ totalMatches, query, view, matches:[…], normalizedQuery? }` |
|
|
371
392
|
| `wc` | the bare count (whole-doc adds a tab-separated `sections` column, like `wc`) | `--json` → `{ words, scope, view, sections? }` |
|
|
372
393
|
| `outline` | indented `LOCATOR⇥TEXT` tree (two spaces per level) | `--json` → nested `[{ id, locator, level, style, text, children }]` |
|
package/dist/index.js
CHANGED
|
@@ -4435,6 +4435,20 @@ class XmlNode2 {
|
|
|
4435
4435
|
}
|
|
4436
4436
|
return out;
|
|
4437
4437
|
}
|
|
4438
|
+
collectTextWithBreaks() {
|
|
4439
|
+
if (this.isText)
|
|
4440
|
+
return this.text ?? "";
|
|
4441
|
+
if (this.tag === "w:br" || this.tag === "w:cr")
|
|
4442
|
+
return `
|
|
4443
|
+
`;
|
|
4444
|
+
if (this.tag === "w:tab")
|
|
4445
|
+
return "\t";
|
|
4446
|
+
let out = "";
|
|
4447
|
+
for (const child of this.children) {
|
|
4448
|
+
out += child.collectTextWithBreaks();
|
|
4449
|
+
}
|
|
4450
|
+
return out;
|
|
4451
|
+
}
|
|
4438
4452
|
clone() {
|
|
4439
4453
|
const cloned = new XmlNode2(this.tag, { ...this.attributes });
|
|
4440
4454
|
if (this.text !== undefined)
|
|
@@ -5290,6 +5304,11 @@ function RunElement({ run }) {
|
|
|
5290
5304
|
}
|
|
5291
5305
|
return null;
|
|
5292
5306
|
}
|
|
5307
|
+
function textToRunElements(text) {
|
|
5308
|
+
return textToRuns(text, {}).map((run) => /* @__PURE__ */ jsxDEV(RunElement, {
|
|
5309
|
+
run
|
|
5310
|
+
}, undefined, false, undefined, this)).filter((node) => node !== null);
|
|
5311
|
+
}
|
|
5293
5312
|
function applyParagraphOptionsInPlace(rebuilt, options) {
|
|
5294
5313
|
if (!options.style && !options.alignment && !options.tabs && !options.spacing && !options.indent)
|
|
5295
5314
|
return;
|
|
@@ -17744,14 +17763,7 @@ function noteFirstParagraphRuns({
|
|
|
17744
17763
|
return runs;
|
|
17745
17764
|
if (text2 === undefined)
|
|
17746
17765
|
return [];
|
|
17747
|
-
return
|
|
17748
|
-
/* @__PURE__ */ jsxDEV(w.r, {
|
|
17749
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
17750
|
-
"xml:space": "preserve",
|
|
17751
|
-
children: ` ${text2}`
|
|
17752
|
-
}, undefined, false, undefined, this)
|
|
17753
|
-
}, undefined, false, undefined, this)
|
|
17754
|
-
];
|
|
17766
|
+
return textToRunElements(` ${text2}`);
|
|
17755
17767
|
}
|
|
17756
17768
|
function TrackedNoteBody({
|
|
17757
17769
|
config,
|
|
@@ -17761,6 +17773,19 @@ function TrackedNoteBody({
|
|
|
17761
17773
|
}) {
|
|
17762
17774
|
const Note = config.kind === "footnote" ? w.footnote : w.endnote;
|
|
17763
17775
|
const BodyRef = config.kind === "footnote" ? w.footnoteRef : w.endnoteRef;
|
|
17776
|
+
const insRuns = [
|
|
17777
|
+
/* @__PURE__ */ jsxDEV(w.r, {
|
|
17778
|
+
children: [
|
|
17779
|
+
/* @__PURE__ */ jsxDEV(w.rPr, {
|
|
17780
|
+
children: /* @__PURE__ */ jsxDEV(w.rStyle, {
|
|
17781
|
+
"w-val": config.referenceStyle
|
|
17782
|
+
}, undefined, false, undefined, this)
|
|
17783
|
+
}, undefined, false, undefined, this),
|
|
17784
|
+
/* @__PURE__ */ jsxDEV(BodyRef, {}, undefined, false, undefined, this)
|
|
17785
|
+
]
|
|
17786
|
+
}, undefined, true, undefined, this),
|
|
17787
|
+
...textToRunElements(` ${text2}`)
|
|
17788
|
+
];
|
|
17764
17789
|
return /* @__PURE__ */ jsxDEV(Note, {
|
|
17765
17790
|
"w-id": id,
|
|
17766
17791
|
children: /* @__PURE__ */ jsxDEV(w.p, {
|
|
@@ -17772,25 +17797,8 @@ function TrackedNoteBody({
|
|
|
17772
17797
|
}, undefined, false, undefined, this),
|
|
17773
17798
|
/* @__PURE__ */ jsxDEV(Ins, {
|
|
17774
17799
|
meta,
|
|
17775
|
-
children:
|
|
17776
|
-
|
|
17777
|
-
children: [
|
|
17778
|
-
/* @__PURE__ */ jsxDEV(w.rPr, {
|
|
17779
|
-
children: /* @__PURE__ */ jsxDEV(w.rStyle, {
|
|
17780
|
-
"w-val": config.referenceStyle
|
|
17781
|
-
}, undefined, false, undefined, this)
|
|
17782
|
-
}, undefined, false, undefined, this),
|
|
17783
|
-
/* @__PURE__ */ jsxDEV(BodyRef, {}, undefined, false, undefined, this)
|
|
17784
|
-
]
|
|
17785
|
-
}, undefined, true, undefined, this),
|
|
17786
|
-
/* @__PURE__ */ jsxDEV(w.r, {
|
|
17787
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
17788
|
-
"xml:space": "preserve",
|
|
17789
|
-
children: ` ${text2}`
|
|
17790
|
-
}, undefined, false, undefined, this)
|
|
17791
|
-
}, undefined, false, undefined, this)
|
|
17792
|
-
]
|
|
17793
|
-
}, undefined, true, undefined, this)
|
|
17800
|
+
children: insRuns
|
|
17801
|
+
}, undefined, false, undefined, this)
|
|
17794
17802
|
]
|
|
17795
17803
|
}, undefined, true, undefined, this)
|
|
17796
17804
|
}, undefined, false, undefined, this);
|
|
@@ -17831,15 +17839,9 @@ function wrapNoteBodyAsEdited(noteBody, newText, insMeta, delMeta) {
|
|
|
17831
17839
|
continue;
|
|
17832
17840
|
}
|
|
17833
17841
|
if (!inserted) {
|
|
17834
|
-
const newRun = /* @__PURE__ */ jsxDEV(w.r, {
|
|
17835
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
17836
|
-
"xml:space": "preserve",
|
|
17837
|
-
children: newText
|
|
17838
|
-
}, undefined, false, undefined, this)
|
|
17839
|
-
}, undefined, false, undefined, this);
|
|
17840
17842
|
result.push(/* @__PURE__ */ jsxDEV(Ins, {
|
|
17841
17843
|
meta: insMeta,
|
|
17842
|
-
children:
|
|
17844
|
+
children: textToRunElements(newText)
|
|
17843
17845
|
}, undefined, false, undefined, this));
|
|
17844
17846
|
inserted = true;
|
|
17845
17847
|
}
|
|
@@ -17849,15 +17851,9 @@ function wrapNoteBodyAsEdited(noteBody, newText, insMeta, delMeta) {
|
|
|
17849
17851
|
}, undefined, false, undefined, this));
|
|
17850
17852
|
}
|
|
17851
17853
|
if (!inserted) {
|
|
17852
|
-
const newRun = /* @__PURE__ */ jsxDEV(w.r, {
|
|
17853
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
17854
|
-
"xml:space": "preserve",
|
|
17855
|
-
children: newText
|
|
17856
|
-
}, undefined, false, undefined, this)
|
|
17857
|
-
}, undefined, false, undefined, this);
|
|
17858
17854
|
result.push(/* @__PURE__ */ jsxDEV(Ins, {
|
|
17859
17855
|
meta: insMeta,
|
|
17860
|
-
children:
|
|
17856
|
+
children: textToRunElements(newText)
|
|
17861
17857
|
}, undefined, false, undefined, this));
|
|
17862
17858
|
}
|
|
17863
17859
|
paragraph.children = result;
|
|
@@ -17885,6 +17881,7 @@ function isBareWhitespaceRun(run) {
|
|
|
17885
17881
|
return saw;
|
|
17886
17882
|
}
|
|
17887
17883
|
var init_emit3 = __esm(() => {
|
|
17884
|
+
init_blocks();
|
|
17888
17885
|
init_jsx();
|
|
17889
17886
|
init_track_changes();
|
|
17890
17887
|
init_emit2();
|
|
@@ -21138,6 +21135,9 @@ function nodeText(node) {
|
|
|
21138
21135
|
out += child2.collectText();
|
|
21139
21136
|
else if (child2.tag === "w:tab")
|
|
21140
21137
|
out += "\t";
|
|
21138
|
+
else if (child2.tag === "w:br" || child2.tag === "w:cr")
|
|
21139
|
+
out += `
|
|
21140
|
+
`;
|
|
21141
21141
|
}
|
|
21142
21142
|
return out;
|
|
21143
21143
|
}
|
|
@@ -23700,7 +23700,7 @@ class CommentsView {
|
|
|
23700
23700
|
const author = child2.getAttribute("w:author") ?? "";
|
|
23701
23701
|
const date = child2.getAttribute("w:date") ?? "";
|
|
23702
23702
|
const initials = child2.getAttribute("w:initials");
|
|
23703
|
-
const text2 = child2.
|
|
23703
|
+
const text2 = child2.collectTextWithBreaks();
|
|
23704
23704
|
const anchor = anchors.get(commentId) ?? {
|
|
23705
23705
|
startBlockId: "",
|
|
23706
23706
|
startOffset: 0,
|
|
@@ -24218,7 +24218,7 @@ class NotesView {
|
|
|
24218
24218
|
const numericId = child2.getAttribute("w:id");
|
|
24219
24219
|
if (numericId == null)
|
|
24220
24220
|
continue;
|
|
24221
|
-
const text2 = child2.
|
|
24221
|
+
const text2 = child2.collectTextWithBreaks().trim();
|
|
24222
24222
|
out.push({ id: `${config.idPrefix}${numericId}`, text: text2 });
|
|
24223
24223
|
}
|
|
24224
24224
|
return out;
|
|
@@ -35447,12 +35447,7 @@ function CommentBody({
|
|
|
35447
35447
|
children: /* @__PURE__ */ jsxDEV(w.p, {
|
|
35448
35448
|
"w14:paraId": options.paraId,
|
|
35449
35449
|
"w14:textId": "00000000",
|
|
35450
|
-
children:
|
|
35451
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
35452
|
-
"xml:space": "preserve",
|
|
35453
|
-
children: options.text
|
|
35454
|
-
}, undefined, false, undefined, this)
|
|
35455
|
-
}, undefined, false, undefined, this)
|
|
35450
|
+
children: textToRunElements(options.text)
|
|
35456
35451
|
}, undefined, false, undefined, this)
|
|
35457
35452
|
}, undefined, false, undefined, this);
|
|
35458
35453
|
}
|
|
@@ -35519,6 +35514,7 @@ function containsCommentReference(run, numericId) {
|
|
|
35519
35514
|
var SpanOutOfRangeError;
|
|
35520
35515
|
var init_markers = __esm(() => {
|
|
35521
35516
|
init_comments();
|
|
35517
|
+
init_blocks();
|
|
35522
35518
|
init_jsx();
|
|
35523
35519
|
init_parser();
|
|
35524
35520
|
init_jsx_dev_runtime();
|
|
@@ -74332,7 +74328,7 @@ function buildContentParagraph(spec, contentWidth) {
|
|
|
74332
74328
|
}, undefined, false, undefined, this)
|
|
74333
74329
|
}, undefined, false, undefined, this)
|
|
74334
74330
|
}, undefined, false, undefined, this),
|
|
74335
|
-
|
|
74331
|
+
textToRunElements(spec.text),
|
|
74336
74332
|
/* @__PURE__ */ jsxDEV(w.r, {
|
|
74337
74333
|
children: /* @__PURE__ */ jsxDEV(w.tab, {}, undefined, false, undefined, this)
|
|
74338
74334
|
}, undefined, false, undefined, this),
|
|
@@ -74357,7 +74353,7 @@ function buildContentParagraph(spec, contentWidth) {
|
|
|
74357
74353
|
alignment ? /* @__PURE__ */ jsxDEV(w.pPr, {
|
|
74358
74354
|
children: alignment
|
|
74359
74355
|
}, undefined, false, undefined, this) : null,
|
|
74360
|
-
hasText ?
|
|
74356
|
+
hasText ? textToRunElements(spec.text) : null
|
|
74361
74357
|
]
|
|
74362
74358
|
}, undefined, true, undefined, this);
|
|
74363
74359
|
}
|
|
@@ -74368,22 +74364,14 @@ function alignChild(align) {
|
|
|
74368
74364
|
"w-val": align
|
|
74369
74365
|
}, undefined, false, undefined, this);
|
|
74370
74366
|
}
|
|
74371
|
-
function textRun(text6) {
|
|
74372
|
-
return /* @__PURE__ */ jsxDEV(w.r, {
|
|
74373
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
74374
|
-
"xml:space": "preserve",
|
|
74375
|
-
children: text6
|
|
74376
|
-
}, undefined, false, undefined, this)
|
|
74377
|
-
}, undefined, false, undefined, this);
|
|
74378
|
-
}
|
|
74379
74367
|
function fieldRuns(field) {
|
|
74380
74368
|
switch (field.type) {
|
|
74381
74369
|
case "page":
|
|
74382
74370
|
if (field.ofPages) {
|
|
74383
74371
|
return [
|
|
74384
|
-
|
|
74372
|
+
...textToRunElements("Page "),
|
|
74385
74373
|
fieldSimple(" PAGE ", "1"),
|
|
74386
|
-
|
|
74374
|
+
...textToRunElements(" of "),
|
|
74387
74375
|
fieldSimple(" NUMPAGES ", "1")
|
|
74388
74376
|
];
|
|
74389
74377
|
}
|
|
@@ -74445,6 +74433,7 @@ function relationshipTarget(partName) {
|
|
|
74445
74433
|
}
|
|
74446
74434
|
var NS_W3 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", NS_R2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
74447
74435
|
var init_marginals2 = __esm(() => {
|
|
74436
|
+
init_blocks();
|
|
74448
74437
|
init_jsx();
|
|
74449
74438
|
init_sections();
|
|
74450
74439
|
init_track_changes();
|
|
@@ -82797,6 +82786,12 @@ async function rejectShellMangledValue(text6, help, label = "this value") {
|
|
|
82797
82786
|
return await fail("USAGE", `${label} contains "${fragment}" \u2014 a number with no integer part, the signature of a "$" amount gutted by the shell (bash turns double-quoted "$300.00" into ".00" and "$10,000" into ",000"). docx would write the corrupted value verbatim.`, `Wrap any "$"-bearing value in SINGLE quotes ('$300.00') so bash leaves it alone, or supply it via --batch FILE (JSONL never touches the shell). If you really mean "${fragment}", write its integer part (0${fragment}) or use --batch. Help:
|
|
82798
82787
|
${help}`);
|
|
82799
82788
|
}
|
|
82789
|
+
function decodeInlineEscapes(value) {
|
|
82790
|
+
if (value === undefined)
|
|
82791
|
+
return;
|
|
82792
|
+
return value.replace(/\\r\\n|\\[nrt]/g, (match) => match === "\\t" ? "\t" : `
|
|
82793
|
+
`);
|
|
82794
|
+
}
|
|
82800
82795
|
function parseTaskFlag(value) {
|
|
82801
82796
|
const normalized = value.toLowerCase();
|
|
82802
82797
|
if (normalized === "checked" || normalized === "true" || normalized === "1")
|
|
@@ -83206,7 +83201,7 @@ async function run(args) {
|
|
|
83206
83201
|
const atInput = parsed.values.at;
|
|
83207
83202
|
const anchorInput = parsed.values.anchor;
|
|
83208
83203
|
const batchInput = parsed.values.batch;
|
|
83209
|
-
const text6 = parsed.values.text;
|
|
83204
|
+
const text6 = decodeInlineEscapes(parsed.values.text);
|
|
83210
83205
|
const occurrenceRaw = parsed.values.occurrence;
|
|
83211
83206
|
const defaultAuthor = resolveAuthor(parsed.values.author);
|
|
83212
83207
|
const outputPath = parsed.values.output;
|
|
@@ -83690,7 +83685,7 @@ async function run4(args) {
|
|
|
83690
83685
|
if (!path2)
|
|
83691
83686
|
return fail("USAGE", "Missing FILE argument", HELP4);
|
|
83692
83687
|
const parentInput = parsed.values.at;
|
|
83693
|
-
const text6 = parsed.values.text;
|
|
83688
|
+
const text6 = decodeInlineEscapes(parsed.values.text);
|
|
83694
83689
|
if (!parentInput)
|
|
83695
83690
|
return fail("USAGE", "Missing --at cN", HELP4);
|
|
83696
83691
|
if (!text6)
|
|
@@ -83764,6 +83759,7 @@ Examples:
|
|
|
83764
83759
|
`;
|
|
83765
83760
|
var init_reply = __esm(() => {
|
|
83766
83761
|
init_core2();
|
|
83762
|
+
init_parse_helpers();
|
|
83767
83763
|
init_respond();
|
|
83768
83764
|
});
|
|
83769
83765
|
|
|
@@ -84077,12 +84073,7 @@ function DocumentBody({ text: text6 }) {
|
|
|
84077
84073
|
children: /* @__PURE__ */ jsxDEV(w.body, {
|
|
84078
84074
|
children: [
|
|
84079
84075
|
text6 !== undefined ? /* @__PURE__ */ jsxDEV(w.p, {
|
|
84080
|
-
children:
|
|
84081
|
-
children: /* @__PURE__ */ jsxDEV(w.t, {
|
|
84082
|
-
"xml:space": "preserve",
|
|
84083
|
-
children: text6
|
|
84084
|
-
}, undefined, false, undefined, this)
|
|
84085
|
-
}, undefined, false, undefined, this)
|
|
84076
|
+
children: textToRunElements(text6)
|
|
84086
84077
|
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(w.p, {}, undefined, false, undefined, this),
|
|
84087
84078
|
/* @__PURE__ */ jsxDEV(DefaultSectionProperties, {}, undefined, false, undefined, this)
|
|
84088
84079
|
]
|
|
@@ -84180,6 +84171,7 @@ function serializeWithDeclaration(root2) {
|
|
|
84180
84171
|
}
|
|
84181
84172
|
var DOC_NAMESPACES, CONTENT_TYPES, ROOT_RELS, DOCUMENT_RELS;
|
|
84182
84173
|
var init_template = __esm(() => {
|
|
84174
|
+
init_blocks();
|
|
84183
84175
|
init_jsx();
|
|
84184
84176
|
init_parser();
|
|
84185
84177
|
init_canonical_parts();
|
|
@@ -84260,7 +84252,7 @@ async function run7(args) {
|
|
|
84260
84252
|
if (!path2) {
|
|
84261
84253
|
return fail("USAGE", "Missing FILE argument", HELP7);
|
|
84262
84254
|
}
|
|
84263
|
-
const text6 = parsed.values.text;
|
|
84255
|
+
const text6 = decodeInlineEscapes(parsed.values.text);
|
|
84264
84256
|
const fromPath = parsed.values.from;
|
|
84265
84257
|
const textFilePath = parsed.values["text-file"];
|
|
84266
84258
|
const contentSources = [text6, fromPath, textFilePath].filter((value) => value !== undefined);
|
|
@@ -85841,7 +85833,7 @@ async function validateParagraphEdit(values2, paragraphOptions) {
|
|
|
85841
85833
|
return { kind: "clear", tags };
|
|
85842
85834
|
}
|
|
85843
85835
|
}
|
|
85844
|
-
const text6 = values2.text;
|
|
85836
|
+
const text6 = decodeInlineEscapes(values2.text);
|
|
85845
85837
|
const runsJson = values2.runs;
|
|
85846
85838
|
const codeInline = values2.code;
|
|
85847
85839
|
const codeFile = values2["code-file"];
|
|
@@ -85895,7 +85887,7 @@ async function validateParagraphEdit(values2, paragraphOptions) {
|
|
|
85895
85887
|
}
|
|
85896
85888
|
return { kind: "task", checked };
|
|
85897
85889
|
}
|
|
85898
|
-
const markdownInline = values2.markdown;
|
|
85890
|
+
const markdownInline = decodeInlineEscapes(values2.markdown);
|
|
85899
85891
|
const markdownFile = values2["markdown-file"];
|
|
85900
85892
|
const contentFlags = [
|
|
85901
85893
|
text6 !== undefined,
|
|
@@ -86442,9 +86434,9 @@ async function runAddNote(args, kind) {
|
|
|
86442
86434
|
if (anchorCount > 1) {
|
|
86443
86435
|
return fail("USAGE", "--at and --anchor are mutually exclusive", help);
|
|
86444
86436
|
}
|
|
86445
|
-
const text6 = parsed.values.text;
|
|
86437
|
+
const text6 = decodeInlineEscapes(parsed.values.text);
|
|
86446
86438
|
const runsJson = parsed.values.runs;
|
|
86447
|
-
const markdown2 = parsed.values.markdown;
|
|
86439
|
+
const markdown2 = decodeInlineEscapes(parsed.values.markdown);
|
|
86448
86440
|
const markdownFile = parsed.values["markdown-file"];
|
|
86449
86441
|
const bodyCount = (text6 !== undefined ? 1 : 0) + (runsJson !== undefined ? 1 : 0) + (markdown2 !== undefined ? 1 : 0) + (markdownFile !== undefined ? 1 : 0);
|
|
86450
86442
|
if (bodyCount === 0) {
|
|
@@ -86868,9 +86860,9 @@ async function runEditNote(args, kind) {
|
|
|
86868
86860
|
const config = noteConfig(kind);
|
|
86869
86861
|
if (!idInput)
|
|
86870
86862
|
return fail("USAGE", `Missing --at ${config.idPrefix}N`, help);
|
|
86871
|
-
const text6 = parsed.values.text;
|
|
86863
|
+
const text6 = decodeInlineEscapes(parsed.values.text);
|
|
86872
86864
|
const runsJson = parsed.values.runs;
|
|
86873
|
-
const markdown2 = parsed.values.markdown;
|
|
86865
|
+
const markdown2 = decodeInlineEscapes(parsed.values.markdown);
|
|
86874
86866
|
const markdownFile = parsed.values["markdown-file"];
|
|
86875
86867
|
const bodyCount = (text6 !== undefined ? 1 : 0) + (runsJson !== undefined ? 1 : 0) + (markdown2 !== undefined ? 1 : 0) + (markdownFile !== undefined ? 1 : 0);
|
|
86876
86868
|
if (bodyCount === 0) {
|
|
@@ -87645,7 +87637,7 @@ async function runSetMarginal(args, kind) {
|
|
|
87645
87637
|
if (align !== undefined && !isAlign(align)) {
|
|
87646
87638
|
return fail("USAGE", `Invalid --align: ${align}`, "Valid: left, center, right.");
|
|
87647
87639
|
}
|
|
87648
|
-
const text6 = parsed.values.text;
|
|
87640
|
+
const text6 = decodeInlineEscapes(parsed.values.text);
|
|
87649
87641
|
if (text6 === undefined && field === undefined) {
|
|
87650
87642
|
return fail("USAGE", `Nothing to set \u2014 pass --text and/or a field (--page-number/--date/--style-ref/--field).`, helpFor7(kind));
|
|
87651
87643
|
}
|
|
@@ -87740,6 +87732,7 @@ var OPTION_SPEC4, SAVE_FLAGS_HELP = `Output:
|
|
|
87740
87732
|
-h, --help Show this help`;
|
|
87741
87733
|
var init_set = __esm(() => {
|
|
87742
87734
|
init_core2();
|
|
87735
|
+
init_parse_helpers();
|
|
87743
87736
|
init_respond();
|
|
87744
87737
|
init_shared();
|
|
87745
87738
|
OPTION_SPEC4 = {
|
|
@@ -88879,10 +88872,10 @@ async function buildSingleShotOptions(filePath, values2) {
|
|
|
88879
88872
|
if (typeof spec === "number")
|
|
88880
88873
|
return spec;
|
|
88881
88874
|
if (spec.kind === "text") {
|
|
88882
|
-
const rejected = await rejectMarkdownInText(
|
|
88875
|
+
const rejected = await rejectMarkdownInText(spec.text, HELP20);
|
|
88883
88876
|
if (typeof rejected === "number")
|
|
88884
88877
|
return rejected;
|
|
88885
|
-
const mangled = await rejectShellMangledValue(
|
|
88878
|
+
const mangled = await rejectShellMangledValue(spec.text, HELP20, "--text");
|
|
88886
88879
|
if (typeof mangled === "number")
|
|
88887
88880
|
return mangled;
|
|
88888
88881
|
}
|
|
@@ -89022,7 +89015,7 @@ async function chooseContentSpec(values2) {
|
|
|
89022
89015
|
}
|
|
89023
89016
|
async function resolveMarkdownSpec(values2, flag) {
|
|
89024
89017
|
if (flag === "markdown") {
|
|
89025
|
-
const source2 = values2.markdown;
|
|
89018
|
+
const source2 = decodeInlineEscapes(values2.markdown);
|
|
89026
89019
|
return { kind: "markdown", source: source2 };
|
|
89027
89020
|
}
|
|
89028
89021
|
const path2 = values2["markdown-file"];
|
|
@@ -89067,7 +89060,7 @@ async function parseImageFlags(values2) {
|
|
|
89067
89060
|
const alt = values2.alt;
|
|
89068
89061
|
if (alt !== undefined)
|
|
89069
89062
|
out.alt = alt;
|
|
89070
|
-
const caption = values2.caption;
|
|
89063
|
+
const caption = decodeInlineEscapes(values2.caption);
|
|
89071
89064
|
if (caption !== undefined)
|
|
89072
89065
|
out.caption = caption;
|
|
89073
89066
|
const widthRaw = values2.width;
|
|
@@ -89092,7 +89085,7 @@ function buildTextSpec(values2) {
|
|
|
89092
89085
|
const url = values2.url;
|
|
89093
89086
|
return {
|
|
89094
89087
|
kind: "text",
|
|
89095
|
-
text: values2.text,
|
|
89088
|
+
text: decodeInlineEscapes(values2.text),
|
|
89096
89089
|
format: {
|
|
89097
89090
|
color: values2.color,
|
|
89098
89091
|
bold: values2.bold,
|
|
@@ -91608,7 +91601,7 @@ function formatNote(type, pairs, bareTokens = []) {
|
|
|
91608
91601
|
return `<!-- ${parts.join(" ")} -->`;
|
|
91609
91602
|
}
|
|
91610
91603
|
function htmlAttr(key, value) {
|
|
91611
|
-
const escaped = value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
91604
|
+
const escaped = value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, "	");
|
|
91612
91605
|
return `${key}="${escaped}"`;
|
|
91613
91606
|
}
|
|
91614
91607
|
function twipsToInches(twips) {
|
|
@@ -93257,6 +93250,13 @@ async function runReplaceBatch(filePath, batchSource, values2) {
|
|
|
93257
93250
|
return EXIT2.OK;
|
|
93258
93251
|
}
|
|
93259
93252
|
await document4.save(outputPath);
|
|
93253
|
+
const noops = results.filter((entry) => entry.replaced === 0);
|
|
93254
|
+
if (noops.length > 0) {
|
|
93255
|
+
const list3 = noops.map((entry) => JSON.stringify(entry.pattern)).join(", ");
|
|
93256
|
+
const applied = results.length - noops.length;
|
|
93257
|
+
const savedNote = applied === 0 ? "No entry matched, so the document is unchanged." : `The other ${applied} ${applied === 1 ? "entry was" : "entries were"} applied and SAVED \u2014 this nonzero exit means "some entries missed," not "nothing changed."`;
|
|
93258
|
+
return await fail("MATCH_NOT_FOUND", `${noops.length} of ${results.length} replace ${results.length === 1 ? "entry" : "entries"} matched nothing (0 occurrences): ${list3}. ${savedNote}`, `Those patterns weren't found as LITERAL document text \u2014 check for read-view markup (\`<mark>\`/\`<u>\`) or a locator in the pattern. \`docx find\` locates the real text; --ignore-case/--regex/--at scope the search.`);
|
|
93259
|
+
}
|
|
93260
93260
|
await respondAck({
|
|
93261
93261
|
ok: true,
|
|
93262
93262
|
operation: "replace",
|
|
@@ -93459,22 +93459,8 @@ async function run40(args) {
|
|
|
93459
93459
|
return EXIT2.OK;
|
|
93460
93460
|
}
|
|
93461
93461
|
if (selected.length === 0) {
|
|
93462
|
-
|
|
93463
|
-
|
|
93464
|
-
operation: "replace",
|
|
93465
|
-
path: path2,
|
|
93466
|
-
pattern,
|
|
93467
|
-
replacement,
|
|
93468
|
-
regex: useRegex,
|
|
93469
|
-
ignoreCase,
|
|
93470
|
-
view: findView,
|
|
93471
|
-
...atScope ? { at: atScope } : {},
|
|
93472
|
-
totalMatches: 0,
|
|
93473
|
-
replaced: 0,
|
|
93474
|
-
matches: [],
|
|
93475
|
-
...normalizationFields
|
|
93476
|
-
});
|
|
93477
|
-
return EXIT2.OK;
|
|
93462
|
+
const scopeNote = atScope ? ` within ${atScope}` : "";
|
|
93463
|
+
return await fail("MATCH_NOT_FOUND", `Pattern not found${scopeNote}: ${JSON.stringify(pattern)} \u2014 0 occurrences, nothing changed.`, `Match LITERAL document text, not read-view markup (\`<mark>\`/\`<u>\`/\`**\`\u2026) or a locator. Run \`docx find ${JSON.stringify(pattern)} ${path2}\` to see if/where it occurs; add --ignore-case, --regex, --at <locator> to scope, or --current/--baseline to search tracked-change text.`);
|
|
93478
93464
|
}
|
|
93479
93465
|
const reversed = [...selected].sort((leftMatch, rightMatch) => {
|
|
93480
93466
|
if (leftMatch.blockId !== rightMatch.blockId) {
|
|
@@ -93580,6 +93566,10 @@ Output:
|
|
|
93580
93566
|
addressable handle (matched-span locators shift as text changes; re-read or
|
|
93581
93567
|
use --dry-run to see them). --verbose / --dry-run print
|
|
93582
93568
|
{ok:true, operation, totalMatches, replaced, matches:[{locator,\u2026}], \u2026}.
|
|
93569
|
+
A PATTERN that matches NOTHING is an error, not a silent success \u2014 0 occurrences
|
|
93570
|
+
exits nonzero (MATCH_NOT_FOUND), so a no-op replace can't read as "done." Unsure it
|
|
93571
|
+
matches? Probe with \`docx find PATTERN FILE\` or --dry-run and READ the reported count
|
|
93572
|
+
\u2014 both exit 0 whether or not it matches; only the real replace exits nonzero on 0.
|
|
93583
93573
|
Errors print {code, error, hint?} with a nonzero exit.
|
|
93584
93574
|
|
|
93585
93575
|
Examples:
|
|
@@ -98024,7 +98014,7 @@ Examples:
|
|
|
98024
98014
|
// package.json
|
|
98025
98015
|
var package_default = {
|
|
98026
98016
|
name: "bun-docx",
|
|
98027
|
-
version: "0.
|
|
98017
|
+
version: "0.20.0",
|
|
98028
98018
|
description: "Read, edit, redline, and comment on Microsoft Word .docx files from the command line \u2014 built for AI agents.",
|
|
98029
98019
|
keywords: [
|
|
98030
98020
|
"docx",
|