bun-docx 0.1.1 → 0.3.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 +27 -26
- package/dist/index.js +2370 -1477
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -47,57 +47,6 @@ var __export = (target, all) => {
|
|
|
47
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
48
|
var __require = import.meta.require;
|
|
49
49
|
|
|
50
|
-
// src/cli/respond.ts
|
|
51
|
-
async function respond(payload) {
|
|
52
|
-
await Bun.write(Bun.stdout, `${JSON.stringify(payload)}
|
|
53
|
-
`);
|
|
54
|
-
}
|
|
55
|
-
async function writeStdout(text) {
|
|
56
|
-
await Bun.write(Bun.stdout, text);
|
|
57
|
-
}
|
|
58
|
-
async function fail(code, message, hint) {
|
|
59
|
-
const payload = {
|
|
60
|
-
ok: false,
|
|
61
|
-
code,
|
|
62
|
-
error: message
|
|
63
|
-
};
|
|
64
|
-
if (hint)
|
|
65
|
-
payload.hint = hint;
|
|
66
|
-
await respond(payload);
|
|
67
|
-
return exitCodeFor(code);
|
|
68
|
-
}
|
|
69
|
-
function exitCodeFor(code) {
|
|
70
|
-
switch (code) {
|
|
71
|
-
case "USAGE":
|
|
72
|
-
case "INVALID_LOCATOR":
|
|
73
|
-
return EXIT.USAGE_ERROR;
|
|
74
|
-
case "FILE_NOT_FOUND":
|
|
75
|
-
case "PART_NOT_FOUND":
|
|
76
|
-
case "BLOCK_NOT_FOUND":
|
|
77
|
-
case "COMMENT_NOT_FOUND":
|
|
78
|
-
case "IMAGE_NOT_FOUND":
|
|
79
|
-
return EXIT.NOT_FOUND;
|
|
80
|
-
case "PERMISSION_DENIED":
|
|
81
|
-
return EXIT.PERMISSION_DENIED;
|
|
82
|
-
case "ALREADY_APPLIED":
|
|
83
|
-
return EXIT.ALREADY_APPLIED;
|
|
84
|
-
case "NOT_A_ZIP":
|
|
85
|
-
case "UNHANDLED":
|
|
86
|
-
return EXIT.GENERAL_ERROR;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
var EXIT;
|
|
90
|
-
var init_respond = __esm(() => {
|
|
91
|
-
EXIT = {
|
|
92
|
-
OK: 0,
|
|
93
|
-
GENERAL_ERROR: 1,
|
|
94
|
-
USAGE_ERROR: 2,
|
|
95
|
-
NOT_FOUND: 3,
|
|
96
|
-
PERMISSION_DENIED: 4,
|
|
97
|
-
ALREADY_APPLIED: 5
|
|
98
|
-
};
|
|
99
|
-
});
|
|
100
|
-
|
|
101
50
|
// node_modules/process-nextick-args/index.js
|
|
102
51
|
var require_process_nextick_args = __commonJS((exports, module) => {
|
|
103
52
|
if (typeof process === "undefined" || !process.version || process.version.indexOf("v0.") === 0 || process.version.indexOf("v1.") === 0 && process.version.indexOf("v1.8.") !== 0) {
|
|
@@ -9177,92 +9126,6 @@ var require_lib3 = __commonJS((exports, module) => {
|
|
|
9177
9126
|
module.exports = JSZip;
|
|
9178
9127
|
});
|
|
9179
9128
|
|
|
9180
|
-
// src/core/package/index.ts
|
|
9181
|
-
class Pkg {
|
|
9182
|
-
zip;
|
|
9183
|
-
path;
|
|
9184
|
-
constructor(zip, path) {
|
|
9185
|
-
this.zip = zip;
|
|
9186
|
-
this.path = path;
|
|
9187
|
-
}
|
|
9188
|
-
static async open(path) {
|
|
9189
|
-
const file = Bun.file(path);
|
|
9190
|
-
if (!await file.exists()) {
|
|
9191
|
-
throw new PkgError("FILE_NOT_FOUND", `File not found: ${path}`);
|
|
9192
|
-
}
|
|
9193
|
-
const buf = await file.arrayBuffer();
|
|
9194
|
-
let zip;
|
|
9195
|
-
try {
|
|
9196
|
-
zip = await import_jszip.default.loadAsync(buf);
|
|
9197
|
-
} catch (err) {
|
|
9198
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
9199
|
-
throw new PkgError("NOT_A_ZIP", `Not a valid .docx (zip): ${reason}`);
|
|
9200
|
-
}
|
|
9201
|
-
return new Pkg(zip, path);
|
|
9202
|
-
}
|
|
9203
|
-
hasPart(name) {
|
|
9204
|
-
return this.zip.file(name) !== null;
|
|
9205
|
-
}
|
|
9206
|
-
listParts() {
|
|
9207
|
-
const names = [];
|
|
9208
|
-
this.zip.forEach((p) => {
|
|
9209
|
-
names.push(p);
|
|
9210
|
-
});
|
|
9211
|
-
return names;
|
|
9212
|
-
}
|
|
9213
|
-
async readText(name) {
|
|
9214
|
-
const entry = this.zip.file(name);
|
|
9215
|
-
if (!entry) {
|
|
9216
|
-
throw new PkgError("PART_NOT_FOUND", `Part not found: ${name}`);
|
|
9217
|
-
}
|
|
9218
|
-
return entry.async("string");
|
|
9219
|
-
}
|
|
9220
|
-
async readBytes(name) {
|
|
9221
|
-
const entry = this.zip.file(name);
|
|
9222
|
-
if (!entry) {
|
|
9223
|
-
throw new PkgError("PART_NOT_FOUND", `Part not found: ${name}`);
|
|
9224
|
-
}
|
|
9225
|
-
return entry.async("uint8array");
|
|
9226
|
-
}
|
|
9227
|
-
writeText(name, content) {
|
|
9228
|
-
this.zip.file(name, content);
|
|
9229
|
-
}
|
|
9230
|
-
writeBytes(name, content) {
|
|
9231
|
-
this.zip.file(name, content);
|
|
9232
|
-
}
|
|
9233
|
-
deletePart(name) {
|
|
9234
|
-
this.zip.remove(name);
|
|
9235
|
-
}
|
|
9236
|
-
async save(path) {
|
|
9237
|
-
const target = path ?? this.path;
|
|
9238
|
-
const buf = await this.zip.generateAsync({
|
|
9239
|
-
type: "uint8array",
|
|
9240
|
-
compression: "DEFLATE",
|
|
9241
|
-
compressionOptions: { level: 6 }
|
|
9242
|
-
});
|
|
9243
|
-
await Bun.write(target, buf);
|
|
9244
|
-
}
|
|
9245
|
-
async toBytes() {
|
|
9246
|
-
return this.zip.generateAsync({
|
|
9247
|
-
type: "uint8array",
|
|
9248
|
-
compression: "DEFLATE",
|
|
9249
|
-
compressionOptions: { level: 6 }
|
|
9250
|
-
});
|
|
9251
|
-
}
|
|
9252
|
-
}
|
|
9253
|
-
var import_jszip, PkgError;
|
|
9254
|
-
var init_package = __esm(() => {
|
|
9255
|
-
import_jszip = __toESM(require_lib3(), 1);
|
|
9256
|
-
PkgError = class PkgError extends Error {
|
|
9257
|
-
code;
|
|
9258
|
-
constructor(code, message) {
|
|
9259
|
-
super(message);
|
|
9260
|
-
this.code = code;
|
|
9261
|
-
this.name = "PkgError";
|
|
9262
|
-
}
|
|
9263
|
-
};
|
|
9264
|
-
});
|
|
9265
|
-
|
|
9266
9129
|
// node_modules/path-expression-matcher/src/Expression.js
|
|
9267
9130
|
class Expression {
|
|
9268
9131
|
constructor(pattern, options = {}, data) {
|
|
@@ -13559,10 +13422,8 @@ class XmlNode2 {
|
|
|
13559
13422
|
}
|
|
13560
13423
|
static serialize(tree) {
|
|
13561
13424
|
const builder = new Builder(BUILD_OPTIONS);
|
|
13562
|
-
const flat = [];
|
|
13563
|
-
flattenFragments(tree, flat);
|
|
13564
13425
|
const pojo = [];
|
|
13565
|
-
for (const node of
|
|
13426
|
+
for (const node of tree) {
|
|
13566
13427
|
pojo.push(node.toObject());
|
|
13567
13428
|
}
|
|
13568
13429
|
return builder.build(pojo);
|
|
@@ -13653,6 +13514,15 @@ class XmlNode2 {
|
|
|
13653
13514
|
}
|
|
13654
13515
|
return out;
|
|
13655
13516
|
}
|
|
13517
|
+
clone() {
|
|
13518
|
+
const cloned = new XmlNode2(this.tag, { ...this.attributes });
|
|
13519
|
+
if (this.text !== undefined)
|
|
13520
|
+
cloned.text = this.text;
|
|
13521
|
+
for (const child of this.children) {
|
|
13522
|
+
cloned.children.push(child.clone());
|
|
13523
|
+
}
|
|
13524
|
+
return cloned;
|
|
13525
|
+
}
|
|
13656
13526
|
toObject() {
|
|
13657
13527
|
if (this.isText) {
|
|
13658
13528
|
return { "#text": this.text ?? "" };
|
|
@@ -13676,16 +13546,7 @@ class XmlNode2 {
|
|
|
13676
13546
|
return result;
|
|
13677
13547
|
}
|
|
13678
13548
|
}
|
|
13679
|
-
|
|
13680
|
-
for (const node of tree) {
|
|
13681
|
-
if (node.tag === FRAGMENT_TAG) {
|
|
13682
|
-
flattenFragments(node.children, out);
|
|
13683
|
-
continue;
|
|
13684
|
-
}
|
|
13685
|
-
out.push(node);
|
|
13686
|
-
}
|
|
13687
|
-
}
|
|
13688
|
-
var PARSE_OPTIONS, BUILD_OPTIONS, FRAGMENT_TAG = "#fragment";
|
|
13549
|
+
var PARSE_OPTIONS, BUILD_OPTIONS;
|
|
13689
13550
|
var init_xml_node = __esm(() => {
|
|
13690
13551
|
init_fxb();
|
|
13691
13552
|
init_fxp();
|
|
@@ -13708,66 +13569,252 @@ var init_xml_node = __esm(() => {
|
|
|
13708
13569
|
};
|
|
13709
13570
|
});
|
|
13710
13571
|
|
|
13572
|
+
// src/core/parser/run-ops.ts
|
|
13573
|
+
function runTextLength(run) {
|
|
13574
|
+
let total = 0;
|
|
13575
|
+
for (const child of run.children) {
|
|
13576
|
+
if (child.tag === "w:t")
|
|
13577
|
+
total += child.collectText().length;
|
|
13578
|
+
}
|
|
13579
|
+
return total;
|
|
13580
|
+
}
|
|
13581
|
+
function sliceRun(run, start, end) {
|
|
13582
|
+
const sliced = new XmlNode2("w:r", { ...run.attributes });
|
|
13583
|
+
let consumed = 0;
|
|
13584
|
+
for (const child of run.children) {
|
|
13585
|
+
if (child.tag === "w:t") {
|
|
13586
|
+
const text = child.collectText();
|
|
13587
|
+
const localStart = Math.max(0, start - consumed);
|
|
13588
|
+
const localEnd = Math.min(text.length, end - consumed);
|
|
13589
|
+
if (localStart < localEnd) {
|
|
13590
|
+
const slicedText = new XmlNode2("w:t", { "xml:space": "preserve" });
|
|
13591
|
+
slicedText.children.push(XmlNode2.textNode(text.slice(localStart, localEnd)));
|
|
13592
|
+
sliced.children.push(slicedText);
|
|
13593
|
+
}
|
|
13594
|
+
consumed += text.length;
|
|
13595
|
+
continue;
|
|
13596
|
+
}
|
|
13597
|
+
sliced.children.push(child.clone());
|
|
13598
|
+
}
|
|
13599
|
+
return sliced;
|
|
13600
|
+
}
|
|
13601
|
+
var init_run_ops = __esm(() => {
|
|
13602
|
+
init_xml_node();
|
|
13603
|
+
});
|
|
13604
|
+
|
|
13711
13605
|
// src/core/parser/index.ts
|
|
13712
13606
|
var init_parser = __esm(() => {
|
|
13607
|
+
init_run_ops();
|
|
13713
13608
|
init_xml_node();
|
|
13714
13609
|
});
|
|
13715
13610
|
|
|
13716
|
-
// src/core/
|
|
13717
|
-
function
|
|
13718
|
-
|
|
13719
|
-
const
|
|
13720
|
-
|
|
13721
|
-
if (!documentRoot) {
|
|
13722
|
-
throw new Error("Invalid .docx: missing <w:document>");
|
|
13723
|
-
}
|
|
13724
|
-
const body = documentRoot.findChild("w:body");
|
|
13725
|
-
if (!body) {
|
|
13726
|
-
throw new Error("Invalid .docx: missing <w:body>");
|
|
13727
|
-
}
|
|
13728
|
-
const state = {
|
|
13729
|
-
imageIndex: 0,
|
|
13730
|
-
commentAnchors: new Map,
|
|
13731
|
-
openComments: new Map
|
|
13732
|
-
};
|
|
13733
|
-
const blocks = readBlocks(view, body, state);
|
|
13734
|
-
const comments = readComments(view, state.commentAnchors);
|
|
13735
|
-
return { schemaVersion: 1, path, properties, blocks, comments };
|
|
13736
|
-
}
|
|
13737
|
-
function readBlocks(view, body, state) {
|
|
13738
|
-
const blocks = [];
|
|
13739
|
-
let paragraphIndex = 0;
|
|
13740
|
-
let tableIndex = 0;
|
|
13741
|
-
let sectionIndex = 0;
|
|
13742
|
-
for (const child of body.children) {
|
|
13743
|
-
if (child.tag === "w:p") {
|
|
13744
|
-
const id = `p${paragraphIndex++}`;
|
|
13745
|
-
blocks.push(readParagraph(view, child, id, state));
|
|
13746
|
-
view.blockReferences.set(id, { node: child, parent: body.children });
|
|
13611
|
+
// src/core/package/parts.ts
|
|
13612
|
+
function nextRelationshipId(relationships) {
|
|
13613
|
+
let highest = 0;
|
|
13614
|
+
for (const child of relationships.children) {
|
|
13615
|
+
if (child.tag !== "Relationship")
|
|
13747
13616
|
continue;
|
|
13748
|
-
|
|
13749
|
-
if (
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13617
|
+
const id = child.getAttribute("Id");
|
|
13618
|
+
if (!id)
|
|
13619
|
+
continue;
|
|
13620
|
+
const match = id.match(/^rId(\d+)$/);
|
|
13621
|
+
if (!match)
|
|
13753
13622
|
continue;
|
|
13623
|
+
const numeric = Number(match[1]);
|
|
13624
|
+
if (Number.isFinite(numeric) && numeric > highest)
|
|
13625
|
+
highest = numeric;
|
|
13626
|
+
}
|
|
13627
|
+
return `rId${highest + 1}`;
|
|
13628
|
+
}
|
|
13629
|
+
function registerPart(relationshipsTree, contentTypesTree, part) {
|
|
13630
|
+
const relationships = XmlNode2.findRoot(relationshipsTree, "Relationships");
|
|
13631
|
+
if (relationships) {
|
|
13632
|
+
const alreadyLinked = relationships.children.some((child) => child.tag === "Relationship" && child.getAttribute("Type") === part.relationshipType);
|
|
13633
|
+
if (!alreadyLinked) {
|
|
13634
|
+
relationships.children.push(new XmlNode2("Relationship", {
|
|
13635
|
+
Id: nextRelationshipId(relationships),
|
|
13636
|
+
Type: part.relationshipType,
|
|
13637
|
+
Target: part.target
|
|
13638
|
+
}));
|
|
13754
13639
|
}
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
|
|
13640
|
+
}
|
|
13641
|
+
const types = XmlNode2.findRoot(contentTypesTree, "Types");
|
|
13642
|
+
if (types) {
|
|
13643
|
+
const overrideExists = types.children.some((child) => child.tag === "Override" && child.getAttribute("PartName") === `/${part.partName}`);
|
|
13644
|
+
if (!overrideExists) {
|
|
13645
|
+
types.children.push(new XmlNode2("Override", {
|
|
13646
|
+
PartName: `/${part.partName}`,
|
|
13647
|
+
ContentType: part.contentType
|
|
13648
|
+
}));
|
|
13759
13649
|
}
|
|
13760
13650
|
}
|
|
13761
|
-
return blocks;
|
|
13762
13651
|
}
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13652
|
+
var init_parts = __esm(() => {
|
|
13653
|
+
init_parser();
|
|
13654
|
+
});
|
|
13655
|
+
|
|
13656
|
+
// src/core/package/index.ts
|
|
13657
|
+
import { lstat, rename, unlink } from "fs/promises";
|
|
13658
|
+
async function writeAtomic(target, buf) {
|
|
13659
|
+
let isSymlink = false;
|
|
13660
|
+
try {
|
|
13661
|
+
isSymlink = (await lstat(target)).isSymbolicLink();
|
|
13662
|
+
} catch {}
|
|
13663
|
+
if (isSymlink) {
|
|
13664
|
+
await Bun.write(target, buf);
|
|
13665
|
+
return;
|
|
13768
13666
|
}
|
|
13769
|
-
const
|
|
13770
|
-
|
|
13667
|
+
const tmp = `${target}.docx-cli-tmp-${process.pid}-${Date.now()}`;
|
|
13668
|
+
try {
|
|
13669
|
+
await Bun.write(tmp, buf);
|
|
13670
|
+
await rename(tmp, target);
|
|
13671
|
+
} catch (err) {
|
|
13672
|
+
await unlink(tmp).catch(() => {});
|
|
13673
|
+
throw err;
|
|
13674
|
+
}
|
|
13675
|
+
}
|
|
13676
|
+
|
|
13677
|
+
class Pkg {
|
|
13678
|
+
zip;
|
|
13679
|
+
path;
|
|
13680
|
+
constructor(zip, path) {
|
|
13681
|
+
this.zip = zip;
|
|
13682
|
+
this.path = path;
|
|
13683
|
+
}
|
|
13684
|
+
static async open(path) {
|
|
13685
|
+
const file = Bun.file(path);
|
|
13686
|
+
if (!await file.exists()) {
|
|
13687
|
+
throw new PkgError("FILE_NOT_FOUND", `File not found: ${path}`);
|
|
13688
|
+
}
|
|
13689
|
+
const buf = await file.arrayBuffer();
|
|
13690
|
+
let zip;
|
|
13691
|
+
try {
|
|
13692
|
+
zip = await import_jszip.default.loadAsync(buf);
|
|
13693
|
+
} catch (err) {
|
|
13694
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
13695
|
+
throw new PkgError("NOT_A_ZIP", `Not a valid .docx (zip): ${reason}`);
|
|
13696
|
+
}
|
|
13697
|
+
return new Pkg(zip, path);
|
|
13698
|
+
}
|
|
13699
|
+
hasPart(name) {
|
|
13700
|
+
return this.zip.file(name) !== null;
|
|
13701
|
+
}
|
|
13702
|
+
listParts() {
|
|
13703
|
+
const names = [];
|
|
13704
|
+
this.zip.forEach((p) => {
|
|
13705
|
+
names.push(p);
|
|
13706
|
+
});
|
|
13707
|
+
return names;
|
|
13708
|
+
}
|
|
13709
|
+
async readText(name) {
|
|
13710
|
+
const entry = this.zip.file(name);
|
|
13711
|
+
if (!entry) {
|
|
13712
|
+
throw new PkgError("PART_NOT_FOUND", `Part not found: ${name}`);
|
|
13713
|
+
}
|
|
13714
|
+
return entry.async("string");
|
|
13715
|
+
}
|
|
13716
|
+
async readBytes(name) {
|
|
13717
|
+
const entry = this.zip.file(name);
|
|
13718
|
+
if (!entry) {
|
|
13719
|
+
throw new PkgError("PART_NOT_FOUND", `Part not found: ${name}`);
|
|
13720
|
+
}
|
|
13721
|
+
return entry.async("uint8array");
|
|
13722
|
+
}
|
|
13723
|
+
writeText(name, content) {
|
|
13724
|
+
this.zip.file(name, content);
|
|
13725
|
+
}
|
|
13726
|
+
writeBytes(name, content) {
|
|
13727
|
+
this.zip.file(name, content);
|
|
13728
|
+
}
|
|
13729
|
+
deletePart(name) {
|
|
13730
|
+
this.zip.remove(name);
|
|
13731
|
+
}
|
|
13732
|
+
async save(path) {
|
|
13733
|
+
const target = path ?? this.path;
|
|
13734
|
+
const buf = await this.zip.generateAsync({
|
|
13735
|
+
type: "uint8array",
|
|
13736
|
+
compression: "DEFLATE",
|
|
13737
|
+
compressionOptions: { level: 6 }
|
|
13738
|
+
});
|
|
13739
|
+
await writeAtomic(target, buf);
|
|
13740
|
+
}
|
|
13741
|
+
async toBytes() {
|
|
13742
|
+
return this.zip.generateAsync({
|
|
13743
|
+
type: "uint8array",
|
|
13744
|
+
compression: "DEFLATE",
|
|
13745
|
+
compressionOptions: { level: 6 }
|
|
13746
|
+
});
|
|
13747
|
+
}
|
|
13748
|
+
}
|
|
13749
|
+
var import_jszip, PkgError;
|
|
13750
|
+
var init_package = __esm(() => {
|
|
13751
|
+
init_parts();
|
|
13752
|
+
import_jszip = __toESM(require_lib3(), 1);
|
|
13753
|
+
PkgError = class PkgError extends Error {
|
|
13754
|
+
code;
|
|
13755
|
+
constructor(code, message) {
|
|
13756
|
+
super(message);
|
|
13757
|
+
this.code = code;
|
|
13758
|
+
this.name = "PkgError";
|
|
13759
|
+
}
|
|
13760
|
+
};
|
|
13761
|
+
});
|
|
13762
|
+
|
|
13763
|
+
// src/core/ast/read.ts
|
|
13764
|
+
function buildDoc(view, path) {
|
|
13765
|
+
readImageRelationships(view);
|
|
13766
|
+
const properties = readDocProperties(view);
|
|
13767
|
+
const documentRoot = XmlNode2.findRoot(view.documentTree, "w:document");
|
|
13768
|
+
if (!documentRoot) {
|
|
13769
|
+
throw new Error("Invalid .docx: missing <w:document>");
|
|
13770
|
+
}
|
|
13771
|
+
const body = documentRoot.findChild("w:body");
|
|
13772
|
+
if (!body) {
|
|
13773
|
+
throw new Error("Invalid .docx: missing <w:body>");
|
|
13774
|
+
}
|
|
13775
|
+
const state = {
|
|
13776
|
+
imageIndex: 0,
|
|
13777
|
+
commentAnchors: new Map,
|
|
13778
|
+
openComments: new Map
|
|
13779
|
+
};
|
|
13780
|
+
const blocks = readBlocks(view, body, state);
|
|
13781
|
+
const comments = readComments(view, state.commentAnchors);
|
|
13782
|
+
return { schemaVersion: 1, path, properties, blocks, comments };
|
|
13783
|
+
}
|
|
13784
|
+
function readBlocks(view, body, state) {
|
|
13785
|
+
const blocks = [];
|
|
13786
|
+
let paragraphIndex = 0;
|
|
13787
|
+
let tableIndex = 0;
|
|
13788
|
+
let sectionIndex = 0;
|
|
13789
|
+
for (const child of body.children) {
|
|
13790
|
+
if (child.tag === "w:p") {
|
|
13791
|
+
const id = `p${paragraphIndex++}`;
|
|
13792
|
+
blocks.push(readParagraph(view, child, id, state));
|
|
13793
|
+
view.blockReferences.set(id, { node: child, parent: body.children });
|
|
13794
|
+
continue;
|
|
13795
|
+
}
|
|
13796
|
+
if (child.tag === "w:tbl") {
|
|
13797
|
+
const id = `t${tableIndex++}`;
|
|
13798
|
+
blocks.push(readTable(view, child, id, state));
|
|
13799
|
+
view.blockReferences.set(id, { node: child, parent: body.children });
|
|
13800
|
+
continue;
|
|
13801
|
+
}
|
|
13802
|
+
if (child.tag === "w:sectPr") {
|
|
13803
|
+
const id = `s${sectionIndex++}`;
|
|
13804
|
+
blocks.push({ id, type: "sectionBreak" });
|
|
13805
|
+
view.blockReferences.set(id, { node: child, parent: body.children });
|
|
13806
|
+
}
|
|
13807
|
+
}
|
|
13808
|
+
return blocks;
|
|
13809
|
+
}
|
|
13810
|
+
function readParagraph(view, node, id, state) {
|
|
13811
|
+
const paragraph = { id, type: "paragraph", runs: [] };
|
|
13812
|
+
const paragraphProperties = node.findChild("w:pPr");
|
|
13813
|
+
if (paragraphProperties) {
|
|
13814
|
+
applyParagraphProperties(paragraph, paragraphProperties);
|
|
13815
|
+
}
|
|
13816
|
+
const activeComments = new Set;
|
|
13817
|
+
let offset = 0;
|
|
13771
13818
|
for (const child of node.children) {
|
|
13772
13819
|
if (child.tag === "w:pPr")
|
|
13773
13820
|
continue;
|
|
@@ -13815,6 +13862,33 @@ function readParagraph(view, node, id, state) {
|
|
|
13815
13862
|
revisionId: child.getAttribute("w:id") ?? ""
|
|
13816
13863
|
};
|
|
13817
13864
|
for (const inner of child.children) {
|
|
13865
|
+
if (inner.tag === "w:commentRangeStart") {
|
|
13866
|
+
const commentId = inner.getAttribute("w:id");
|
|
13867
|
+
if (commentId) {
|
|
13868
|
+
const key = `c${commentId}`;
|
|
13869
|
+
activeComments.add(key);
|
|
13870
|
+
state.openComments.set(key, { blockId: id, offset });
|
|
13871
|
+
}
|
|
13872
|
+
continue;
|
|
13873
|
+
}
|
|
13874
|
+
if (inner.tag === "w:commentRangeEnd") {
|
|
13875
|
+
const commentId = inner.getAttribute("w:id");
|
|
13876
|
+
if (commentId) {
|
|
13877
|
+
const key = `c${commentId}`;
|
|
13878
|
+
activeComments.delete(key);
|
|
13879
|
+
const opened = state.openComments.get(key);
|
|
13880
|
+
if (opened) {
|
|
13881
|
+
state.commentAnchors.set(key, {
|
|
13882
|
+
startBlockId: opened.blockId,
|
|
13883
|
+
startOffset: opened.offset,
|
|
13884
|
+
endBlockId: id,
|
|
13885
|
+
endOffset: offset
|
|
13886
|
+
});
|
|
13887
|
+
state.openComments.delete(key);
|
|
13888
|
+
}
|
|
13889
|
+
}
|
|
13890
|
+
continue;
|
|
13891
|
+
}
|
|
13818
13892
|
if (inner.tag !== "w:r")
|
|
13819
13893
|
continue;
|
|
13820
13894
|
const run = readRun(view, inner, activeComments, change, state);
|
|
@@ -13869,8 +13943,9 @@ function readRun(view, node, activeComments, trackedChange, state) {
|
|
|
13869
13943
|
}
|
|
13870
13944
|
let combinedText = "";
|
|
13871
13945
|
for (const child of node.children) {
|
|
13872
|
-
if (child.tag === "w:t")
|
|
13946
|
+
if (child.tag === "w:t" || child.tag === "w:delText") {
|
|
13873
13947
|
combinedText += child.collectText();
|
|
13948
|
+
}
|
|
13874
13949
|
}
|
|
13875
13950
|
if (combinedText.length === 0)
|
|
13876
13951
|
return null;
|
|
@@ -13954,26 +14029,33 @@ function readImageFromDrawing(view, drawing, state) {
|
|
|
13954
14029
|
}
|
|
13955
14030
|
function readTable(view, node, id, state) {
|
|
13956
14031
|
const rows = [];
|
|
14032
|
+
let rowIndex = 0;
|
|
13957
14033
|
for (const child of node.children) {
|
|
13958
14034
|
if (child.tag !== "w:tr")
|
|
13959
14035
|
continue;
|
|
13960
14036
|
const cells = [];
|
|
14037
|
+
let columnIndex = 0;
|
|
13961
14038
|
for (const cellNode of child.children) {
|
|
13962
14039
|
if (cellNode.tag !== "w:tc")
|
|
13963
14040
|
continue;
|
|
13964
|
-
cells.push({
|
|
14041
|
+
cells.push({
|
|
14042
|
+
blocks: readCellBlocks(view, cellNode, id, rowIndex, columnIndex, state)
|
|
14043
|
+
});
|
|
14044
|
+
columnIndex++;
|
|
13965
14045
|
}
|
|
13966
14046
|
rows.push({ cells });
|
|
14047
|
+
rowIndex++;
|
|
13967
14048
|
}
|
|
13968
14049
|
return { id, type: "table", rows };
|
|
13969
14050
|
}
|
|
13970
|
-
function readCellBlocks(view, cell, state) {
|
|
14051
|
+
function readCellBlocks(view, cell, tableId, rowIndex, columnIndex, state) {
|
|
13971
14052
|
const blocks = [];
|
|
13972
14053
|
let paragraphIndex = 0;
|
|
13973
14054
|
for (const child of cell.children) {
|
|
13974
14055
|
if (child.tag === "w:p") {
|
|
13975
|
-
const id =
|
|
14056
|
+
const id = `${tableId}:r${rowIndex}c${columnIndex}:p${paragraphIndex++}`;
|
|
13976
14057
|
blocks.push(readParagraph(view, child, id, state));
|
|
14058
|
+
view.blockReferences.set(id, { node: child, parent: cell.children });
|
|
13977
14059
|
}
|
|
13978
14060
|
}
|
|
13979
14061
|
return blocks;
|
|
@@ -14136,6 +14218,7 @@ async function openDocView(path) {
|
|
|
14136
14218
|
const commentsTree = pkg.hasPart("word/comments.xml") ? XmlNode2.parse(await pkg.readText("word/comments.xml")) : undefined;
|
|
14137
14219
|
const commentsExtTree = pkg.hasPart("word/commentsExtended.xml") ? XmlNode2.parse(await pkg.readText("word/commentsExtended.xml")) : undefined;
|
|
14138
14220
|
const corePropertiesTree = pkg.hasPart("docProps/core.xml") ? XmlNode2.parse(await pkg.readText("docProps/core.xml")) : undefined;
|
|
14221
|
+
const settingsTree = pkg.hasPart("word/settings.xml") ? XmlNode2.parse(await pkg.readText("word/settings.xml")) : undefined;
|
|
14139
14222
|
const view = {
|
|
14140
14223
|
pkg,
|
|
14141
14224
|
documentTree,
|
|
@@ -14144,6 +14227,7 @@ async function openDocView(path) {
|
|
|
14144
14227
|
relationshipsTree,
|
|
14145
14228
|
contentTypesTree,
|
|
14146
14229
|
corePropertiesTree,
|
|
14230
|
+
settingsTree,
|
|
14147
14231
|
doc: undefined,
|
|
14148
14232
|
blockReferences: new Map,
|
|
14149
14233
|
commentReferences: new Map,
|
|
@@ -14163,6 +14247,9 @@ async function saveDocView(view, path) {
|
|
|
14163
14247
|
if (view.commentsExtTree) {
|
|
14164
14248
|
view.pkg.writeText("word/commentsExtended.xml", XmlNode2.serialize(view.commentsExtTree));
|
|
14165
14249
|
}
|
|
14250
|
+
if (view.settingsTree) {
|
|
14251
|
+
view.pkg.writeText("word/settings.xml", XmlNode2.serialize(view.settingsTree));
|
|
14252
|
+
}
|
|
14166
14253
|
await view.pkg.save(path);
|
|
14167
14254
|
}
|
|
14168
14255
|
async function enrichImageHashes(view) {
|
|
@@ -14290,7 +14377,7 @@ var init_parse = __esm(() => {
|
|
|
14290
14377
|
this.name = "LocatorParseError";
|
|
14291
14378
|
}
|
|
14292
14379
|
};
|
|
14293
|
-
BLOCK_RE = /^(p|t|s
|
|
14380
|
+
BLOCK_RE = /^(p|t|s)(\d+)$/;
|
|
14294
14381
|
SPAN_RE = /^p(\d+):(\d+)-(\d+)$/;
|
|
14295
14382
|
RANGE_RE = /^p(\d+):(\d+)-p(\d+):(\d+)$/;
|
|
14296
14383
|
COMMENT_RE = /^c(\d+)$/;
|
|
@@ -14299,6 +14386,26 @@ var init_parse = __esm(() => {
|
|
|
14299
14386
|
});
|
|
14300
14387
|
|
|
14301
14388
|
// src/core/locators/resolve.ts
|
|
14389
|
+
function locatorToBlockTarget(locator) {
|
|
14390
|
+
if (locator.kind === "block")
|
|
14391
|
+
return { blockId: locator.blockId };
|
|
14392
|
+
if (locator.kind === "blockSpan") {
|
|
14393
|
+
return {
|
|
14394
|
+
blockId: locator.blockId,
|
|
14395
|
+
span: { start: locator.start, end: locator.end }
|
|
14396
|
+
};
|
|
14397
|
+
}
|
|
14398
|
+
if (locator.kind === "cell" && locator.inner) {
|
|
14399
|
+
const inner = locatorToBlockTarget(locator.inner);
|
|
14400
|
+
if (!inner)
|
|
14401
|
+
return null;
|
|
14402
|
+
return {
|
|
14403
|
+
blockId: `${locator.tableId}:r${locator.row}c${locator.col}:${inner.blockId}`,
|
|
14404
|
+
span: inner.span
|
|
14405
|
+
};
|
|
14406
|
+
}
|
|
14407
|
+
return null;
|
|
14408
|
+
}
|
|
14302
14409
|
function resolveBlock(view, blockId) {
|
|
14303
14410
|
const reference = view.blockReferences.get(blockId);
|
|
14304
14411
|
if (!reference) {
|
|
@@ -14324,15 +14431,82 @@ var init_locators = __esm(() => {
|
|
|
14324
14431
|
init_resolve();
|
|
14325
14432
|
});
|
|
14326
14433
|
|
|
14327
|
-
// src/core/index.ts
|
|
14328
|
-
|
|
14329
|
-
|
|
14330
|
-
|
|
14331
|
-
|
|
14434
|
+
// src/core/track-changes/index.ts
|
|
14435
|
+
function isTrackChangesEnabled(view) {
|
|
14436
|
+
if (!view.settingsTree)
|
|
14437
|
+
return false;
|
|
14438
|
+
const settingsRoot = XmlNode2.findRoot(view.settingsTree, "w:settings");
|
|
14439
|
+
if (!settingsRoot)
|
|
14440
|
+
return false;
|
|
14441
|
+
return settingsRoot.children.some((child) => child.tag === "w:trackChanges");
|
|
14442
|
+
}
|
|
14443
|
+
function createRevisionAllocator(view) {
|
|
14444
|
+
let nextId = computeMaxRevisionId(view) + 1;
|
|
14445
|
+
return {
|
|
14446
|
+
next() {
|
|
14447
|
+
const id = nextId;
|
|
14448
|
+
nextId += 1;
|
|
14449
|
+
return id;
|
|
14450
|
+
}
|
|
14451
|
+
};
|
|
14452
|
+
}
|
|
14453
|
+
function resolveAuthor(authorFlag) {
|
|
14454
|
+
if (authorFlag)
|
|
14455
|
+
return authorFlag;
|
|
14456
|
+
if (Bun.env.DOCX_AUTHOR)
|
|
14457
|
+
return Bun.env.DOCX_AUTHOR;
|
|
14458
|
+
if (Bun.env.DOCX_CLI_AUTHOR)
|
|
14459
|
+
return Bun.env.DOCX_CLI_AUTHOR;
|
|
14460
|
+
return "docx-cli";
|
|
14461
|
+
}
|
|
14462
|
+
function resolveDate() {
|
|
14463
|
+
return Bun.env.DOCX_CLI_NOW ?? new Date().toISOString();
|
|
14464
|
+
}
|
|
14465
|
+
function convertTextToDelText(node) {
|
|
14466
|
+
const cloned = node.clone();
|
|
14467
|
+
mutateTextToDelText([cloned]);
|
|
14468
|
+
return cloned;
|
|
14469
|
+
}
|
|
14470
|
+
function computeMaxRevisionId(view) {
|
|
14471
|
+
let max = -1;
|
|
14472
|
+
walkXml(view.documentTree, (node) => {
|
|
14473
|
+
if (node.tag !== "w:ins" && node.tag !== "w:del")
|
|
14474
|
+
return;
|
|
14475
|
+
const idAttr = node.getAttribute("w:id");
|
|
14476
|
+
if (!idAttr)
|
|
14477
|
+
return;
|
|
14478
|
+
const value = Number(idAttr);
|
|
14479
|
+
if (Number.isFinite(value) && value > max)
|
|
14480
|
+
max = value;
|
|
14481
|
+
});
|
|
14482
|
+
return max;
|
|
14483
|
+
}
|
|
14484
|
+
function walkXml(nodes, visit) {
|
|
14485
|
+
for (const node of nodes) {
|
|
14486
|
+
visit(node);
|
|
14487
|
+
if (node.children.length > 0)
|
|
14488
|
+
walkXml(node.children, visit);
|
|
14489
|
+
}
|
|
14490
|
+
}
|
|
14491
|
+
function mutateTextToDelText(nodes) {
|
|
14492
|
+
for (const node of nodes) {
|
|
14493
|
+
if (node.tag === "w:t")
|
|
14494
|
+
node.tag = "w:delText";
|
|
14495
|
+
mutateTextToDelText(node.children);
|
|
14496
|
+
}
|
|
14497
|
+
}
|
|
14498
|
+
var init_track_changes = __esm(() => {
|
|
14332
14499
|
init_parser();
|
|
14333
14500
|
});
|
|
14334
14501
|
|
|
14335
14502
|
// src/core/jsx/index.ts
|
|
14503
|
+
function normalizeChildren(children) {
|
|
14504
|
+
if (children === undefined || children === null)
|
|
14505
|
+
return [];
|
|
14506
|
+
if (Array.isArray(children))
|
|
14507
|
+
return children;
|
|
14508
|
+
return [children];
|
|
14509
|
+
}
|
|
14336
14510
|
function namespace(prefix, tags) {
|
|
14337
14511
|
const result = {};
|
|
14338
14512
|
for (const tagName of tags) {
|
|
@@ -14341,19 +14515,22 @@ function namespace(prefix, tags) {
|
|
|
14341
14515
|
return result;
|
|
14342
14516
|
}
|
|
14343
14517
|
function makeTag(qualifiedName) {
|
|
14344
|
-
return (props
|
|
14518
|
+
return (props) => {
|
|
14345
14519
|
const attributes = {};
|
|
14520
|
+
let childrenProp;
|
|
14346
14521
|
if (props) {
|
|
14347
14522
|
for (const [key, value] of Object.entries(props)) {
|
|
14348
|
-
if (key === "children")
|
|
14523
|
+
if (key === "children") {
|
|
14524
|
+
childrenProp = value;
|
|
14349
14525
|
continue;
|
|
14526
|
+
}
|
|
14350
14527
|
if (value === false || value == null)
|
|
14351
14528
|
continue;
|
|
14352
14529
|
attributes[mapAttributeName(key)] = String(value);
|
|
14353
14530
|
}
|
|
14354
14531
|
}
|
|
14355
14532
|
const childNodes = [];
|
|
14356
|
-
flatten(
|
|
14533
|
+
flatten(normalizeChildren(childrenProp), childNodes);
|
|
14357
14534
|
return new XmlNode2(qualifiedName, attributes, childNodes);
|
|
14358
14535
|
};
|
|
14359
14536
|
}
|
|
@@ -14366,7 +14543,7 @@ function flatten(items, out) {
|
|
|
14366
14543
|
continue;
|
|
14367
14544
|
}
|
|
14368
14545
|
if (item instanceof XmlNode2) {
|
|
14369
|
-
if (item.tag ===
|
|
14546
|
+
if (item.tag === FRAGMENT_TAG) {
|
|
14370
14547
|
for (const child of item.children)
|
|
14371
14548
|
out.push(child);
|
|
14372
14549
|
continue;
|
|
@@ -14385,7 +14562,7 @@ function mapAttributeName(name) {
|
|
|
14385
14562
|
return name;
|
|
14386
14563
|
return `${name.slice(0, dashIndex)}:${name.slice(dashIndex + 1)}`;
|
|
14387
14564
|
}
|
|
14388
|
-
var
|
|
14565
|
+
var FRAGMENT_TAG = "#fragment", W_TAGS, R_TAGS, A_TAGS, WP_TAGS, PIC_TAGS, CP_TAGS, DC_TAGS, DCTERMS_TAGS, W14_TAGS, W15_TAGS, w, r, a, wp, pic, cp, dc, dcterms, w14, w15;
|
|
14389
14566
|
var init_jsx = __esm(() => {
|
|
14390
14567
|
init_parser();
|
|
14391
14568
|
W_TAGS = [
|
|
@@ -14423,6 +14600,7 @@ var init_jsx = __esm(() => {
|
|
|
14423
14600
|
"tcPr",
|
|
14424
14601
|
"ins",
|
|
14425
14602
|
"del",
|
|
14603
|
+
"delText",
|
|
14426
14604
|
"commentRangeStart",
|
|
14427
14605
|
"commentRangeEnd",
|
|
14428
14606
|
"commentReference",
|
|
@@ -14477,18 +14655,9 @@ var init_jsx = __esm(() => {
|
|
|
14477
14655
|
|
|
14478
14656
|
// src/core/jsx/jsx-runtime.ts
|
|
14479
14657
|
function jsx(type, props, _key) {
|
|
14480
|
-
const
|
|
14481
|
-
const childArgs = normalizeChildren(children);
|
|
14482
|
-
const result = type(rest, ...childArgs);
|
|
14658
|
+
const result = type(props);
|
|
14483
14659
|
return result ?? new XmlNode2("#fragment");
|
|
14484
14660
|
}
|
|
14485
|
-
function normalizeChildren(children) {
|
|
14486
|
-
if (children === undefined)
|
|
14487
|
-
return [];
|
|
14488
|
-
if (Array.isArray(children))
|
|
14489
|
-
return children;
|
|
14490
|
-
return [children];
|
|
14491
|
-
}
|
|
14492
14661
|
var jsxDEV;
|
|
14493
14662
|
var init_jsx_runtime = __esm(() => {
|
|
14494
14663
|
init_parser();
|
|
@@ -14501,63 +14670,191 @@ var init_jsx_dev_runtime = __esm(() => {
|
|
|
14501
14670
|
init_jsx_runtime();
|
|
14502
14671
|
});
|
|
14503
14672
|
|
|
14504
|
-
// src/
|
|
14505
|
-
function
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
|
|
14515
|
-
const idAttribute = child.getAttribute("w:id");
|
|
14516
|
-
if (idAttribute == null)
|
|
14517
|
-
continue;
|
|
14518
|
-
const numeric = Number(idAttribute);
|
|
14519
|
-
if (Number.isFinite(numeric) && numeric > highest)
|
|
14520
|
-
highest = numeric;
|
|
14521
|
-
}
|
|
14522
|
-
return String(highest + 1);
|
|
14523
|
-
}
|
|
14524
|
-
function generateParaId() {
|
|
14525
|
-
const bytes = new Uint8Array(4);
|
|
14526
|
-
crypto.getRandomValues(bytes);
|
|
14527
|
-
let hex = "";
|
|
14528
|
-
for (const byte of bytes)
|
|
14529
|
-
hex += byte.toString(16).padStart(2, "0");
|
|
14530
|
-
return hex.toUpperCase();
|
|
14673
|
+
// src/core/track-changes/emit.tsx
|
|
14674
|
+
function Ins({
|
|
14675
|
+
meta,
|
|
14676
|
+
children
|
|
14677
|
+
}) {
|
|
14678
|
+
return /* @__PURE__ */ jsxDEV(w.ins, {
|
|
14679
|
+
"w-id": String(meta.revisionId),
|
|
14680
|
+
"w-author": meta.author,
|
|
14681
|
+
"w-date": meta.date,
|
|
14682
|
+
children
|
|
14683
|
+
}, undefined, false, undefined, this);
|
|
14531
14684
|
}
|
|
14532
|
-
function
|
|
14533
|
-
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14685
|
+
function Del({
|
|
14686
|
+
meta,
|
|
14687
|
+
children
|
|
14688
|
+
}) {
|
|
14689
|
+
return /* @__PURE__ */ jsxDEV(w.del, {
|
|
14690
|
+
"w-id": String(meta.revisionId),
|
|
14691
|
+
"w-author": meta.author,
|
|
14692
|
+
"w-date": meta.date,
|
|
14693
|
+
children
|
|
14694
|
+
}, undefined, false, undefined, this);
|
|
14539
14695
|
}
|
|
14540
|
-
function
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
}
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14696
|
+
function markParagraphMarkAs(paragraph, kind, meta) {
|
|
14697
|
+
let pPr = paragraph.findChild("w:pPr");
|
|
14698
|
+
if (!pPr) {
|
|
14699
|
+
pPr = /* @__PURE__ */ jsxDEV(w.pPr, {}, undefined, false, undefined, this);
|
|
14700
|
+
paragraph.children.unshift(pPr);
|
|
14701
|
+
}
|
|
14702
|
+
let rPr = pPr.findChild("w:rPr");
|
|
14703
|
+
if (!rPr) {
|
|
14704
|
+
rPr = /* @__PURE__ */ jsxDEV(w.rPr, {}, undefined, false, undefined, this);
|
|
14705
|
+
pPr.children.push(rPr);
|
|
14706
|
+
}
|
|
14707
|
+
const marker = kind === "ins" ? /* @__PURE__ */ jsxDEV(Ins, {
|
|
14708
|
+
meta
|
|
14709
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Del, {
|
|
14710
|
+
meta
|
|
14549
14711
|
}, undefined, false, undefined, this);
|
|
14550
|
-
|
|
14551
|
-
registerPart(view, {
|
|
14552
|
-
partName: "word/comments.xml",
|
|
14553
|
-
contentType: COMMENTS_CONTENT_TYPE,
|
|
14554
|
-
relationshipType: COMMENTS_REL_TYPE,
|
|
14555
|
-
target: "comments.xml"
|
|
14556
|
-
});
|
|
14557
|
-
return root;
|
|
14712
|
+
rPr.children.push(marker);
|
|
14558
14713
|
}
|
|
14559
|
-
|
|
14560
|
-
|
|
14714
|
+
var init_emit = __esm(() => {
|
|
14715
|
+
init_jsx();
|
|
14716
|
+
init_jsx_dev_runtime();
|
|
14717
|
+
});
|
|
14718
|
+
|
|
14719
|
+
// src/core/index.ts
|
|
14720
|
+
var init_core = __esm(() => {
|
|
14721
|
+
init_ast();
|
|
14722
|
+
init_locators();
|
|
14723
|
+
init_package();
|
|
14724
|
+
init_parser();
|
|
14725
|
+
init_track_changes();
|
|
14726
|
+
init_emit();
|
|
14727
|
+
});
|
|
14728
|
+
|
|
14729
|
+
// src/cli/respond.ts
|
|
14730
|
+
async function respond(payload) {
|
|
14731
|
+
await Bun.write(Bun.stdout, `${JSON.stringify(payload)}
|
|
14732
|
+
`);
|
|
14733
|
+
}
|
|
14734
|
+
async function writeStdout(text) {
|
|
14735
|
+
await Bun.write(Bun.stdout, text);
|
|
14736
|
+
}
|
|
14737
|
+
async function fail(code, message, hint) {
|
|
14738
|
+
const payload = {
|
|
14739
|
+
ok: false,
|
|
14740
|
+
code,
|
|
14741
|
+
error: message
|
|
14742
|
+
};
|
|
14743
|
+
if (hint)
|
|
14744
|
+
payload.hint = hint;
|
|
14745
|
+
await respond(payload);
|
|
14746
|
+
return exitCodeFor(code);
|
|
14747
|
+
}
|
|
14748
|
+
function exitCodeFor(code) {
|
|
14749
|
+
switch (code) {
|
|
14750
|
+
case "USAGE":
|
|
14751
|
+
case "INVALID_LOCATOR":
|
|
14752
|
+
return EXIT.USAGE_ERROR;
|
|
14753
|
+
case "FILE_NOT_FOUND":
|
|
14754
|
+
case "PART_NOT_FOUND":
|
|
14755
|
+
case "BLOCK_NOT_FOUND":
|
|
14756
|
+
case "COMMENT_NOT_FOUND":
|
|
14757
|
+
case "IMAGE_NOT_FOUND":
|
|
14758
|
+
case "MATCH_NOT_FOUND":
|
|
14759
|
+
return EXIT.NOT_FOUND;
|
|
14760
|
+
case "NOT_A_ZIP":
|
|
14761
|
+
case "TRACKED_CHANGE_CONFLICT":
|
|
14762
|
+
case "UNHANDLED":
|
|
14763
|
+
return EXIT.GENERAL_ERROR;
|
|
14764
|
+
}
|
|
14765
|
+
}
|
|
14766
|
+
async function openOrFail(path) {
|
|
14767
|
+
try {
|
|
14768
|
+
return await openDocView(path);
|
|
14769
|
+
} catch (err) {
|
|
14770
|
+
if (err instanceof PkgError) {
|
|
14771
|
+
if (err.code === "FILE_NOT_FOUND") {
|
|
14772
|
+
return await fail("FILE_NOT_FOUND", err.message);
|
|
14773
|
+
}
|
|
14774
|
+
if (err.code === "NOT_A_ZIP")
|
|
14775
|
+
return await fail("NOT_A_ZIP", err.message);
|
|
14776
|
+
}
|
|
14777
|
+
throw err;
|
|
14778
|
+
}
|
|
14779
|
+
}
|
|
14780
|
+
async function resolveBlockOrFail(view, locator) {
|
|
14781
|
+
try {
|
|
14782
|
+
return resolveBlock(view, locator);
|
|
14783
|
+
} catch (err) {
|
|
14784
|
+
if (err instanceof LocatorResolveError) {
|
|
14785
|
+
return await fail("BLOCK_NOT_FOUND", err.message);
|
|
14786
|
+
}
|
|
14787
|
+
throw err;
|
|
14788
|
+
}
|
|
14789
|
+
}
|
|
14790
|
+
var EXIT;
|
|
14791
|
+
var init_respond = __esm(() => {
|
|
14792
|
+
init_core();
|
|
14793
|
+
EXIT = {
|
|
14794
|
+
OK: 0,
|
|
14795
|
+
GENERAL_ERROR: 1,
|
|
14796
|
+
USAGE_ERROR: 2,
|
|
14797
|
+
NOT_FOUND: 3
|
|
14798
|
+
};
|
|
14799
|
+
});
|
|
14800
|
+
|
|
14801
|
+
// src/cli/comments/helpers.tsx
|
|
14802
|
+
function nextCommentId(view) {
|
|
14803
|
+
if (!view.commentsTree)
|
|
14804
|
+
return "0";
|
|
14805
|
+
const root = XmlNode2.findRoot(view.commentsTree, "w:comments");
|
|
14806
|
+
if (!root)
|
|
14807
|
+
return "0";
|
|
14808
|
+
let highest = -1;
|
|
14809
|
+
for (const child of root.children) {
|
|
14810
|
+
if (child.tag !== "w:comment")
|
|
14811
|
+
continue;
|
|
14812
|
+
const idAttribute = child.getAttribute("w:id");
|
|
14813
|
+
if (idAttribute == null)
|
|
14814
|
+
continue;
|
|
14815
|
+
const numeric = Number(idAttribute);
|
|
14816
|
+
if (Number.isFinite(numeric) && numeric > highest)
|
|
14817
|
+
highest = numeric;
|
|
14818
|
+
}
|
|
14819
|
+
return String(highest + 1);
|
|
14820
|
+
}
|
|
14821
|
+
function generateParaId() {
|
|
14822
|
+
const bytes = new Uint8Array(4);
|
|
14823
|
+
crypto.getRandomValues(bytes);
|
|
14824
|
+
let hex = "";
|
|
14825
|
+
for (const byte of bytes)
|
|
14826
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
14827
|
+
return hex.toUpperCase();
|
|
14828
|
+
}
|
|
14829
|
+
function authorInitials(author) {
|
|
14830
|
+
const parts = author.trim().split(/\s+/).filter(Boolean);
|
|
14831
|
+
if (parts.length === 0)
|
|
14832
|
+
return "?";
|
|
14833
|
+
const first = parts[0]?.charAt(0) ?? "";
|
|
14834
|
+
const last = parts.length > 1 ? parts[parts.length - 1]?.charAt(0) ?? "" : "";
|
|
14835
|
+
return (first + last).toUpperCase() || "?";
|
|
14836
|
+
}
|
|
14837
|
+
function ensureCommentsPart(view) {
|
|
14838
|
+
if (view.commentsTree) {
|
|
14839
|
+
const existing = XmlNode2.findRoot(view.commentsTree, "w:comments");
|
|
14840
|
+
if (existing)
|
|
14841
|
+
return existing;
|
|
14842
|
+
}
|
|
14843
|
+
const root = /* @__PURE__ */ jsxDEV(w.comments, {
|
|
14844
|
+
"xmlns:w": NS_W,
|
|
14845
|
+
"xmlns:w14": NS_W14
|
|
14846
|
+
}, undefined, false, undefined, this);
|
|
14847
|
+
view.commentsTree = [root];
|
|
14848
|
+
registerPart(view.relationshipsTree, view.contentTypesTree, {
|
|
14849
|
+
partName: "word/comments.xml",
|
|
14850
|
+
contentType: COMMENTS_CONTENT_TYPE,
|
|
14851
|
+
relationshipType: COMMENTS_REL_TYPE,
|
|
14852
|
+
target: "comments.xml"
|
|
14853
|
+
});
|
|
14854
|
+
return root;
|
|
14855
|
+
}
|
|
14856
|
+
function ensureCommentsExtPart(view) {
|
|
14857
|
+
if (view.commentsExtTree) {
|
|
14561
14858
|
const existing = XmlNode2.findRoot(view.commentsExtTree, "w15:commentsEx");
|
|
14562
14859
|
if (existing)
|
|
14563
14860
|
return existing;
|
|
@@ -14568,7 +14865,7 @@ function ensureCommentsExtPart(view) {
|
|
|
14568
14865
|
"mc:Ignorable": "w15"
|
|
14569
14866
|
}, undefined, false, undefined, this);
|
|
14570
14867
|
view.commentsExtTree = [root];
|
|
14571
|
-
registerPart(view, {
|
|
14868
|
+
registerPart(view.relationshipsTree, view.contentTypesTree, {
|
|
14572
14869
|
partName: "word/commentsExtended.xml",
|
|
14573
14870
|
contentType: COMMENTS_EXT_CONTENT_TYPE,
|
|
14574
14871
|
relationshipType: COMMENTS_EXT_REL_TYPE,
|
|
@@ -14576,46 +14873,6 @@ function ensureCommentsExtPart(view) {
|
|
|
14576
14873
|
});
|
|
14577
14874
|
return root;
|
|
14578
14875
|
}
|
|
14579
|
-
function registerPart(view, part) {
|
|
14580
|
-
const relationships = XmlNode2.findRoot(view.relationshipsTree, "Relationships");
|
|
14581
|
-
if (relationships) {
|
|
14582
|
-
const alreadyLinked = relationships.children.some((child) => child.tag === "Relationship" && child.getAttribute("Type") === part.relationshipType);
|
|
14583
|
-
if (!alreadyLinked) {
|
|
14584
|
-
relationships.children.push(new XmlNode2("Relationship", {
|
|
14585
|
-
Id: nextRelationshipId(relationships),
|
|
14586
|
-
Type: part.relationshipType,
|
|
14587
|
-
Target: part.target
|
|
14588
|
-
}));
|
|
14589
|
-
}
|
|
14590
|
-
}
|
|
14591
|
-
const types = XmlNode2.findRoot(view.contentTypesTree, "Types");
|
|
14592
|
-
if (types) {
|
|
14593
|
-
const overrideExists = types.children.some((child) => child.tag === "Override" && child.getAttribute("PartName") === `/${part.partName}`);
|
|
14594
|
-
if (!overrideExists) {
|
|
14595
|
-
types.children.push(new XmlNode2("Override", {
|
|
14596
|
-
PartName: `/${part.partName}`,
|
|
14597
|
-
ContentType: part.contentType
|
|
14598
|
-
}));
|
|
14599
|
-
}
|
|
14600
|
-
}
|
|
14601
|
-
}
|
|
14602
|
-
function nextRelationshipId(relationships) {
|
|
14603
|
-
let highest = 0;
|
|
14604
|
-
for (const child of relationships.children) {
|
|
14605
|
-
if (child.tag !== "Relationship")
|
|
14606
|
-
continue;
|
|
14607
|
-
const id = child.getAttribute("Id");
|
|
14608
|
-
if (!id)
|
|
14609
|
-
continue;
|
|
14610
|
-
const match = id.match(/^rId(\d+)$/);
|
|
14611
|
-
if (!match)
|
|
14612
|
-
continue;
|
|
14613
|
-
const numeric = Number(match[1]);
|
|
14614
|
-
if (Number.isFinite(numeric) && numeric > highest)
|
|
14615
|
-
highest = numeric;
|
|
14616
|
-
}
|
|
14617
|
-
return `rId${highest + 1}`;
|
|
14618
|
-
}
|
|
14619
14876
|
function paragraphTextLength(paragraph) {
|
|
14620
14877
|
let total = 0;
|
|
14621
14878
|
for (const child of paragraph.children) {
|
|
@@ -14636,17 +14893,46 @@ function addCommentMarkersToParagraph(paragraph, commentId, span) {
|
|
|
14636
14893
|
if (range.start < 0 || range.end > total || range.start > range.end) {
|
|
14637
14894
|
throw new SpanOutOfRangeError(`Span ${range.start}-${range.end} out of paragraph length ${total}`);
|
|
14638
14895
|
}
|
|
14639
|
-
|
|
14640
|
-
|
|
14641
|
-
|
|
14642
|
-
|
|
14643
|
-
|
|
14896
|
+
placeMarkersInParagraph(paragraph, [
|
|
14897
|
+
{ offset: range.start, node: commentRangeStartMarker(commentId) },
|
|
14898
|
+
{
|
|
14899
|
+
offset: range.end,
|
|
14900
|
+
node: commentRangeEndMarker(commentId),
|
|
14901
|
+
follower: commentReferenceRun(commentId)
|
|
14902
|
+
}
|
|
14903
|
+
]);
|
|
14904
|
+
}
|
|
14905
|
+
function addCommentRangeMarkers(startParagraph, startOffset, endParagraph, endOffset, commentId) {
|
|
14906
|
+
if (startParagraph === endParagraph) {
|
|
14907
|
+
addCommentMarkersToParagraph(startParagraph, commentId, {
|
|
14908
|
+
start: startOffset,
|
|
14909
|
+
end: endOffset
|
|
14910
|
+
});
|
|
14911
|
+
return;
|
|
14912
|
+
}
|
|
14913
|
+
placeMarkersInParagraph(startParagraph, [
|
|
14914
|
+
{ offset: startOffset, node: commentRangeStartMarker(commentId) }
|
|
14915
|
+
]);
|
|
14916
|
+
placeMarkersInParagraph(endParagraph, [
|
|
14917
|
+
{
|
|
14918
|
+
offset: endOffset,
|
|
14919
|
+
node: commentRangeEndMarker(commentId),
|
|
14920
|
+
follower: commentReferenceRun(commentId)
|
|
14921
|
+
}
|
|
14922
|
+
]);
|
|
14923
|
+
}
|
|
14924
|
+
function commentRangeStartMarker(commentId) {
|
|
14925
|
+
return /* @__PURE__ */ jsxDEV(w.commentRangeStart, {
|
|
14644
14926
|
"w-id": commentId
|
|
14645
14927
|
}, undefined, false, undefined, this);
|
|
14646
|
-
|
|
14928
|
+
}
|
|
14929
|
+
function commentRangeEndMarker(commentId) {
|
|
14930
|
+
return /* @__PURE__ */ jsxDEV(w.commentRangeEnd, {
|
|
14647
14931
|
"w-id": commentId
|
|
14648
14932
|
}, undefined, false, undefined, this);
|
|
14649
|
-
|
|
14933
|
+
}
|
|
14934
|
+
function commentReferenceRun(commentId) {
|
|
14935
|
+
return /* @__PURE__ */ jsxDEV(w.r, {
|
|
14650
14936
|
children: [
|
|
14651
14937
|
/* @__PURE__ */ jsxDEV(w.rPr, {
|
|
14652
14938
|
children: /* @__PURE__ */ jsxDEV(w.rStyle, {
|
|
@@ -14658,108 +14944,97 @@ function addCommentMarkersToParagraph(paragraph, commentId, span) {
|
|
|
14658
14944
|
}, undefined, false, undefined, this)
|
|
14659
14945
|
]
|
|
14660
14946
|
}, undefined, true, undefined, this);
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14947
|
+
}
|
|
14948
|
+
function placeMarkersInParagraph(paragraph, markers) {
|
|
14949
|
+
if (markers.length === 0)
|
|
14950
|
+
return;
|
|
14951
|
+
const total = paragraphTextLength(paragraph);
|
|
14952
|
+
for (const marker of markers) {
|
|
14953
|
+
if (marker.offset < 0 || marker.offset > total) {
|
|
14954
|
+
throw new SpanOutOfRangeError(`Marker offset ${marker.offset} out of paragraph length ${total}`);
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
const pending = markers.slice();
|
|
14958
|
+
const state = { offset: 0, placedCount: 0 };
|
|
14959
|
+
paragraph.children = walkAndPlace(paragraph.children, pending, true, state);
|
|
14960
|
+
flushAtCurrentOffset(paragraph.children, pending, state);
|
|
14961
|
+
if (state.placedCount !== markers.length) {
|
|
14962
|
+
throw new SpanOutOfRangeError(`Could not place comment markers (placed ${state.placedCount} of ${markers.length})`);
|
|
14963
|
+
}
|
|
14964
|
+
}
|
|
14965
|
+
function walkAndPlace(children, pending, isParagraphLevel, state) {
|
|
14966
|
+
const result = [];
|
|
14967
|
+
for (const child of children) {
|
|
14968
|
+
if (child.tag === "w:r") {
|
|
14969
|
+
const length = runTextLength(child);
|
|
14970
|
+
const runStart = state.offset;
|
|
14971
|
+
const runEnd = state.offset + length;
|
|
14972
|
+
const splits = [];
|
|
14973
|
+
for (let i = 0;i < pending.length; i++) {
|
|
14974
|
+
const marker = pending[i];
|
|
14975
|
+
if (!marker)
|
|
14668
14976
|
continue;
|
|
14977
|
+
if (runStart <= marker.offset && marker.offset <= runEnd) {
|
|
14978
|
+
splits.push({ at: marker.offset - runStart, index: i });
|
|
14669
14979
|
}
|
|
14670
|
-
newChildren.push(startMarker);
|
|
14671
14980
|
}
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14981
|
+
splits.sort((left, right) => left.at - right.at || left.index - right.index);
|
|
14982
|
+
if (splits.length === 0) {
|
|
14983
|
+
result.push(child);
|
|
14984
|
+
} else {
|
|
14985
|
+
let cursor = 0;
|
|
14986
|
+
for (const split of splits) {
|
|
14987
|
+
if (split.at > cursor) {
|
|
14988
|
+
result.push(sliceRun(child, cursor, split.at));
|
|
14989
|
+
}
|
|
14990
|
+
const marker = pending[split.index];
|
|
14991
|
+
if (!marker)
|
|
14992
|
+
continue;
|
|
14993
|
+
result.push(marker.node);
|
|
14994
|
+
if (marker.follower)
|
|
14995
|
+
result.push(marker.follower);
|
|
14996
|
+
pending[split.index] = null;
|
|
14997
|
+
state.placedCount++;
|
|
14998
|
+
cursor = split.at;
|
|
14999
|
+
}
|
|
15000
|
+
if (cursor < length) {
|
|
15001
|
+
result.push(sliceRun(child, cursor, length));
|
|
15002
|
+
}
|
|
14676
15003
|
}
|
|
14677
|
-
|
|
15004
|
+
state.offset = runEnd;
|
|
14678
15005
|
continue;
|
|
14679
15006
|
}
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
}
|
|
14688
|
-
if (!placedEnd && runStart <= range.end && range.end <= runEnd) {
|
|
14689
|
-
splits.push({ at: range.end - runStart, node: endMarker });
|
|
14690
|
-
placedEnd = true;
|
|
14691
|
-
}
|
|
14692
|
-
splits.sort((leftSplit, rightSplit) => leftSplit.at - rightSplit.at);
|
|
14693
|
-
if (splits.length === 0) {
|
|
14694
|
-
newChildren.push(child);
|
|
14695
|
-
} else {
|
|
14696
|
-
let cursor = 0;
|
|
14697
|
-
for (const split of splits) {
|
|
14698
|
-
if (split.at > cursor) {
|
|
14699
|
-
newChildren.push(sliceRun(child, cursor, split.at));
|
|
14700
|
-
}
|
|
14701
|
-
newChildren.push(split.node);
|
|
14702
|
-
cursor = split.at;
|
|
14703
|
-
}
|
|
14704
|
-
if (cursor < length) {
|
|
14705
|
-
newChildren.push(sliceRun(child, cursor, length));
|
|
14706
|
-
}
|
|
14707
|
-
if (splits.some((split) => split.node === endMarker)) {
|
|
14708
|
-
newChildren.push(referenceRun);
|
|
14709
|
-
}
|
|
15007
|
+
if (isParagraphLevel && (child.tag === "w:ins" || child.tag === "w:del")) {
|
|
15008
|
+
flushAtCurrentOffset(result, pending, state);
|
|
15009
|
+
const innerChildren = walkAndPlace(child.children, pending, false, state);
|
|
15010
|
+
const wrapper = new XmlNode2(child.tag, { ...child.attributes });
|
|
15011
|
+
wrapper.children = innerChildren;
|
|
15012
|
+
result.push(wrapper);
|
|
15013
|
+
continue;
|
|
14710
15014
|
}
|
|
14711
|
-
|
|
14712
|
-
|
|
14713
|
-
|
|
14714
|
-
newChildren.push(startMarker);
|
|
14715
|
-
placedStart = true;
|
|
14716
|
-
}
|
|
14717
|
-
if (!placedEnd && offset === range.end) {
|
|
14718
|
-
newChildren.push(endMarker);
|
|
14719
|
-
newChildren.push(referenceRun);
|
|
14720
|
-
placedEnd = true;
|
|
14721
|
-
}
|
|
14722
|
-
if (!placedStart || !placedEnd) {
|
|
14723
|
-
throw new SpanOutOfRangeError(`Could not place comment markers (start placed: ${placedStart}, end placed: ${placedEnd})`);
|
|
14724
|
-
}
|
|
14725
|
-
paragraph.children = newChildren;
|
|
14726
|
-
}
|
|
14727
|
-
function runTextLength(run) {
|
|
14728
|
-
let total = 0;
|
|
14729
|
-
for (const child of run.children) {
|
|
14730
|
-
if (child.tag === "w:t")
|
|
14731
|
-
total += child.collectText().length;
|
|
14732
|
-
}
|
|
14733
|
-
return total;
|
|
14734
|
-
}
|
|
14735
|
-
function sliceRun(run, start, end) {
|
|
14736
|
-
const sliced = new XmlNode2("w:r", { ...run.attributes });
|
|
14737
|
-
let consumed = 0;
|
|
14738
|
-
for (const child of run.children) {
|
|
14739
|
-
if (child.tag === "w:t") {
|
|
14740
|
-
const text = child.collectText();
|
|
14741
|
-
const localStart = Math.max(0, start - consumed);
|
|
14742
|
-
const localEnd = Math.min(text.length, end - consumed);
|
|
14743
|
-
if (localStart < localEnd) {
|
|
14744
|
-
const slicedText = new XmlNode2("w:t", { "xml:space": "preserve" });
|
|
14745
|
-
slicedText.children.push(XmlNode2.textNode(text.slice(localStart, localEnd)));
|
|
14746
|
-
sliced.children.push(slicedText);
|
|
14747
|
-
}
|
|
14748
|
-
consumed += text.length;
|
|
15015
|
+
if (child.tag === "w:pPr") {
|
|
15016
|
+
result.push(child);
|
|
15017
|
+
flushAtCurrentOffset(result, pending, state);
|
|
14749
15018
|
continue;
|
|
14750
15019
|
}
|
|
14751
|
-
|
|
15020
|
+
flushAtCurrentOffset(result, pending, state);
|
|
15021
|
+
result.push(child);
|
|
14752
15022
|
}
|
|
14753
|
-
return
|
|
15023
|
+
return result;
|
|
14754
15024
|
}
|
|
14755
|
-
function
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
|
|
14759
|
-
|
|
14760
|
-
|
|
15025
|
+
function flushAtCurrentOffset(out, pending, state) {
|
|
15026
|
+
for (let i = 0;i < pending.length; i++) {
|
|
15027
|
+
const marker = pending[i];
|
|
15028
|
+
if (!marker)
|
|
15029
|
+
continue;
|
|
15030
|
+
if (marker.offset !== state.offset)
|
|
15031
|
+
continue;
|
|
15032
|
+
out.push(marker.node);
|
|
15033
|
+
if (marker.follower)
|
|
15034
|
+
out.push(marker.follower);
|
|
15035
|
+
pending[i] = null;
|
|
15036
|
+
state.placedCount++;
|
|
14761
15037
|
}
|
|
14762
|
-
return clone;
|
|
14763
15038
|
}
|
|
14764
15039
|
function CommentBody({
|
|
14765
15040
|
options
|
|
@@ -14871,6 +15146,7 @@ function containsCommentReference(run, numericId) {
|
|
|
14871
15146
|
var COMMENTS_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", COMMENTS_EXT_REL_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_W = "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", SpanOutOfRangeError;
|
|
14872
15147
|
var init_helpers = __esm(() => {
|
|
14873
15148
|
init_jsx();
|
|
15149
|
+
init_package();
|
|
14874
15150
|
init_parser();
|
|
14875
15151
|
init_jsx_dev_runtime();
|
|
14876
15152
|
SpanOutOfRangeError = class SpanOutOfRangeError extends Error {
|
|
@@ -14897,6 +15173,7 @@ async function run(args) {
|
|
|
14897
15173
|
range: { type: "string" },
|
|
14898
15174
|
text: { type: "string" },
|
|
14899
15175
|
author: { type: "string" },
|
|
15176
|
+
output: { type: "string", short: "o" },
|
|
14900
15177
|
"dry-run": { type: "boolean" },
|
|
14901
15178
|
help: { type: "boolean", short: "h" }
|
|
14902
15179
|
}
|
|
@@ -14927,38 +15204,26 @@ async function run(args) {
|
|
|
14927
15204
|
}
|
|
14928
15205
|
throw error;
|
|
14929
15206
|
}
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
}
|
|
14946
|
-
throw openError;
|
|
14947
|
-
}
|
|
14948
|
-
let paragraphRef;
|
|
14949
|
-
try {
|
|
14950
|
-
paragraphRef = resolveBlock(view, blockId);
|
|
14951
|
-
} catch (error) {
|
|
14952
|
-
if (error instanceof LocatorResolveError) {
|
|
14953
|
-
return fail("BLOCK_NOT_FOUND", error.message);
|
|
14954
|
-
}
|
|
14955
|
-
throw error;
|
|
14956
|
-
}
|
|
14957
|
-
const span = locator.kind === "blockSpan" ? { start: locator.start, end: locator.end } : undefined;
|
|
15207
|
+
if (locator.kind === "range") {
|
|
15208
|
+
return runCrossBlock(parsed, path, rangeInput, locator, text);
|
|
15209
|
+
}
|
|
15210
|
+
const target = locatorToBlockTarget(locator);
|
|
15211
|
+
if (!target) {
|
|
15212
|
+
return fail("INVALID_LOCATOR", "comments add supports paragraph locators only (pN, pN:start-end, pN:S-pM:E, or tT:rRcC:pK[:start-end])", "Comments and images are not valid anchors.");
|
|
15213
|
+
}
|
|
15214
|
+
const blockId = target.blockId;
|
|
15215
|
+
const view = await openOrFail(path);
|
|
15216
|
+
if (typeof view === "number")
|
|
15217
|
+
return view;
|
|
15218
|
+
const paragraphRef = await resolveBlockOrFail(view, blockId);
|
|
15219
|
+
if (typeof paragraphRef === "number")
|
|
15220
|
+
return paragraphRef;
|
|
15221
|
+
const span = target.span;
|
|
14958
15222
|
const author = parsed.values.author ?? Bun.env.DOCX_AUTHOR ?? "";
|
|
14959
15223
|
const date = new Date().toISOString();
|
|
14960
15224
|
const numericId = nextCommentId(view);
|
|
14961
15225
|
const paraId = generateParaId();
|
|
15226
|
+
const outputPath = parsed.values.output;
|
|
14962
15227
|
if (parsed.values["dry-run"]) {
|
|
14963
15228
|
await respond({
|
|
14964
15229
|
ok: true,
|
|
@@ -14966,7 +15231,8 @@ async function run(args) {
|
|
|
14966
15231
|
dryRun: true,
|
|
14967
15232
|
path,
|
|
14968
15233
|
commentId: `c${numericId}`,
|
|
14969
|
-
locator: rangeInput
|
|
15234
|
+
locator: rangeInput,
|
|
15235
|
+
...outputPath ? { output: outputPath } : {}
|
|
14970
15236
|
});
|
|
14971
15237
|
return EXIT.OK;
|
|
14972
15238
|
}
|
|
@@ -14989,34 +15255,96 @@ async function run(args) {
|
|
|
14989
15255
|
text
|
|
14990
15256
|
}
|
|
14991
15257
|
}, undefined, false, undefined, this));
|
|
14992
|
-
await saveDocView(view);
|
|
15258
|
+
await saveDocView(view, outputPath);
|
|
14993
15259
|
await respond({
|
|
14994
15260
|
ok: true,
|
|
14995
15261
|
operation: "comments.add",
|
|
14996
|
-
path,
|
|
15262
|
+
path: outputPath ?? path,
|
|
14997
15263
|
commentId: `c${numericId}`,
|
|
14998
15264
|
locator: rangeInput
|
|
14999
15265
|
});
|
|
15000
15266
|
return EXIT.OK;
|
|
15001
15267
|
}
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15268
|
+
async function runCrossBlock(parsed, path, rangeInput, locator, text) {
|
|
15269
|
+
const view = await openOrFail(path);
|
|
15270
|
+
if (typeof view === "number")
|
|
15271
|
+
return view;
|
|
15272
|
+
const startRef = await resolveBlockOrFail(view, locator.start.blockId);
|
|
15273
|
+
if (typeof startRef === "number")
|
|
15274
|
+
return startRef;
|
|
15275
|
+
const endRef = await resolveBlockOrFail(view, locator.end.blockId);
|
|
15276
|
+
if (typeof endRef === "number")
|
|
15277
|
+
return endRef;
|
|
15278
|
+
const author = parsed.values.author ?? Bun.env.DOCX_AUTHOR ?? "";
|
|
15279
|
+
const date = new Date().toISOString();
|
|
15280
|
+
const numericId = nextCommentId(view);
|
|
15281
|
+
const paraId = generateParaId();
|
|
15282
|
+
const outputPath = parsed.values.output;
|
|
15283
|
+
if (parsed.values["dry-run"]) {
|
|
15284
|
+
await respond({
|
|
15285
|
+
ok: true,
|
|
15286
|
+
operation: "comments.add",
|
|
15287
|
+
dryRun: true,
|
|
15288
|
+
path,
|
|
15289
|
+
commentId: `c${numericId}`,
|
|
15290
|
+
locator: rangeInput,
|
|
15291
|
+
...outputPath ? { output: outputPath } : {}
|
|
15292
|
+
});
|
|
15293
|
+
return EXIT.OK;
|
|
15294
|
+
}
|
|
15295
|
+
try {
|
|
15296
|
+
addCommentRangeMarkers(startRef.node, locator.start.offset, endRef.node, locator.end.offset, numericId);
|
|
15297
|
+
} catch (error) {
|
|
15298
|
+
if (error instanceof SpanOutOfRangeError) {
|
|
15299
|
+
return fail("INVALID_LOCATOR", error.message);
|
|
15300
|
+
}
|
|
15301
|
+
throw error;
|
|
15302
|
+
}
|
|
15303
|
+
const commentsRoot = ensureCommentsPart(view);
|
|
15304
|
+
commentsRoot.children.push(/* @__PURE__ */ jsxDEV(CommentBody, {
|
|
15305
|
+
options: {
|
|
15306
|
+
id: numericId,
|
|
15307
|
+
author,
|
|
15308
|
+
date,
|
|
15309
|
+
initials: authorInitials(author),
|
|
15310
|
+
paraId,
|
|
15311
|
+
text
|
|
15312
|
+
}
|
|
15313
|
+
}, undefined, false, undefined, this));
|
|
15314
|
+
await saveDocView(view, outputPath);
|
|
15315
|
+
await respond({
|
|
15316
|
+
ok: true,
|
|
15317
|
+
operation: "comments.add",
|
|
15318
|
+
path: outputPath ?? path,
|
|
15319
|
+
commentId: `c${numericId}`,
|
|
15320
|
+
locator: rangeInput
|
|
15321
|
+
});
|
|
15322
|
+
return EXIT.OK;
|
|
15323
|
+
}
|
|
15324
|
+
var HELP = `docx comments add \u2014 anchor a new comment to a locator
|
|
15325
|
+
|
|
15326
|
+
Usage:
|
|
15327
|
+
docx comments add FILE [options]
|
|
15328
|
+
|
|
15329
|
+
Required:
|
|
15330
|
+
--range LOCATOR Where to anchor. Supports:
|
|
15331
|
+
pN whole paragraph
|
|
15332
|
+
pN:S-E chars S..E of pN
|
|
15333
|
+
pN:S-pM:E chars S of pN through char E of pM (cross-paragraph)
|
|
15334
|
+
tT:rRcC:pK whole cell paragraph
|
|
15335
|
+
tT:rRcC:pK:S-E chars S..E of cell paragraph
|
|
15336
|
+
--text TEXT Comment body
|
|
15337
|
+
|
|
15012
15338
|
Optional:
|
|
15013
15339
|
--author NAME Author name (default: $DOCX_AUTHOR)
|
|
15340
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
15014
15341
|
--dry-run Print what would be added; do not write the file
|
|
15015
15342
|
-h, --help Show this help
|
|
15016
15343
|
|
|
15017
15344
|
Examples:
|
|
15018
15345
|
docx comments add doc.docx --range p3 --text "Reconsider this paragraph"
|
|
15019
15346
|
docx comments add doc.docx --range p3:5-20 --text "Sharper wording?" --author "Jane"
|
|
15347
|
+
docx comments add doc.docx --range p3:5-p5:10 --text "Whole section?" --author "Reviewer"
|
|
15020
15348
|
`;
|
|
15021
15349
|
var init_add = __esm(() => {
|
|
15022
15350
|
init_core();
|
|
@@ -15025,53 +15353,6 @@ var init_add = __esm(() => {
|
|
|
15025
15353
|
init_jsx_dev_runtime();
|
|
15026
15354
|
});
|
|
15027
15355
|
|
|
15028
|
-
// src/cli/comments/trash.ts
|
|
15029
|
-
import { dirname, join } from "path";
|
|
15030
|
-
function trashPathFor(docPath) {
|
|
15031
|
-
return join(dirname(docPath), TRASH_DIR, TRASH_FILE);
|
|
15032
|
-
}
|
|
15033
|
-
async function readTrash(docPath) {
|
|
15034
|
-
const path = trashPathFor(docPath);
|
|
15035
|
-
const file = Bun.file(path);
|
|
15036
|
-
if (!await file.exists()) {
|
|
15037
|
-
return { version: TRASH_VERSION, entries: [] };
|
|
15038
|
-
}
|
|
15039
|
-
const parsed = await file.json();
|
|
15040
|
-
if (parsed.version !== TRASH_VERSION) {
|
|
15041
|
-
return { version: TRASH_VERSION, entries: [] };
|
|
15042
|
-
}
|
|
15043
|
-
return parsed;
|
|
15044
|
-
}
|
|
15045
|
-
async function writeTrash(docPath, trash) {
|
|
15046
|
-
const path = trashPathFor(docPath);
|
|
15047
|
-
await Bun.write(path, `${JSON.stringify(trash, null, 2)}
|
|
15048
|
-
`);
|
|
15049
|
-
}
|
|
15050
|
-
async function pushTrashEntry(docPath, entry) {
|
|
15051
|
-
const trash = await readTrash(docPath);
|
|
15052
|
-
trash.entries.push(entry);
|
|
15053
|
-
await writeTrash(docPath, trash);
|
|
15054
|
-
}
|
|
15055
|
-
async function popTrashEntry(docPath, commentId) {
|
|
15056
|
-
const trash = await readTrash(docPath);
|
|
15057
|
-
const fileName = docPath.split("/").pop() ?? docPath;
|
|
15058
|
-
for (let index = trash.entries.length - 1;index >= 0; index--) {
|
|
15059
|
-
const entry = trash.entries[index];
|
|
15060
|
-
if (!entry)
|
|
15061
|
-
continue;
|
|
15062
|
-
if (entry.file !== fileName)
|
|
15063
|
-
continue;
|
|
15064
|
-
if (entry.commentId !== commentId)
|
|
15065
|
-
continue;
|
|
15066
|
-
trash.entries.splice(index, 1);
|
|
15067
|
-
await writeTrash(docPath, trash);
|
|
15068
|
-
return entry;
|
|
15069
|
-
}
|
|
15070
|
-
return;
|
|
15071
|
-
}
|
|
15072
|
-
var TRASH_VERSION = 1, TRASH_DIR = ".docx-cli", TRASH_FILE = "trash.json";
|
|
15073
|
-
var init_trash = () => {};
|
|
15074
|
-
|
|
15075
15356
|
// src/cli/comments/delete.ts
|
|
15076
15357
|
var exports_delete = {};
|
|
15077
15358
|
__export(exports_delete, {
|
|
@@ -15086,6 +15367,7 @@ async function run2(args) {
|
|
|
15086
15367
|
allowPositionals: true,
|
|
15087
15368
|
options: {
|
|
15088
15369
|
id: { type: "string" },
|
|
15370
|
+
output: { type: "string", short: "o" },
|
|
15089
15371
|
"dry-run": { type: "boolean" },
|
|
15090
15372
|
help: { type: "boolean", short: "h" }
|
|
15091
15373
|
}
|
|
@@ -15106,49 +15388,26 @@ async function run2(args) {
|
|
|
15106
15388
|
return fail("USAGE", "Missing --id COMMENT_ID", HELP2);
|
|
15107
15389
|
const numericId = idInput.startsWith("c") ? idInput.slice(1) : idInput;
|
|
15108
15390
|
const commentId = `c${numericId}`;
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
view
|
|
15112
|
-
} catch (openError) {
|
|
15113
|
-
if (openError instanceof PkgError) {
|
|
15114
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
15115
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
15116
|
-
}
|
|
15117
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
15118
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
15119
|
-
}
|
|
15120
|
-
}
|
|
15121
|
-
throw openError;
|
|
15122
|
-
}
|
|
15391
|
+
const view = await openOrFail(path);
|
|
15392
|
+
if (typeof view === "number")
|
|
15393
|
+
return view;
|
|
15123
15394
|
const commentReference = findCommentByNumericId(view, numericId);
|
|
15124
15395
|
if (!commentReference) {
|
|
15125
15396
|
return fail("COMMENT_NOT_FOUND", `Comment not found: ${commentId}`);
|
|
15126
15397
|
}
|
|
15127
|
-
const
|
|
15128
|
-
if (!anchor) {
|
|
15129
|
-
return fail("COMMENT_NOT_FOUND", `Anchor not found for ${commentId}`);
|
|
15130
|
-
}
|
|
15398
|
+
const outputPath = parsed.values.output;
|
|
15131
15399
|
if (parsed.values["dry-run"]) {
|
|
15132
15400
|
await respond({
|
|
15133
15401
|
ok: true,
|
|
15134
15402
|
operation: "comments.delete",
|
|
15135
15403
|
dryRun: true,
|
|
15136
15404
|
path,
|
|
15137
|
-
commentId
|
|
15405
|
+
commentId,
|
|
15406
|
+
...outputPath ? { output: outputPath } : {}
|
|
15138
15407
|
});
|
|
15139
15408
|
return EXIT.OK;
|
|
15140
15409
|
}
|
|
15141
|
-
const commentXml = XmlNode2.serialize([commentReference.node]);
|
|
15142
15410
|
const paraId = findCommentParaId(view, commentId);
|
|
15143
|
-
const extXml = paraId ? extractExtEntryXml(view, paraId) : null;
|
|
15144
|
-
await pushTrashEntry(path, {
|
|
15145
|
-
file: path.split("/").pop() ?? path,
|
|
15146
|
-
deletedAt: new Date().toISOString(),
|
|
15147
|
-
commentId,
|
|
15148
|
-
anchor,
|
|
15149
|
-
commentXml,
|
|
15150
|
-
extXml
|
|
15151
|
-
});
|
|
15152
15411
|
const commentIndex = commentReference.parent.indexOf(commentReference.node);
|
|
15153
15412
|
if (commentIndex !== -1)
|
|
15154
15413
|
commentReference.parent.splice(commentIndex, 1);
|
|
@@ -15159,28 +15418,15 @@ async function run2(args) {
|
|
|
15159
15418
|
}
|
|
15160
15419
|
}
|
|
15161
15420
|
removeCommentMarkers(view.documentTree, numericId);
|
|
15162
|
-
await saveDocView(view);
|
|
15421
|
+
await saveDocView(view, outputPath);
|
|
15163
15422
|
await respond({
|
|
15164
15423
|
ok: true,
|
|
15165
15424
|
operation: "comments.delete",
|
|
15166
|
-
path,
|
|
15425
|
+
path: outputPath ?? path,
|
|
15167
15426
|
commentId
|
|
15168
15427
|
});
|
|
15169
15428
|
return EXIT.OK;
|
|
15170
15429
|
}
|
|
15171
|
-
function extractExtEntryXml(view, paraId) {
|
|
15172
|
-
if (!view.commentsExtTree)
|
|
15173
|
-
return null;
|
|
15174
|
-
const root = XmlNode2.findRoot(view.commentsExtTree, "w15:commentsEx");
|
|
15175
|
-
if (!root)
|
|
15176
|
-
return null;
|
|
15177
|
-
for (const child of root.children) {
|
|
15178
|
-
if (child.tag === "w15:commentEx" && child.getAttribute("w15:paraId") === paraId) {
|
|
15179
|
-
return XmlNode2.serialize([child]);
|
|
15180
|
-
}
|
|
15181
|
-
}
|
|
15182
|
-
return null;
|
|
15183
|
-
}
|
|
15184
15430
|
var HELP2 = `docx comments delete \u2014 remove a comment
|
|
15185
15431
|
|
|
15186
15432
|
Usage:
|
|
@@ -15190,12 +15436,10 @@ Required:
|
|
|
15190
15436
|
--id ID Comment id (e.g., c0)
|
|
15191
15437
|
|
|
15192
15438
|
Optional:
|
|
15439
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
15193
15440
|
--dry-run Print what would be removed; do not write the file
|
|
15194
15441
|
-h, --help Show this help
|
|
15195
15442
|
|
|
15196
|
-
The deleted comment is journaled to <dir>/.docx-cli/trash.json so it can
|
|
15197
|
-
be brought back via "docx comments restore".
|
|
15198
|
-
|
|
15199
15443
|
Examples:
|
|
15200
15444
|
docx comments delete doc.docx --id c2
|
|
15201
15445
|
`;
|
|
@@ -15204,7 +15448,6 @@ var init_delete = __esm(() => {
|
|
|
15204
15448
|
init_parser();
|
|
15205
15449
|
init_respond();
|
|
15206
15450
|
init_helpers();
|
|
15207
|
-
init_trash();
|
|
15208
15451
|
});
|
|
15209
15452
|
|
|
15210
15453
|
// src/cli/comments/list.ts
|
|
@@ -15236,20 +15479,9 @@ async function run3(args) {
|
|
|
15236
15479
|
const path = parsed.positionals[0];
|
|
15237
15480
|
if (!path)
|
|
15238
15481
|
return fail("USAGE", "Missing FILE argument", HELP3);
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
view
|
|
15242
|
-
} catch (openError) {
|
|
15243
|
-
if (openError instanceof PkgError) {
|
|
15244
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
15245
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
15246
|
-
}
|
|
15247
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
15248
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
15249
|
-
}
|
|
15250
|
-
}
|
|
15251
|
-
throw openError;
|
|
15252
|
-
}
|
|
15482
|
+
const view = await openOrFail(path);
|
|
15483
|
+
if (typeof view === "number")
|
|
15484
|
+
return view;
|
|
15253
15485
|
let comments = view.doc.comments;
|
|
15254
15486
|
if (!parsed.values["include-resolved"]) {
|
|
15255
15487
|
comments = comments.filter((comment) => !comment.resolved);
|
|
@@ -15293,7 +15525,6 @@ Examples:
|
|
|
15293
15525
|
docx comments list doc.docx --include-resolved | jq '.[] | select(.author == "Jane")'
|
|
15294
15526
|
`;
|
|
15295
15527
|
var init_list = __esm(() => {
|
|
15296
|
-
init_core();
|
|
15297
15528
|
init_respond();
|
|
15298
15529
|
});
|
|
15299
15530
|
|
|
@@ -15313,6 +15544,7 @@ async function run4(args) {
|
|
|
15313
15544
|
to: { type: "string" },
|
|
15314
15545
|
text: { type: "string" },
|
|
15315
15546
|
author: { type: "string" },
|
|
15547
|
+
output: { type: "string", short: "o" },
|
|
15316
15548
|
"dry-run": { type: "boolean" },
|
|
15317
15549
|
help: { type: "boolean", short: "h" }
|
|
15318
15550
|
}
|
|
@@ -15335,20 +15567,9 @@ async function run4(args) {
|
|
|
15335
15567
|
if (!text)
|
|
15336
15568
|
return fail("USAGE", "Missing --text TEXT", HELP4);
|
|
15337
15569
|
const parentNumericId = parentInput.startsWith("c") ? parentInput.slice(1) : parentInput;
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
view
|
|
15341
|
-
} catch (openError) {
|
|
15342
|
-
if (openError instanceof PkgError) {
|
|
15343
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
15344
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
15345
|
-
}
|
|
15346
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
15347
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
15348
|
-
}
|
|
15349
|
-
}
|
|
15350
|
-
throw openError;
|
|
15351
|
-
}
|
|
15570
|
+
const view = await openOrFail(path);
|
|
15571
|
+
if (typeof view === "number")
|
|
15572
|
+
return view;
|
|
15352
15573
|
const parentReference = findCommentByNumericId(view, parentNumericId);
|
|
15353
15574
|
if (!parentReference) {
|
|
15354
15575
|
return fail("COMMENT_NOT_FOUND", `Parent comment not found: c${parentNumericId}`);
|
|
@@ -15361,6 +15582,7 @@ async function run4(args) {
|
|
|
15361
15582
|
const date = new Date().toISOString();
|
|
15362
15583
|
const numericId = nextCommentId(view);
|
|
15363
15584
|
const replyParaId = generateParaId();
|
|
15585
|
+
const outputPath = parsed.values.output;
|
|
15364
15586
|
if (parsed.values["dry-run"]) {
|
|
15365
15587
|
await respond({
|
|
15366
15588
|
ok: true,
|
|
@@ -15368,7 +15590,8 @@ async function run4(args) {
|
|
|
15368
15590
|
dryRun: true,
|
|
15369
15591
|
path,
|
|
15370
15592
|
commentId: `c${numericId}`,
|
|
15371
|
-
parentId: `c${parentNumericId}
|
|
15593
|
+
parentId: `c${parentNumericId}`,
|
|
15594
|
+
...outputPath ? { output: outputPath } : {}
|
|
15372
15595
|
});
|
|
15373
15596
|
return EXIT.OK;
|
|
15374
15597
|
}
|
|
@@ -15389,11 +15612,11 @@ async function run4(args) {
|
|
|
15389
15612
|
"w15:paraIdParent": parentParaId,
|
|
15390
15613
|
"w15:done": "0"
|
|
15391
15614
|
}));
|
|
15392
|
-
await saveDocView(view);
|
|
15615
|
+
await saveDocView(view, outputPath);
|
|
15393
15616
|
await respond({
|
|
15394
15617
|
ok: true,
|
|
15395
15618
|
operation: "comments.reply",
|
|
15396
|
-
path,
|
|
15619
|
+
path: outputPath ?? path,
|
|
15397
15620
|
commentId: `c${numericId}`,
|
|
15398
15621
|
parentId: `c${parentNumericId}`
|
|
15399
15622
|
});
|
|
@@ -15410,6 +15633,7 @@ Required:
|
|
|
15410
15633
|
|
|
15411
15634
|
Optional:
|
|
15412
15635
|
--author NAME Author name (default: $DOCX_AUTHOR)
|
|
15636
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
15413
15637
|
--dry-run Print what would be added; do not write the file
|
|
15414
15638
|
-h, --help Show this help
|
|
15415
15639
|
|
|
@@ -15439,6 +15663,7 @@ async function run5(args) {
|
|
|
15439
15663
|
options: {
|
|
15440
15664
|
id: { type: "string" },
|
|
15441
15665
|
unset: { type: "boolean" },
|
|
15666
|
+
output: { type: "string", short: "o" },
|
|
15442
15667
|
"dry-run": { type: "boolean" },
|
|
15443
15668
|
help: { type: "boolean", short: "h" }
|
|
15444
15669
|
}
|
|
@@ -15459,20 +15684,9 @@ async function run5(args) {
|
|
|
15459
15684
|
return fail("USAGE", "Missing --id COMMENT_ID", HELP5);
|
|
15460
15685
|
const numericId = idInput.startsWith("c") ? idInput.slice(1) : idInput;
|
|
15461
15686
|
const resolved = !parsed.values.unset;
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
view
|
|
15465
|
-
} catch (openError) {
|
|
15466
|
-
if (openError instanceof PkgError) {
|
|
15467
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
15468
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
15469
|
-
}
|
|
15470
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
15471
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
15472
|
-
}
|
|
15473
|
-
}
|
|
15474
|
-
throw openError;
|
|
15475
|
-
}
|
|
15687
|
+
const view = await openOrFail(path);
|
|
15688
|
+
if (typeof view === "number")
|
|
15689
|
+
return view;
|
|
15476
15690
|
const commentReference = findCommentByNumericId(view, numericId);
|
|
15477
15691
|
if (!commentReference) {
|
|
15478
15692
|
return fail("COMMENT_NOT_FOUND", `Comment not found: c${numericId}`);
|
|
@@ -15481,6 +15695,7 @@ async function run5(args) {
|
|
|
15481
15695
|
if (!paraId) {
|
|
15482
15696
|
return fail("COMMENT_NOT_FOUND", `Comment c${numericId} could not be assigned a w14:paraId.`);
|
|
15483
15697
|
}
|
|
15698
|
+
const outputPath = parsed.values.output;
|
|
15484
15699
|
if (parsed.values["dry-run"]) {
|
|
15485
15700
|
await respond({
|
|
15486
15701
|
ok: true,
|
|
@@ -15488,7 +15703,8 @@ async function run5(args) {
|
|
|
15488
15703
|
dryRun: true,
|
|
15489
15704
|
path,
|
|
15490
15705
|
commentId: `c${numericId}`,
|
|
15491
|
-
resolved
|
|
15706
|
+
resolved,
|
|
15707
|
+
...outputPath ? { output: outputPath } : {}
|
|
15492
15708
|
});
|
|
15493
15709
|
return EXIT.OK;
|
|
15494
15710
|
}
|
|
@@ -15502,11 +15718,11 @@ async function run5(args) {
|
|
|
15502
15718
|
entry.setAttribute("w15:done", "1");
|
|
15503
15719
|
else
|
|
15504
15720
|
delete entry.attributes["w15:done"];
|
|
15505
|
-
await saveDocView(view);
|
|
15721
|
+
await saveDocView(view, outputPath);
|
|
15506
15722
|
await respond({
|
|
15507
15723
|
ok: true,
|
|
15508
15724
|
operation: "comments.resolve",
|
|
15509
|
-
path,
|
|
15725
|
+
path: outputPath ?? path,
|
|
15510
15726
|
commentId: `c${numericId}`,
|
|
15511
15727
|
resolved
|
|
15512
15728
|
});
|
|
@@ -15522,6 +15738,7 @@ Required:
|
|
|
15522
15738
|
|
|
15523
15739
|
Optional:
|
|
15524
15740
|
--unset Mark unresolved instead of resolved
|
|
15741
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
15525
15742
|
--dry-run Print what would change; do not write the file
|
|
15526
15743
|
-h, --help Show this help
|
|
15527
15744
|
|
|
@@ -15536,147 +15753,15 @@ var init_resolve2 = __esm(() => {
|
|
|
15536
15753
|
init_helpers();
|
|
15537
15754
|
});
|
|
15538
15755
|
|
|
15539
|
-
// src/cli/comments/restore.ts
|
|
15540
|
-
var exports_restore = {};
|
|
15541
|
-
__export(exports_restore, {
|
|
15542
|
-
run: () => run6
|
|
15543
|
-
});
|
|
15544
|
-
import { parseArgs as parseArgs6 } from "util";
|
|
15545
|
-
async function run6(args) {
|
|
15546
|
-
let parsed;
|
|
15547
|
-
try {
|
|
15548
|
-
parsed = parseArgs6({
|
|
15549
|
-
args,
|
|
15550
|
-
allowPositionals: true,
|
|
15551
|
-
options: {
|
|
15552
|
-
id: { type: "string" },
|
|
15553
|
-
"dry-run": { type: "boolean" },
|
|
15554
|
-
help: { type: "boolean", short: "h" }
|
|
15555
|
-
}
|
|
15556
|
-
});
|
|
15557
|
-
} catch (parseError) {
|
|
15558
|
-
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
15559
|
-
return fail("USAGE", message, HELP6);
|
|
15560
|
-
}
|
|
15561
|
-
if (parsed.values.help) {
|
|
15562
|
-
await writeStdout(HELP6);
|
|
15563
|
-
return EXIT.OK;
|
|
15564
|
-
}
|
|
15565
|
-
const path = parsed.positionals[0];
|
|
15566
|
-
if (!path)
|
|
15567
|
-
return fail("USAGE", "Missing FILE argument", HELP6);
|
|
15568
|
-
const idInput = parsed.values.id;
|
|
15569
|
-
if (!idInput)
|
|
15570
|
-
return fail("USAGE", "Missing --id COMMENT_ID", HELP6);
|
|
15571
|
-
const commentId = idInput.startsWith("c") ? idInput : `c${idInput}`;
|
|
15572
|
-
const numericId = commentId.slice(1);
|
|
15573
|
-
if (parsed.values["dry-run"]) {
|
|
15574
|
-
await respond({
|
|
15575
|
-
ok: true,
|
|
15576
|
-
operation: "comments.restore",
|
|
15577
|
-
dryRun: true,
|
|
15578
|
-
path,
|
|
15579
|
-
commentId
|
|
15580
|
-
});
|
|
15581
|
-
return EXIT.OK;
|
|
15582
|
-
}
|
|
15583
|
-
const entry = await popTrashEntry(path, commentId);
|
|
15584
|
-
if (!entry) {
|
|
15585
|
-
return fail("COMMENT_NOT_FOUND", `No trashed entry for ${commentId} in ${path}`, "Trash lives at <dir>/.docx-cli/trash.json \u2014 make sure it's the same directory.");
|
|
15586
|
-
}
|
|
15587
|
-
if (entry.anchor.startBlockId !== entry.anchor.endBlockId) {
|
|
15588
|
-
return fail("USAGE", "Cross-block comment restore is not yet supported", "v1 supports restoring single-paragraph comment anchors only.");
|
|
15589
|
-
}
|
|
15590
|
-
let view;
|
|
15591
|
-
try {
|
|
15592
|
-
view = await openDocView(path);
|
|
15593
|
-
} catch (openError) {
|
|
15594
|
-
if (openError instanceof PkgError) {
|
|
15595
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
15596
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
15597
|
-
}
|
|
15598
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
15599
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
15600
|
-
}
|
|
15601
|
-
}
|
|
15602
|
-
throw openError;
|
|
15603
|
-
}
|
|
15604
|
-
const blockId = entry.anchor.startBlockId;
|
|
15605
|
-
const block = view.blockReferences.get(blockId);
|
|
15606
|
-
if (!block) {
|
|
15607
|
-
return fail("BLOCK_NOT_FOUND", `Original anchor block ${blockId} no longer exists`);
|
|
15608
|
-
}
|
|
15609
|
-
const span = {
|
|
15610
|
-
start: entry.anchor.startOffset,
|
|
15611
|
-
end: entry.anchor.endOffset
|
|
15612
|
-
};
|
|
15613
|
-
try {
|
|
15614
|
-
addCommentMarkersToParagraph(block.node, numericId, span);
|
|
15615
|
-
} catch (error) {
|
|
15616
|
-
if (error instanceof SpanOutOfRangeError) {
|
|
15617
|
-
return fail("INVALID_LOCATOR", `Saved span no longer fits the block: ${error.message}`);
|
|
15618
|
-
}
|
|
15619
|
-
throw error;
|
|
15620
|
-
}
|
|
15621
|
-
const commentNodes = XmlNode2.parse(entry.commentXml);
|
|
15622
|
-
const commentNode = commentNodes[0];
|
|
15623
|
-
if (!commentNode) {
|
|
15624
|
-
return fail("USAGE", "Trashed commentXml is empty");
|
|
15625
|
-
}
|
|
15626
|
-
const commentsRoot = ensureCommentsPart(view);
|
|
15627
|
-
commentsRoot.children.push(commentNode);
|
|
15628
|
-
if (entry.extXml) {
|
|
15629
|
-
const extNodes = XmlNode2.parse(entry.extXml);
|
|
15630
|
-
const extNode = extNodes[0];
|
|
15631
|
-
if (extNode) {
|
|
15632
|
-
const extRoot = ensureCommentsExtPart(view);
|
|
15633
|
-
extRoot.children.push(extNode);
|
|
15634
|
-
}
|
|
15635
|
-
}
|
|
15636
|
-
await saveDocView(view);
|
|
15637
|
-
await respond({
|
|
15638
|
-
ok: true,
|
|
15639
|
-
operation: "comments.restore",
|
|
15640
|
-
path,
|
|
15641
|
-
commentId
|
|
15642
|
-
});
|
|
15643
|
-
return EXIT.OK;
|
|
15644
|
-
}
|
|
15645
|
-
var HELP6 = `docx comments restore \u2014 undo a recent delete
|
|
15646
|
-
|
|
15647
|
-
Usage:
|
|
15648
|
-
docx comments restore FILE --id cN [options]
|
|
15649
|
-
|
|
15650
|
-
Required:
|
|
15651
|
-
--id ID Comment id to restore (e.g., c0)
|
|
15652
|
-
|
|
15653
|
-
Optional:
|
|
15654
|
-
--dry-run Print what would be restored; do not write the file
|
|
15655
|
-
-h, --help Show this help
|
|
15656
|
-
|
|
15657
|
-
Pulls the most recent matching entry from <dir>/.docx-cli/trash.json
|
|
15658
|
-
and re-anchors the comment at its original location.
|
|
15659
|
-
|
|
15660
|
-
Examples:
|
|
15661
|
-
docx comments restore doc.docx --id c2
|
|
15662
|
-
`;
|
|
15663
|
-
var init_restore = __esm(() => {
|
|
15664
|
-
init_core();
|
|
15665
|
-
init_parser();
|
|
15666
|
-
init_respond();
|
|
15667
|
-
init_helpers();
|
|
15668
|
-
init_trash();
|
|
15669
|
-
});
|
|
15670
|
-
|
|
15671
15756
|
// src/cli/comments/index.ts
|
|
15672
15757
|
var exports_comments = {};
|
|
15673
15758
|
__export(exports_comments, {
|
|
15674
|
-
run: () =>
|
|
15759
|
+
run: () => run6
|
|
15675
15760
|
});
|
|
15676
|
-
async function
|
|
15761
|
+
async function run6(args) {
|
|
15677
15762
|
const verb = args[0];
|
|
15678
15763
|
if (!verb || verb === "--help" || verb === "-h" || verb === "help") {
|
|
15679
|
-
await writeStdout(
|
|
15764
|
+
await writeStdout(HELP6);
|
|
15680
15765
|
return verb ? 0 : 2;
|
|
15681
15766
|
}
|
|
15682
15767
|
const loader = SUBCOMMANDS[verb];
|
|
@@ -15686,7 +15771,7 @@ async function run7(args) {
|
|
|
15686
15771
|
const module_ = await loader();
|
|
15687
15772
|
return module_.run(args.slice(1));
|
|
15688
15773
|
}
|
|
15689
|
-
var SUBCOMMANDS,
|
|
15774
|
+
var SUBCOMMANDS, HELP6 = `docx comments \u2014 manage Word comments
|
|
15690
15775
|
|
|
15691
15776
|
Usage:
|
|
15692
15777
|
docx comments <verb> FILE [options]
|
|
@@ -15697,7 +15782,6 @@ Verbs:
|
|
|
15697
15782
|
list Print existing comments as JSON
|
|
15698
15783
|
resolve Mark a comment resolved
|
|
15699
15784
|
delete Remove a comment
|
|
15700
|
-
restore Restore a recently deleted comment
|
|
15701
15785
|
|
|
15702
15786
|
Run "docx comments <verb> --help" for verb-specific help.
|
|
15703
15787
|
`;
|
|
@@ -15708,8 +15792,7 @@ var init_comments = __esm(() => {
|
|
|
15708
15792
|
delete: () => Promise.resolve().then(() => (init_delete(), exports_delete)),
|
|
15709
15793
|
list: () => Promise.resolve().then(() => (init_list(), exports_list)),
|
|
15710
15794
|
reply: () => Promise.resolve().then(() => (init_reply(), exports_reply)),
|
|
15711
|
-
resolve: () => Promise.resolve().then(() => (init_resolve2(), exports_resolve))
|
|
15712
|
-
restore: () => Promise.resolve().then(() => (init_restore(), exports_restore))
|
|
15795
|
+
resolve: () => Promise.resolve().then(() => (init_resolve2(), exports_resolve))
|
|
15713
15796
|
};
|
|
15714
15797
|
});
|
|
15715
15798
|
|
|
@@ -15805,13 +15888,13 @@ var init_template = __esm(() => {
|
|
|
15805
15888
|
// src/cli/create/index.tsx
|
|
15806
15889
|
var exports_create = {};
|
|
15807
15890
|
__export(exports_create, {
|
|
15808
|
-
run: () =>
|
|
15891
|
+
run: () => run7
|
|
15809
15892
|
});
|
|
15810
|
-
import { parseArgs as
|
|
15811
|
-
async function
|
|
15893
|
+
import { parseArgs as parseArgs6 } from "util";
|
|
15894
|
+
async function run7(args) {
|
|
15812
15895
|
let parsed;
|
|
15813
15896
|
try {
|
|
15814
|
-
parsed =
|
|
15897
|
+
parsed = parseArgs6({
|
|
15815
15898
|
args,
|
|
15816
15899
|
allowPositionals: true,
|
|
15817
15900
|
options: {
|
|
@@ -15823,20 +15906,20 @@ async function run8(args) {
|
|
|
15823
15906
|
}
|
|
15824
15907
|
});
|
|
15825
15908
|
} catch (e) {
|
|
15826
|
-
return fail("USAGE", e instanceof Error ? e.message : String(e),
|
|
15909
|
+
return fail("USAGE", e instanceof Error ? e.message : String(e), HELP7);
|
|
15827
15910
|
}
|
|
15828
15911
|
if (parsed.values.help) {
|
|
15829
|
-
await writeStdout(
|
|
15912
|
+
await writeStdout(HELP7);
|
|
15830
15913
|
return EXIT.OK;
|
|
15831
15914
|
}
|
|
15832
15915
|
const path = parsed.positionals[0];
|
|
15833
15916
|
if (!path) {
|
|
15834
|
-
return fail("USAGE", "Missing FILE argument",
|
|
15917
|
+
return fail("USAGE", "Missing FILE argument", HELP7);
|
|
15835
15918
|
}
|
|
15836
15919
|
if (await Bun.file(path).exists() && !parsed.values.force) {
|
|
15837
15920
|
return fail("USAGE", `File already exists: ${path}`, "Pass --force to overwrite.");
|
|
15838
15921
|
}
|
|
15839
|
-
const author = parsed.values.author ??
|
|
15922
|
+
const author = parsed.values.author ?? Bun.env.DOCX_AUTHOR ?? "";
|
|
15840
15923
|
const title = parsed.values.title ?? "";
|
|
15841
15924
|
const text = parsed.values.text;
|
|
15842
15925
|
const now = new Date().toISOString();
|
|
@@ -15851,7 +15934,7 @@ async function run8(args) {
|
|
|
15851
15934
|
compression: "DEFLATE",
|
|
15852
15935
|
compressionOptions: { level: 6 }
|
|
15853
15936
|
});
|
|
15854
|
-
await
|
|
15937
|
+
await writeAtomic(path, buf);
|
|
15855
15938
|
await respond({
|
|
15856
15939
|
ok: true,
|
|
15857
15940
|
operation: "create",
|
|
@@ -15861,7 +15944,7 @@ async function run8(args) {
|
|
|
15861
15944
|
});
|
|
15862
15945
|
return EXIT.OK;
|
|
15863
15946
|
}
|
|
15864
|
-
var import_jszip2,
|
|
15947
|
+
var import_jszip2, HELP7 = `docx create \u2014 create a new minimal .docx
|
|
15865
15948
|
|
|
15866
15949
|
Usage:
|
|
15867
15950
|
docx create FILE [options]
|
|
@@ -15878,86 +15961,116 @@ Examples:
|
|
|
15878
15961
|
docx create out.docx --title "Spec" --author "Claude" --text "First paragraph."
|
|
15879
15962
|
`;
|
|
15880
15963
|
var init_create = __esm(() => {
|
|
15964
|
+
init_package();
|
|
15881
15965
|
init_respond();
|
|
15882
15966
|
init_template();
|
|
15883
15967
|
import_jszip2 = __toESM(require_lib3(), 1);
|
|
15884
15968
|
});
|
|
15885
15969
|
|
|
15886
|
-
// src/cli/delete/index.
|
|
15970
|
+
// src/cli/delete/index.tsx
|
|
15887
15971
|
var exports_delete2 = {};
|
|
15888
15972
|
__export(exports_delete2, {
|
|
15889
|
-
run: () =>
|
|
15973
|
+
run: () => run8
|
|
15890
15974
|
});
|
|
15891
|
-
import { parseArgs as
|
|
15892
|
-
async function
|
|
15975
|
+
import { parseArgs as parseArgs7 } from "util";
|
|
15976
|
+
async function run8(args) {
|
|
15893
15977
|
let parsed;
|
|
15894
15978
|
try {
|
|
15895
|
-
parsed =
|
|
15979
|
+
parsed = parseArgs7({
|
|
15896
15980
|
args,
|
|
15897
15981
|
allowPositionals: true,
|
|
15898
15982
|
options: {
|
|
15899
15983
|
at: { type: "string" },
|
|
15984
|
+
output: { type: "string", short: "o" },
|
|
15900
15985
|
"dry-run": { type: "boolean" },
|
|
15901
15986
|
help: { type: "boolean", short: "h" }
|
|
15902
15987
|
}
|
|
15903
15988
|
});
|
|
15904
15989
|
} catch (parseError) {
|
|
15905
15990
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
15906
|
-
return fail("USAGE", message,
|
|
15991
|
+
return fail("USAGE", message, HELP8);
|
|
15907
15992
|
}
|
|
15908
15993
|
if (parsed.values.help) {
|
|
15909
|
-
await writeStdout(
|
|
15994
|
+
await writeStdout(HELP8);
|
|
15910
15995
|
return EXIT.OK;
|
|
15911
15996
|
}
|
|
15912
15997
|
const path = parsed.positionals[0];
|
|
15913
15998
|
if (!path)
|
|
15914
|
-
return fail("USAGE", "Missing FILE argument",
|
|
15999
|
+
return fail("USAGE", "Missing FILE argument", HELP8);
|
|
15915
16000
|
const at = parsed.values.at;
|
|
15916
16001
|
if (!at)
|
|
15917
|
-
return fail("USAGE", "Missing --at LOCATOR",
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
view
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
15925
|
-
}
|
|
15926
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
15927
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
15928
|
-
}
|
|
15929
|
-
}
|
|
15930
|
-
throw openError;
|
|
15931
|
-
}
|
|
15932
|
-
let blockRef;
|
|
15933
|
-
try {
|
|
15934
|
-
blockRef = resolveBlock(view, at);
|
|
15935
|
-
} catch (resolveError) {
|
|
15936
|
-
if (resolveError instanceof LocatorResolveError) {
|
|
15937
|
-
return fail("BLOCK_NOT_FOUND", resolveError.message);
|
|
15938
|
-
}
|
|
15939
|
-
throw resolveError;
|
|
15940
|
-
}
|
|
16002
|
+
return fail("USAGE", "Missing --at LOCATOR", HELP8);
|
|
16003
|
+
const view = await openOrFail(path);
|
|
16004
|
+
if (typeof view === "number")
|
|
16005
|
+
return view;
|
|
16006
|
+
const blockRef = await resolveBlockOrFail(view, at);
|
|
16007
|
+
if (typeof blockRef === "number")
|
|
16008
|
+
return blockRef;
|
|
15941
16009
|
const targetIndex = blockRef.parent.indexOf(blockRef.node);
|
|
15942
16010
|
if (targetIndex === -1) {
|
|
15943
16011
|
return fail("BLOCK_NOT_FOUND", "Block reference is stale (parent does not contain it)");
|
|
15944
16012
|
}
|
|
16013
|
+
const outputPath = parsed.values.output;
|
|
15945
16014
|
if (parsed.values["dry-run"]) {
|
|
15946
16015
|
await respond({
|
|
15947
16016
|
ok: true,
|
|
15948
16017
|
operation: "delete",
|
|
15949
16018
|
dryRun: true,
|
|
15950
16019
|
path,
|
|
15951
|
-
locator: at
|
|
16020
|
+
locator: at,
|
|
16021
|
+
...outputPath ? { output: outputPath } : {}
|
|
15952
16022
|
});
|
|
15953
16023
|
return EXIT.OK;
|
|
15954
16024
|
}
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
16025
|
+
if (isTrackChangesEnabled(view)) {
|
|
16026
|
+
if (blockRef.node.tag !== "w:p") {
|
|
16027
|
+
return fail("TRACKED_CHANGE_CONFLICT", "Tracked deletion of non-paragraph blocks (e.g., tables) is not supported", "Use `docx track-changes off` first, or delete table contents row-by-row.");
|
|
16028
|
+
}
|
|
16029
|
+
applyTrackedDeletion(view, blockRef.node);
|
|
16030
|
+
} else {
|
|
16031
|
+
blockRef.parent.splice(targetIndex, 1);
|
|
16032
|
+
}
|
|
16033
|
+
await saveDocView(view, outputPath);
|
|
16034
|
+
await respond({
|
|
16035
|
+
ok: true,
|
|
16036
|
+
operation: "delete",
|
|
16037
|
+
path: outputPath ?? path,
|
|
16038
|
+
locator: at
|
|
16039
|
+
});
|
|
15958
16040
|
return EXIT.OK;
|
|
15959
16041
|
}
|
|
15960
|
-
|
|
16042
|
+
function applyTrackedDeletion(view, paragraph) {
|
|
16043
|
+
const allocator = createRevisionAllocator(view);
|
|
16044
|
+
const baseMeta = { author: resolveAuthor(), date: resolveDate() };
|
|
16045
|
+
const mintMeta = () => ({
|
|
16046
|
+
...baseMeta,
|
|
16047
|
+
revisionId: allocator.next()
|
|
16048
|
+
});
|
|
16049
|
+
const newChildren = [];
|
|
16050
|
+
let runBuffer = [];
|
|
16051
|
+
const flush = () => {
|
|
16052
|
+
if (runBuffer.length === 0)
|
|
16053
|
+
return;
|
|
16054
|
+
const converted = runBuffer.map((run9) => convertTextToDelText(run9));
|
|
16055
|
+
newChildren.push(/* @__PURE__ */ jsxDEV(Del, {
|
|
16056
|
+
meta: mintMeta(),
|
|
16057
|
+
children: converted
|
|
16058
|
+
}, undefined, false, undefined, this));
|
|
16059
|
+
runBuffer = [];
|
|
16060
|
+
};
|
|
16061
|
+
for (const child of paragraph.children) {
|
|
16062
|
+
if (child.tag === "w:r") {
|
|
16063
|
+
runBuffer.push(child);
|
|
16064
|
+
continue;
|
|
16065
|
+
}
|
|
16066
|
+
flush();
|
|
16067
|
+
newChildren.push(child);
|
|
16068
|
+
}
|
|
16069
|
+
flush();
|
|
16070
|
+
paragraph.children = newChildren;
|
|
16071
|
+
markParagraphMarkAs(paragraph, "del", mintMeta());
|
|
16072
|
+
}
|
|
16073
|
+
var HELP8 = `docx delete \u2014 remove a block at a locator
|
|
15961
16074
|
|
|
15962
16075
|
Usage:
|
|
15963
16076
|
docx delete FILE [options]
|
|
@@ -15965,6 +16078,7 @@ Usage:
|
|
|
15965
16078
|
Locator (required):
|
|
15966
16079
|
--at LOCATOR Block to remove (e.g., p3, t0)
|
|
15967
16080
|
|
|
16081
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
15968
16082
|
--dry-run Print what would be removed; do not write the file
|
|
15969
16083
|
-h, --help Show this help
|
|
15970
16084
|
|
|
@@ -15975,6 +16089,7 @@ Examples:
|
|
|
15975
16089
|
var init_delete2 = __esm(() => {
|
|
15976
16090
|
init_core();
|
|
15977
16091
|
init_respond();
|
|
16092
|
+
init_jsx_dev_runtime();
|
|
15978
16093
|
});
|
|
15979
16094
|
|
|
15980
16095
|
// src/cli/insert/emit.tsx
|
|
@@ -15986,68 +16101,68 @@ function Paragraph(props) {
|
|
|
15986
16101
|
/* @__PURE__ */ jsxDEV(ParagraphProperties, {
|
|
15987
16102
|
options: { style, alignment }
|
|
15988
16103
|
}, undefined, false, undefined, this),
|
|
15989
|
-
runs.map((
|
|
15990
|
-
run:
|
|
16104
|
+
runs.map((run9) => /* @__PURE__ */ jsxDEV(RunElement, {
|
|
16105
|
+
run: run9
|
|
15991
16106
|
}, undefined, false, undefined, this))
|
|
15992
16107
|
]
|
|
15993
16108
|
}, undefined, true, undefined, this);
|
|
15994
16109
|
}
|
|
15995
|
-
function RunElement({ run:
|
|
15996
|
-
if (
|
|
16110
|
+
function RunElement({ run: run9 }) {
|
|
16111
|
+
if (run9.type === "text")
|
|
15997
16112
|
return /* @__PURE__ */ jsxDEV(TextRunElement, {
|
|
15998
|
-
run:
|
|
16113
|
+
run: run9
|
|
15999
16114
|
}, undefined, false, undefined, this);
|
|
16000
|
-
if (
|
|
16115
|
+
if (run9.type === "break") {
|
|
16001
16116
|
return /* @__PURE__ */ jsxDEV(w.r, {
|
|
16002
16117
|
children: /* @__PURE__ */ jsxDEV(w.br, {
|
|
16003
|
-
"w-type":
|
|
16118
|
+
"w-type": run9.kind === "line" ? undefined : run9.kind
|
|
16004
16119
|
}, undefined, false, undefined, this)
|
|
16005
16120
|
}, undefined, false, undefined, this);
|
|
16006
16121
|
}
|
|
16007
|
-
if (
|
|
16122
|
+
if (run9.type === "tab") {
|
|
16008
16123
|
return /* @__PURE__ */ jsxDEV(w.r, {
|
|
16009
16124
|
children: /* @__PURE__ */ jsxDEV(w.tab, {}, undefined, false, undefined, this)
|
|
16010
16125
|
}, undefined, false, undefined, this);
|
|
16011
16126
|
}
|
|
16012
|
-
throw new Error(`Cannot emit run of type "${
|
|
16127
|
+
throw new Error(`Cannot emit run of type "${run9.type}" \u2014 image emission lives in the images command.`);
|
|
16013
16128
|
}
|
|
16014
|
-
function TextRunElement({ run:
|
|
16129
|
+
function TextRunElement({ run: run9 }) {
|
|
16015
16130
|
return /* @__PURE__ */ jsxDEV(w.r, {
|
|
16016
16131
|
children: [
|
|
16017
16132
|
/* @__PURE__ */ jsxDEV(RunProperties, {
|
|
16018
|
-
run:
|
|
16133
|
+
run: run9
|
|
16019
16134
|
}, undefined, false, undefined, this),
|
|
16020
16135
|
/* @__PURE__ */ jsxDEV(w.t, {
|
|
16021
16136
|
"xml:space": "preserve",
|
|
16022
|
-
children:
|
|
16137
|
+
children: run9.text
|
|
16023
16138
|
}, undefined, false, undefined, this)
|
|
16024
16139
|
]
|
|
16025
16140
|
}, undefined, true, undefined, this);
|
|
16026
16141
|
}
|
|
16027
|
-
function RunProperties({ run:
|
|
16028
|
-
const isEmpty = FORMATTING_KEYS.every((key) =>
|
|
16142
|
+
function RunProperties({ run: run9 }) {
|
|
16143
|
+
const isEmpty = FORMATTING_KEYS.every((key) => run9[key] == null);
|
|
16029
16144
|
if (isEmpty)
|
|
16030
16145
|
return null;
|
|
16031
16146
|
return /* @__PURE__ */ jsxDEV(w.rPr, {
|
|
16032
16147
|
children: [
|
|
16033
|
-
|
|
16034
|
-
"w-val":
|
|
16148
|
+
run9.color && /* @__PURE__ */ jsxDEV(w.color, {
|
|
16149
|
+
"w-val": run9.color
|
|
16035
16150
|
}, undefined, false, undefined, this),
|
|
16036
|
-
|
|
16037
|
-
"w-val":
|
|
16151
|
+
run9.highlight && /* @__PURE__ */ jsxDEV(w.highlight, {
|
|
16152
|
+
"w-val": run9.highlight
|
|
16038
16153
|
}, undefined, false, undefined, this),
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
"w-val":
|
|
16154
|
+
run9.bold && /* @__PURE__ */ jsxDEV(w.b, {}, undefined, false, undefined, this),
|
|
16155
|
+
run9.italic && /* @__PURE__ */ jsxDEV(w.i, {}, undefined, false, undefined, this),
|
|
16156
|
+
run9.underline && /* @__PURE__ */ jsxDEV(w.u, {
|
|
16157
|
+
"w-val": run9.underline
|
|
16043
16158
|
}, undefined, false, undefined, this),
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
"w-ascii":
|
|
16047
|
-
"w-hAnsi":
|
|
16159
|
+
run9.strike && /* @__PURE__ */ jsxDEV(w.strike, {}, undefined, false, undefined, this),
|
|
16160
|
+
run9.font && /* @__PURE__ */ jsxDEV(w.rFonts, {
|
|
16161
|
+
"w-ascii": run9.font,
|
|
16162
|
+
"w-hAnsi": run9.font
|
|
16048
16163
|
}, undefined, false, undefined, this),
|
|
16049
|
-
|
|
16050
|
-
"w-val": String(
|
|
16164
|
+
run9.sizeHalfPoints !== undefined && /* @__PURE__ */ jsxDEV(w.sz, {
|
|
16165
|
+
"w-val": String(run9.sizeHalfPoints)
|
|
16051
16166
|
}, undefined, false, undefined, this)
|
|
16052
16167
|
]
|
|
16053
16168
|
}, undefined, true, undefined, this);
|
|
@@ -16079,7 +16194,7 @@ function textRunFromProps(props) {
|
|
|
16079
16194
|
return { type: "text", text, ...formatting };
|
|
16080
16195
|
}
|
|
16081
16196
|
var FORMATTING_KEYS;
|
|
16082
|
-
var
|
|
16197
|
+
var init_emit2 = __esm(() => {
|
|
16083
16198
|
init_jsx();
|
|
16084
16199
|
init_jsx_dev_runtime();
|
|
16085
16200
|
FORMATTING_KEYS = [
|
|
@@ -16097,13 +16212,13 @@ var init_emit = __esm(() => {
|
|
|
16097
16212
|
// src/cli/edit/index.tsx
|
|
16098
16213
|
var exports_edit = {};
|
|
16099
16214
|
__export(exports_edit, {
|
|
16100
|
-
run: () =>
|
|
16215
|
+
run: () => run9
|
|
16101
16216
|
});
|
|
16102
|
-
import { parseArgs as
|
|
16103
|
-
async function
|
|
16217
|
+
import { parseArgs as parseArgs8 } from "util";
|
|
16218
|
+
async function run9(args) {
|
|
16104
16219
|
let parsed;
|
|
16105
16220
|
try {
|
|
16106
|
-
parsed =
|
|
16221
|
+
parsed = parseArgs8({
|
|
16107
16222
|
args,
|
|
16108
16223
|
allowPositionals: true,
|
|
16109
16224
|
options: {
|
|
@@ -16115,31 +16230,32 @@ async function run10(args) {
|
|
|
16115
16230
|
color: { type: "string" },
|
|
16116
16231
|
bold: { type: "boolean" },
|
|
16117
16232
|
italic: { type: "boolean" },
|
|
16233
|
+
output: { type: "string", short: "o" },
|
|
16118
16234
|
"dry-run": { type: "boolean" },
|
|
16119
16235
|
help: { type: "boolean", short: "h" }
|
|
16120
16236
|
}
|
|
16121
16237
|
});
|
|
16122
16238
|
} catch (parseError) {
|
|
16123
16239
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
16124
|
-
return fail("USAGE", message,
|
|
16240
|
+
return fail("USAGE", message, HELP9);
|
|
16125
16241
|
}
|
|
16126
16242
|
if (parsed.values.help) {
|
|
16127
|
-
await writeStdout(
|
|
16243
|
+
await writeStdout(HELP9);
|
|
16128
16244
|
return EXIT.OK;
|
|
16129
16245
|
}
|
|
16130
16246
|
const path = parsed.positionals[0];
|
|
16131
16247
|
if (!path)
|
|
16132
|
-
return fail("USAGE", "Missing FILE argument",
|
|
16248
|
+
return fail("USAGE", "Missing FILE argument", HELP9);
|
|
16133
16249
|
const at = parsed.values.at;
|
|
16134
16250
|
if (!at)
|
|
16135
|
-
return fail("USAGE", "Missing --at LOCATOR",
|
|
16251
|
+
return fail("USAGE", "Missing --at LOCATOR", HELP9);
|
|
16136
16252
|
const text = parsed.values.text;
|
|
16137
16253
|
const runsJson = parsed.values.runs;
|
|
16138
16254
|
if (!text && !runsJson) {
|
|
16139
|
-
return fail("USAGE", "Missing content: pass --text or --runs",
|
|
16255
|
+
return fail("USAGE", "Missing content: pass --text or --runs", HELP9);
|
|
16140
16256
|
}
|
|
16141
16257
|
if (text && runsJson) {
|
|
16142
|
-
return fail("USAGE", "Pass either --text or --runs, not both",
|
|
16258
|
+
return fail("USAGE", "Pass either --text or --runs, not both", HELP9);
|
|
16143
16259
|
}
|
|
16144
16260
|
const paragraphOptions = {};
|
|
16145
16261
|
const styleValue = parsed.values.style;
|
|
@@ -16152,29 +16268,12 @@ async function run10(args) {
|
|
|
16152
16268
|
}
|
|
16153
16269
|
paragraphOptions.alignment = alignmentValue;
|
|
16154
16270
|
}
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
view
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
16162
|
-
}
|
|
16163
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
16164
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
16165
|
-
}
|
|
16166
|
-
}
|
|
16167
|
-
throw openError;
|
|
16168
|
-
}
|
|
16169
|
-
let blockRef;
|
|
16170
|
-
try {
|
|
16171
|
-
blockRef = resolveBlock(view, at);
|
|
16172
|
-
} catch (resolveError) {
|
|
16173
|
-
if (resolveError instanceof LocatorResolveError) {
|
|
16174
|
-
return fail("BLOCK_NOT_FOUND", resolveError.message);
|
|
16175
|
-
}
|
|
16176
|
-
throw resolveError;
|
|
16177
|
-
}
|
|
16271
|
+
const view = await openOrFail(path);
|
|
16272
|
+
if (typeof view === "number")
|
|
16273
|
+
return view;
|
|
16274
|
+
const blockRef = await resolveBlockOrFail(view, at);
|
|
16275
|
+
if (typeof blockRef === "number")
|
|
16276
|
+
return blockRef;
|
|
16178
16277
|
let paragraphNode;
|
|
16179
16278
|
if (text !== undefined) {
|
|
16180
16279
|
const color = parsed.values.color;
|
|
@@ -16205,22 +16304,81 @@ async function run10(args) {
|
|
|
16205
16304
|
if (targetIndex === -1) {
|
|
16206
16305
|
return fail("BLOCK_NOT_FOUND", "Block reference is stale (parent does not contain it)");
|
|
16207
16306
|
}
|
|
16307
|
+
const outputPath = parsed.values.output;
|
|
16208
16308
|
if (parsed.values["dry-run"]) {
|
|
16209
16309
|
await respond({
|
|
16210
16310
|
ok: true,
|
|
16211
16311
|
operation: "edit",
|
|
16212
16312
|
dryRun: true,
|
|
16213
16313
|
path,
|
|
16214
|
-
locator: at
|
|
16314
|
+
locator: at,
|
|
16315
|
+
...outputPath ? { output: outputPath } : {}
|
|
16215
16316
|
});
|
|
16216
16317
|
return EXIT.OK;
|
|
16217
16318
|
}
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16319
|
+
if (isTrackChangesEnabled(view)) {
|
|
16320
|
+
applyTrackedEdit(view, blockRef.node, paragraphNode);
|
|
16321
|
+
} else {
|
|
16322
|
+
blockRef.parent.splice(targetIndex, 1, paragraphNode);
|
|
16323
|
+
}
|
|
16324
|
+
await saveDocView(view, outputPath);
|
|
16325
|
+
await respond({
|
|
16326
|
+
ok: true,
|
|
16327
|
+
operation: "edit",
|
|
16328
|
+
path: outputPath ?? path,
|
|
16329
|
+
locator: at
|
|
16330
|
+
});
|
|
16221
16331
|
return EXIT.OK;
|
|
16222
16332
|
}
|
|
16223
|
-
|
|
16333
|
+
function applyTrackedEdit(view, existingParagraph, newParagraph) {
|
|
16334
|
+
const allocator = createRevisionAllocator(view);
|
|
16335
|
+
const baseMeta = { author: resolveAuthor(), date: resolveDate() };
|
|
16336
|
+
const mintMeta = () => ({
|
|
16337
|
+
...baseMeta,
|
|
16338
|
+
revisionId: allocator.next()
|
|
16339
|
+
});
|
|
16340
|
+
const oldRuns = [];
|
|
16341
|
+
const oldNonRuns = [];
|
|
16342
|
+
for (const child of existingParagraph.children) {
|
|
16343
|
+
if (child.tag === "w:r")
|
|
16344
|
+
oldRuns.push(child);
|
|
16345
|
+
else
|
|
16346
|
+
oldNonRuns.push(child);
|
|
16347
|
+
}
|
|
16348
|
+
let newPPr = null;
|
|
16349
|
+
const newRuns = [];
|
|
16350
|
+
for (const child of newParagraph.children) {
|
|
16351
|
+
if (child.tag === "w:pPr")
|
|
16352
|
+
newPPr = child;
|
|
16353
|
+
else if (child.tag === "w:r")
|
|
16354
|
+
newRuns.push(child);
|
|
16355
|
+
}
|
|
16356
|
+
const rebuilt = [];
|
|
16357
|
+
if (newPPr) {
|
|
16358
|
+
rebuilt.push(newPPr);
|
|
16359
|
+
for (const child of oldNonRuns) {
|
|
16360
|
+
if (child.tag !== "w:pPr")
|
|
16361
|
+
rebuilt.push(child);
|
|
16362
|
+
}
|
|
16363
|
+
} else {
|
|
16364
|
+
rebuilt.push(...oldNonRuns);
|
|
16365
|
+
}
|
|
16366
|
+
if (oldRuns.length > 0) {
|
|
16367
|
+
const deletedRuns = oldRuns.map((run10) => convertTextToDelText(run10));
|
|
16368
|
+
rebuilt.push(/* @__PURE__ */ jsxDEV(Del, {
|
|
16369
|
+
meta: mintMeta(),
|
|
16370
|
+
children: deletedRuns
|
|
16371
|
+
}, undefined, false, undefined, this));
|
|
16372
|
+
}
|
|
16373
|
+
if (newRuns.length > 0) {
|
|
16374
|
+
rebuilt.push(/* @__PURE__ */ jsxDEV(Ins, {
|
|
16375
|
+
meta: mintMeta(),
|
|
16376
|
+
children: newRuns
|
|
16377
|
+
}, undefined, false, undefined, this));
|
|
16378
|
+
}
|
|
16379
|
+
existingParagraph.children = rebuilt;
|
|
16380
|
+
}
|
|
16381
|
+
var HELP9 = `docx edit \u2014 replace a paragraph at a locator
|
|
16224
16382
|
|
|
16225
16383
|
Usage:
|
|
16226
16384
|
docx edit FILE [options]
|
|
@@ -16241,6 +16399,7 @@ Run options (only with --text):
|
|
|
16241
16399
|
--bold Bold
|
|
16242
16400
|
--italic Italic
|
|
16243
16401
|
|
|
16402
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
16244
16403
|
--dry-run Print what would change; do not write the file
|
|
16245
16404
|
-h, --help Show this help
|
|
16246
16405
|
|
|
@@ -16250,17 +16409,232 @@ Examples:
|
|
|
16250
16409
|
`;
|
|
16251
16410
|
var init_edit = __esm(() => {
|
|
16252
16411
|
init_core();
|
|
16253
|
-
|
|
16412
|
+
init_emit2();
|
|
16254
16413
|
init_respond();
|
|
16255
16414
|
init_jsx_dev_runtime();
|
|
16256
16415
|
});
|
|
16257
16416
|
|
|
16417
|
+
// src/core/find/index.ts
|
|
16418
|
+
function findTextSpans(doc, query, options = {}) {
|
|
16419
|
+
const matcher = options.regex ? regexMatcher(query, options.ignoreCase ?? false) : literalMatcher(query, options.ignoreCase ?? false);
|
|
16420
|
+
const out = [];
|
|
16421
|
+
collectMatches(doc.blocks, matcher, out);
|
|
16422
|
+
return out;
|
|
16423
|
+
}
|
|
16424
|
+
function literalMatcher(query, ignoreCase) {
|
|
16425
|
+
if (query.length === 0) {
|
|
16426
|
+
throw new Error("query cannot be empty");
|
|
16427
|
+
}
|
|
16428
|
+
const needle = ignoreCase ? query.toLowerCase() : query;
|
|
16429
|
+
return (paragraphText) => {
|
|
16430
|
+
const haystack = ignoreCase ? paragraphText.toLowerCase() : paragraphText;
|
|
16431
|
+
const matches = [];
|
|
16432
|
+
let cursor = haystack.indexOf(needle);
|
|
16433
|
+
while (cursor !== -1) {
|
|
16434
|
+
matches.push({
|
|
16435
|
+
start: cursor,
|
|
16436
|
+
end: cursor + needle.length,
|
|
16437
|
+
text: paragraphText.slice(cursor, cursor + needle.length)
|
|
16438
|
+
});
|
|
16439
|
+
cursor = haystack.indexOf(needle, cursor + needle.length);
|
|
16440
|
+
}
|
|
16441
|
+
return matches;
|
|
16442
|
+
};
|
|
16443
|
+
}
|
|
16444
|
+
function regexMatcher(pattern, ignoreCase) {
|
|
16445
|
+
const flags = `g${ignoreCase ? "i" : ""}`;
|
|
16446
|
+
const regex = new RegExp(pattern, flags);
|
|
16447
|
+
return (paragraphText) => {
|
|
16448
|
+
const matches = [];
|
|
16449
|
+
regex.lastIndex = 0;
|
|
16450
|
+
let result = regex.exec(paragraphText);
|
|
16451
|
+
while (result !== null) {
|
|
16452
|
+
const matched = result[0];
|
|
16453
|
+
if (matched.length === 0) {
|
|
16454
|
+
regex.lastIndex += 1;
|
|
16455
|
+
result = regex.exec(paragraphText);
|
|
16456
|
+
continue;
|
|
16457
|
+
}
|
|
16458
|
+
matches.push({
|
|
16459
|
+
start: result.index,
|
|
16460
|
+
end: result.index + matched.length,
|
|
16461
|
+
text: matched
|
|
16462
|
+
});
|
|
16463
|
+
result = regex.exec(paragraphText);
|
|
16464
|
+
}
|
|
16465
|
+
return matches;
|
|
16466
|
+
};
|
|
16467
|
+
}
|
|
16468
|
+
function collectMatches(blocks, matcher, out) {
|
|
16469
|
+
for (const block of blocks) {
|
|
16470
|
+
if (block.type === "paragraph") {
|
|
16471
|
+
const paragraphText = block.runs.map((run10) => run10.type === "text" ? run10.text : "").join("");
|
|
16472
|
+
for (const span of matcher(paragraphText)) {
|
|
16473
|
+
const match = {
|
|
16474
|
+
blockId: block.id,
|
|
16475
|
+
start: span.start,
|
|
16476
|
+
end: span.end,
|
|
16477
|
+
text: span.text
|
|
16478
|
+
};
|
|
16479
|
+
const overlaps = trackedChangesOverlapping(block, span.start, span.end);
|
|
16480
|
+
if (overlaps.length > 0)
|
|
16481
|
+
match.trackedChanges = overlaps;
|
|
16482
|
+
out.push(match);
|
|
16483
|
+
}
|
|
16484
|
+
continue;
|
|
16485
|
+
}
|
|
16486
|
+
if (block.type === "table") {
|
|
16487
|
+
for (const row of block.rows) {
|
|
16488
|
+
for (const cell of row.cells) {
|
|
16489
|
+
collectMatches(cell.blocks, matcher, out);
|
|
16490
|
+
}
|
|
16491
|
+
}
|
|
16492
|
+
}
|
|
16493
|
+
}
|
|
16494
|
+
}
|
|
16495
|
+
function trackedChangesOverlapping(paragraph, start, end) {
|
|
16496
|
+
const seen = new Set;
|
|
16497
|
+
const out = [];
|
|
16498
|
+
let offset = 0;
|
|
16499
|
+
for (const run10 of paragraph.runs) {
|
|
16500
|
+
const length = run10.type === "text" ? run10.text.length : 0;
|
|
16501
|
+
const runStart = offset;
|
|
16502
|
+
const runEnd = offset + length;
|
|
16503
|
+
offset = runEnd;
|
|
16504
|
+
if (run10.type !== "text")
|
|
16505
|
+
continue;
|
|
16506
|
+
if (runEnd <= start || runStart >= end)
|
|
16507
|
+
continue;
|
|
16508
|
+
const change = run10.trackedChange;
|
|
16509
|
+
if (!change)
|
|
16510
|
+
continue;
|
|
16511
|
+
const key = `${change.kind}:${change.revisionId}:${change.author}`;
|
|
16512
|
+
if (seen.has(key))
|
|
16513
|
+
continue;
|
|
16514
|
+
seen.add(key);
|
|
16515
|
+
out.push(change);
|
|
16516
|
+
}
|
|
16517
|
+
return out;
|
|
16518
|
+
}
|
|
16519
|
+
|
|
16520
|
+
// src/cli/find/index.ts
|
|
16521
|
+
var exports_find = {};
|
|
16522
|
+
__export(exports_find, {
|
|
16523
|
+
run: () => run10
|
|
16524
|
+
});
|
|
16525
|
+
import { parseArgs as parseArgs9 } from "util";
|
|
16526
|
+
async function run10(args) {
|
|
16527
|
+
let parsed;
|
|
16528
|
+
try {
|
|
16529
|
+
parsed = parseArgs9({
|
|
16530
|
+
args,
|
|
16531
|
+
allowPositionals: true,
|
|
16532
|
+
options: {
|
|
16533
|
+
regex: { type: "boolean" },
|
|
16534
|
+
"ignore-case": { type: "boolean" },
|
|
16535
|
+
all: { type: "boolean" },
|
|
16536
|
+
nth: { type: "string" },
|
|
16537
|
+
help: { type: "boolean", short: "h" }
|
|
16538
|
+
}
|
|
16539
|
+
});
|
|
16540
|
+
} catch (parseError) {
|
|
16541
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
16542
|
+
return fail("USAGE", message, HELP10);
|
|
16543
|
+
}
|
|
16544
|
+
if (parsed.values.help) {
|
|
16545
|
+
await writeStdout(HELP10);
|
|
16546
|
+
return EXIT.OK;
|
|
16547
|
+
}
|
|
16548
|
+
const path = parsed.positionals[0];
|
|
16549
|
+
const query = parsed.positionals[1];
|
|
16550
|
+
if (!path)
|
|
16551
|
+
return fail("USAGE", "Missing FILE argument", HELP10);
|
|
16552
|
+
if (query == null)
|
|
16553
|
+
return fail("USAGE", "Missing QUERY argument", HELP10);
|
|
16554
|
+
const ignoreCase = Boolean(parsed.values["ignore-case"]);
|
|
16555
|
+
const useRegex = Boolean(parsed.values.regex);
|
|
16556
|
+
const wantAll = Boolean(parsed.values.all);
|
|
16557
|
+
const nthRaw = parsed.values.nth;
|
|
16558
|
+
const nth = nthRaw === undefined ? undefined : Number(nthRaw);
|
|
16559
|
+
if (nth !== undefined && (!Number.isInteger(nth) || nth < 0)) {
|
|
16560
|
+
return fail("USAGE", `--nth must be a non-negative integer, got "${nthRaw}"`);
|
|
16561
|
+
}
|
|
16562
|
+
const view = await openOrFail(path);
|
|
16563
|
+
if (typeof view === "number")
|
|
16564
|
+
return view;
|
|
16565
|
+
let allMatches;
|
|
16566
|
+
try {
|
|
16567
|
+
allMatches = findTextSpans(view.doc, query, {
|
|
16568
|
+
regex: useRegex,
|
|
16569
|
+
ignoreCase
|
|
16570
|
+
});
|
|
16571
|
+
} catch (matcherError) {
|
|
16572
|
+
const message = matcherError instanceof Error ? matcherError.message : String(matcherError);
|
|
16573
|
+
return fail("USAGE", `Invalid query: ${message}`);
|
|
16574
|
+
}
|
|
16575
|
+
let selected;
|
|
16576
|
+
if (nth !== undefined) {
|
|
16577
|
+
const single = allMatches[nth];
|
|
16578
|
+
if (!single) {
|
|
16579
|
+
return fail("MATCH_NOT_FOUND", `Only ${allMatches.length} match(es); --nth ${nth} is out of range`);
|
|
16580
|
+
}
|
|
16581
|
+
selected = [single];
|
|
16582
|
+
} else if (wantAll) {
|
|
16583
|
+
selected = allMatches;
|
|
16584
|
+
} else {
|
|
16585
|
+
selected = allMatches.slice(0, 1);
|
|
16586
|
+
}
|
|
16587
|
+
await respond({
|
|
16588
|
+
ok: true,
|
|
16589
|
+
operation: "find",
|
|
16590
|
+
path,
|
|
16591
|
+
query,
|
|
16592
|
+
regex: useRegex,
|
|
16593
|
+
ignoreCase,
|
|
16594
|
+
totalMatches: allMatches.length,
|
|
16595
|
+
matches: selected.map((match) => ({
|
|
16596
|
+
locator: `${match.blockId}:${match.start}-${match.end}`,
|
|
16597
|
+
...match
|
|
16598
|
+
}))
|
|
16599
|
+
});
|
|
16600
|
+
return EXIT.OK;
|
|
16601
|
+
}
|
|
16602
|
+
var HELP10 = `docx find \u2014 locate text spans and return their locators
|
|
16603
|
+
|
|
16604
|
+
Usage:
|
|
16605
|
+
docx find FILE QUERY [options]
|
|
16606
|
+
|
|
16607
|
+
Required positional:
|
|
16608
|
+
QUERY literal substring (or regex if --regex)
|
|
16609
|
+
|
|
16610
|
+
Options:
|
|
16611
|
+
--regex treat QUERY as a JavaScript regular expression
|
|
16612
|
+
--ignore-case case-insensitive match
|
|
16613
|
+
--all return every match (default: just the first)
|
|
16614
|
+
--nth N return only the Nth match (0-indexed)
|
|
16615
|
+
-h, --help show this help
|
|
16616
|
+
|
|
16617
|
+
Within-paragraph matches only \u2014 cross-paragraph ranges aren't supported
|
|
16618
|
+
yet. Searches the concatenated text of each paragraph in document order,
|
|
16619
|
+
including paragraphs nested in table cells (locators look like
|
|
16620
|
+
tT:rRcC:pK:S-E for those).
|
|
16621
|
+
|
|
16622
|
+
Examples:
|
|
16623
|
+
docx find doc.docx "fox"
|
|
16624
|
+
docx find doc.docx "Action Item:" --all
|
|
16625
|
+
docx find doc.docx "TODO|FIXME" --regex --ignore-case
|
|
16626
|
+
docx comments add doc.docx --range "$(docx find doc.docx fox | jq -r .matches[0].locator)" --text "..."
|
|
16627
|
+
`;
|
|
16628
|
+
var init_find = __esm(() => {
|
|
16629
|
+
init_respond();
|
|
16630
|
+
});
|
|
16631
|
+
|
|
16258
16632
|
// src/cli/images/extract.ts
|
|
16259
16633
|
var exports_extract = {};
|
|
16260
16634
|
__export(exports_extract, {
|
|
16261
16635
|
run: () => run11
|
|
16262
16636
|
});
|
|
16263
|
-
import { join
|
|
16637
|
+
import { join } from "path";
|
|
16264
16638
|
import { parseArgs as parseArgs10 } from "util";
|
|
16265
16639
|
async function run11(args) {
|
|
16266
16640
|
let parsed;
|
|
@@ -16289,20 +16663,9 @@ async function run11(args) {
|
|
|
16289
16663
|
if (!outputDir)
|
|
16290
16664
|
return fail("USAGE", "Missing --to DIR", HELP11);
|
|
16291
16665
|
const targetId = parsed.values.id;
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
view
|
|
16295
|
-
} catch (openError) {
|
|
16296
|
-
if (openError instanceof PkgError) {
|
|
16297
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
16298
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
16299
|
-
}
|
|
16300
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
16301
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
16302
|
-
}
|
|
16303
|
-
}
|
|
16304
|
-
throw openError;
|
|
16305
|
-
}
|
|
16666
|
+
const view = await openOrFail(path);
|
|
16667
|
+
if (typeof view === "number")
|
|
16668
|
+
return view;
|
|
16306
16669
|
await enrichImageHashes(view);
|
|
16307
16670
|
const allImages = [];
|
|
16308
16671
|
collectImages(view.doc.blocks, allImages);
|
|
@@ -16318,7 +16681,7 @@ async function run11(args) {
|
|
|
16318
16681
|
continue;
|
|
16319
16682
|
const extension = extensionFor(image.contentType, reference.partName);
|
|
16320
16683
|
const fileName = `${image.hash}.${extension}`;
|
|
16321
|
-
const outputPath =
|
|
16684
|
+
const outputPath = join(outputDir, fileName);
|
|
16322
16685
|
if (!seenHashes.has(image.hash)) {
|
|
16323
16686
|
const bytes = await view.pkg.readBytes(reference.partName);
|
|
16324
16687
|
await Bun.write(outputPath, bytes);
|
|
@@ -16423,20 +16786,9 @@ async function run12(args) {
|
|
|
16423
16786
|
const path = parsed.positionals[0];
|
|
16424
16787
|
if (!path)
|
|
16425
16788
|
return fail("USAGE", "Missing FILE argument", HELP12);
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
view
|
|
16429
|
-
} catch (openError) {
|
|
16430
|
-
if (openError instanceof PkgError) {
|
|
16431
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
16432
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
16433
|
-
}
|
|
16434
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
16435
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
16436
|
-
}
|
|
16437
|
-
}
|
|
16438
|
-
throw openError;
|
|
16439
|
-
}
|
|
16789
|
+
const view = await openOrFail(path);
|
|
16790
|
+
if (typeof view === "number")
|
|
16791
|
+
return view;
|
|
16440
16792
|
await enrichImageHashes(view);
|
|
16441
16793
|
const images = [];
|
|
16442
16794
|
collectImages2(view.doc.blocks, images);
|
|
@@ -16492,6 +16844,7 @@ async function run13(args) {
|
|
|
16492
16844
|
options: {
|
|
16493
16845
|
at: { type: "string" },
|
|
16494
16846
|
with: { type: "string" },
|
|
16847
|
+
output: { type: "string", short: "o" },
|
|
16495
16848
|
"dry-run": { type: "boolean" },
|
|
16496
16849
|
help: { type: "boolean", short: "h" }
|
|
16497
16850
|
}
|
|
@@ -16522,26 +16875,16 @@ async function run13(args) {
|
|
|
16522
16875
|
if (!newExtension) {
|
|
16523
16876
|
return fail("USAGE", `Unsupported replacement image type: ${newMimeType}`, "Supported: png, jpeg, gif, webp, bmp, tiff, svg, emf, wmf, ico.");
|
|
16524
16877
|
}
|
|
16525
|
-
|
|
16526
|
-
|
|
16527
|
-
view
|
|
16528
|
-
} catch (openError) {
|
|
16529
|
-
if (openError instanceof PkgError) {
|
|
16530
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
16531
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
16532
|
-
}
|
|
16533
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
16534
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
16535
|
-
}
|
|
16536
|
-
}
|
|
16537
|
-
throw openError;
|
|
16538
|
-
}
|
|
16878
|
+
const view = await openOrFail(path);
|
|
16879
|
+
if (typeof view === "number")
|
|
16880
|
+
return view;
|
|
16539
16881
|
const reference = view.imageById.get(targetId);
|
|
16540
16882
|
if (!reference) {
|
|
16541
16883
|
return fail("IMAGE_NOT_FOUND", `Image not found: ${targetId}`);
|
|
16542
16884
|
}
|
|
16543
16885
|
const originalPartName = reference.partName;
|
|
16544
16886
|
const newPartName = renameExtension(originalPartName, newExtension);
|
|
16887
|
+
const outputPath = parsed.values.output;
|
|
16545
16888
|
if (parsed.values["dry-run"]) {
|
|
16546
16889
|
await respond({
|
|
16547
16890
|
ok: true,
|
|
@@ -16550,7 +16893,8 @@ async function run13(args) {
|
|
|
16550
16893
|
path,
|
|
16551
16894
|
imageId: targetId,
|
|
16552
16895
|
from: { partName: originalPartName, mimeType: reference.contentType },
|
|
16553
|
-
to: { partName: newPartName, mimeType: newMimeType }
|
|
16896
|
+
to: { partName: newPartName, mimeType: newMimeType },
|
|
16897
|
+
...outputPath ? { output: outputPath } : {}
|
|
16554
16898
|
});
|
|
16555
16899
|
return EXIT.OK;
|
|
16556
16900
|
}
|
|
@@ -16565,11 +16909,11 @@ async function run13(args) {
|
|
|
16565
16909
|
reference.partName = newPartName;
|
|
16566
16910
|
reference.contentType = newMimeType;
|
|
16567
16911
|
}
|
|
16568
|
-
await saveDocView(view);
|
|
16912
|
+
await saveDocView(view, outputPath);
|
|
16569
16913
|
await respond({
|
|
16570
16914
|
ok: true,
|
|
16571
16915
|
operation: "images.replace",
|
|
16572
|
-
path,
|
|
16916
|
+
path: outputPath ?? path,
|
|
16573
16917
|
imageId: targetId,
|
|
16574
16918
|
partName: newPartName,
|
|
16575
16919
|
mimeType: newMimeType,
|
|
@@ -16623,6 +16967,7 @@ Required:
|
|
|
16623
16967
|
--with PATH New image file (any image MIME type)
|
|
16624
16968
|
|
|
16625
16969
|
Optional:
|
|
16970
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
16626
16971
|
--dry-run Print what would change; do not write the file
|
|
16627
16972
|
-h, --help Show this help
|
|
16628
16973
|
|
|
@@ -16691,9 +17036,118 @@ var init_images = __esm(() => {
|
|
|
16691
17036
|
};
|
|
16692
17037
|
});
|
|
16693
17038
|
|
|
16694
|
-
// src/
|
|
16695
|
-
var
|
|
16696
|
-
|
|
17039
|
+
// src/core/ast/types.ts
|
|
17040
|
+
var types_default = `export type Doc = {
|
|
17041
|
+
schemaVersion: 1;
|
|
17042
|
+
path: string;
|
|
17043
|
+
properties: DocProperties;
|
|
17044
|
+
blocks: Block[];
|
|
17045
|
+
comments: Comment[];
|
|
17046
|
+
};
|
|
17047
|
+
|
|
17048
|
+
export type DocProperties = {
|
|
17049
|
+
title?: string;
|
|
17050
|
+
author?: string;
|
|
17051
|
+
created?: string;
|
|
17052
|
+
modified?: string;
|
|
17053
|
+
};
|
|
17054
|
+
|
|
17055
|
+
export type Block = Paragraph | Table | SectionBreak;
|
|
17056
|
+
|
|
17057
|
+
export type Paragraph = {
|
|
17058
|
+
id: string;
|
|
17059
|
+
type: "paragraph";
|
|
17060
|
+
style?: string;
|
|
17061
|
+
alignment?: "left" | "center" | "right" | "justify";
|
|
17062
|
+
list?: { level: number; numId: string };
|
|
17063
|
+
runs: Run[];
|
|
17064
|
+
};
|
|
17065
|
+
|
|
17066
|
+
export type Table = {
|
|
17067
|
+
id: string;
|
|
17068
|
+
type: "table";
|
|
17069
|
+
rows: TableRow[];
|
|
17070
|
+
};
|
|
17071
|
+
|
|
17072
|
+
export type TableRow = {
|
|
17073
|
+
cells: TableCell[];
|
|
17074
|
+
};
|
|
17075
|
+
|
|
17076
|
+
export type TableCell = {
|
|
17077
|
+
blocks: Block[];
|
|
17078
|
+
};
|
|
17079
|
+
|
|
17080
|
+
export type SectionBreak = {
|
|
17081
|
+
id: string;
|
|
17082
|
+
type: "sectionBreak";
|
|
17083
|
+
};
|
|
17084
|
+
|
|
17085
|
+
export type Run = TextRun | ImageRun | BreakRun | TabRun;
|
|
17086
|
+
|
|
17087
|
+
export type TextRun = {
|
|
17088
|
+
type: "text";
|
|
17089
|
+
text: string;
|
|
17090
|
+
color?: string;
|
|
17091
|
+
highlight?: string;
|
|
17092
|
+
bold?: boolean;
|
|
17093
|
+
italic?: boolean;
|
|
17094
|
+
underline?: string;
|
|
17095
|
+
strike?: boolean;
|
|
17096
|
+
font?: string;
|
|
17097
|
+
sizeHalfPoints?: number;
|
|
17098
|
+
comments?: string[];
|
|
17099
|
+
trackedChange?: TrackedChange;
|
|
17100
|
+
};
|
|
17101
|
+
|
|
17102
|
+
export type ImageRun = {
|
|
17103
|
+
type: "image";
|
|
17104
|
+
id: string;
|
|
17105
|
+
contentType: string;
|
|
17106
|
+
hash: string;
|
|
17107
|
+
widthEmu?: number;
|
|
17108
|
+
heightEmu?: number;
|
|
17109
|
+
alt?: string;
|
|
17110
|
+
};
|
|
17111
|
+
|
|
17112
|
+
export type BreakRun = {
|
|
17113
|
+
type: "break";
|
|
17114
|
+
kind: "page" | "line" | "column";
|
|
17115
|
+
};
|
|
17116
|
+
|
|
17117
|
+
export type TabRun = {
|
|
17118
|
+
type: "tab";
|
|
17119
|
+
};
|
|
17120
|
+
|
|
17121
|
+
export type TrackedChange = {
|
|
17122
|
+
kind: "ins" | "del";
|
|
17123
|
+
author: string;
|
|
17124
|
+
date: string;
|
|
17125
|
+
revisionId: string;
|
|
17126
|
+
};
|
|
17127
|
+
|
|
17128
|
+
export type Comment = {
|
|
17129
|
+
id: string;
|
|
17130
|
+
author: string;
|
|
17131
|
+
initials?: string;
|
|
17132
|
+
date: string;
|
|
17133
|
+
text: string;
|
|
17134
|
+
parentId?: string;
|
|
17135
|
+
resolved?: boolean;
|
|
17136
|
+
anchor: CommentAnchor;
|
|
17137
|
+
};
|
|
17138
|
+
|
|
17139
|
+
export type CommentAnchor = {
|
|
17140
|
+
startBlockId: string;
|
|
17141
|
+
startOffset: number;
|
|
17142
|
+
endBlockId: string;
|
|
17143
|
+
endOffset: number;
|
|
17144
|
+
};
|
|
17145
|
+
`;
|
|
17146
|
+
var init_types = () => {};
|
|
17147
|
+
|
|
17148
|
+
// src/cli/info/schema.ts
|
|
17149
|
+
var exports_schema = {};
|
|
17150
|
+
__export(exports_schema, {
|
|
16697
17151
|
run: () => run15
|
|
16698
17152
|
});
|
|
16699
17153
|
import { parseArgs as parseArgs13 } from "util";
|
|
@@ -16704,16 +17158,8 @@ async function run15(args) {
|
|
|
16704
17158
|
args,
|
|
16705
17159
|
allowPositionals: true,
|
|
16706
17160
|
options: {
|
|
16707
|
-
|
|
16708
|
-
|
|
16709
|
-
text: { type: "string" },
|
|
16710
|
-
runs: { type: "string" },
|
|
16711
|
-
style: { type: "string" },
|
|
16712
|
-
alignment: { type: "string" },
|
|
16713
|
-
color: { type: "string" },
|
|
16714
|
-
bold: { type: "boolean" },
|
|
16715
|
-
italic: { type: "boolean" },
|
|
16716
|
-
"dry-run": { type: "boolean" },
|
|
17161
|
+
json: { type: "boolean" },
|
|
17162
|
+
ts: { type: "boolean" },
|
|
16717
17163
|
help: { type: "boolean", short: "h" }
|
|
16718
17164
|
}
|
|
16719
17165
|
});
|
|
@@ -16725,153 +17171,205 @@ async function run15(args) {
|
|
|
16725
17171
|
await writeStdout(HELP15);
|
|
16726
17172
|
return EXIT.OK;
|
|
16727
17173
|
}
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
return fail("USAGE", "Missing FILE argument", HELP15);
|
|
16731
|
-
const after = parsed.values.after;
|
|
16732
|
-
const before = parsed.values.before;
|
|
16733
|
-
if (!after && !before) {
|
|
16734
|
-
return fail("USAGE", "Missing locator: pass --after or --before", HELP15);
|
|
16735
|
-
}
|
|
16736
|
-
if (after && before) {
|
|
16737
|
-
return fail("USAGE", "Pass either --after or --before, not both", HELP15);
|
|
16738
|
-
}
|
|
16739
|
-
const text = parsed.values.text;
|
|
16740
|
-
const runsJson = parsed.values.runs;
|
|
16741
|
-
if (!text && !runsJson) {
|
|
16742
|
-
return fail("USAGE", "Missing content: pass --text or --runs", HELP15);
|
|
16743
|
-
}
|
|
16744
|
-
if (text && runsJson) {
|
|
16745
|
-
return fail("USAGE", "Pass either --text or --runs, not both", HELP15);
|
|
16746
|
-
}
|
|
16747
|
-
const paragraphOptions = {};
|
|
16748
|
-
const styleValue = parsed.values.style;
|
|
16749
|
-
if (styleValue)
|
|
16750
|
-
paragraphOptions.style = styleValue;
|
|
16751
|
-
const alignmentValue = parsed.values.alignment;
|
|
16752
|
-
if (alignmentValue) {
|
|
16753
|
-
if (alignmentValue !== "left" && alignmentValue !== "center" && alignmentValue !== "right" && alignmentValue !== "justify") {
|
|
16754
|
-
return fail("USAGE", `Invalid --alignment: ${alignmentValue}`, "Valid values: left, center, right, justify");
|
|
16755
|
-
}
|
|
16756
|
-
paragraphOptions.alignment = alignmentValue;
|
|
16757
|
-
}
|
|
16758
|
-
let view;
|
|
16759
|
-
try {
|
|
16760
|
-
view = await openDocView(path);
|
|
16761
|
-
} catch (openError) {
|
|
16762
|
-
if (openError instanceof PkgError) {
|
|
16763
|
-
if (openError.code === "FILE_NOT_FOUND") {
|
|
16764
|
-
return fail("FILE_NOT_FOUND", openError.message);
|
|
16765
|
-
}
|
|
16766
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
16767
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
16768
|
-
}
|
|
16769
|
-
}
|
|
16770
|
-
throw openError;
|
|
16771
|
-
}
|
|
16772
|
-
const targetLocator = after ?? before;
|
|
16773
|
-
let blockRef;
|
|
16774
|
-
try {
|
|
16775
|
-
blockRef = resolveBlock(view, targetLocator);
|
|
16776
|
-
} catch (resolveError) {
|
|
16777
|
-
if (resolveError instanceof LocatorResolveError) {
|
|
16778
|
-
return fail("BLOCK_NOT_FOUND", resolveError.message);
|
|
16779
|
-
}
|
|
16780
|
-
throw resolveError;
|
|
16781
|
-
}
|
|
16782
|
-
let paragraphNode;
|
|
16783
|
-
if (text !== undefined) {
|
|
16784
|
-
const color = parsed.values.color;
|
|
16785
|
-
paragraphNode = /* @__PURE__ */ jsxDEV(Paragraph, {
|
|
16786
|
-
text,
|
|
16787
|
-
...paragraphOptions,
|
|
16788
|
-
...color ? { color } : {},
|
|
16789
|
-
...parsed.values.bold ? { bold: true } : {},
|
|
16790
|
-
...parsed.values.italic ? { italic: true } : {}
|
|
16791
|
-
}, undefined, false, undefined, this);
|
|
16792
|
-
} else {
|
|
16793
|
-
let runsValue;
|
|
16794
|
-
try {
|
|
16795
|
-
runsValue = JSON.parse(runsJson);
|
|
16796
|
-
} catch (jsonError) {
|
|
16797
|
-
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
16798
|
-
return fail("USAGE", `Invalid --runs JSON: ${message}`);
|
|
16799
|
-
}
|
|
16800
|
-
if (!Array.isArray(runsValue)) {
|
|
16801
|
-
return fail("USAGE", "--runs must be a JSON array of Run objects");
|
|
16802
|
-
}
|
|
16803
|
-
paragraphNode = /* @__PURE__ */ jsxDEV(Paragraph, {
|
|
16804
|
-
runs: runsValue,
|
|
16805
|
-
...paragraphOptions
|
|
16806
|
-
}, undefined, false, undefined, this);
|
|
16807
|
-
}
|
|
16808
|
-
const targetIndex = blockRef.parent.indexOf(blockRef.node);
|
|
16809
|
-
if (targetIndex === -1) {
|
|
16810
|
-
return fail("BLOCK_NOT_FOUND", "Block reference is stale (parent does not contain it)");
|
|
16811
|
-
}
|
|
16812
|
-
const insertIndex = after !== undefined ? targetIndex + 1 : targetIndex;
|
|
16813
|
-
if (parsed.values["dry-run"]) {
|
|
16814
|
-
await respond({
|
|
16815
|
-
ok: true,
|
|
16816
|
-
operation: "insert",
|
|
16817
|
-
dryRun: true,
|
|
16818
|
-
path,
|
|
16819
|
-
locator: targetLocator,
|
|
16820
|
-
placement: after !== undefined ? "after" : "before"
|
|
16821
|
-
});
|
|
17174
|
+
if (parsed.values.ts) {
|
|
17175
|
+
await writeStdout(types_default);
|
|
16822
17176
|
return EXIT.OK;
|
|
16823
17177
|
}
|
|
16824
|
-
|
|
16825
|
-
await saveDocView(view);
|
|
16826
|
-
await respond({
|
|
16827
|
-
ok: true,
|
|
16828
|
-
operation: "insert",
|
|
16829
|
-
path,
|
|
16830
|
-
locator: targetLocator,
|
|
16831
|
-
placement: after !== undefined ? "after" : "before"
|
|
16832
|
-
});
|
|
17178
|
+
await respond(JSON_SCHEMA);
|
|
16833
17179
|
return EXIT.OK;
|
|
16834
17180
|
}
|
|
16835
|
-
var HELP15 = `docx
|
|
17181
|
+
var HELP15 = `docx schema \u2014 print the AST type definitions
|
|
16836
17182
|
|
|
16837
17183
|
Usage:
|
|
16838
|
-
docx
|
|
16839
|
-
|
|
16840
|
-
Locator (one required):
|
|
16841
|
-
--after LOCATOR Insert after the block at LOCATOR (e.g., p3)
|
|
16842
|
-
--before LOCATOR Insert before the block at LOCATOR
|
|
16843
|
-
|
|
16844
|
-
Content (one required):
|
|
16845
|
-
--text TEXT Insert a paragraph with this text
|
|
16846
|
-
--runs JSON Insert a paragraph with custom runs (Run[] JSON)
|
|
16847
|
-
|
|
16848
|
-
Paragraph options:
|
|
16849
|
-
--style NAME Apply paragraph style (e.g., Heading1)
|
|
16850
|
-
--alignment ALIGN left | center | right | justify
|
|
16851
|
-
|
|
16852
|
-
Run options (only with --text):
|
|
16853
|
-
--color HEX Run color, hex (e.g., 800080 for purple)
|
|
16854
|
-
--bold Bold
|
|
16855
|
-
--italic Italic
|
|
17184
|
+
docx schema [options]
|
|
16856
17185
|
|
|
16857
|
-
|
|
16858
|
-
|
|
17186
|
+
Options:
|
|
17187
|
+
--json Print as a JSON Schema document (default)
|
|
17188
|
+
--ts Print TypeScript type source (read live from src/core/ast/types.ts)
|
|
17189
|
+
-h, --help Show this help
|
|
16859
17190
|
|
|
16860
17191
|
Examples:
|
|
16861
|
-
docx
|
|
16862
|
-
docx
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
init_core();
|
|
17192
|
+
docx schema | jq '.definitions.Run'
|
|
17193
|
+
docx schema --ts > ast.d.ts
|
|
17194
|
+
`, JSON_SCHEMA;
|
|
17195
|
+
var init_schema = __esm(() => {
|
|
17196
|
+
init_types();
|
|
16867
17197
|
init_respond();
|
|
16868
|
-
|
|
16869
|
-
|
|
17198
|
+
JSON_SCHEMA = {
|
|
17199
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17200
|
+
$id: "https://github.com/kklimuk/docx-cli/schema",
|
|
17201
|
+
title: "docx-cli AST",
|
|
17202
|
+
type: "object",
|
|
17203
|
+
required: ["schemaVersion", "path", "properties", "blocks", "comments"],
|
|
17204
|
+
properties: {
|
|
17205
|
+
schemaVersion: { const: 1 },
|
|
17206
|
+
path: { type: "string" },
|
|
17207
|
+
properties: {
|
|
17208
|
+
type: "object",
|
|
17209
|
+
properties: {
|
|
17210
|
+
title: { type: "string" },
|
|
17211
|
+
author: { type: "string" },
|
|
17212
|
+
created: { type: "string" },
|
|
17213
|
+
modified: { type: "string" }
|
|
17214
|
+
}
|
|
17215
|
+
},
|
|
17216
|
+
blocks: { type: "array", items: { $ref: "#/$defs/Block" } },
|
|
17217
|
+
comments: { type: "array", items: { $ref: "#/$defs/Comment" } }
|
|
17218
|
+
},
|
|
17219
|
+
$defs: {
|
|
17220
|
+
Block: {
|
|
17221
|
+
oneOf: [
|
|
17222
|
+
{ $ref: "#/$defs/Paragraph" },
|
|
17223
|
+
{ $ref: "#/$defs/Table" },
|
|
17224
|
+
{ $ref: "#/$defs/SectionBreak" }
|
|
17225
|
+
]
|
|
17226
|
+
},
|
|
17227
|
+
Paragraph: {
|
|
17228
|
+
type: "object",
|
|
17229
|
+
required: ["id", "type", "runs"],
|
|
17230
|
+
properties: {
|
|
17231
|
+
id: { type: "string" },
|
|
17232
|
+
type: { const: "paragraph" },
|
|
17233
|
+
style: { type: "string" },
|
|
17234
|
+
alignment: { enum: ["left", "center", "right", "justify"] },
|
|
17235
|
+
list: {
|
|
17236
|
+
type: "object",
|
|
17237
|
+
required: ["level", "numId"],
|
|
17238
|
+
properties: {
|
|
17239
|
+
level: { type: "number" },
|
|
17240
|
+
numId: { type: "string" }
|
|
17241
|
+
}
|
|
17242
|
+
},
|
|
17243
|
+
runs: { type: "array", items: { $ref: "#/$defs/Run" } }
|
|
17244
|
+
}
|
|
17245
|
+
},
|
|
17246
|
+
Run: {
|
|
17247
|
+
oneOf: [
|
|
17248
|
+
{ $ref: "#/$defs/TextRun" },
|
|
17249
|
+
{ $ref: "#/$defs/ImageRun" },
|
|
17250
|
+
{ $ref: "#/$defs/BreakRun" },
|
|
17251
|
+
{ $ref: "#/$defs/TabRun" }
|
|
17252
|
+
]
|
|
17253
|
+
},
|
|
17254
|
+
TextRun: {
|
|
17255
|
+
type: "object",
|
|
17256
|
+
required: ["type", "text"],
|
|
17257
|
+
properties: {
|
|
17258
|
+
type: { const: "text" },
|
|
17259
|
+
text: { type: "string" },
|
|
17260
|
+
color: { type: "string" },
|
|
17261
|
+
highlight: { type: "string" },
|
|
17262
|
+
bold: { type: "boolean" },
|
|
17263
|
+
italic: { type: "boolean" },
|
|
17264
|
+
underline: { type: "string" },
|
|
17265
|
+
strike: { type: "boolean" },
|
|
17266
|
+
font: { type: "string" },
|
|
17267
|
+
sizeHalfPoints: { type: "number" },
|
|
17268
|
+
comments: { type: "array", items: { type: "string" } },
|
|
17269
|
+
trackedChange: {
|
|
17270
|
+
type: "object",
|
|
17271
|
+
required: ["kind", "author", "date", "revisionId"],
|
|
17272
|
+
properties: {
|
|
17273
|
+
kind: { enum: ["ins", "del"] },
|
|
17274
|
+
author: { type: "string" },
|
|
17275
|
+
date: { type: "string" },
|
|
17276
|
+
revisionId: { type: "string" }
|
|
17277
|
+
}
|
|
17278
|
+
}
|
|
17279
|
+
}
|
|
17280
|
+
},
|
|
17281
|
+
ImageRun: {
|
|
17282
|
+
type: "object",
|
|
17283
|
+
required: ["type", "id", "contentType", "hash"],
|
|
17284
|
+
properties: {
|
|
17285
|
+
type: { const: "image" },
|
|
17286
|
+
id: { type: "string" },
|
|
17287
|
+
contentType: { type: "string" },
|
|
17288
|
+
hash: { type: "string" },
|
|
17289
|
+
widthEmu: { type: "number" },
|
|
17290
|
+
heightEmu: { type: "number" },
|
|
17291
|
+
alt: { type: "string" }
|
|
17292
|
+
}
|
|
17293
|
+
},
|
|
17294
|
+
BreakRun: {
|
|
17295
|
+
type: "object",
|
|
17296
|
+
required: ["type", "kind"],
|
|
17297
|
+
properties: {
|
|
17298
|
+
type: { const: "break" },
|
|
17299
|
+
kind: { enum: ["page", "line", "column"] }
|
|
17300
|
+
}
|
|
17301
|
+
},
|
|
17302
|
+
TabRun: {
|
|
17303
|
+
type: "object",
|
|
17304
|
+
required: ["type"],
|
|
17305
|
+
properties: { type: { const: "tab" } }
|
|
17306
|
+
},
|
|
17307
|
+
Table: {
|
|
17308
|
+
type: "object",
|
|
17309
|
+
required: ["id", "type", "rows"],
|
|
17310
|
+
properties: {
|
|
17311
|
+
id: { type: "string" },
|
|
17312
|
+
type: { const: "table" },
|
|
17313
|
+
rows: {
|
|
17314
|
+
type: "array",
|
|
17315
|
+
items: {
|
|
17316
|
+
type: "object",
|
|
17317
|
+
properties: {
|
|
17318
|
+
cells: {
|
|
17319
|
+
type: "array",
|
|
17320
|
+
items: {
|
|
17321
|
+
type: "object",
|
|
17322
|
+
properties: {
|
|
17323
|
+
blocks: {
|
|
17324
|
+
type: "array",
|
|
17325
|
+
items: { $ref: "#/$defs/Block" }
|
|
17326
|
+
}
|
|
17327
|
+
}
|
|
17328
|
+
}
|
|
17329
|
+
}
|
|
17330
|
+
}
|
|
17331
|
+
}
|
|
17332
|
+
}
|
|
17333
|
+
}
|
|
17334
|
+
},
|
|
17335
|
+
SectionBreak: {
|
|
17336
|
+
type: "object",
|
|
17337
|
+
required: ["id", "type"],
|
|
17338
|
+
properties: {
|
|
17339
|
+
id: { type: "string" },
|
|
17340
|
+
type: { const: "sectionBreak" }
|
|
17341
|
+
}
|
|
17342
|
+
},
|
|
17343
|
+
Comment: {
|
|
17344
|
+
type: "object",
|
|
17345
|
+
required: ["id", "author", "date", "text", "anchor"],
|
|
17346
|
+
properties: {
|
|
17347
|
+
id: { type: "string" },
|
|
17348
|
+
author: { type: "string" },
|
|
17349
|
+
initials: { type: "string" },
|
|
17350
|
+
date: { type: "string" },
|
|
17351
|
+
text: { type: "string" },
|
|
17352
|
+
parentId: { type: "string" },
|
|
17353
|
+
resolved: { type: "boolean" },
|
|
17354
|
+
anchor: {
|
|
17355
|
+
type: "object",
|
|
17356
|
+
required: ["startBlockId", "startOffset", "endBlockId", "endOffset"],
|
|
17357
|
+
properties: {
|
|
17358
|
+
startBlockId: { type: "string" },
|
|
17359
|
+
startOffset: { type: "number" },
|
|
17360
|
+
endBlockId: { type: "string" },
|
|
17361
|
+
endOffset: { type: "number" }
|
|
17362
|
+
}
|
|
17363
|
+
}
|
|
17364
|
+
}
|
|
17365
|
+
}
|
|
17366
|
+
}
|
|
17367
|
+
};
|
|
16870
17368
|
});
|
|
16871
17369
|
|
|
16872
|
-
// src/cli/locators
|
|
16873
|
-
var
|
|
16874
|
-
__export(
|
|
17370
|
+
// src/cli/info/locators.ts
|
|
17371
|
+
var exports_locators = {};
|
|
17372
|
+
__export(exports_locators, {
|
|
16875
17373
|
run: () => run16
|
|
16876
17374
|
});
|
|
16877
17375
|
import { parseArgs as parseArgs14 } from "util";
|
|
@@ -16895,11 +17393,11 @@ async function run16(args) {
|
|
|
16895
17393
|
return EXIT.OK;
|
|
16896
17394
|
}
|
|
16897
17395
|
if (parsed.values.json) {
|
|
16898
|
-
await
|
|
17396
|
+
await writeStdout(`${JSON.stringify(JSON_REFERENCE, null, 2)}
|
|
16899
17397
|
`);
|
|
16900
17398
|
return EXIT.OK;
|
|
16901
17399
|
}
|
|
16902
|
-
await
|
|
17400
|
+
await writeStdout(REFERENCE);
|
|
16903
17401
|
return EXIT.OK;
|
|
16904
17402
|
}
|
|
16905
17403
|
var HELP16 = `docx locators \u2014 print the locator grammar reference
|
|
@@ -16913,21 +17411,22 @@ Options:
|
|
|
16913
17411
|
`, REFERENCE = `LOCATOR GRAMMAR
|
|
16914
17412
|
|
|
16915
17413
|
Block locators:
|
|
16916
|
-
pN
|
|
16917
|
-
tN
|
|
16918
|
-
sN
|
|
16919
|
-
|
|
17414
|
+
pN Paragraph N (e.g., p3)
|
|
17415
|
+
tN Table N
|
|
17416
|
+
sN Section break N
|
|
17417
|
+
tT:rRcC:pK Paragraph K of cell (R,C) in table T
|
|
16920
17418
|
|
|
16921
17419
|
Span locators (within a single paragraph):
|
|
16922
|
-
pN:S-E
|
|
17420
|
+
pN:S-E Characters S..E of paragraph N (inclusive start, exclusive end)
|
|
17421
|
+
tT:rRcC:pK:S-E Characters S..E of a cell paragraph
|
|
16923
17422
|
|
|
16924
17423
|
Range locators (across blocks):
|
|
16925
|
-
pN:S-pM:E
|
|
17424
|
+
pN:S-pM:E From char S of paragraph N to char E of paragraph M
|
|
16926
17425
|
|
|
16927
17426
|
Entity locators:
|
|
16928
|
-
cN
|
|
16929
|
-
imgN
|
|
16930
|
-
tN:rRcC
|
|
17427
|
+
cN Comment id (e.g., c0)
|
|
17428
|
+
imgN Image id (e.g., img2)
|
|
17429
|
+
tN:rRcC Cell at row R, column C of table tN
|
|
16931
17430
|
|
|
16932
17431
|
Examples:
|
|
16933
17432
|
p3 -> the entire paragraph p3
|
|
@@ -16937,25 +17436,27 @@ Examples:
|
|
|
16937
17436
|
img0 -> image img0
|
|
16938
17437
|
t0:r1c2 -> cell at row 1, col 2 of table t0
|
|
16939
17438
|
t0:r1c2:p0 -> first paragraph of that cell
|
|
17439
|
+
t0:r1c2:p0:5-10 -> chars 5..10 of that paragraph
|
|
16940
17440
|
|
|
16941
17441
|
Notes:
|
|
16942
17442
|
Block ids are positional and shift after structural edits \u2014 re-read
|
|
16943
17443
|
between non-trivial mutations. Mutating commands print before/after
|
|
16944
17444
|
locator info in their JSON ack so agents can chain operations.
|
|
16945
17445
|
`, JSON_REFERENCE;
|
|
16946
|
-
var
|
|
17446
|
+
var init_locators2 = __esm(() => {
|
|
16947
17447
|
init_respond();
|
|
16948
17448
|
JSON_REFERENCE = {
|
|
16949
17449
|
blockLocators: {
|
|
16950
17450
|
paragraph: { syntax: "pN", example: "p3" },
|
|
16951
17451
|
table: { syntax: "tN", example: "t0" },
|
|
16952
17452
|
sectionBreak: { syntax: "sN", example: "s0" },
|
|
16953
|
-
cellParagraph: { syntax: "
|
|
17453
|
+
cellParagraph: { syntax: "tT:rRcC:pK", example: "t0:r1c2:p0" }
|
|
16954
17454
|
},
|
|
16955
17455
|
spanLocator: {
|
|
16956
17456
|
syntax: "pN:S-E",
|
|
16957
17457
|
example: "p3:5-20",
|
|
16958
|
-
semantics: "Characters S..E within paragraph N (start inclusive, end exclusive)"
|
|
17458
|
+
semantics: "Characters S..E within paragraph N (start inclusive, end exclusive)",
|
|
17459
|
+
cellSyntax: "tT:rRcC:pK:S-E"
|
|
16959
17460
|
},
|
|
16960
17461
|
rangeLocator: {
|
|
16961
17462
|
syntax: "pN:S-pM:E",
|
|
@@ -16976,16 +17477,251 @@ var init_locators_cmd = __esm(() => {
|
|
|
16976
17477
|
};
|
|
16977
17478
|
});
|
|
16978
17479
|
|
|
17480
|
+
// src/cli/info/index.ts
|
|
17481
|
+
var exports_info = {};
|
|
17482
|
+
__export(exports_info, {
|
|
17483
|
+
run: () => run17
|
|
17484
|
+
});
|
|
17485
|
+
async function run17(args) {
|
|
17486
|
+
const topic = args[0];
|
|
17487
|
+
if (!topic || topic === "--help" || topic === "-h" || topic === "help") {
|
|
17488
|
+
await writeStdout(HELP17);
|
|
17489
|
+
return topic ? 0 : 2;
|
|
17490
|
+
}
|
|
17491
|
+
const loader = SUBCOMMANDS3[topic];
|
|
17492
|
+
if (!loader) {
|
|
17493
|
+
return fail("USAGE", `Unknown info topic: ${topic}`, 'Run "docx info --help".');
|
|
17494
|
+
}
|
|
17495
|
+
const module_ = await loader();
|
|
17496
|
+
return module_.run(args.slice(1));
|
|
17497
|
+
}
|
|
17498
|
+
var SUBCOMMANDS3, HELP17 = `docx info \u2014 print reference material about the CLI
|
|
17499
|
+
|
|
17500
|
+
Usage:
|
|
17501
|
+
docx info <topic> [options]
|
|
17502
|
+
|
|
17503
|
+
Topics:
|
|
17504
|
+
schema Dump AST JSON Schema (or TS source via --ts)
|
|
17505
|
+
locators Dump locator grammar reference
|
|
17506
|
+
|
|
17507
|
+
Run "docx info <topic> --help" for topic-specific help.
|
|
17508
|
+
`;
|
|
17509
|
+
var init_info = __esm(() => {
|
|
17510
|
+
init_respond();
|
|
17511
|
+
SUBCOMMANDS3 = {
|
|
17512
|
+
schema: () => Promise.resolve().then(() => (init_schema(), exports_schema)),
|
|
17513
|
+
locators: () => Promise.resolve().then(() => (init_locators2(), exports_locators))
|
|
17514
|
+
};
|
|
17515
|
+
});
|
|
17516
|
+
|
|
17517
|
+
// src/cli/insert/index.tsx
|
|
17518
|
+
var exports_insert = {};
|
|
17519
|
+
__export(exports_insert, {
|
|
17520
|
+
run: () => run18
|
|
17521
|
+
});
|
|
17522
|
+
import { parseArgs as parseArgs15 } from "util";
|
|
17523
|
+
async function run18(args) {
|
|
17524
|
+
let parsed;
|
|
17525
|
+
try {
|
|
17526
|
+
parsed = parseArgs15({
|
|
17527
|
+
args,
|
|
17528
|
+
allowPositionals: true,
|
|
17529
|
+
options: {
|
|
17530
|
+
after: { type: "string" },
|
|
17531
|
+
before: { type: "string" },
|
|
17532
|
+
text: { type: "string" },
|
|
17533
|
+
runs: { type: "string" },
|
|
17534
|
+
style: { type: "string" },
|
|
17535
|
+
alignment: { type: "string" },
|
|
17536
|
+
color: { type: "string" },
|
|
17537
|
+
bold: { type: "boolean" },
|
|
17538
|
+
italic: { type: "boolean" },
|
|
17539
|
+
output: { type: "string", short: "o" },
|
|
17540
|
+
"dry-run": { type: "boolean" },
|
|
17541
|
+
help: { type: "boolean", short: "h" }
|
|
17542
|
+
}
|
|
17543
|
+
});
|
|
17544
|
+
} catch (parseError) {
|
|
17545
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17546
|
+
return fail("USAGE", message, HELP18);
|
|
17547
|
+
}
|
|
17548
|
+
if (parsed.values.help) {
|
|
17549
|
+
await writeStdout(HELP18);
|
|
17550
|
+
return EXIT.OK;
|
|
17551
|
+
}
|
|
17552
|
+
const path = parsed.positionals[0];
|
|
17553
|
+
if (!path)
|
|
17554
|
+
return fail("USAGE", "Missing FILE argument", HELP18);
|
|
17555
|
+
const after = parsed.values.after;
|
|
17556
|
+
const before = parsed.values.before;
|
|
17557
|
+
if (!after && !before) {
|
|
17558
|
+
return fail("USAGE", "Missing locator: pass --after or --before", HELP18);
|
|
17559
|
+
}
|
|
17560
|
+
if (after && before) {
|
|
17561
|
+
return fail("USAGE", "Pass either --after or --before, not both", HELP18);
|
|
17562
|
+
}
|
|
17563
|
+
const text = parsed.values.text;
|
|
17564
|
+
const runsJson = parsed.values.runs;
|
|
17565
|
+
if (!text && !runsJson) {
|
|
17566
|
+
return fail("USAGE", "Missing content: pass --text or --runs", HELP18);
|
|
17567
|
+
}
|
|
17568
|
+
if (text && runsJson) {
|
|
17569
|
+
return fail("USAGE", "Pass either --text or --runs, not both", HELP18);
|
|
17570
|
+
}
|
|
17571
|
+
const paragraphOptions = {};
|
|
17572
|
+
const styleValue = parsed.values.style;
|
|
17573
|
+
if (styleValue)
|
|
17574
|
+
paragraphOptions.style = styleValue;
|
|
17575
|
+
const alignmentValue = parsed.values.alignment;
|
|
17576
|
+
if (alignmentValue) {
|
|
17577
|
+
if (alignmentValue !== "left" && alignmentValue !== "center" && alignmentValue !== "right" && alignmentValue !== "justify") {
|
|
17578
|
+
return fail("USAGE", `Invalid --alignment: ${alignmentValue}`, "Valid values: left, center, right, justify");
|
|
17579
|
+
}
|
|
17580
|
+
paragraphOptions.alignment = alignmentValue;
|
|
17581
|
+
}
|
|
17582
|
+
const view = await openOrFail(path);
|
|
17583
|
+
if (typeof view === "number")
|
|
17584
|
+
return view;
|
|
17585
|
+
const targetLocator = after ?? before;
|
|
17586
|
+
const blockRef = await resolveBlockOrFail(view, targetLocator);
|
|
17587
|
+
if (typeof blockRef === "number")
|
|
17588
|
+
return blockRef;
|
|
17589
|
+
let paragraphNode;
|
|
17590
|
+
if (text !== undefined) {
|
|
17591
|
+
const color = parsed.values.color;
|
|
17592
|
+
paragraphNode = /* @__PURE__ */ jsxDEV(Paragraph, {
|
|
17593
|
+
text,
|
|
17594
|
+
...paragraphOptions,
|
|
17595
|
+
...color ? { color } : {},
|
|
17596
|
+
...parsed.values.bold ? { bold: true } : {},
|
|
17597
|
+
...parsed.values.italic ? { italic: true } : {}
|
|
17598
|
+
}, undefined, false, undefined, this);
|
|
17599
|
+
} else {
|
|
17600
|
+
let runsValue;
|
|
17601
|
+
try {
|
|
17602
|
+
runsValue = JSON.parse(runsJson);
|
|
17603
|
+
} catch (jsonError) {
|
|
17604
|
+
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
17605
|
+
return fail("USAGE", `Invalid --runs JSON: ${message}`);
|
|
17606
|
+
}
|
|
17607
|
+
if (!Array.isArray(runsValue)) {
|
|
17608
|
+
return fail("USAGE", "--runs must be a JSON array of Run objects");
|
|
17609
|
+
}
|
|
17610
|
+
paragraphNode = /* @__PURE__ */ jsxDEV(Paragraph, {
|
|
17611
|
+
runs: runsValue,
|
|
17612
|
+
...paragraphOptions
|
|
17613
|
+
}, undefined, false, undefined, this);
|
|
17614
|
+
}
|
|
17615
|
+
const targetIndex = blockRef.parent.indexOf(blockRef.node);
|
|
17616
|
+
if (targetIndex === -1) {
|
|
17617
|
+
return fail("BLOCK_NOT_FOUND", "Block reference is stale (parent does not contain it)");
|
|
17618
|
+
}
|
|
17619
|
+
const insertIndex = after !== undefined ? targetIndex + 1 : targetIndex;
|
|
17620
|
+
if (isTrackChangesEnabled(view)) {
|
|
17621
|
+
applyTrackedInsertion(paragraphNode, view);
|
|
17622
|
+
}
|
|
17623
|
+
const outputPath = parsed.values.output;
|
|
17624
|
+
if (parsed.values["dry-run"]) {
|
|
17625
|
+
await respond({
|
|
17626
|
+
ok: true,
|
|
17627
|
+
operation: "insert",
|
|
17628
|
+
dryRun: true,
|
|
17629
|
+
path,
|
|
17630
|
+
locator: targetLocator,
|
|
17631
|
+
placement: after !== undefined ? "after" : "before",
|
|
17632
|
+
...outputPath ? { output: outputPath } : {}
|
|
17633
|
+
});
|
|
17634
|
+
return EXIT.OK;
|
|
17635
|
+
}
|
|
17636
|
+
blockRef.parent.splice(insertIndex, 0, paragraphNode);
|
|
17637
|
+
await saveDocView(view, outputPath);
|
|
17638
|
+
await respond({
|
|
17639
|
+
ok: true,
|
|
17640
|
+
operation: "insert",
|
|
17641
|
+
path: outputPath ?? path,
|
|
17642
|
+
locator: targetLocator,
|
|
17643
|
+
placement: after !== undefined ? "after" : "before"
|
|
17644
|
+
});
|
|
17645
|
+
return EXIT.OK;
|
|
17646
|
+
}
|
|
17647
|
+
function applyTrackedInsertion(paragraph, view) {
|
|
17648
|
+
const allocator = createRevisionAllocator(view);
|
|
17649
|
+
const baseMeta = { author: resolveAuthor(), date: resolveDate() };
|
|
17650
|
+
const mintMeta = () => ({
|
|
17651
|
+
...baseMeta,
|
|
17652
|
+
revisionId: allocator.next()
|
|
17653
|
+
});
|
|
17654
|
+
const newChildren = [];
|
|
17655
|
+
let runBuffer = [];
|
|
17656
|
+
const flush = () => {
|
|
17657
|
+
if (runBuffer.length === 0)
|
|
17658
|
+
return;
|
|
17659
|
+
newChildren.push(/* @__PURE__ */ jsxDEV(Ins, {
|
|
17660
|
+
meta: mintMeta(),
|
|
17661
|
+
children: runBuffer
|
|
17662
|
+
}, undefined, false, undefined, this));
|
|
17663
|
+
runBuffer = [];
|
|
17664
|
+
};
|
|
17665
|
+
for (const child of paragraph.children) {
|
|
17666
|
+
if (child.tag === "w:r") {
|
|
17667
|
+
runBuffer.push(child);
|
|
17668
|
+
continue;
|
|
17669
|
+
}
|
|
17670
|
+
flush();
|
|
17671
|
+
newChildren.push(child);
|
|
17672
|
+
}
|
|
17673
|
+
flush();
|
|
17674
|
+
paragraph.children = newChildren;
|
|
17675
|
+
markParagraphMarkAs(paragraph, "ins", mintMeta());
|
|
17676
|
+
}
|
|
17677
|
+
var HELP18 = `docx insert \u2014 insert a paragraph at a locator
|
|
17678
|
+
|
|
17679
|
+
Usage:
|
|
17680
|
+
docx insert FILE [options]
|
|
17681
|
+
|
|
17682
|
+
Locator (one required):
|
|
17683
|
+
--after LOCATOR Insert after the block at LOCATOR (e.g., p3)
|
|
17684
|
+
--before LOCATOR Insert before the block at LOCATOR
|
|
17685
|
+
|
|
17686
|
+
Content (one required):
|
|
17687
|
+
--text TEXT Insert a paragraph with this text
|
|
17688
|
+
--runs JSON Insert a paragraph with custom runs (Run[] JSON)
|
|
17689
|
+
|
|
17690
|
+
Paragraph options:
|
|
17691
|
+
--style NAME Apply paragraph style (e.g., Heading1)
|
|
17692
|
+
--alignment ALIGN left | center | right | justify
|
|
17693
|
+
|
|
17694
|
+
Run options (only with --text):
|
|
17695
|
+
--color HEX Run color, hex (e.g., 800080 for purple)
|
|
17696
|
+
--bold Bold
|
|
17697
|
+
--italic Italic
|
|
17698
|
+
|
|
17699
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
17700
|
+
--dry-run Print what would be inserted; do not write the file
|
|
17701
|
+
-h, --help Show this help
|
|
17702
|
+
|
|
17703
|
+
Examples:
|
|
17704
|
+
docx insert doc.docx --after p3 --text "Section header" --style Heading2
|
|
17705
|
+
docx insert doc.docx --before p0 --text "ALERT" --color CC0000 --bold
|
|
17706
|
+
docx insert doc.docx --after p2 --runs '[{"type":"text","text":"X","bold":true}]'
|
|
17707
|
+
`;
|
|
17708
|
+
var init_insert = __esm(() => {
|
|
17709
|
+
init_core();
|
|
17710
|
+
init_respond();
|
|
17711
|
+
init_emit2();
|
|
17712
|
+
init_jsx_dev_runtime();
|
|
17713
|
+
});
|
|
17714
|
+
|
|
16979
17715
|
// src/cli/read/index.ts
|
|
16980
17716
|
var exports_read = {};
|
|
16981
17717
|
__export(exports_read, {
|
|
16982
|
-
run: () =>
|
|
17718
|
+
run: () => run19
|
|
16983
17719
|
});
|
|
16984
|
-
import { parseArgs as
|
|
16985
|
-
async function
|
|
17720
|
+
import { parseArgs as parseArgs16 } from "util";
|
|
17721
|
+
async function run19(args) {
|
|
16986
17722
|
let parsed;
|
|
16987
17723
|
try {
|
|
16988
|
-
parsed =
|
|
17724
|
+
parsed = parseArgs16({
|
|
16989
17725
|
args,
|
|
16990
17726
|
allowPositionals: true,
|
|
16991
17727
|
options: {
|
|
@@ -16994,32 +17730,23 @@ async function run17(args) {
|
|
|
16994
17730
|
}
|
|
16995
17731
|
});
|
|
16996
17732
|
} catch (e) {
|
|
16997
|
-
return fail("USAGE", e instanceof Error ? e.message : String(e),
|
|
17733
|
+
return fail("USAGE", e instanceof Error ? e.message : String(e), HELP19);
|
|
16998
17734
|
}
|
|
16999
17735
|
if (parsed.values.help) {
|
|
17000
|
-
await writeStdout(
|
|
17736
|
+
await writeStdout(HELP19);
|
|
17001
17737
|
return EXIT.OK;
|
|
17002
17738
|
}
|
|
17003
17739
|
const path = parsed.positionals[0];
|
|
17004
17740
|
if (!path)
|
|
17005
|
-
return fail("USAGE", "Missing FILE argument",
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
view
|
|
17009
|
-
} catch (e) {
|
|
17010
|
-
if (e instanceof PkgError) {
|
|
17011
|
-
if (e.code === "FILE_NOT_FOUND")
|
|
17012
|
-
return fail("FILE_NOT_FOUND", e.message);
|
|
17013
|
-
if (e.code === "NOT_A_ZIP")
|
|
17014
|
-
return fail("NOT_A_ZIP", e.message);
|
|
17015
|
-
}
|
|
17016
|
-
throw e;
|
|
17017
|
-
}
|
|
17741
|
+
return fail("USAGE", "Missing FILE argument", HELP19);
|
|
17742
|
+
const view = await openOrFail(path);
|
|
17743
|
+
if (typeof view === "number")
|
|
17744
|
+
return view;
|
|
17018
17745
|
await enrichImageHashes(view);
|
|
17019
17746
|
await respond(view.doc);
|
|
17020
17747
|
return EXIT.OK;
|
|
17021
17748
|
}
|
|
17022
|
-
var
|
|
17749
|
+
var HELP19 = `docx read \u2014 print AST as JSON
|
|
17023
17750
|
|
|
17024
17751
|
Usage:
|
|
17025
17752
|
docx read FILE [options]
|
|
@@ -17037,394 +17764,524 @@ var init_read2 = __esm(() => {
|
|
|
17037
17764
|
init_respond();
|
|
17038
17765
|
});
|
|
17039
17766
|
|
|
17040
|
-
// src/
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
}
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
|
|
17079
|
-
}
|
|
17080
|
-
|
|
17081
|
-
|
|
17082
|
-
|
|
17083
|
-
|
|
17084
|
-
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
|
|
17088
|
-
|
|
17089
|
-
|
|
17090
|
-
|
|
17091
|
-
|
|
17092
|
-
|
|
17093
|
-
|
|
17094
|
-
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
|
|
17098
|
-
|
|
17099
|
-
|
|
17100
|
-
|
|
17101
|
-
|
|
17102
|
-
|
|
17103
|
-
|
|
17104
|
-
|
|
17105
|
-
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17109
|
-
|
|
17110
|
-
|
|
17111
|
-
|
|
17112
|
-
};
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
}
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
|
|
17141
|
-
|
|
17142
|
-
|
|
17143
|
-
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
|
|
17767
|
+
// src/cli/replace/replace-span.tsx
|
|
17768
|
+
function replaceSpanInParagraph(paragraph, span, replacement, tracked) {
|
|
17769
|
+
if (span.start > span.end) {
|
|
17770
|
+
throw new Error(`replaceSpanInParagraph: invalid span ${span.start}-${span.end}`);
|
|
17771
|
+
}
|
|
17772
|
+
const slots = collectRunSlots(paragraph);
|
|
17773
|
+
const overlapping = slots.filter((slot) => slot.offsetBefore + slot.length > span.start && slot.offsetBefore < span.end);
|
|
17774
|
+
const firstSlot = overlapping[0];
|
|
17775
|
+
if (!firstSlot) {
|
|
17776
|
+
paragraph.children.push(/* @__PURE__ */ jsxDEV(ReplacementRun, {
|
|
17777
|
+
runProperties: null,
|
|
17778
|
+
text: replacement
|
|
17779
|
+
}, undefined, false, undefined, this));
|
|
17780
|
+
return;
|
|
17781
|
+
}
|
|
17782
|
+
const inheritedProperties = firstSlot.run.findChild("w:rPr")?.clone() ?? null;
|
|
17783
|
+
const firstParent = firstSlot.parent;
|
|
17784
|
+
const allSameParent = overlapping.every((slot) => slot.parent === firstParent);
|
|
17785
|
+
if (allSameParent) {
|
|
17786
|
+
const containerStart = firstParent === paragraph ? 0 : firstSlot.offsetBefore;
|
|
17787
|
+
rebuildContainer(firstParent, containerStart, span, replacement, inheritedProperties, firstParent === paragraph, tracked ?? null);
|
|
17788
|
+
return;
|
|
17789
|
+
}
|
|
17790
|
+
rebuildAcrossBoundaries(paragraph, span, replacement, inheritedProperties, tracked ?? null);
|
|
17791
|
+
}
|
|
17792
|
+
function collectRunSlots(paragraph) {
|
|
17793
|
+
const slots = [];
|
|
17794
|
+
let offset = 0;
|
|
17795
|
+
for (const child of paragraph.children) {
|
|
17796
|
+
if (child.tag === "w:r") {
|
|
17797
|
+
const length = runTextLength(child);
|
|
17798
|
+
slots.push({
|
|
17799
|
+
parent: paragraph,
|
|
17800
|
+
run: child,
|
|
17801
|
+
offsetBefore: offset,
|
|
17802
|
+
length
|
|
17803
|
+
});
|
|
17804
|
+
offset += length;
|
|
17805
|
+
continue;
|
|
17806
|
+
}
|
|
17807
|
+
if (child.tag === "w:ins" || child.tag === "w:del") {
|
|
17808
|
+
for (const inner of child.children) {
|
|
17809
|
+
if (inner.tag !== "w:r")
|
|
17810
|
+
continue;
|
|
17811
|
+
const length = runTextLength(inner);
|
|
17812
|
+
slots.push({
|
|
17813
|
+
parent: child,
|
|
17814
|
+
run: inner,
|
|
17815
|
+
offsetBefore: offset,
|
|
17816
|
+
length
|
|
17817
|
+
});
|
|
17818
|
+
offset += length;
|
|
17819
|
+
}
|
|
17820
|
+
}
|
|
17821
|
+
}
|
|
17822
|
+
return slots;
|
|
17823
|
+
}
|
|
17824
|
+
function rebuildContainer(container, baseOffset, span, replacement, runProperties, isParagraph, tracked) {
|
|
17825
|
+
const newChildren = [];
|
|
17826
|
+
let offset = baseOffset;
|
|
17827
|
+
let placed = false;
|
|
17828
|
+
const placeReplacement = () => {
|
|
17829
|
+
if (placed)
|
|
17830
|
+
return;
|
|
17831
|
+
placed = true;
|
|
17832
|
+
const run20 = /* @__PURE__ */ jsxDEV(ReplacementRun, {
|
|
17833
|
+
runProperties,
|
|
17834
|
+
text: replacement
|
|
17835
|
+
}, undefined, false, undefined, this);
|
|
17836
|
+
newChildren.push(tracked && isParagraph ? /* @__PURE__ */ jsxDEV(Ins, {
|
|
17837
|
+
meta: mintMeta(tracked),
|
|
17838
|
+
children: run20
|
|
17839
|
+
}, undefined, false, undefined, this) : run20);
|
|
17840
|
+
};
|
|
17841
|
+
for (const child of container.children) {
|
|
17842
|
+
if (child.tag === "w:r") {
|
|
17843
|
+
const length = runTextLength(child);
|
|
17844
|
+
const runStart = offset;
|
|
17845
|
+
const runEnd = offset + length;
|
|
17846
|
+
offset = runEnd;
|
|
17847
|
+
if (runEnd <= span.start) {
|
|
17848
|
+
newChildren.push(child);
|
|
17849
|
+
continue;
|
|
17850
|
+
}
|
|
17851
|
+
if (runStart >= span.end) {
|
|
17852
|
+
placeReplacement();
|
|
17853
|
+
newChildren.push(child);
|
|
17854
|
+
continue;
|
|
17855
|
+
}
|
|
17856
|
+
const sliceStartInRun = Math.max(0, span.start - runStart);
|
|
17857
|
+
const sliceEndInRun = Math.min(length, span.end - runStart);
|
|
17858
|
+
if (sliceStartInRun > 0) {
|
|
17859
|
+
newChildren.push(sliceRun(child, 0, sliceStartInRun));
|
|
17860
|
+
}
|
|
17861
|
+
if (tracked) {
|
|
17862
|
+
const cutRun = sliceRun(child, sliceStartInRun, sliceEndInRun);
|
|
17863
|
+
convertRunTextToDelText(cutRun);
|
|
17864
|
+
newChildren.push(/* @__PURE__ */ jsxDEV(Del, {
|
|
17865
|
+
meta: mintMeta(tracked),
|
|
17866
|
+
children: cutRun
|
|
17867
|
+
}, undefined, false, undefined, this));
|
|
17868
|
+
}
|
|
17869
|
+
placeReplacement();
|
|
17870
|
+
if (sliceEndInRun < length) {
|
|
17871
|
+
newChildren.push(sliceRun(child, sliceEndInRun, length));
|
|
17872
|
+
}
|
|
17873
|
+
continue;
|
|
17874
|
+
}
|
|
17875
|
+
if (isParagraph && (child.tag === "w:ins" || child.tag === "w:del")) {
|
|
17876
|
+
let innerLength = 0;
|
|
17877
|
+
for (const inner of child.children) {
|
|
17878
|
+
if (inner.tag === "w:r")
|
|
17879
|
+
innerLength += runTextLength(inner);
|
|
17880
|
+
}
|
|
17881
|
+
offset += innerLength;
|
|
17882
|
+
newChildren.push(child);
|
|
17883
|
+
continue;
|
|
17884
|
+
}
|
|
17885
|
+
newChildren.push(child);
|
|
17886
|
+
}
|
|
17887
|
+
if (!placed)
|
|
17888
|
+
placeReplacement();
|
|
17889
|
+
container.children = newChildren;
|
|
17890
|
+
}
|
|
17891
|
+
function rebuildAcrossBoundaries(paragraph, span, replacement, runProperties, tracked) {
|
|
17892
|
+
const newChildren = [];
|
|
17893
|
+
let offset = 0;
|
|
17894
|
+
let placed = false;
|
|
17895
|
+
const placeReplacement = () => {
|
|
17896
|
+
if (placed)
|
|
17897
|
+
return;
|
|
17898
|
+
placed = true;
|
|
17899
|
+
const run20 = /* @__PURE__ */ jsxDEV(ReplacementRun, {
|
|
17900
|
+
runProperties,
|
|
17901
|
+
text: replacement
|
|
17902
|
+
}, undefined, false, undefined, this);
|
|
17903
|
+
newChildren.push(tracked ? /* @__PURE__ */ jsxDEV(Ins, {
|
|
17904
|
+
meta: mintMeta(tracked),
|
|
17905
|
+
children: run20
|
|
17906
|
+
}, undefined, false, undefined, this) : run20);
|
|
17907
|
+
};
|
|
17908
|
+
for (const child of paragraph.children) {
|
|
17909
|
+
if (child.tag === "w:r") {
|
|
17910
|
+
const length = runTextLength(child);
|
|
17911
|
+
const runStart = offset;
|
|
17912
|
+
const runEnd = offset + length;
|
|
17913
|
+
offset = runEnd;
|
|
17914
|
+
if (runEnd <= span.start) {
|
|
17915
|
+
newChildren.push(child);
|
|
17916
|
+
continue;
|
|
17917
|
+
}
|
|
17918
|
+
if (runStart >= span.end) {
|
|
17919
|
+
placeReplacement();
|
|
17920
|
+
newChildren.push(child);
|
|
17921
|
+
continue;
|
|
17922
|
+
}
|
|
17923
|
+
const sliceStartInRun = Math.max(0, span.start - runStart);
|
|
17924
|
+
const sliceEndInRun = Math.min(length, span.end - runStart);
|
|
17925
|
+
if (sliceStartInRun > 0) {
|
|
17926
|
+
newChildren.push(sliceRun(child, 0, sliceStartInRun));
|
|
17927
|
+
}
|
|
17928
|
+
if (tracked) {
|
|
17929
|
+
const cutRun = sliceRun(child, sliceStartInRun, sliceEndInRun);
|
|
17930
|
+
convertRunTextToDelText(cutRun);
|
|
17931
|
+
newChildren.push(/* @__PURE__ */ jsxDEV(Del, {
|
|
17932
|
+
meta: mintMeta(tracked),
|
|
17933
|
+
children: cutRun
|
|
17934
|
+
}, undefined, false, undefined, this));
|
|
17935
|
+
}
|
|
17936
|
+
placeReplacement();
|
|
17937
|
+
if (sliceEndInRun < length) {
|
|
17938
|
+
newChildren.push(sliceRun(child, sliceEndInRun, length));
|
|
17939
|
+
}
|
|
17940
|
+
continue;
|
|
17941
|
+
}
|
|
17942
|
+
if (child.tag === "w:ins" || child.tag === "w:del") {
|
|
17943
|
+
const innerLength = sumInnerRunLengths(child);
|
|
17944
|
+
const wrapperStart = offset;
|
|
17945
|
+
const wrapperEnd = offset + innerLength;
|
|
17946
|
+
offset = wrapperEnd;
|
|
17947
|
+
if (wrapperEnd <= span.start) {
|
|
17948
|
+
newChildren.push(child);
|
|
17949
|
+
continue;
|
|
17950
|
+
}
|
|
17951
|
+
if (wrapperStart >= span.end) {
|
|
17952
|
+
placeReplacement();
|
|
17953
|
+
newChildren.push(child);
|
|
17954
|
+
continue;
|
|
17955
|
+
}
|
|
17956
|
+
splitWrapperAcrossSpan(child, wrapperStart, span, tracked, newChildren, placeReplacement);
|
|
17957
|
+
continue;
|
|
17958
|
+
}
|
|
17959
|
+
newChildren.push(child);
|
|
17960
|
+
}
|
|
17961
|
+
if (!placed)
|
|
17962
|
+
placeReplacement();
|
|
17963
|
+
paragraph.children = newChildren;
|
|
17964
|
+
}
|
|
17965
|
+
function splitWrapperAcrossSpan(wrapper, wrapperStart, span, tracked, out, placeReplacement) {
|
|
17966
|
+
const isDel = wrapper.tag === "w:del";
|
|
17967
|
+
const preInner = [];
|
|
17968
|
+
const cutInner = [];
|
|
17969
|
+
const postInner = [];
|
|
17970
|
+
let innerOffset = wrapperStart;
|
|
17971
|
+
for (const inner of wrapper.children) {
|
|
17972
|
+
if (inner.tag !== "w:r") {
|
|
17973
|
+
preInner.push(inner);
|
|
17974
|
+
continue;
|
|
17975
|
+
}
|
|
17976
|
+
const length = runTextLength(inner);
|
|
17977
|
+
const runStart = innerOffset;
|
|
17978
|
+
const runEnd = innerOffset + length;
|
|
17979
|
+
innerOffset = runEnd;
|
|
17980
|
+
if (runEnd <= span.start) {
|
|
17981
|
+
preInner.push(inner);
|
|
17982
|
+
continue;
|
|
17983
|
+
}
|
|
17984
|
+
if (runStart >= span.end) {
|
|
17985
|
+
postInner.push(inner);
|
|
17986
|
+
continue;
|
|
17987
|
+
}
|
|
17988
|
+
const sliceStartInRun = Math.max(0, span.start - runStart);
|
|
17989
|
+
const sliceEndInRun = Math.min(length, span.end - runStart);
|
|
17990
|
+
if (sliceStartInRun > 0)
|
|
17991
|
+
preInner.push(sliceRun(inner, 0, sliceStartInRun));
|
|
17992
|
+
cutInner.push(sliceRun(inner, sliceStartInRun, sliceEndInRun));
|
|
17993
|
+
if (sliceEndInRun < length)
|
|
17994
|
+
postInner.push(sliceRun(inner, sliceEndInRun, length));
|
|
17995
|
+
}
|
|
17996
|
+
const preChildren = preInner.slice();
|
|
17997
|
+
if (isDel) {
|
|
17998
|
+
preChildren.push(...cutInner);
|
|
17999
|
+
} else if (tracked && cutInner.length > 0) {
|
|
18000
|
+
for (const cutRun of cutInner)
|
|
18001
|
+
convertRunTextToDelText(cutRun);
|
|
18002
|
+
preChildren.push(/* @__PURE__ */ jsxDEV(Del, {
|
|
18003
|
+
meta: mintMeta(tracked),
|
|
18004
|
+
children: cutInner
|
|
18005
|
+
}, undefined, false, undefined, this));
|
|
18006
|
+
}
|
|
18007
|
+
if (preChildren.length > 0) {
|
|
18008
|
+
const preWrapper = new XmlNode2(wrapper.tag, { ...wrapper.attributes });
|
|
18009
|
+
preWrapper.children = preChildren;
|
|
18010
|
+
out.push(preWrapper);
|
|
18011
|
+
}
|
|
18012
|
+
placeReplacement();
|
|
18013
|
+
if (postInner.length > 0) {
|
|
18014
|
+
const postWrapper = new XmlNode2(wrapper.tag, { ...wrapper.attributes });
|
|
18015
|
+
postWrapper.children = postInner;
|
|
18016
|
+
out.push(postWrapper);
|
|
18017
|
+
}
|
|
18018
|
+
}
|
|
18019
|
+
function sumInnerRunLengths(wrapper) {
|
|
18020
|
+
let total = 0;
|
|
18021
|
+
for (const inner of wrapper.children) {
|
|
18022
|
+
if (inner.tag === "w:r")
|
|
18023
|
+
total += runTextLength(inner);
|
|
18024
|
+
}
|
|
18025
|
+
return total;
|
|
18026
|
+
}
|
|
18027
|
+
function mintMeta(tracked) {
|
|
18028
|
+
return { ...tracked.meta, revisionId: tracked.allocator.next() };
|
|
18029
|
+
}
|
|
18030
|
+
function convertRunTextToDelText(run20) {
|
|
18031
|
+
for (const child of run20.children) {
|
|
18032
|
+
if (child.tag === "w:t")
|
|
18033
|
+
child.tag = "w:delText";
|
|
18034
|
+
}
|
|
18035
|
+
}
|
|
18036
|
+
function ReplacementRun({
|
|
18037
|
+
runProperties,
|
|
18038
|
+
text
|
|
18039
|
+
}) {
|
|
18040
|
+
return /* @__PURE__ */ jsxDEV(w.r, {
|
|
18041
|
+
children: [
|
|
18042
|
+
runProperties,
|
|
18043
|
+
/* @__PURE__ */ jsxDEV(w.t, {
|
|
18044
|
+
"xml:space": "preserve",
|
|
18045
|
+
children: text
|
|
18046
|
+
}, undefined, false, undefined, this)
|
|
18047
|
+
]
|
|
18048
|
+
}, undefined, true, undefined, this);
|
|
18049
|
+
}
|
|
18050
|
+
var init_replace_span = __esm(() => {
|
|
18051
|
+
init_core();
|
|
18052
|
+
init_jsx();
|
|
18053
|
+
init_parser();
|
|
18054
|
+
init_jsx_dev_runtime();
|
|
18055
|
+
});
|
|
17149
18056
|
|
|
17150
|
-
// src/cli/
|
|
17151
|
-
var
|
|
17152
|
-
__export(
|
|
17153
|
-
run: () =>
|
|
18057
|
+
// src/cli/replace/index.ts
|
|
18058
|
+
var exports_replace2 = {};
|
|
18059
|
+
__export(exports_replace2, {
|
|
18060
|
+
run: () => run20
|
|
17154
18061
|
});
|
|
17155
|
-
import { parseArgs as
|
|
17156
|
-
async function
|
|
18062
|
+
import { parseArgs as parseArgs17 } from "util";
|
|
18063
|
+
async function run20(args) {
|
|
17157
18064
|
let parsed;
|
|
17158
18065
|
try {
|
|
17159
|
-
parsed =
|
|
18066
|
+
parsed = parseArgs17({
|
|
17160
18067
|
args,
|
|
17161
18068
|
allowPositionals: true,
|
|
17162
18069
|
options: {
|
|
17163
|
-
|
|
17164
|
-
|
|
18070
|
+
regex: { type: "boolean" },
|
|
18071
|
+
"ignore-case": { type: "boolean" },
|
|
18072
|
+
all: { type: "boolean" },
|
|
18073
|
+
limit: { type: "string" },
|
|
18074
|
+
output: { type: "string", short: "o" },
|
|
18075
|
+
"dry-run": { type: "boolean" },
|
|
17165
18076
|
help: { type: "boolean", short: "h" }
|
|
17166
18077
|
}
|
|
17167
18078
|
});
|
|
17168
|
-
} catch (parseError) {
|
|
17169
|
-
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17170
|
-
return fail("USAGE", message,
|
|
17171
|
-
}
|
|
17172
|
-
if (parsed.values.help) {
|
|
17173
|
-
await writeStdout(
|
|
18079
|
+
} catch (parseError) {
|
|
18080
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
18081
|
+
return fail("USAGE", message, HELP20);
|
|
18082
|
+
}
|
|
18083
|
+
if (parsed.values.help) {
|
|
18084
|
+
await writeStdout(HELP20);
|
|
18085
|
+
return EXIT.OK;
|
|
18086
|
+
}
|
|
18087
|
+
const path = parsed.positionals[0];
|
|
18088
|
+
const pattern = parsed.positionals[1];
|
|
18089
|
+
const replacement = parsed.positionals[2];
|
|
18090
|
+
if (!path)
|
|
18091
|
+
return fail("USAGE", "Missing FILE argument", HELP20);
|
|
18092
|
+
if (pattern == null)
|
|
18093
|
+
return fail("USAGE", "Missing PATTERN argument", HELP20);
|
|
18094
|
+
if (replacement == null) {
|
|
18095
|
+
return fail("USAGE", "Missing REPLACEMENT argument", HELP20);
|
|
18096
|
+
}
|
|
18097
|
+
const ignoreCase = Boolean(parsed.values["ignore-case"]);
|
|
18098
|
+
const useRegex = Boolean(parsed.values.regex);
|
|
18099
|
+
const wantAll = Boolean(parsed.values.all);
|
|
18100
|
+
const limitRaw = parsed.values.limit;
|
|
18101
|
+
const limit = limitRaw === undefined ? undefined : Number(limitRaw);
|
|
18102
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {
|
|
18103
|
+
return fail("USAGE", `--limit must be a positive integer, got "${limitRaw}"`);
|
|
18104
|
+
}
|
|
18105
|
+
const view = await openOrFail(path);
|
|
18106
|
+
if (typeof view === "number")
|
|
18107
|
+
return view;
|
|
18108
|
+
let allMatches;
|
|
18109
|
+
try {
|
|
18110
|
+
allMatches = findTextSpans(view.doc, pattern, {
|
|
18111
|
+
regex: useRegex,
|
|
18112
|
+
ignoreCase
|
|
18113
|
+
});
|
|
18114
|
+
} catch (matcherError) {
|
|
18115
|
+
const message = matcherError instanceof Error ? matcherError.message : String(matcherError);
|
|
18116
|
+
return fail("USAGE", `Invalid pattern: ${message}`);
|
|
18117
|
+
}
|
|
18118
|
+
let selected;
|
|
18119
|
+
if (limit !== undefined) {
|
|
18120
|
+
selected = allMatches.slice(0, limit);
|
|
18121
|
+
} else if (wantAll) {
|
|
18122
|
+
selected = allMatches;
|
|
18123
|
+
} else {
|
|
18124
|
+
selected = allMatches.slice(0, 1);
|
|
18125
|
+
}
|
|
18126
|
+
const matchesPayload = selected.map((match) => ({
|
|
18127
|
+
locator: `${match.blockId}:${match.start}-${match.end}`,
|
|
18128
|
+
blockId: match.blockId,
|
|
18129
|
+
start: match.start,
|
|
18130
|
+
end: match.end,
|
|
18131
|
+
text: match.text
|
|
18132
|
+
}));
|
|
18133
|
+
const outputPath = parsed.values.output;
|
|
18134
|
+
if (parsed.values["dry-run"]) {
|
|
18135
|
+
await respond({
|
|
18136
|
+
ok: true,
|
|
18137
|
+
operation: "replace",
|
|
18138
|
+
dryRun: true,
|
|
18139
|
+
path,
|
|
18140
|
+
pattern,
|
|
18141
|
+
replacement,
|
|
18142
|
+
regex: useRegex,
|
|
18143
|
+
ignoreCase,
|
|
18144
|
+
totalMatches: allMatches.length,
|
|
18145
|
+
replaced: selected.length,
|
|
18146
|
+
matches: matchesPayload,
|
|
18147
|
+
...outputPath ? { output: outputPath } : {}
|
|
18148
|
+
});
|
|
17174
18149
|
return EXIT.OK;
|
|
17175
18150
|
}
|
|
17176
|
-
if (
|
|
17177
|
-
await
|
|
18151
|
+
if (selected.length === 0) {
|
|
18152
|
+
await respond({
|
|
18153
|
+
ok: true,
|
|
18154
|
+
operation: "replace",
|
|
18155
|
+
path,
|
|
18156
|
+
pattern,
|
|
18157
|
+
replacement,
|
|
18158
|
+
regex: useRegex,
|
|
18159
|
+
ignoreCase,
|
|
18160
|
+
totalMatches: 0,
|
|
18161
|
+
replaced: 0,
|
|
18162
|
+
matches: []
|
|
18163
|
+
});
|
|
17178
18164
|
return EXIT.OK;
|
|
17179
18165
|
}
|
|
17180
|
-
|
|
18166
|
+
const reversed = [...selected].sort((leftMatch, rightMatch) => {
|
|
18167
|
+
if (leftMatch.blockId !== rightMatch.blockId) {
|
|
18168
|
+
return rightMatch.blockId.localeCompare(leftMatch.blockId);
|
|
18169
|
+
}
|
|
18170
|
+
return rightMatch.start - leftMatch.start;
|
|
18171
|
+
});
|
|
18172
|
+
const tracked = isTrackChangesEnabled(view) ? {
|
|
18173
|
+
meta: { author: resolveAuthor(), date: resolveDate() },
|
|
18174
|
+
allocator: createRevisionAllocator(view)
|
|
18175
|
+
} : undefined;
|
|
18176
|
+
const regexFlags = ignoreCase ? "i" : "";
|
|
18177
|
+
for (const match of reversed) {
|
|
18178
|
+
const concreteReplacement = useRegex ? match.text.replace(new RegExp(pattern, regexFlags), replacement) : replacement;
|
|
18179
|
+
const blockRef = resolveBlock(view, match.blockId);
|
|
18180
|
+
replaceSpanInParagraph(blockRef.node, { start: match.start, end: match.end }, concreteReplacement, tracked);
|
|
18181
|
+
}
|
|
18182
|
+
await saveDocView(view, outputPath);
|
|
18183
|
+
await respond({
|
|
18184
|
+
ok: true,
|
|
18185
|
+
operation: "replace",
|
|
18186
|
+
path: outputPath ?? path,
|
|
18187
|
+
pattern,
|
|
18188
|
+
replacement,
|
|
18189
|
+
regex: useRegex,
|
|
18190
|
+
ignoreCase,
|
|
18191
|
+
totalMatches: allMatches.length,
|
|
18192
|
+
replaced: selected.length,
|
|
18193
|
+
matches: matchesPayload
|
|
18194
|
+
});
|
|
17181
18195
|
return EXIT.OK;
|
|
17182
18196
|
}
|
|
17183
|
-
var
|
|
18197
|
+
var HELP20 = `docx replace \u2014 substitute text spans (sed for docx)
|
|
17184
18198
|
|
|
17185
18199
|
Usage:
|
|
17186
|
-
docx
|
|
18200
|
+
docx replace FILE PATTERN REPLACEMENT [options]
|
|
17187
18201
|
|
|
17188
18202
|
Options:
|
|
17189
|
-
--
|
|
17190
|
-
--
|
|
17191
|
-
|
|
18203
|
+
--regex treat PATTERN as a JavaScript regular expression
|
|
18204
|
+
--ignore-case case-insensitive match
|
|
18205
|
+
--all replace every match (default: just the first)
|
|
18206
|
+
--limit N replace at most N matches (in document order)
|
|
18207
|
+
-o, --output PATH write to PATH instead of overwriting FILE
|
|
18208
|
+
--dry-run report what would change without writing the file
|
|
18209
|
+
-h, --help show this help
|
|
18210
|
+
|
|
18211
|
+
Within-paragraph matches only. Run formatting (rPr) on the surrounding text
|
|
18212
|
+
is preserved; the replacement run inherits the rPr of the first run that
|
|
18213
|
+
overlaps the matched span. When a single invocation produces multiple
|
|
18214
|
+
replacements in the same paragraph, they're applied in reverse offset order
|
|
18215
|
+
so earlier offsets don't shift before being applied.
|
|
18216
|
+
|
|
18217
|
+
With --regex, REPLACEMENT supports JS String.replace substitution syntax:
|
|
18218
|
+
$1, $2, ... numbered capture groups
|
|
18219
|
+
$& the matched substring
|
|
18220
|
+
$\` text before the match
|
|
18221
|
+
$' text after the match
|
|
18222
|
+
$$ a literal $
|
|
17192
18223
|
|
|
17193
18224
|
Examples:
|
|
17194
|
-
docx
|
|
17195
|
-
docx
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
18225
|
+
docx replace doc.docx "fox" "cat"
|
|
18226
|
+
docx replace doc.docx "fox" "cat" --all
|
|
18227
|
+
docx replace doc.docx "TODO|FIXME" "DONE" --regex --all
|
|
18228
|
+
docx replace doc.docx "(\\w+) (\\w+)" "$2 $1" --regex --all
|
|
18229
|
+
docx replace doc.docx "wordy phrase" "tighter phrase" --all --dry-run
|
|
18230
|
+
`;
|
|
18231
|
+
var init_replace2 = __esm(() => {
|
|
18232
|
+
init_core();
|
|
17199
18233
|
init_respond();
|
|
17200
|
-
|
|
17201
|
-
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17202
|
-
$id: "https://github.com/kklimuk/docx-cli/schema",
|
|
17203
|
-
title: "docx-cli AST",
|
|
17204
|
-
type: "object",
|
|
17205
|
-
required: ["schemaVersion", "path", "properties", "blocks", "comments"],
|
|
17206
|
-
properties: {
|
|
17207
|
-
schemaVersion: { const: 1 },
|
|
17208
|
-
path: { type: "string" },
|
|
17209
|
-
properties: {
|
|
17210
|
-
type: "object",
|
|
17211
|
-
properties: {
|
|
17212
|
-
title: { type: "string" },
|
|
17213
|
-
author: { type: "string" },
|
|
17214
|
-
created: { type: "string" },
|
|
17215
|
-
modified: { type: "string" }
|
|
17216
|
-
}
|
|
17217
|
-
},
|
|
17218
|
-
blocks: { type: "array", items: { $ref: "#/$defs/Block" } },
|
|
17219
|
-
comments: { type: "array", items: { $ref: "#/$defs/Comment" } }
|
|
17220
|
-
},
|
|
17221
|
-
$defs: {
|
|
17222
|
-
Block: {
|
|
17223
|
-
oneOf: [
|
|
17224
|
-
{ $ref: "#/$defs/Paragraph" },
|
|
17225
|
-
{ $ref: "#/$defs/Table" },
|
|
17226
|
-
{ $ref: "#/$defs/SectionBreak" }
|
|
17227
|
-
]
|
|
17228
|
-
},
|
|
17229
|
-
Paragraph: {
|
|
17230
|
-
type: "object",
|
|
17231
|
-
required: ["id", "type", "runs"],
|
|
17232
|
-
properties: {
|
|
17233
|
-
id: { type: "string" },
|
|
17234
|
-
type: { const: "paragraph" },
|
|
17235
|
-
style: { type: "string" },
|
|
17236
|
-
alignment: { enum: ["left", "center", "right", "justify"] },
|
|
17237
|
-
list: {
|
|
17238
|
-
type: "object",
|
|
17239
|
-
required: ["level", "numId"],
|
|
17240
|
-
properties: {
|
|
17241
|
-
level: { type: "number" },
|
|
17242
|
-
numId: { type: "string" }
|
|
17243
|
-
}
|
|
17244
|
-
},
|
|
17245
|
-
runs: { type: "array", items: { $ref: "#/$defs/Run" } }
|
|
17246
|
-
}
|
|
17247
|
-
},
|
|
17248
|
-
Run: {
|
|
17249
|
-
oneOf: [
|
|
17250
|
-
{ $ref: "#/$defs/TextRun" },
|
|
17251
|
-
{ $ref: "#/$defs/ImageRun" },
|
|
17252
|
-
{ $ref: "#/$defs/BreakRun" },
|
|
17253
|
-
{ $ref: "#/$defs/TabRun" }
|
|
17254
|
-
]
|
|
17255
|
-
},
|
|
17256
|
-
TextRun: {
|
|
17257
|
-
type: "object",
|
|
17258
|
-
required: ["type", "text"],
|
|
17259
|
-
properties: {
|
|
17260
|
-
type: { const: "text" },
|
|
17261
|
-
text: { type: "string" },
|
|
17262
|
-
color: { type: "string" },
|
|
17263
|
-
highlight: { type: "string" },
|
|
17264
|
-
bold: { type: "boolean" },
|
|
17265
|
-
italic: { type: "boolean" },
|
|
17266
|
-
underline: { type: "string" },
|
|
17267
|
-
strike: { type: "boolean" },
|
|
17268
|
-
font: { type: "string" },
|
|
17269
|
-
sizeHalfPoints: { type: "number" },
|
|
17270
|
-
comments: { type: "array", items: { type: "string" } },
|
|
17271
|
-
trackedChange: {
|
|
17272
|
-
type: "object",
|
|
17273
|
-
required: ["kind", "author", "date", "revisionId"],
|
|
17274
|
-
properties: {
|
|
17275
|
-
kind: { enum: ["ins", "del"] },
|
|
17276
|
-
author: { type: "string" },
|
|
17277
|
-
date: { type: "string" },
|
|
17278
|
-
revisionId: { type: "string" }
|
|
17279
|
-
}
|
|
17280
|
-
}
|
|
17281
|
-
}
|
|
17282
|
-
},
|
|
17283
|
-
ImageRun: {
|
|
17284
|
-
type: "object",
|
|
17285
|
-
required: ["type", "id", "contentType", "hash"],
|
|
17286
|
-
properties: {
|
|
17287
|
-
type: { const: "image" },
|
|
17288
|
-
id: { type: "string" },
|
|
17289
|
-
contentType: { type: "string" },
|
|
17290
|
-
hash: { type: "string" },
|
|
17291
|
-
widthEmu: { type: "number" },
|
|
17292
|
-
heightEmu: { type: "number" },
|
|
17293
|
-
alt: { type: "string" }
|
|
17294
|
-
}
|
|
17295
|
-
},
|
|
17296
|
-
BreakRun: {
|
|
17297
|
-
type: "object",
|
|
17298
|
-
required: ["type", "kind"],
|
|
17299
|
-
properties: {
|
|
17300
|
-
type: { const: "break" },
|
|
17301
|
-
kind: { enum: ["page", "line", "column"] }
|
|
17302
|
-
}
|
|
17303
|
-
},
|
|
17304
|
-
TabRun: {
|
|
17305
|
-
type: "object",
|
|
17306
|
-
required: ["type"],
|
|
17307
|
-
properties: { type: { const: "tab" } }
|
|
17308
|
-
},
|
|
17309
|
-
Table: {
|
|
17310
|
-
type: "object",
|
|
17311
|
-
required: ["id", "type", "rows"],
|
|
17312
|
-
properties: {
|
|
17313
|
-
id: { type: "string" },
|
|
17314
|
-
type: { const: "table" },
|
|
17315
|
-
rows: {
|
|
17316
|
-
type: "array",
|
|
17317
|
-
items: {
|
|
17318
|
-
type: "object",
|
|
17319
|
-
properties: {
|
|
17320
|
-
cells: {
|
|
17321
|
-
type: "array",
|
|
17322
|
-
items: {
|
|
17323
|
-
type: "object",
|
|
17324
|
-
properties: {
|
|
17325
|
-
blocks: {
|
|
17326
|
-
type: "array",
|
|
17327
|
-
items: { $ref: "#/$defs/Block" }
|
|
17328
|
-
}
|
|
17329
|
-
}
|
|
17330
|
-
}
|
|
17331
|
-
}
|
|
17332
|
-
}
|
|
17333
|
-
}
|
|
17334
|
-
}
|
|
17335
|
-
}
|
|
17336
|
-
},
|
|
17337
|
-
SectionBreak: {
|
|
17338
|
-
type: "object",
|
|
17339
|
-
required: ["id", "type"],
|
|
17340
|
-
properties: {
|
|
17341
|
-
id: { type: "string" },
|
|
17342
|
-
type: { const: "sectionBreak" }
|
|
17343
|
-
}
|
|
17344
|
-
},
|
|
17345
|
-
Comment: {
|
|
17346
|
-
type: "object",
|
|
17347
|
-
required: ["id", "author", "date", "text", "anchor"],
|
|
17348
|
-
properties: {
|
|
17349
|
-
id: { type: "string" },
|
|
17350
|
-
author: { type: "string" },
|
|
17351
|
-
initials: { type: "string" },
|
|
17352
|
-
date: { type: "string" },
|
|
17353
|
-
text: { type: "string" },
|
|
17354
|
-
parentId: { type: "string" },
|
|
17355
|
-
resolved: { type: "boolean" },
|
|
17356
|
-
anchor: {
|
|
17357
|
-
type: "object",
|
|
17358
|
-
required: ["startBlockId", "startOffset", "endBlockId", "endOffset"],
|
|
17359
|
-
properties: {
|
|
17360
|
-
startBlockId: { type: "string" },
|
|
17361
|
-
startOffset: { type: "number" },
|
|
17362
|
-
endBlockId: { type: "string" },
|
|
17363
|
-
endOffset: { type: "number" }
|
|
17364
|
-
}
|
|
17365
|
-
}
|
|
17366
|
-
}
|
|
17367
|
-
}
|
|
17368
|
-
}
|
|
17369
|
-
};
|
|
18234
|
+
init_replace_span();
|
|
17370
18235
|
});
|
|
17371
18236
|
|
|
17372
18237
|
// src/cli/track-changes/index.tsx
|
|
17373
18238
|
var exports_track_changes = {};
|
|
17374
18239
|
__export(exports_track_changes, {
|
|
17375
|
-
run: () =>
|
|
18240
|
+
run: () => run21
|
|
17376
18241
|
});
|
|
17377
|
-
import { parseArgs as
|
|
17378
|
-
async function
|
|
18242
|
+
import { parseArgs as parseArgs18 } from "util";
|
|
18243
|
+
async function run21(args) {
|
|
17379
18244
|
let parsed;
|
|
17380
18245
|
try {
|
|
17381
|
-
parsed =
|
|
18246
|
+
parsed = parseArgs18({
|
|
17382
18247
|
args,
|
|
17383
18248
|
allowPositionals: true,
|
|
17384
18249
|
options: {
|
|
18250
|
+
output: { type: "string", short: "o" },
|
|
17385
18251
|
"dry-run": { type: "boolean" },
|
|
17386
18252
|
help: { type: "boolean", short: "h" }
|
|
17387
18253
|
}
|
|
17388
18254
|
});
|
|
17389
18255
|
} catch (parseError) {
|
|
17390
18256
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17391
|
-
return fail("USAGE", message,
|
|
18257
|
+
return fail("USAGE", message, HELP21);
|
|
17392
18258
|
}
|
|
17393
18259
|
if (parsed.values.help) {
|
|
17394
|
-
await writeStdout(
|
|
18260
|
+
await writeStdout(HELP21);
|
|
17395
18261
|
return EXIT.OK;
|
|
17396
18262
|
}
|
|
17397
18263
|
const path = parsed.positionals[0];
|
|
17398
18264
|
if (!path)
|
|
17399
|
-
return fail("USAGE", "Missing FILE argument",
|
|
18265
|
+
return fail("USAGE", "Missing FILE argument", HELP21);
|
|
17400
18266
|
const mode = parsed.positionals[1];
|
|
17401
18267
|
if (mode !== "on" && mode !== "off") {
|
|
17402
|
-
return fail("USAGE", `Expected "on" or "off", got: ${mode ?? "<nothing>"}`,
|
|
17403
|
-
}
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
view
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
}
|
|
17412
|
-
if (openError.code === "NOT_A_ZIP") {
|
|
17413
|
-
return fail("NOT_A_ZIP", openError.message);
|
|
17414
|
-
}
|
|
17415
|
-
}
|
|
17416
|
-
throw openError;
|
|
17417
|
-
}
|
|
17418
|
-
const settingsXml = view.pkg.hasPart(SETTINGS_PART) ? await view.pkg.readText(SETTINGS_PART) : null;
|
|
17419
|
-
const settingsTree = settingsXml ? XmlNode2.parse(settingsXml) : [];
|
|
17420
|
-
let settingsRoot = XmlNode2.findRoot(settingsTree, "w:settings");
|
|
18268
|
+
return fail("USAGE", `Expected "on" or "off", got: ${mode ?? "<nothing>"}`, HELP21);
|
|
18269
|
+
}
|
|
18270
|
+
const view = await openOrFail(path);
|
|
18271
|
+
if (typeof view === "number")
|
|
18272
|
+
return view;
|
|
18273
|
+
const wasMissing = view.settingsTree === undefined;
|
|
18274
|
+
if (!view.settingsTree)
|
|
18275
|
+
view.settingsTree = [];
|
|
18276
|
+
let settingsRoot = XmlNode2.findRoot(view.settingsTree, "w:settings");
|
|
17421
18277
|
if (!settingsRoot) {
|
|
17422
18278
|
settingsRoot = /* @__PURE__ */ jsxDEV(w.settings, {
|
|
17423
18279
|
"xmlns:w": NS_W2
|
|
17424
18280
|
}, undefined, false, undefined, this);
|
|
17425
|
-
settingsTree.push(settingsRoot);
|
|
18281
|
+
view.settingsTree.push(settingsRoot);
|
|
17426
18282
|
}
|
|
17427
18283
|
const hasTrackChanges = settingsRoot.children.some((child) => child.tag === "w:trackChanges");
|
|
18284
|
+
const outputPath = parsed.values.output;
|
|
17428
18285
|
if (parsed.values["dry-run"]) {
|
|
17429
18286
|
await respond({
|
|
17430
18287
|
ok: true,
|
|
@@ -17432,7 +18289,8 @@ async function run19(args) {
|
|
|
17432
18289
|
dryRun: true,
|
|
17433
18290
|
path,
|
|
17434
18291
|
mode,
|
|
17435
|
-
previouslyOn: hasTrackChanges
|
|
18292
|
+
previouslyOn: hasTrackChanges,
|
|
18293
|
+
...outputPath ? { output: outputPath } : {}
|
|
17436
18294
|
});
|
|
17437
18295
|
return EXIT.OK;
|
|
17438
18296
|
}
|
|
@@ -17441,71 +18299,36 @@ async function run19(args) {
|
|
|
17441
18299
|
} else if (mode === "off" && hasTrackChanges) {
|
|
17442
18300
|
settingsRoot.children = settingsRoot.children.filter((child) => child.tag !== "w:trackChanges");
|
|
17443
18301
|
}
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
|
|
18302
|
+
if (wasMissing) {
|
|
18303
|
+
registerPart(view.relationshipsTree, view.contentTypesTree, {
|
|
18304
|
+
partName: SETTINGS_PART,
|
|
18305
|
+
contentType: SETTINGS_CONTENT_TYPE,
|
|
18306
|
+
relationshipType: SETTINGS_REL_TYPE,
|
|
18307
|
+
target: "settings.xml"
|
|
18308
|
+
});
|
|
17447
18309
|
}
|
|
17448
|
-
view
|
|
17449
|
-
view.pkg.writeText("[Content_Types].xml", XmlNode2.serializeWithDeclaration(view.contentTypesTree));
|
|
17450
|
-
await view.pkg.save();
|
|
18310
|
+
await saveDocView(view, outputPath);
|
|
17451
18311
|
await respond({
|
|
17452
18312
|
ok: true,
|
|
17453
18313
|
operation: "track-changes",
|
|
17454
|
-
path,
|
|
18314
|
+
path: outputPath ?? path,
|
|
17455
18315
|
mode,
|
|
17456
18316
|
previouslyOn: hasTrackChanges
|
|
17457
18317
|
});
|
|
17458
18318
|
return EXIT.OK;
|
|
17459
18319
|
}
|
|
17460
|
-
|
|
17461
|
-
const relationships = XmlNode2.findRoot(view.relationshipsTree, "Relationships");
|
|
17462
|
-
if (relationships) {
|
|
17463
|
-
const alreadyLinked = relationships.children.some((child) => child.tag === "Relationship" && child.getAttribute("Type") === SETTINGS_REL_TYPE);
|
|
17464
|
-
if (!alreadyLinked) {
|
|
17465
|
-
relationships.children.push(new XmlNode2("Relationship", {
|
|
17466
|
-
Id: nextRelationshipId2(relationships),
|
|
17467
|
-
Type: SETTINGS_REL_TYPE,
|
|
17468
|
-
Target: "settings.xml"
|
|
17469
|
-
}));
|
|
17470
|
-
}
|
|
17471
|
-
}
|
|
17472
|
-
const types = XmlNode2.findRoot(view.contentTypesTree, "Types");
|
|
17473
|
-
if (types) {
|
|
17474
|
-
const exists = types.children.some((child) => child.tag === "Override" && child.getAttribute("PartName") === `/${SETTINGS_PART}`);
|
|
17475
|
-
if (!exists) {
|
|
17476
|
-
types.children.push(new XmlNode2("Override", {
|
|
17477
|
-
PartName: `/${SETTINGS_PART}`,
|
|
17478
|
-
ContentType: SETTINGS_CONTENT_TYPE
|
|
17479
|
-
}));
|
|
17480
|
-
}
|
|
17481
|
-
}
|
|
17482
|
-
}
|
|
17483
|
-
function nextRelationshipId2(relationships) {
|
|
17484
|
-
let highest = 0;
|
|
17485
|
-
for (const child of relationships.children) {
|
|
17486
|
-
if (child.tag !== "Relationship")
|
|
17487
|
-
continue;
|
|
17488
|
-
const id = child.getAttribute("Id");
|
|
17489
|
-
if (!id)
|
|
17490
|
-
continue;
|
|
17491
|
-
const match = id.match(/^rId(\d+)$/);
|
|
17492
|
-
if (!match)
|
|
17493
|
-
continue;
|
|
17494
|
-
const numeric = Number(match[1]);
|
|
17495
|
-
if (Number.isFinite(numeric) && numeric > highest)
|
|
17496
|
-
highest = numeric;
|
|
17497
|
-
}
|
|
17498
|
-
return `rId${highest + 1}`;
|
|
17499
|
-
}
|
|
17500
|
-
var HELP19 = `docx track-changes \u2014 toggle the document's tracked-changes mode
|
|
18320
|
+
var HELP21 = `docx track-changes \u2014 toggle the document's tracked-changes mode
|
|
17501
18321
|
|
|
17502
18322
|
Usage:
|
|
17503
18323
|
docx track-changes FILE on|off [options]
|
|
17504
18324
|
|
|
17505
|
-
Sets <w:trackChanges/> in word/settings.xml. When on,
|
|
17506
|
-
|
|
18325
|
+
Sets <w:trackChanges/> in word/settings.xml. When on, this CLI's
|
|
18326
|
+
insert/edit/delete/replace commands also emit <w:ins>/<w:del> markers
|
|
18327
|
+
(attributed to $DOCX_AUTHOR) so changes remain reviewable. Existing
|
|
18328
|
+
<w:ins>/<w:del> markers are unaffected by the toggle itself.
|
|
17507
18329
|
|
|
17508
18330
|
Options:
|
|
18331
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
17509
18332
|
--dry-run Print what would change; do not write the file
|
|
17510
18333
|
-h, --help Show this help
|
|
17511
18334
|
|
|
@@ -17513,17 +18336,79 @@ Examples:
|
|
|
17513
18336
|
docx track-changes doc.docx on
|
|
17514
18337
|
docx track-changes doc.docx off
|
|
17515
18338
|
`, SETTINGS_PART = "word/settings.xml", SETTINGS_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", SETTINGS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", NS_W2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
17516
|
-
var
|
|
18339
|
+
var init_track_changes2 = __esm(() => {
|
|
17517
18340
|
init_core();
|
|
17518
18341
|
init_jsx();
|
|
18342
|
+
init_package();
|
|
17519
18343
|
init_parser();
|
|
17520
18344
|
init_respond();
|
|
17521
18345
|
init_jsx_dev_runtime();
|
|
17522
18346
|
});
|
|
18347
|
+
// package.json
|
|
18348
|
+
var package_default = {
|
|
18349
|
+
name: "bun-docx",
|
|
18350
|
+
version: "0.3.0",
|
|
18351
|
+
description: "CLI for AI agents (Claude, Codex) to read, edit, and comment on .docx files with full format fidelity.",
|
|
18352
|
+
keywords: [
|
|
18353
|
+
"docx",
|
|
18354
|
+
"openxml",
|
|
18355
|
+
"ooxml",
|
|
18356
|
+
"word",
|
|
18357
|
+
"cli",
|
|
18358
|
+
"ai",
|
|
18359
|
+
"claude",
|
|
18360
|
+
"codex",
|
|
18361
|
+
"agentic"
|
|
18362
|
+
],
|
|
18363
|
+
license: "MIT",
|
|
18364
|
+
author: "Kirill Klimuk",
|
|
18365
|
+
repository: {
|
|
18366
|
+
type: "git",
|
|
18367
|
+
url: "git+https://github.com/kklimuk/docx-cli.git"
|
|
18368
|
+
},
|
|
18369
|
+
homepage: "https://github.com/kklimuk/docx-cli#readme",
|
|
18370
|
+
bugs: "https://github.com/kklimuk/docx-cli/issues",
|
|
18371
|
+
type: "module",
|
|
18372
|
+
packageManager: "bun@1.3.13",
|
|
18373
|
+
bin: {
|
|
18374
|
+
docx: "./dist/index.js"
|
|
18375
|
+
},
|
|
18376
|
+
files: [
|
|
18377
|
+
"dist/index.js",
|
|
18378
|
+
"README.md",
|
|
18379
|
+
"LICENSE"
|
|
18380
|
+
],
|
|
18381
|
+
engines: {
|
|
18382
|
+
bun: ">=1.3.0"
|
|
18383
|
+
},
|
|
18384
|
+
scripts: {
|
|
18385
|
+
dev: "bun src/index.ts",
|
|
18386
|
+
build: "bun build src/index.ts --outfile dist/index.js --target bun",
|
|
18387
|
+
"build:binary": "bun build --compile --outfile=dist/docx src/index.ts",
|
|
18388
|
+
check: "biome check . && knip-bun && bunx tsc --noEmit",
|
|
18389
|
+
test: "bun test",
|
|
18390
|
+
"test:unit": "bun test tests/core tests/cli",
|
|
18391
|
+
"test:integration": "bun test tests/integration",
|
|
18392
|
+
prepack: "bun run build",
|
|
18393
|
+
prepare: "husky"
|
|
18394
|
+
},
|
|
18395
|
+
devDependencies: {
|
|
18396
|
+
"@biomejs/biome": "^2.4.10",
|
|
18397
|
+
"@types/bun": "^1.3.13",
|
|
18398
|
+
husky: "^9.1.7",
|
|
18399
|
+
knip: "^6.3.0",
|
|
18400
|
+
typescript: "^6.0.2"
|
|
18401
|
+
},
|
|
18402
|
+
dependencies: {
|
|
18403
|
+
"fast-xml-builder": "^1.1.6",
|
|
18404
|
+
"fast-xml-parser": "^5.7.2",
|
|
18405
|
+
jszip: "^3.10.1"
|
|
18406
|
+
}
|
|
18407
|
+
};
|
|
17523
18408
|
|
|
17524
18409
|
// src/cli/help.ts
|
|
17525
18410
|
init_respond();
|
|
17526
|
-
var VERSION =
|
|
18411
|
+
var VERSION = package_default.version;
|
|
17527
18412
|
var TOP_HELP = `docx ${VERSION} \u2014 read, edit, and comment on .docx files
|
|
17528
18413
|
|
|
17529
18414
|
Usage:
|
|
@@ -17535,16 +18420,23 @@ Commands:
|
|
|
17535
18420
|
edit FILE Replace text/properties at a locator
|
|
17536
18421
|
insert FILE Insert a block or run
|
|
17537
18422
|
delete FILE Remove a block or run range
|
|
17538
|
-
|
|
18423
|
+
find FILE QUERY Find text spans, return locators
|
|
18424
|
+
replace FILE PATTERN REPL Substitute text spans (sed for docx)
|
|
18425
|
+
comments \u2026 Add, reply, resolve, delete, list comments
|
|
17539
18426
|
images \u2026 Extract, replace, list images
|
|
17540
18427
|
track-changes FILE on|off Toggle tracked-changes mode
|
|
17541
|
-
|
|
17542
|
-
locators Dump locator grammar reference
|
|
18428
|
+
info \u2026 Reference material (schema, locators)
|
|
17543
18429
|
|
|
17544
18430
|
Run "docx <command> --help" for command-specific help.
|
|
17545
18431
|
|
|
17546
18432
|
Environment:
|
|
17547
|
-
DOCX_AUTHOR
|
|
18433
|
+
DOCX_AUTHOR Default author for comments and tracked-change attribution
|
|
18434
|
+
DOCX_CLI_NOW Override the timestamp used for tracked changes (test only)
|
|
18435
|
+
|
|
18436
|
+
Tracked changes:
|
|
18437
|
+
When <w:trackChanges/> is set in the doc (toggle via "docx track-changes
|
|
18438
|
+
FILE on"), insert/edit/delete/replace automatically emit <w:ins>/<w:del>
|
|
18439
|
+
markers attributed to $DOCX_AUTHOR.
|
|
17548
18440
|
`;
|
|
17549
18441
|
async function printTopHelp() {
|
|
17550
18442
|
await writeStdout(TOP_HELP);
|
|
@@ -17557,18 +18449,19 @@ var COMMANDS = {
|
|
|
17557
18449
|
create: () => Promise.resolve().then(() => (init_create(), exports_create)),
|
|
17558
18450
|
delete: () => Promise.resolve().then(() => (init_delete2(), exports_delete2)),
|
|
17559
18451
|
edit: () => Promise.resolve().then(() => (init_edit(), exports_edit)),
|
|
18452
|
+
find: () => Promise.resolve().then(() => (init_find(), exports_find)),
|
|
17560
18453
|
images: () => Promise.resolve().then(() => (init_images(), exports_images)),
|
|
18454
|
+
info: () => Promise.resolve().then(() => (init_info(), exports_info)),
|
|
17561
18455
|
insert: () => Promise.resolve().then(() => (init_insert(), exports_insert)),
|
|
17562
|
-
locators: () => Promise.resolve().then(() => (init_locators_cmd(), exports_locators_cmd)),
|
|
17563
18456
|
read: () => Promise.resolve().then(() => (init_read2(), exports_read)),
|
|
17564
|
-
|
|
17565
|
-
"track-changes": () => Promise.resolve().then(() => (
|
|
18457
|
+
replace: () => Promise.resolve().then(() => (init_replace2(), exports_replace2)),
|
|
18458
|
+
"track-changes": () => Promise.resolve().then(() => (init_track_changes2(), exports_track_changes))
|
|
17566
18459
|
};
|
|
17567
18460
|
async function main(argv) {
|
|
17568
18461
|
const args = argv.slice(2);
|
|
17569
18462
|
const cmd = args[0];
|
|
17570
18463
|
if (!cmd) {
|
|
17571
|
-
|
|
18464
|
+
Bun.stderr.write(`Usage: docx <command> [options]
|
|
17572
18465
|
Run "docx --help" for available commands.
|
|
17573
18466
|
`);
|
|
17574
18467
|
return 2;
|