bun-docx 0.16.0 → 0.17.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 +16 -6
- package/dist/index.js +551 -106
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -184,17 +184,19 @@ docx edit FILE --at LOCATOR <content> # LOCATOR = pN | pN:S-
|
|
|
184
184
|
docx delete FILE --at LOCATOR # LOCATOR = pN | pN-pM | tN | sN | tN:rRcC:pK (cell paragraph)
|
|
185
185
|
docx sections FILE [--at LOCATOR] [--columns N] [--type T] [--orientation O] [--size SIZE] [--margins M] # LOCATOR = pN-pM | pN (wrap a range in N columns) | sN (edit one section's columns/type/page geometry). Multi-column layout AND page setup live HERE. PAGE GEOMETRY (margins/orientation/size) with NO --at applies to the WHOLE document (every section); --at sN targets one. Columns/type need --at.
|
|
186
186
|
docx styles set-default-font FILE "Font Name" [--size N] [--all] # document-wide font: sets styles.xml docDefaults + theme major/minor; --all also repoints styles/runs that pin their own font
|
|
187
|
-
docx replace FILE PATTERN REPLACEMENT [--regex] [--ignore-case] [--all] [--limit N] [--current | --baseline] [--exact] [--track] [--dry-run]
|
|
187
|
+
docx replace FILE PATTERN REPLACEMENT [--at pN] [--regex] [--ignore-case] [--all] [--limit N] [--current | --baseline] [--exact] [--track] [--dry-run]
|
|
188
188
|
# Keeps the run's formatting (bold/font) and any tabs — the no-rebuild way to fill a
|
|
189
189
|
# formatted/tabbed template line (e.g. "**Org Name**⇥Date"); don't hand-build --runs to refill it.
|
|
190
|
-
#
|
|
190
|
+
# --at pN (or a cell paragraph tT:rRcC:pN) CONFINES the replace to one paragraph — use it when the
|
|
191
|
+
# SAME placeholder repeats across the doc (a résumé's "City, State" in every entry) and you want THE
|
|
192
|
+
# one in a specific paragraph, instead of find → edit --at pN:S-E span surgery. Batch entries take "at" too.
|
|
191
193
|
|
|
192
194
|
# Batch — apply many changes from ONE read (no re-reading between edits). Keys
|
|
193
195
|
# on each JSONL line mirror the command's flags; all locators address the doc as
|
|
194
196
|
# read. insert/edit also accept --batch - to read JSONL from stdin.
|
|
195
197
|
docx edit FILE --batch fills.jsonl # { at, <one of: text|clear|markdown|runs|code|task>, style?, … }
|
|
196
198
|
docx insert FILE --batch additions.jsonl # { after|before, <content>, style?, color?, … }
|
|
197
|
-
docx replace FILE --batch script.jsonl # { pattern, replacement, regex?, all?, limit?, … } applied in order
|
|
199
|
+
docx replace FILE --batch script.jsonl # { pattern, replacement, at?, regex?, all?, limit?, … } applied in order ("at" scopes that entry to one paragraph)
|
|
198
200
|
docx delete FILE --batch drop.jsonl # { at } per line — whole blocks (pN/tN/cell), resolved live-first
|
|
199
201
|
|
|
200
202
|
# All four of insert/edit/delete/replace accept --track to record that one
|
|
@@ -283,14 +285,22 @@ docx tables unmerge FILE --at tN:rRcC
|
|
|
283
285
|
docx tables borders FILE --at tN [--style single|double|none] [--size N] [--color HEX]
|
|
284
286
|
|
|
285
287
|
docx track-changes on|off FILE
|
|
288
|
+
docx track-changes list FILE [--json]
|
|
286
289
|
docx track-changes accept FILE (--at tcN [--at tcM ...] | --at revN | --all)
|
|
287
290
|
docx track-changes reject FILE (--at tcN [--at tcM ...] | --at revN | --all)
|
|
288
|
-
|
|
289
|
-
#
|
|
291
|
+
docx track-changes apply FILE [--accept H ...] [--reject H ...]
|
|
292
|
+
# `list` defaults to a text table, one LOGICAL change per line (revN collapses a del+ins
|
|
293
|
+
# pair onto one line); `--json` for the raw array. A del+ins REPLACE pair shares a
|
|
294
|
+
# "group": "revN"; `--at revN` accepts/rejects both halves in one call.
|
|
295
|
+
# To FINALIZE a review (accept some, reject the rest), use `apply` — it takes both decision
|
|
296
|
+
# lists in ONE call, resolved against the original ids, so nothing renumbers mid-operation
|
|
297
|
+
# and the file is never left half-finalized. Doing it as separate accept then reject calls
|
|
298
|
+
# renumbers the ids between them. After a subset accept/reject/apply, the confirmation
|
|
299
|
+
# re-lists what remains with its renumbered handles.
|
|
290
300
|
```
|
|
291
301
|
|
|
292
302
|
> **One rule to memorize: addressing an existing thing is always `--at`.**
|
|
293
|
-
> `comments reply/resolve/delete`, `footnotes/endnotes edit/delete`, `images extract/replace/delete`, `hyperlinks replace/delete`, `tables *`, `track-changes accept/reject`, `edit`, and `delete` all take `--at LOCATOR`. The exceptions are positional or directional by nature: `insert` uses `--after`/`--before LOCATOR` (or `--at-start`/`--at-end` for the document boundaries, no locator); `read` slices with `--from`/`--to LOCATOR`; `wc` takes a positional `[LOCATOR]`; `find`/`replace` take a positional `QUERY`/`PATTERN
|
|
303
|
+
> `comments reply/resolve/delete`, `footnotes/endnotes edit/delete`, `images extract/replace/delete`, `hyperlinks replace/delete`, `tables *`, `track-changes accept/reject`, `edit`, and `delete` all take `--at LOCATOR`. The exceptions are positional or directional by nature: `insert` uses `--after`/`--before LOCATOR` (or `--at-start`/`--at-end` for the document boundaries, no locator); `read` slices with `--from`/`--to LOCATOR`; `wc` takes a positional `[LOCATOR]`; `find`/`replace` take a positional `QUERY`/`PATTERN` (and `replace` accepts an optional `--at pN` to *confine* the substitution to one paragraph). `images extract --to DIR` is an *output directory*, not a locator.
|
|
294
304
|
|
|
295
305
|
## Output contract
|
|
296
306
|
|
package/dist/index.js
CHANGED
|
@@ -18088,9 +18088,25 @@ function previewTrackedChanges(document2, target, verb) {
|
|
|
18088
18088
|
}
|
|
18089
18089
|
function applyTrackedChanges(document2, target, verb) {
|
|
18090
18090
|
const targets = resolveTargets(document2, target);
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18091
|
+
return applyResolvedTargets(document2, targets.map((found) => ({ found, verb })));
|
|
18092
|
+
}
|
|
18093
|
+
function applyTrackedDecisions(document2, accepts, rejects) {
|
|
18094
|
+
const acceptTargets = resolveTargets(document2, accepts).map((found) => ({
|
|
18095
|
+
found,
|
|
18096
|
+
verb: "accept"
|
|
18097
|
+
}));
|
|
18098
|
+
const rejectTargets = resolveTargets(document2, rejects).map((found) => ({
|
|
18099
|
+
found,
|
|
18100
|
+
verb: "reject"
|
|
18101
|
+
}));
|
|
18102
|
+
return applyResolvedTargets(document2, [...acceptTargets, ...rejectTargets]);
|
|
18103
|
+
}
|
|
18104
|
+
function applyResolvedTargets(document2, decisions) {
|
|
18105
|
+
const order = collectTrackedChanges(document2);
|
|
18106
|
+
const sorted = [...decisions].sort((left, right) => indexOfFound(order, left.found) - indexOfFound(order, right.found));
|
|
18107
|
+
const records = sorted.map(({ found, verb }) => recordFor(found, verb));
|
|
18108
|
+
const affectedNotes = collectAffectedNotes(sorted.map(({ found }) => found));
|
|
18109
|
+
for (const { found, verb } of [...sorted].reverse()) {
|
|
18094
18110
|
if (verb === "accept")
|
|
18095
18111
|
applyAccept(found);
|
|
18096
18112
|
else
|
|
@@ -18100,6 +18116,9 @@ function applyTrackedChanges(document2, target, verb) {
|
|
|
18100
18116
|
applyNotePairing(document2, affectedNotes);
|
|
18101
18117
|
return records;
|
|
18102
18118
|
}
|
|
18119
|
+
function indexOfFound(inventory, found) {
|
|
18120
|
+
return inventory.findIndex((change) => change.node === found.node);
|
|
18121
|
+
}
|
|
18103
18122
|
function resolveTargets(document2, target) {
|
|
18104
18123
|
const allChanges = collectTrackedChanges(document2);
|
|
18105
18124
|
if (target === "all")
|
|
@@ -18602,6 +18621,9 @@ class TrackChanges {
|
|
|
18602
18621
|
reject(target) {
|
|
18603
18622
|
return applyTrackedChanges(this.document, target, "reject");
|
|
18604
18623
|
}
|
|
18624
|
+
apply(accepts, rejects) {
|
|
18625
|
+
return applyTrackedDecisions(this.document, accepts, rejects);
|
|
18626
|
+
}
|
|
18605
18627
|
applyInsertion(paragraph, authorFlag, allocator) {
|
|
18606
18628
|
const mintMeta = this.metaMinter(authorFlag, allocator);
|
|
18607
18629
|
paragraph.children = wrapContiguousTrackable(paragraph.children, (runs) => /* @__PURE__ */ jsxDEV(Ins, {
|
|
@@ -33807,6 +33829,13 @@ function NormalStyle() {
|
|
|
33807
33829
|
]
|
|
33808
33830
|
}, undefined, true, undefined, this);
|
|
33809
33831
|
}
|
|
33832
|
+
function HeadingThemeFonts() {
|
|
33833
|
+
return /* @__PURE__ */ jsxDEV(w.rFonts, {
|
|
33834
|
+
"w-asciiTheme": "majorHAnsi",
|
|
33835
|
+
"w-hAnsiTheme": "majorHAnsi",
|
|
33836
|
+
"w-cstheme": "majorBidi"
|
|
33837
|
+
}, undefined, false, undefined, this);
|
|
33838
|
+
}
|
|
33810
33839
|
function TitleStyle() {
|
|
33811
33840
|
return /* @__PURE__ */ jsxDEV(w.style, {
|
|
33812
33841
|
"w-type": "paragraph",
|
|
@@ -33829,10 +33858,7 @@ function TitleStyle() {
|
|
|
33829
33858
|
}, undefined, false, undefined, this),
|
|
33830
33859
|
/* @__PURE__ */ jsxDEV(w.rPr, {
|
|
33831
33860
|
children: [
|
|
33832
|
-
/* @__PURE__ */ jsxDEV(
|
|
33833
|
-
"w-ascii": "Calibri Light",
|
|
33834
|
-
"w-hAnsi": "Calibri Light"
|
|
33835
|
-
}, undefined, false, undefined, this),
|
|
33861
|
+
/* @__PURE__ */ jsxDEV(HeadingThemeFonts, {}, undefined, false, undefined, this),
|
|
33836
33862
|
/* @__PURE__ */ jsxDEV(w.color, {
|
|
33837
33863
|
"w-val": "1F3864"
|
|
33838
33864
|
}, undefined, false, undefined, this),
|
|
@@ -33866,10 +33892,7 @@ function SubtitleStyle() {
|
|
|
33866
33892
|
}, undefined, false, undefined, this),
|
|
33867
33893
|
/* @__PURE__ */ jsxDEV(w.rPr, {
|
|
33868
33894
|
children: [
|
|
33869
|
-
/* @__PURE__ */ jsxDEV(
|
|
33870
|
-
"w-ascii": "Calibri Light",
|
|
33871
|
-
"w-hAnsi": "Calibri Light"
|
|
33872
|
-
}, undefined, false, undefined, this),
|
|
33895
|
+
/* @__PURE__ */ jsxDEV(HeadingThemeFonts, {}, undefined, false, undefined, this),
|
|
33873
33896
|
/* @__PURE__ */ jsxDEV(w.color, {
|
|
33874
33897
|
"w-val": "5A5A5A"
|
|
33875
33898
|
}, undefined, false, undefined, this),
|
|
@@ -33919,10 +33942,7 @@ function HeadingStyle({
|
|
|
33919
33942
|
}, undefined, true, undefined, this),
|
|
33920
33943
|
/* @__PURE__ */ jsxDEV(w.rPr, {
|
|
33921
33944
|
children: [
|
|
33922
|
-
/* @__PURE__ */ jsxDEV(
|
|
33923
|
-
"w-ascii": "Calibri Light",
|
|
33924
|
-
"w-hAnsi": "Calibri Light"
|
|
33925
|
-
}, undefined, false, undefined, this),
|
|
33945
|
+
/* @__PURE__ */ jsxDEV(HeadingThemeFonts, {}, undefined, false, undefined, this),
|
|
33926
33946
|
bold2 && /* @__PURE__ */ jsxDEV(w.b, {}, undefined, false, undefined, this),
|
|
33927
33947
|
italic && /* @__PURE__ */ jsxDEV(w.i, {}, undefined, false, undefined, this),
|
|
33928
33948
|
color && /* @__PURE__ */ jsxDEV(w.color, {
|
|
@@ -90938,8 +90958,7 @@ function computeSectionStarts(blocks) {
|
|
|
90938
90958
|
continue;
|
|
90939
90959
|
const entry = {
|
|
90940
90960
|
block: blocks[index2],
|
|
90941
|
-
breakIndex: index2
|
|
90942
|
-
isTrailing: index2 === blocks.length - 1
|
|
90961
|
+
breakIndex: index2
|
|
90943
90962
|
};
|
|
90944
90963
|
const group = starts.get(sectionStart) ?? [];
|
|
90945
90964
|
group.push(entry);
|
|
@@ -90949,12 +90968,7 @@ function computeSectionStarts(blocks) {
|
|
|
90949
90968
|
return starts;
|
|
90950
90969
|
}
|
|
90951
90970
|
function renderSectionStart(entry, blocks, ctx) {
|
|
90952
|
-
|
|
90953
|
-
if (entry.isTrailing) {
|
|
90954
|
-
return marginalLines && marginalLines.length > 0 ? marginalLines.join(`
|
|
90955
|
-
`) : null;
|
|
90956
|
-
}
|
|
90957
|
-
return renderSectionBreak(entry.block, governedRange(blocks, entry.breakIndex), marginalLines);
|
|
90971
|
+
return renderSectionBreak(entry.block, governedRange(blocks, entry.breakIndex), ctx.marginalsBySection.get(entry.block.id));
|
|
90958
90972
|
}
|
|
90959
90973
|
function computeGoverningColumns(blocks) {
|
|
90960
90974
|
const map3 = new Map;
|
|
@@ -92148,6 +92162,55 @@ var init_render2 = __esm(() => {
|
|
|
92148
92162
|
};
|
|
92149
92163
|
});
|
|
92150
92164
|
|
|
92165
|
+
// src/cli/replace/scope.ts
|
|
92166
|
+
function validateScopeShape(at) {
|
|
92167
|
+
let parsed;
|
|
92168
|
+
try {
|
|
92169
|
+
parsed = parseLocator(at);
|
|
92170
|
+
} catch (error) {
|
|
92171
|
+
if (error instanceof LocatorParseError) {
|
|
92172
|
+
throw new ScopeError("INVALID_LOCATOR", error.message);
|
|
92173
|
+
}
|
|
92174
|
+
throw error;
|
|
92175
|
+
}
|
|
92176
|
+
if (!isParagraphLocator(parsed)) {
|
|
92177
|
+
throw new ScopeError("INVALID_LOCATOR", `--at scope must be a single paragraph (pN) or cell paragraph (tT:rRcC:pN), got "${at}" \u2014 replace targets text within one paragraph`);
|
|
92178
|
+
}
|
|
92179
|
+
return at;
|
|
92180
|
+
}
|
|
92181
|
+
function isParagraphLocator(parsed) {
|
|
92182
|
+
if (parsed.kind === "block")
|
|
92183
|
+
return parsed.blockId[0] === "p";
|
|
92184
|
+
let inner2 = parsed;
|
|
92185
|
+
while (inner2?.kind === "cell")
|
|
92186
|
+
inner2 = inner2.inner;
|
|
92187
|
+
return inner2?.kind === "block" && inner2.blockId[0] === "p";
|
|
92188
|
+
}
|
|
92189
|
+
function resolveReplaceScope(document4, at) {
|
|
92190
|
+
const blockId = validateScopeShape(at);
|
|
92191
|
+
try {
|
|
92192
|
+
document4.body.resolveBlock(at);
|
|
92193
|
+
} catch (error) {
|
|
92194
|
+
throw new ScopeError("BLOCK_NOT_FOUND", error.message);
|
|
92195
|
+
}
|
|
92196
|
+
return blockId;
|
|
92197
|
+
}
|
|
92198
|
+
function matchesInScope(matches, blockId) {
|
|
92199
|
+
return matches.filter((match) => match.blockId === blockId);
|
|
92200
|
+
}
|
|
92201
|
+
var ScopeError;
|
|
92202
|
+
var init_scope = __esm(() => {
|
|
92203
|
+
init_core2();
|
|
92204
|
+
ScopeError = class ScopeError extends Error {
|
|
92205
|
+
code;
|
|
92206
|
+
constructor(code3, message) {
|
|
92207
|
+
super(message);
|
|
92208
|
+
this.code = code3;
|
|
92209
|
+
this.name = "ScopeError";
|
|
92210
|
+
}
|
|
92211
|
+
};
|
|
92212
|
+
});
|
|
92213
|
+
|
|
92151
92214
|
// src/cli/replace/batch.ts
|
|
92152
92215
|
async function runReplaceBatch(filePath, batchSource, values2) {
|
|
92153
92216
|
const authorFlag = values2.author;
|
|
@@ -92181,6 +92244,13 @@ async function runReplaceBatch(filePath, batchSource, values2) {
|
|
|
92181
92244
|
const spec = specs[index2];
|
|
92182
92245
|
if (!spec)
|
|
92183
92246
|
continue;
|
|
92247
|
+
if (spec.at !== undefined) {
|
|
92248
|
+
try {
|
|
92249
|
+
document4.body.resolveBlock(spec.at);
|
|
92250
|
+
} catch {
|
|
92251
|
+
return fail("BLOCK_NOT_FOUND", `entry ${index2}: scope "${spec.at}" not found`);
|
|
92252
|
+
}
|
|
92253
|
+
}
|
|
92184
92254
|
let findResult;
|
|
92185
92255
|
try {
|
|
92186
92256
|
findResult = findTextSpans(document4.body, spec.pattern, {
|
|
@@ -92193,7 +92263,7 @@ async function runReplaceBatch(filePath, batchSource, values2) {
|
|
|
92193
92263
|
const message = matcherError instanceof Error ? matcherError.message : String(matcherError);
|
|
92194
92264
|
return fail("USAGE", `entry ${index2}: invalid pattern: ${message}`);
|
|
92195
92265
|
}
|
|
92196
|
-
const all2 = findResult.matches;
|
|
92266
|
+
const all2 = spec.at !== undefined ? matchesInScope(findResult.matches, spec.at) : findResult.matches;
|
|
92197
92267
|
const selected = spec.limit !== undefined ? all2.slice(0, spec.limit) : spec.all ? all2 : all2.slice(0, 1);
|
|
92198
92268
|
const tracked = allocator ? {
|
|
92199
92269
|
meta: {
|
|
@@ -92265,6 +92335,20 @@ function validateSpec(raw, index2) {
|
|
|
92265
92335
|
}
|
|
92266
92336
|
limit = value;
|
|
92267
92337
|
}
|
|
92338
|
+
let at;
|
|
92339
|
+
if (raw.at !== undefined) {
|
|
92340
|
+
if (typeof raw.at !== "string") {
|
|
92341
|
+
throw new EntryError3("USAGE", `entry ${index2}: "at" must be a string`);
|
|
92342
|
+
}
|
|
92343
|
+
try {
|
|
92344
|
+
at = validateScopeShape(raw.at);
|
|
92345
|
+
} catch (error) {
|
|
92346
|
+
if (error instanceof ScopeError) {
|
|
92347
|
+
throw new EntryError3(error.code, `entry ${index2}: ${error.message}`);
|
|
92348
|
+
}
|
|
92349
|
+
throw error;
|
|
92350
|
+
}
|
|
92351
|
+
}
|
|
92268
92352
|
return {
|
|
92269
92353
|
pattern: raw.pattern,
|
|
92270
92354
|
replacement: raw.replacement,
|
|
@@ -92274,7 +92358,8 @@ function validateSpec(raw, index2) {
|
|
|
92274
92358
|
all: Boolean(raw.all),
|
|
92275
92359
|
...limit !== undefined ? { limit } : {},
|
|
92276
92360
|
view: wantCurrent ? "current" : wantBaseline ? "baseline" : "accepted",
|
|
92277
|
-
...typeof raw.author === "string" ? { author: raw.author } : {}
|
|
92361
|
+
...typeof raw.author === "string" ? { author: raw.author } : {},
|
|
92362
|
+
...at !== undefined ? { at } : {}
|
|
92278
92363
|
};
|
|
92279
92364
|
}
|
|
92280
92365
|
var EntryError3;
|
|
@@ -92283,6 +92368,7 @@ var init_batch4 = __esm(() => {
|
|
|
92283
92368
|
init_find();
|
|
92284
92369
|
init_parse_helpers();
|
|
92285
92370
|
init_respond();
|
|
92371
|
+
init_scope();
|
|
92286
92372
|
EntryError3 = class EntryError3 extends Error {
|
|
92287
92373
|
code;
|
|
92288
92374
|
hint;
|
|
@@ -92303,6 +92389,7 @@ __export(exports_replace3, {
|
|
|
92303
92389
|
async function run37(args) {
|
|
92304
92390
|
const parsed = await tryParseArgs(args, {
|
|
92305
92391
|
batch: { type: "string" },
|
|
92392
|
+
at: { type: "string" },
|
|
92306
92393
|
regex: { type: "boolean" },
|
|
92307
92394
|
"ignore-case": { type: "boolean" },
|
|
92308
92395
|
all: { type: "boolean" },
|
|
@@ -92329,6 +92416,9 @@ async function run37(args) {
|
|
|
92329
92416
|
if (parsed.positionals.length > 1) {
|
|
92330
92417
|
return fail("USAGE", "--batch reads pattern/replacement from the JSONL file; don't also pass them as positionals", HELP33);
|
|
92331
92418
|
}
|
|
92419
|
+
if (parsed.values.at !== undefined) {
|
|
92420
|
+
return fail("USAGE", '--at is per-entry in batch mode: put "at" on each JSONL line, not on the command', HELP33);
|
|
92421
|
+
}
|
|
92332
92422
|
return runReplaceBatch(path2, batchInput, parsed.values);
|
|
92333
92423
|
}
|
|
92334
92424
|
const pattern = parsed.positionals[1];
|
|
@@ -92366,7 +92456,19 @@ async function run37(args) {
|
|
|
92366
92456
|
const message = matcherError instanceof Error ? matcherError.message : String(matcherError);
|
|
92367
92457
|
return fail("USAGE", `Invalid pattern: ${message}`);
|
|
92368
92458
|
}
|
|
92369
|
-
const
|
|
92459
|
+
const atScope = parsed.values.at;
|
|
92460
|
+
let allMatches = findResult.matches;
|
|
92461
|
+
if (atScope !== undefined) {
|
|
92462
|
+
try {
|
|
92463
|
+
const blockId = resolveReplaceScope(document4, atScope);
|
|
92464
|
+
allMatches = matchesInScope(allMatches, blockId);
|
|
92465
|
+
} catch (scopeError) {
|
|
92466
|
+
if (scopeError instanceof ScopeError) {
|
|
92467
|
+
return fail(scopeError.code, scopeError.message);
|
|
92468
|
+
}
|
|
92469
|
+
throw scopeError;
|
|
92470
|
+
}
|
|
92471
|
+
}
|
|
92370
92472
|
const normalizationFields = findResult.normalizedQuery !== undefined ? {
|
|
92371
92473
|
normalizedPattern: findResult.normalizedQuery,
|
|
92372
92474
|
normalizationApplied: findResult.normalizationApplied
|
|
@@ -92397,6 +92499,7 @@ async function run37(args) {
|
|
|
92397
92499
|
regex: useRegex,
|
|
92398
92500
|
ignoreCase,
|
|
92399
92501
|
view: findView,
|
|
92502
|
+
...atScope ? { at: atScope } : {},
|
|
92400
92503
|
totalMatches: allMatches.length,
|
|
92401
92504
|
replaced: selected.length,
|
|
92402
92505
|
matches: matchesPayload,
|
|
@@ -92415,6 +92518,7 @@ async function run37(args) {
|
|
|
92415
92518
|
regex: useRegex,
|
|
92416
92519
|
ignoreCase,
|
|
92417
92520
|
view: findView,
|
|
92521
|
+
...atScope ? { at: atScope } : {},
|
|
92418
92522
|
totalMatches: 0,
|
|
92419
92523
|
replaced: 0,
|
|
92420
92524
|
matches: [],
|
|
@@ -92451,6 +92555,7 @@ async function run37(args) {
|
|
|
92451
92555
|
regex: useRegex,
|
|
92452
92556
|
ignoreCase,
|
|
92453
92557
|
view: findView,
|
|
92558
|
+
...atScope ? { at: atScope } : {},
|
|
92454
92559
|
totalMatches: allMatches.length,
|
|
92455
92560
|
replaced: selected.length,
|
|
92456
92561
|
matches: matchesPayload,
|
|
@@ -92466,9 +92571,16 @@ Usage:
|
|
|
92466
92571
|
docx replace FILE --batch - [options] # read JSONL from stdin
|
|
92467
92572
|
|
|
92468
92573
|
Options:
|
|
92574
|
+
--at LOCATOR confine the replace to ONE paragraph: a body pN or a cell
|
|
92575
|
+
paragraph tT:rRcC:pN. Use this when the same placeholder
|
|
92576
|
+
repeats across the document (a r\xE9sum\xE9's "City, State" /
|
|
92577
|
+
"Position Title" in every entry) and you want THE one in a
|
|
92578
|
+
specific paragraph \u2014 no find + offset-span edit needed:
|
|
92579
|
+
docx replace doc.docx --at p20 "City, State" "San Francisco, CA"
|
|
92469
92580
|
--regex treat PATTERN as a JavaScript regular expression
|
|
92470
92581
|
--ignore-case case-insensitive match
|
|
92471
|
-
--all replace every match (default: just the first
|
|
92582
|
+
--all replace every match (default: just the first; with --at,
|
|
92583
|
+
scoped to that paragraph)
|
|
92472
92584
|
--limit N replace at most N matches (in document order)
|
|
92473
92585
|
--author NAME author for tracked changes (default: $DOCX_AUTHOR)
|
|
92474
92586
|
--track record substitutions as tracked changes even when the
|
|
@@ -92539,6 +92651,7 @@ var init_replace4 = __esm(() => {
|
|
|
92539
92651
|
init_parse_helpers();
|
|
92540
92652
|
init_respond();
|
|
92541
92653
|
init_batch4();
|
|
92654
|
+
init_scope();
|
|
92542
92655
|
});
|
|
92543
92656
|
|
|
92544
92657
|
// src/cli/sections/index.tsx
|
|
@@ -93663,6 +93776,11 @@ theme-following headings adopt the font. Styles/runs that pin their OWN font
|
|
|
93663
93776
|
(e.g. a code block's monospace, a deliberately-Arial run) are preserved; pass
|
|
93664
93777
|
--all to repoint those too.
|
|
93665
93778
|
|
|
93779
|
+
Theme-following headings only adopt the font if the document HAS a theme part
|
|
93780
|
+
(every doc this CLI creates does; Word docs do too). The ack's "themeUpdated"
|
|
93781
|
+
is false for the rare theme-less doc \u2014 there the body changes but headings keep
|
|
93782
|
+
their fallback font; run with --all to force the headings onto the new font.
|
|
93783
|
+
|
|
93666
93784
|
Options:
|
|
93667
93785
|
--size N Also set the default font size, in points (e.g. 12).
|
|
93668
93786
|
--all Repoint EVERY explicit font \u2014 styles, body runs, and notes \u2014
|
|
@@ -95232,42 +95350,23 @@ var init_groups = __esm(() => {
|
|
|
95232
95350
|
};
|
|
95233
95351
|
});
|
|
95234
95352
|
|
|
95235
|
-
// src/cli/track-changes/list.ts
|
|
95236
|
-
|
|
95237
|
-
__export(exports_list5, {
|
|
95238
|
-
run: () => run49
|
|
95239
|
-
});
|
|
95240
|
-
async function run49(args) {
|
|
95241
|
-
const parsed = await tryParseArgs(args, {
|
|
95242
|
-
help: { type: "boolean", short: "h" }
|
|
95243
|
-
}, HELP45);
|
|
95244
|
-
if (typeof parsed === "number")
|
|
95245
|
-
return parsed;
|
|
95246
|
-
if (parsed.values.help) {
|
|
95247
|
-
await writeStdout(HELP45);
|
|
95248
|
-
return EXIT2.OK;
|
|
95249
|
-
}
|
|
95250
|
-
const path2 = parsed.positionals[0];
|
|
95251
|
-
if (!path2)
|
|
95252
|
-
return fail("USAGE", "Missing FILE argument", HELP45);
|
|
95253
|
-
const document4 = await openOrFail(path2);
|
|
95254
|
-
if (typeof document4 === "number")
|
|
95255
|
-
return document4;
|
|
95353
|
+
// src/cli/track-changes/list-view.ts
|
|
95354
|
+
function collectTrackedChangeRecords(document4) {
|
|
95256
95355
|
const byId = new Map;
|
|
95257
95356
|
for (const paragraph2 of flattenParagraphs(document4.body.blocks)) {
|
|
95258
|
-
for (const
|
|
95259
|
-
if (
|
|
95357
|
+
for (const run49 of paragraph2.runs) {
|
|
95358
|
+
if (run49.type !== "text" || !run49.trackedChange)
|
|
95260
95359
|
continue;
|
|
95261
|
-
const change =
|
|
95360
|
+
const change = run49.trackedChange;
|
|
95262
95361
|
const existing = byId.get(change.id);
|
|
95263
95362
|
if (existing) {
|
|
95264
|
-
existing.text +=
|
|
95363
|
+
existing.text += run49.text;
|
|
95265
95364
|
continue;
|
|
95266
95365
|
}
|
|
95267
95366
|
byId.set(change.id, {
|
|
95268
95367
|
...change,
|
|
95269
95368
|
blockId: paragraph2.id,
|
|
95270
|
-
text:
|
|
95369
|
+
text: run49.text
|
|
95271
95370
|
});
|
|
95272
95371
|
}
|
|
95273
95372
|
}
|
|
@@ -95304,8 +95403,154 @@ async function run49(args) {
|
|
|
95304
95403
|
if (group)
|
|
95305
95404
|
record.group = group;
|
|
95306
95405
|
}
|
|
95307
|
-
|
|
95308
|
-
|
|
95406
|
+
return sorted;
|
|
95407
|
+
}
|
|
95408
|
+
function renderTrackedChangeTable(records) {
|
|
95409
|
+
if (records.length === 0)
|
|
95410
|
+
return `no tracked changes
|
|
95411
|
+
`;
|
|
95412
|
+
const byGroup = new Map;
|
|
95413
|
+
for (const record of records) {
|
|
95414
|
+
if (!record.group)
|
|
95415
|
+
continue;
|
|
95416
|
+
const members = byGroup.get(record.group) ?? [];
|
|
95417
|
+
members.push(record);
|
|
95418
|
+
byGroup.set(record.group, members);
|
|
95419
|
+
}
|
|
95420
|
+
const rows = [];
|
|
95421
|
+
const consumed = new Set;
|
|
95422
|
+
for (const record of records) {
|
|
95423
|
+
if (record.group) {
|
|
95424
|
+
if (consumed.has(record.group))
|
|
95425
|
+
continue;
|
|
95426
|
+
consumed.add(record.group);
|
|
95427
|
+
const members = byGroup.get(record.group) ?? [record];
|
|
95428
|
+
const del = members.find((member) => member.kind === "del");
|
|
95429
|
+
const ins = members.find((member) => member.kind === "ins");
|
|
95430
|
+
rows.push({
|
|
95431
|
+
handle: record.group,
|
|
95432
|
+
verb: "replace",
|
|
95433
|
+
block: record.blockId,
|
|
95434
|
+
desc: `${quote(del?.text ?? "")} \u2192 ${quote(ins?.text ?? "")}`
|
|
95435
|
+
});
|
|
95436
|
+
continue;
|
|
95437
|
+
}
|
|
95438
|
+
rows.push({
|
|
95439
|
+
handle: record.id,
|
|
95440
|
+
verb: verbLabel(record.kind),
|
|
95441
|
+
block: record.blockId,
|
|
95442
|
+
desc: describeSolo(record)
|
|
95443
|
+
});
|
|
95444
|
+
}
|
|
95445
|
+
const handleWidth = Math.max(...rows.map((row) => row.handle.length));
|
|
95446
|
+
const verbWidth = Math.max(...rows.map((row) => row.verb.length));
|
|
95447
|
+
const blockWidth = Math.max(...rows.map((row) => row.block.length));
|
|
95448
|
+
const body = rows.map((row) => ` ${row.handle.padEnd(handleWidth)} ${row.verb.padEnd(verbWidth)} ${row.block.padEnd(blockWidth)} ${row.desc}`).join(`
|
|
95449
|
+
`);
|
|
95450
|
+
const total = records.length;
|
|
95451
|
+
const header = `${total} tracked change${total === 1 ? "" : "s"}${rows.length === total ? "" : ` (${rows.length} logical)`}:`;
|
|
95452
|
+
const footer = `Address by the leftmost handle: accept/reject --at <handle> (repeatable).
|
|
95453
|
+
` + "A revN handle resolves both halves of a replace in one call; --json for full detail.";
|
|
95454
|
+
return `${header}
|
|
95455
|
+
|
|
95456
|
+
${body}
|
|
95457
|
+
|
|
95458
|
+
${footer}
|
|
95459
|
+
`;
|
|
95460
|
+
}
|
|
95461
|
+
async function remainingTrackedChangesBlock(path2, verb) {
|
|
95462
|
+
let document4;
|
|
95463
|
+
try {
|
|
95464
|
+
document4 = await Document.open(path2);
|
|
95465
|
+
} catch {
|
|
95466
|
+
return "";
|
|
95467
|
+
}
|
|
95468
|
+
const remaining = collectTrackedChangeRecords(document4);
|
|
95469
|
+
if (remaining.length === 0)
|
|
95470
|
+
return "";
|
|
95471
|
+
return `
|
|
95472
|
+
Remaining (ids renumbered after this ${verb}):
|
|
95473
|
+
|
|
95474
|
+
${renderTrackedChangeTable(remaining)}`;
|
|
95475
|
+
}
|
|
95476
|
+
function quote(text6, max = 44) {
|
|
95477
|
+
const collapsed = text6.replace(/\s+/g, " ").trim();
|
|
95478
|
+
if (collapsed === "")
|
|
95479
|
+
return "\xB6";
|
|
95480
|
+
const clipped = collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
95481
|
+
return `"${clipped}"`;
|
|
95482
|
+
}
|
|
95483
|
+
function verbLabel(kind) {
|
|
95484
|
+
const labels = {
|
|
95485
|
+
ins: "insert",
|
|
95486
|
+
del: "delete",
|
|
95487
|
+
moveFrom: "move-from",
|
|
95488
|
+
moveTo: "move-to",
|
|
95489
|
+
sectPrChange: "section",
|
|
95490
|
+
pPrChange: "format",
|
|
95491
|
+
rowIns: "row-insert",
|
|
95492
|
+
rowDel: "row-delete",
|
|
95493
|
+
cellIns: "cell-insert",
|
|
95494
|
+
cellDel: "cell-delete",
|
|
95495
|
+
tblGridChange: "grid",
|
|
95496
|
+
tblPrChange: "table-fmt",
|
|
95497
|
+
tcPrChange: "cell-fmt",
|
|
95498
|
+
checkboxToggle: "checkbox"
|
|
95499
|
+
};
|
|
95500
|
+
return labels[kind] ?? kind;
|
|
95501
|
+
}
|
|
95502
|
+
function describeSolo(record) {
|
|
95503
|
+
if (record.kind === "ins" || record.kind === "del" || record.kind === "moveFrom" || record.kind === "moveTo") {
|
|
95504
|
+
return record.text === "" ? "\xB6 paragraph mark" : quote(record.text);
|
|
95505
|
+
}
|
|
95506
|
+
if (record.kind === "pPrChange" || record.kind === "sectPrChange") {
|
|
95507
|
+
const diff2 = propDiff(record.prior, record.current);
|
|
95508
|
+
return diff2 || (record.kind === "pPrChange" ? "paragraph format" : "layout");
|
|
95509
|
+
}
|
|
95510
|
+
const notes = {
|
|
95511
|
+
rowIns: "row inserted",
|
|
95512
|
+
rowDel: "row deleted",
|
|
95513
|
+
cellIns: "cell inserted",
|
|
95514
|
+
cellDel: "cell deleted",
|
|
95515
|
+
tblGridChange: "grid widths",
|
|
95516
|
+
tblPrChange: "table properties",
|
|
95517
|
+
tcPrChange: "cell properties",
|
|
95518
|
+
checkboxToggle: "checkbox toggled"
|
|
95519
|
+
};
|
|
95520
|
+
return notes[record.kind] ?? "";
|
|
95521
|
+
}
|
|
95522
|
+
function propDiff(prior, current) {
|
|
95523
|
+
const before = flattenProps(prior);
|
|
95524
|
+
const after = flattenProps(current);
|
|
95525
|
+
const keys = new Set([...before.keys(), ...after.keys()]);
|
|
95526
|
+
const parts = [];
|
|
95527
|
+
for (const key of keys) {
|
|
95528
|
+
const from = before.get(key);
|
|
95529
|
+
const to = after.get(key);
|
|
95530
|
+
if (from === to)
|
|
95531
|
+
continue;
|
|
95532
|
+
parts.push(`${key} ${from ?? "\xB7"}\u2192${to ?? "\xB7"}`);
|
|
95533
|
+
}
|
|
95534
|
+
if (parts.length === 0)
|
|
95535
|
+
return "";
|
|
95536
|
+
if (parts.length > 3)
|
|
95537
|
+
return `${parts.slice(0, 3).join(", ")}, \u2026`;
|
|
95538
|
+
return parts.join(", ");
|
|
95539
|
+
}
|
|
95540
|
+
function flattenProps(obj, prefix = "") {
|
|
95541
|
+
const out = new Map;
|
|
95542
|
+
if (obj === null || typeof obj !== "object")
|
|
95543
|
+
return out;
|
|
95544
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
95545
|
+
const path2 = prefix ? `${prefix}.${key}` : key;
|
|
95546
|
+
if (value !== null && typeof value === "object") {
|
|
95547
|
+
for (const [k, v] of flattenProps(value, path2))
|
|
95548
|
+
out.set(k, v);
|
|
95549
|
+
} else if (typeof value === "string" || typeof value === "number") {
|
|
95550
|
+
out.set(path2, value);
|
|
95551
|
+
}
|
|
95552
|
+
}
|
|
95553
|
+
return out;
|
|
95309
95554
|
}
|
|
95310
95555
|
function trackedChangeIndex(id) {
|
|
95311
95556
|
const match = id.match(/^tc(\d+)$/);
|
|
@@ -95355,12 +95600,50 @@ function readParagraphPropsSummary(children) {
|
|
|
95355
95600
|
}
|
|
95356
95601
|
return out;
|
|
95357
95602
|
}
|
|
95603
|
+
var init_list_view = __esm(() => {
|
|
95604
|
+
init_core2();
|
|
95605
|
+
init_track_changes();
|
|
95606
|
+
init_groups();
|
|
95607
|
+
});
|
|
95608
|
+
|
|
95609
|
+
// src/cli/track-changes/list.ts
|
|
95610
|
+
var exports_list5 = {};
|
|
95611
|
+
__export(exports_list5, {
|
|
95612
|
+
run: () => run49
|
|
95613
|
+
});
|
|
95614
|
+
async function run49(args) {
|
|
95615
|
+
const parsed = await tryParseArgs(args, {
|
|
95616
|
+
help: { type: "boolean", short: "h" },
|
|
95617
|
+
json: { type: "boolean" }
|
|
95618
|
+
}, HELP45);
|
|
95619
|
+
if (typeof parsed === "number")
|
|
95620
|
+
return parsed;
|
|
95621
|
+
if (parsed.values.help) {
|
|
95622
|
+
await writeStdout(HELP45);
|
|
95623
|
+
return EXIT2.OK;
|
|
95624
|
+
}
|
|
95625
|
+
const path2 = parsed.positionals[0];
|
|
95626
|
+
if (!path2)
|
|
95627
|
+
return fail("USAGE", "Missing FILE argument", HELP45);
|
|
95628
|
+
const document4 = await openOrFail(path2);
|
|
95629
|
+
if (typeof document4 === "number")
|
|
95630
|
+
return document4;
|
|
95631
|
+
const records = collectTrackedChangeRecords(document4);
|
|
95632
|
+
if (parsed.values.json) {
|
|
95633
|
+
await respond(records);
|
|
95634
|
+
return EXIT2.OK;
|
|
95635
|
+
}
|
|
95636
|
+
await writeStdout(renderTrackedChangeTable(records));
|
|
95637
|
+
return EXIT2.OK;
|
|
95638
|
+
}
|
|
95358
95639
|
var HELP45 = `docx track-changes list \u2014 inventory every revision wrapper
|
|
95359
95640
|
|
|
95360
95641
|
Usage:
|
|
95361
95642
|
docx track-changes list FILE [options]
|
|
95362
95643
|
|
|
95363
95644
|
Options:
|
|
95645
|
+
--json Emit the full revision objects as a JSON array (default: a
|
|
95646
|
+
text table, one LOGICAL change per line)
|
|
95364
95647
|
-h, --help Show this help
|
|
95365
95648
|
|
|
95366
95649
|
Lists every revision wrapper with stable tcN ids \u2014 run-level <w:ins>, <w:del>,
|
|
@@ -95370,25 +95653,35 @@ paragraph-mark <w:ins>/<w:del> markers (a self-closing element inside
|
|
|
95370
95653
|
of the same logical move appear as separate entries; their kind tells them
|
|
95371
95654
|
apart.
|
|
95372
95655
|
|
|
95373
|
-
Output: a
|
|
95374
|
-
|
|
95375
|
-
|
|
95376
|
-
|
|
95377
|
-
"
|
|
95378
|
-
|
|
95379
|
-
|
|
95380
|
-
|
|
95381
|
-
|
|
95382
|
-
|
|
95383
|
-
|
|
95384
|
-
|
|
95385
|
-
|
|
95386
|
-
|
|
95387
|
-
|
|
95388
|
-
|
|
95389
|
-
|
|
95390
|
-
|
|
95391
|
-
|
|
95656
|
+
Output: a text table, ONE LOGICAL CHANGE per line \u2014
|
|
95657
|
+
|
|
95658
|
+
rev0 replace p7 "Net 90 from the Company" \u2192 "Net 30 from the Company"
|
|
95659
|
+
tc4 format p22 spacing.line \u2192360
|
|
95660
|
+
tc7 delete p27 "Personal Guarantee."
|
|
95661
|
+
|
|
95662
|
+
The leftmost token is the handle you pass to \`accept\`/\`reject --at\` (repeatable,
|
|
95663
|
+
e.g. \`--at rev0 --at tc4\`). A del+ins REPLACE pair on the same paragraph is
|
|
95664
|
+
collapsed onto ONE line under its shared revN handle, so accepting/rejecting the
|
|
95665
|
+
whole logical change is a single call \u2014 addressing the two tcN halves separately
|
|
95666
|
+
forces a re-list, because tcN ids renumber after each accept. All --at targets in
|
|
95667
|
+
one call resolve against the pre-mutation tree, so the renumbering never bites a
|
|
95668
|
+
batch.
|
|
95669
|
+
|
|
95670
|
+
--json gives a bare JSON array of { id, kind, author, date, revisionId, blockId,
|
|
95671
|
+
text } sorted by id (document order). Each item's "id" (e.g. tc0) is its granular
|
|
95672
|
+
handle; paired halves additionally carry "group": "revN". kind is one of: "ins",
|
|
95673
|
+
"del", "moveFrom", "moveTo", "sectPrChange", "pPrChange", "rowIns", "rowDel",
|
|
95674
|
+
"cellIns", "cellDel", "tblGridChange", "tblPrChange", "tcPrChange",
|
|
95675
|
+
"checkboxToggle". Paragraph-mark entries have kind "ins"/"del" with text "" \u2014
|
|
95676
|
+
their blockId is the owning paragraph's pN. Table-structural entries
|
|
95677
|
+
(rowIns/rowDel/cellIns/cellDel and the property revisions
|
|
95678
|
+
tblGridChange/tblPrChange/tcPrChange) have text "" and blockId set to the owning
|
|
95679
|
+
table's tN. checkboxToggle entries surface a Word checkbox content control's
|
|
95680
|
+
tracked toggle (\u2610\u2194\u2612): metadata comes from the inner <w:ins> (the new glyph);
|
|
95681
|
+
reject restores the prior glyph and flips the w14:checked attribute back.
|
|
95682
|
+
Structural inserts/deletes of a checkbox (Word emits
|
|
95683
|
+
<w:customXmlDel/InsRangeStart/End> around the SDT) round-trip through the
|
|
95684
|
+
XmlNode tree but aren't yet enumerated as a dedicated kind.
|
|
95392
95685
|
|
|
95393
95686
|
Property revisions (kind="sectPrChange" or "pPrChange") additionally include
|
|
95394
95687
|
{ prior, current } objects from before and after the tracked edit, so agents
|
|
@@ -95398,19 +95691,17 @@ style/alignment/spacing/indent.
|
|
|
95398
95691
|
|
|
95399
95692
|
Examples:
|
|
95400
95693
|
docx track-changes list doc.docx
|
|
95401
|
-
docx track-changes
|
|
95402
|
-
docx track-changes list doc.docx | jq '.[] | select(.kind
|
|
95403
|
-
docx track-changes list doc.docx | jq '.[] | select(.kind
|
|
95404
|
-
docx track-changes list doc.docx | jq '.[] | select(.kind == "pPrChange") | .prior, .current'
|
|
95694
|
+
docx track-changes accept doc.docx --at rev0 --at tc4
|
|
95695
|
+
docx track-changes list doc.docx --json | jq '.[] | select(.kind == "del")'
|
|
95696
|
+
docx track-changes list doc.docx --json | jq '.[] | select(.kind | test("move"))'
|
|
95697
|
+
docx track-changes list doc.docx --json | jq '.[] | select(.kind == "pPrChange") | .prior, .current'
|
|
95405
95698
|
`;
|
|
95406
95699
|
var init_list8 = __esm(() => {
|
|
95407
|
-
init_core2();
|
|
95408
|
-
init_track_changes();
|
|
95409
95700
|
init_respond();
|
|
95410
|
-
|
|
95701
|
+
init_list_view();
|
|
95411
95702
|
});
|
|
95412
95703
|
|
|
95413
|
-
// src/cli/track-changes/apply.ts
|
|
95704
|
+
// src/cli/track-changes/run-apply.ts
|
|
95414
95705
|
async function runApply(args, verb, help) {
|
|
95415
95706
|
const parsed = await tryParseArgs(args, {
|
|
95416
95707
|
at: { type: "string", multiple: true },
|
|
@@ -95472,6 +95763,11 @@ async function runApply(args, verb, help) {
|
|
|
95472
95763
|
path: outputPath ?? path2,
|
|
95473
95764
|
applied
|
|
95474
95765
|
});
|
|
95766
|
+
if (!all2 && !parsed.values.verbose) {
|
|
95767
|
+
const remaining = await remainingTrackedChangesBlock(outputPath ?? path2, verb);
|
|
95768
|
+
if (remaining)
|
|
95769
|
+
await writeStdout(remaining);
|
|
95770
|
+
}
|
|
95475
95771
|
return EXIT2.OK;
|
|
95476
95772
|
} catch (error) {
|
|
95477
95773
|
if (error instanceof TrackedChangeNotFoundError) {
|
|
@@ -95480,10 +95776,11 @@ async function runApply(args, verb, help) {
|
|
|
95480
95776
|
throw error;
|
|
95481
95777
|
}
|
|
95482
95778
|
}
|
|
95483
|
-
var
|
|
95779
|
+
var init_run_apply = __esm(() => {
|
|
95484
95780
|
init_track_changes();
|
|
95485
95781
|
init_respond();
|
|
95486
95782
|
init_groups();
|
|
95783
|
+
init_list_view();
|
|
95487
95784
|
});
|
|
95488
95785
|
|
|
95489
95786
|
// src/cli/track-changes/accept.ts
|
|
@@ -95497,7 +95794,7 @@ async function run50(args) {
|
|
|
95497
95794
|
var AT_FORMS14, HELP46;
|
|
95498
95795
|
var init_accept = __esm(() => {
|
|
95499
95796
|
init_core2();
|
|
95500
|
-
|
|
95797
|
+
init_run_apply();
|
|
95501
95798
|
AT_FORMS14 = describeForms(["trackedChange"], " ");
|
|
95502
95799
|
HELP46 = `docx track-changes accept \u2014 accept tracked changes (incorporate into the doc)
|
|
95503
95800
|
|
|
@@ -95568,7 +95865,7 @@ async function run51(args) {
|
|
|
95568
95865
|
var AT_FORMS15, HELP47;
|
|
95569
95866
|
var init_reject = __esm(() => {
|
|
95570
95867
|
init_core2();
|
|
95571
|
-
|
|
95868
|
+
init_run_apply();
|
|
95572
95869
|
AT_FORMS15 = describeForms(["trackedChange"], " ");
|
|
95573
95870
|
HELP47 = `docx track-changes reject \u2014 reject tracked changes (revert to pre-change state)
|
|
95574
95871
|
|
|
@@ -95636,13 +95933,15 @@ Examples:
|
|
|
95636
95933
|
`;
|
|
95637
95934
|
});
|
|
95638
95935
|
|
|
95639
|
-
// src/cli/track-changes/
|
|
95640
|
-
var
|
|
95641
|
-
__export(
|
|
95936
|
+
// src/cli/track-changes/apply.ts
|
|
95937
|
+
var exports_apply = {};
|
|
95938
|
+
__export(exports_apply, {
|
|
95642
95939
|
run: () => run52
|
|
95643
95940
|
});
|
|
95644
95941
|
async function run52(args) {
|
|
95645
95942
|
const parsed = await tryParseArgs(args, {
|
|
95943
|
+
accept: { type: "string", multiple: true },
|
|
95944
|
+
reject: { type: "string", multiple: true },
|
|
95646
95945
|
...SAVE_FLAGS
|
|
95647
95946
|
}, HELP48);
|
|
95648
95947
|
if (typeof parsed === "number")
|
|
@@ -95652,15 +95951,151 @@ async function run52(args) {
|
|
|
95652
95951
|
return EXIT2.OK;
|
|
95653
95952
|
}
|
|
95654
95953
|
setVerboseAck(Boolean(parsed.values.verbose));
|
|
95954
|
+
const path2 = parsed.positionals[0];
|
|
95955
|
+
if (!path2)
|
|
95956
|
+
return fail("USAGE", "Missing FILE argument", HELP48);
|
|
95957
|
+
const acceptRaw = parsed.values.accept ?? [];
|
|
95958
|
+
const rejectRaw = parsed.values.reject ?? [];
|
|
95959
|
+
if (acceptRaw.length === 0 && rejectRaw.length === 0) {
|
|
95960
|
+
return fail("USAGE", "Specify --accept and/or --reject (handle)", HELP48);
|
|
95961
|
+
}
|
|
95962
|
+
const document4 = await openOrFail(path2);
|
|
95963
|
+
if (typeof document4 === "number")
|
|
95964
|
+
return document4;
|
|
95965
|
+
const trackChanges = new TrackChanges(document4);
|
|
95966
|
+
const groups = revisionGroups(trackChanges.list());
|
|
95967
|
+
let accepts;
|
|
95968
|
+
let rejects;
|
|
95969
|
+
try {
|
|
95970
|
+
accepts = expandRevisionTargets(acceptRaw, groups);
|
|
95971
|
+
rejects = expandRevisionTargets(rejectRaw, groups);
|
|
95972
|
+
} catch (error) {
|
|
95973
|
+
if (error instanceof UnknownRevisionError) {
|
|
95974
|
+
return fail("TRACKED_CHANGE_NOT_FOUND", error.message, "Run 'docx track-changes list FILE' \u2014 revN handles appear as the `group` field on paired changes.");
|
|
95975
|
+
}
|
|
95976
|
+
throw error;
|
|
95977
|
+
}
|
|
95978
|
+
const rejectSet = new Set(rejects);
|
|
95979
|
+
const conflict = accepts.find((id) => rejectSet.has(id));
|
|
95980
|
+
if (conflict) {
|
|
95981
|
+
return fail("USAGE", `${conflict} is named in both --accept and --reject`, HELP48);
|
|
95982
|
+
}
|
|
95983
|
+
const outputPath = parsed.values.output;
|
|
95984
|
+
try {
|
|
95985
|
+
if (parsed.values["dry-run"]) {
|
|
95986
|
+
const previewApplied = [
|
|
95987
|
+
...trackChanges.preview(accepts, "accept"),
|
|
95988
|
+
...trackChanges.preview(rejects, "reject")
|
|
95989
|
+
].sort((a2, b) => trackedChangeIndex2(a2.id) - trackedChangeIndex2(b.id));
|
|
95990
|
+
await respond({
|
|
95991
|
+
operation: "track-changes.apply",
|
|
95992
|
+
dryRun: true,
|
|
95993
|
+
path: path2,
|
|
95994
|
+
...outputPath ? { output: outputPath } : {},
|
|
95995
|
+
applied: previewApplied
|
|
95996
|
+
});
|
|
95997
|
+
return EXIT2.OK;
|
|
95998
|
+
}
|
|
95999
|
+
const applied = trackChanges.apply(accepts, rejects);
|
|
96000
|
+
await document4.save(outputPath);
|
|
96001
|
+
await respondAck({
|
|
96002
|
+
ok: true,
|
|
96003
|
+
operation: "track-changes.apply",
|
|
96004
|
+
path: outputPath ?? path2,
|
|
96005
|
+
applied
|
|
96006
|
+
});
|
|
96007
|
+
if (!parsed.values.verbose) {
|
|
96008
|
+
const remaining = await remainingTrackedChangesBlock(outputPath ?? path2, "apply");
|
|
96009
|
+
if (remaining)
|
|
96010
|
+
await writeStdout(remaining);
|
|
96011
|
+
}
|
|
96012
|
+
return EXIT2.OK;
|
|
96013
|
+
} catch (error) {
|
|
96014
|
+
if (error instanceof TrackedChangeNotFoundError) {
|
|
96015
|
+
return fail("TRACKED_CHANGE_NOT_FOUND", error.message, "Run 'docx track-changes list FILE' to see available handles.");
|
|
96016
|
+
}
|
|
96017
|
+
throw error;
|
|
96018
|
+
}
|
|
96019
|
+
}
|
|
96020
|
+
function trackedChangeIndex2(id) {
|
|
96021
|
+
const match = id.match(/^tc(\d+)$/);
|
|
96022
|
+
return match?.[1] ? Number(match[1]) : 0;
|
|
96023
|
+
}
|
|
96024
|
+
var HELP48 = `docx track-changes apply \u2014 finalize: accept AND reject in one atomic call
|
|
96025
|
+
|
|
96026
|
+
Usage:
|
|
96027
|
+
docx track-changes apply FILE (--accept H ... | --reject H ...) [options]
|
|
96028
|
+
|
|
96029
|
+
A document review ends in a finalize: accept the changes you want, reject the
|
|
96030
|
+
rest. Doing that as separate \`accept\` and \`reject\` calls is a trap \u2014 tcN/revN
|
|
96031
|
+
ids renumber after every accept/reject, so the SECOND command addresses a
|
|
96032
|
+
moved target ("tc5 not found", or worse, silently the wrong change). \`apply\`
|
|
96033
|
+
takes BOTH decisions at once and resolves EVERY handle against the original
|
|
96034
|
+
pre-mutation tree, so nothing renumbers mid-operation and the file is never
|
|
96035
|
+
left half-finalized (there is no undo).
|
|
96036
|
+
|
|
96037
|
+
Handles are the same ones \`track-changes list\` prints: a tcN, or a revN that
|
|
96038
|
+
covers both halves of a del+ins replace pair. Both flags repeat.
|
|
96039
|
+
|
|
96040
|
+
Targets (at least one required):
|
|
96041
|
+
--accept H A handle (tcN or revN) to accept. Repeat for several
|
|
96042
|
+
(--accept rev0 --accept rev1 --accept tc4).
|
|
96043
|
+
--reject H A handle (tcN or revN) to reject. Repeat for several.
|
|
96044
|
+
|
|
96045
|
+
A handle may not appear in both lists. Unknown handles error before anything is
|
|
96046
|
+
written. Leftover changes you name in neither list stay tracked (apply finalizes
|
|
96047
|
+
only what you address); the confirmation re-lists them so you can see what's left.
|
|
96048
|
+
|
|
96049
|
+
Options:
|
|
96050
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
96051
|
+
--dry-run Print what would change; do not write the file
|
|
96052
|
+
-v, --verbose Print the success ack JSON (default: a one-line confirmation)
|
|
96053
|
+
-h, --help Show this help
|
|
96054
|
+
|
|
96055
|
+
Output:
|
|
96056
|
+
Prints a one-line confirmation on success (exit 0); when changes remain it
|
|
96057
|
+
also re-lists them with their renumbered handles. --verbose prints
|
|
96058
|
+
{ok:true, operation, path, applied}. --dry-run previews. Errors print
|
|
96059
|
+
{code, error, hint?} with a nonzero exit. Discover handles with
|
|
96060
|
+
\`docx track-changes list FILE\`.
|
|
96061
|
+
|
|
96062
|
+
Examples:
|
|
96063
|
+
docx track-changes apply doc.docx --accept rev0 --accept rev1 --accept tc4 \\
|
|
96064
|
+
--reject rev2 --reject tc7
|
|
96065
|
+
docx track-changes apply doc.docx --reject rev0 --dry-run
|
|
96066
|
+
`;
|
|
96067
|
+
var init_apply2 = __esm(() => {
|
|
96068
|
+
init_track_changes();
|
|
96069
|
+
init_respond();
|
|
96070
|
+
init_groups();
|
|
96071
|
+
init_list_view();
|
|
96072
|
+
});
|
|
96073
|
+
|
|
96074
|
+
// src/cli/track-changes/toggle.tsx
|
|
96075
|
+
var exports_toggle = {};
|
|
96076
|
+
__export(exports_toggle, {
|
|
96077
|
+
run: () => run53
|
|
96078
|
+
});
|
|
96079
|
+
async function run53(args) {
|
|
96080
|
+
const parsed = await tryParseArgs(args, {
|
|
96081
|
+
...SAVE_FLAGS
|
|
96082
|
+
}, HELP49);
|
|
96083
|
+
if (typeof parsed === "number")
|
|
96084
|
+
return parsed;
|
|
96085
|
+
if (parsed.values.help) {
|
|
96086
|
+
await writeStdout(HELP49);
|
|
96087
|
+
return EXIT2.OK;
|
|
96088
|
+
}
|
|
96089
|
+
setVerboseAck(Boolean(parsed.values.verbose));
|
|
95655
96090
|
const [first, second] = parsed.positionals;
|
|
95656
96091
|
const modeFirst = first === "on" || first === "off";
|
|
95657
96092
|
const mode = modeFirst ? first : second;
|
|
95658
96093
|
const path2 = modeFirst ? second : first;
|
|
95659
96094
|
if (mode !== "on" && mode !== "off") {
|
|
95660
|
-
return fail("USAGE", `Expected "on" or "off" (e.g. \`docx track-changes on FILE\`), got: ${parsed.positionals.join(" ") || "<nothing>"}`,
|
|
96095
|
+
return fail("USAGE", `Expected "on" or "off" (e.g. \`docx track-changes on FILE\`), got: ${parsed.positionals.join(" ") || "<nothing>"}`, HELP49);
|
|
95661
96096
|
}
|
|
95662
96097
|
if (!path2)
|
|
95663
|
-
return fail("USAGE", "Missing FILE argument",
|
|
96098
|
+
return fail("USAGE", "Missing FILE argument", HELP49);
|
|
95664
96099
|
const document4 = await openOrFail(path2);
|
|
95665
96100
|
if (typeof document4 === "number")
|
|
95666
96101
|
return document4;
|
|
@@ -95690,7 +96125,7 @@ async function run52(args) {
|
|
|
95690
96125
|
});
|
|
95691
96126
|
return EXIT2.OK;
|
|
95692
96127
|
}
|
|
95693
|
-
var
|
|
96128
|
+
var HELP49 = `docx track-changes \u2014 toggle the document's tracked-changes mode
|
|
95694
96129
|
|
|
95695
96130
|
Usage:
|
|
95696
96131
|
docx track-changes on|off FILE [options]
|
|
@@ -95728,16 +96163,16 @@ var init_toggle2 = __esm(() => {
|
|
|
95728
96163
|
// src/cli/track-changes/index.ts
|
|
95729
96164
|
var exports_track_changes = {};
|
|
95730
96165
|
__export(exports_track_changes, {
|
|
95731
|
-
run: () =>
|
|
96166
|
+
run: () => run54
|
|
95732
96167
|
});
|
|
95733
|
-
async function
|
|
96168
|
+
async function run54(args) {
|
|
95734
96169
|
const first = args[0];
|
|
95735
96170
|
if (first === "--help" || first === "-h" || first === "help") {
|
|
95736
|
-
await writeStdout(
|
|
96171
|
+
await writeStdout(HELP50);
|
|
95737
96172
|
return 0;
|
|
95738
96173
|
}
|
|
95739
96174
|
if (!first) {
|
|
95740
|
-
return fail("USAGE", "Missing arguments",
|
|
96175
|
+
return fail("USAGE", "Missing arguments", HELP50);
|
|
95741
96176
|
}
|
|
95742
96177
|
if (first === "list") {
|
|
95743
96178
|
const module_2 = await Promise.resolve().then(() => (init_list8(), exports_list5));
|
|
@@ -95751,16 +96186,21 @@ async function run53(args) {
|
|
|
95751
96186
|
const module_2 = await Promise.resolve().then(() => (init_reject(), exports_reject));
|
|
95752
96187
|
return module_2.run(args.slice(1));
|
|
95753
96188
|
}
|
|
96189
|
+
if (first === "apply") {
|
|
96190
|
+
const module_2 = await Promise.resolve().then(() => (init_apply2(), exports_apply));
|
|
96191
|
+
return module_2.run(args.slice(1));
|
|
96192
|
+
}
|
|
95754
96193
|
const module_ = await Promise.resolve().then(() => (init_toggle2(), exports_toggle));
|
|
95755
96194
|
return module_.run(args);
|
|
95756
96195
|
}
|
|
95757
|
-
var
|
|
96196
|
+
var HELP50 = `docx track-changes \u2014 manage tracked-changes
|
|
95758
96197
|
|
|
95759
96198
|
Usage:
|
|
95760
96199
|
docx track-changes on|off FILE [options]
|
|
95761
96200
|
docx track-changes list FILE [options]
|
|
95762
96201
|
docx track-changes accept FILE (--at tcN | --all) [options]
|
|
95763
96202
|
docx track-changes reject FILE (--at tcN | --all) [options]
|
|
96203
|
+
docx track-changes apply FILE (--accept H ... | --reject H ...) [options]
|
|
95764
96204
|
|
|
95765
96205
|
Verbs:
|
|
95766
96206
|
on Set <w:trackChanges/> in word/settings.xml
|
|
@@ -95775,9 +96215,14 @@ Verbs:
|
|
|
95775
96215
|
paragraph-mark ins the entire paragraph is removed); del/moveFrom
|
|
95776
96216
|
unwrap (with <w:delText> \u2192 <w:t> rename); sectPrChange restores
|
|
95777
96217
|
its snapshot
|
|
96218
|
+
apply Finalize: accept AND reject in ONE atomic call, every handle
|
|
96219
|
+
resolved against the original tree \u2014 the safe way to apply a
|
|
96220
|
+
review, since separate accept/reject calls renumber ids between
|
|
96221
|
+
them
|
|
95778
96222
|
|
|
95779
96223
|
Exact-change addressing is always --at tcN (repeatable); --all targets every
|
|
95780
|
-
change. Discover ids with
|
|
96224
|
+
change; \`apply\` takes --accept/--reject handle lists. Discover ids with
|
|
96225
|
+
"docx track-changes list FILE".
|
|
95781
96226
|
|
|
95782
96227
|
When tracking is on, the SUBSEQUENT insert/edit/delete/replace commands emit
|
|
95783
96228
|
<w:ins>/<w:del> markers (attributed via --author or $DOCX_AUTHOR on those
|
|
@@ -95894,29 +96339,29 @@ var init_count = __esm(() => {
|
|
|
95894
96339
|
// src/cli/wc/index.ts
|
|
95895
96340
|
var exports_wc = {};
|
|
95896
96341
|
__export(exports_wc, {
|
|
95897
|
-
run: () =>
|
|
96342
|
+
run: () => run55
|
|
95898
96343
|
});
|
|
95899
|
-
async function
|
|
96344
|
+
async function run55(args) {
|
|
95900
96345
|
const parsed = await tryParseArgs(args, {
|
|
95901
96346
|
accepted: { type: "boolean" },
|
|
95902
96347
|
baseline: { type: "boolean" },
|
|
95903
96348
|
current: { type: "boolean" },
|
|
95904
96349
|
json: { type: "boolean" },
|
|
95905
96350
|
help: { type: "boolean", short: "h" }
|
|
95906
|
-
},
|
|
96351
|
+
}, HELP51);
|
|
95907
96352
|
if (typeof parsed === "number")
|
|
95908
96353
|
return parsed;
|
|
95909
96354
|
if (parsed.values.help) {
|
|
95910
|
-
await writeStdout(
|
|
96355
|
+
await writeStdout(HELP51);
|
|
95911
96356
|
return EXIT2.OK;
|
|
95912
96357
|
}
|
|
95913
96358
|
const path2 = parsed.positionals[0];
|
|
95914
96359
|
const locatorInput = parsed.positionals[1];
|
|
95915
96360
|
if (!path2)
|
|
95916
|
-
return fail("USAGE", "Missing FILE argument",
|
|
96361
|
+
return fail("USAGE", "Missing FILE argument", HELP51);
|
|
95917
96362
|
const view = resolveView(parsed.values);
|
|
95918
96363
|
if (!view) {
|
|
95919
|
-
return fail("USAGE", "--accepted, --baseline, and --current are mutually exclusive",
|
|
96364
|
+
return fail("USAGE", "--accepted, --baseline, and --current are mutually exclusive", HELP51);
|
|
95920
96365
|
}
|
|
95921
96366
|
const json2 = Boolean(parsed.values.json);
|
|
95922
96367
|
const pickText = paragraphTextFor(view);
|
|
@@ -96094,13 +96539,13 @@ function paragraphTextFor(view) {
|
|
|
96094
96539
|
return paragraphTextBaseline;
|
|
96095
96540
|
return paragraphText2;
|
|
96096
96541
|
}
|
|
96097
|
-
var
|
|
96542
|
+
var HELP51;
|
|
96098
96543
|
var init_wc = __esm(() => {
|
|
96099
96544
|
init_core2();
|
|
96100
96545
|
init_parse_helpers();
|
|
96101
96546
|
init_respond();
|
|
96102
96547
|
init_count();
|
|
96103
|
-
|
|
96548
|
+
HELP51 = `docx wc \u2014 count words in a document or a locator-addressed slice
|
|
96104
96549
|
|
|
96105
96550
|
Usage:
|
|
96106
96551
|
docx wc FILE [LOCATOR] [options]
|
|
@@ -96164,7 +96609,7 @@ Examples:
|
|
|
96164
96609
|
// package.json
|
|
96165
96610
|
var package_default = {
|
|
96166
96611
|
name: "bun-docx",
|
|
96167
|
-
version: "0.
|
|
96612
|
+
version: "0.17.0",
|
|
96168
96613
|
description: "CLI for AI agents (Claude, Codex) to read, edit, and comment on .docx files with full format fidelity.",
|
|
96169
96614
|
keywords: [
|
|
96170
96615
|
"docx",
|