bun-docx 0.3.0 → 0.5.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 +10 -2
- package/dist/index.js +1541 -305
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13762,7 +13762,7 @@ var init_package = __esm(() => {
|
|
|
13762
13762
|
|
|
13763
13763
|
// src/core/ast/read.ts
|
|
13764
13764
|
function buildDoc(view, path) {
|
|
13765
|
-
|
|
13765
|
+
readRelationships(view);
|
|
13766
13766
|
const properties = readDocProperties(view);
|
|
13767
13767
|
const documentRoot = XmlNode2.findRoot(view.documentTree, "w:document");
|
|
13768
13768
|
if (!documentRoot) {
|
|
@@ -13774,6 +13774,7 @@ function buildDoc(view, path) {
|
|
|
13774
13774
|
}
|
|
13775
13775
|
const state = {
|
|
13776
13776
|
imageIndex: 0,
|
|
13777
|
+
hyperlinkIndex: 0,
|
|
13777
13778
|
commentAnchors: new Map,
|
|
13778
13779
|
openComments: new Map
|
|
13779
13780
|
};
|
|
@@ -13813,17 +13814,30 @@ function readParagraph(view, node, id, state) {
|
|
|
13813
13814
|
if (paragraphProperties) {
|
|
13814
13815
|
applyParagraphProperties(paragraph, paragraphProperties);
|
|
13815
13816
|
}
|
|
13816
|
-
const
|
|
13817
|
-
|
|
13818
|
-
|
|
13817
|
+
const context = {
|
|
13818
|
+
view,
|
|
13819
|
+
blockId: id,
|
|
13820
|
+
paragraph,
|
|
13821
|
+
activeComments: new Set,
|
|
13822
|
+
state,
|
|
13823
|
+
offsetRef: { value: 0 }
|
|
13824
|
+
};
|
|
13825
|
+
walkRunContainer(context, node, undefined, undefined);
|
|
13826
|
+
return paragraph;
|
|
13827
|
+
}
|
|
13828
|
+
function walkRunContainer(context, container, trackedChange, hyperlink) {
|
|
13829
|
+
for (const child of container.children) {
|
|
13819
13830
|
if (child.tag === "w:pPr")
|
|
13820
13831
|
continue;
|
|
13821
13832
|
if (child.tag === "w:commentRangeStart") {
|
|
13822
13833
|
const commentId = child.getAttribute("w:id");
|
|
13823
13834
|
if (commentId) {
|
|
13824
13835
|
const key = `c${commentId}`;
|
|
13825
|
-
activeComments.add(key);
|
|
13826
|
-
state.openComments.set(key, {
|
|
13836
|
+
context.activeComments.add(key);
|
|
13837
|
+
context.state.openComments.set(key, {
|
|
13838
|
+
blockId: context.blockId,
|
|
13839
|
+
offset: context.offsetRef.value
|
|
13840
|
+
});
|
|
13827
13841
|
}
|
|
13828
13842
|
continue;
|
|
13829
13843
|
}
|
|
@@ -13831,26 +13845,26 @@ function readParagraph(view, node, id, state) {
|
|
|
13831
13845
|
const commentId = child.getAttribute("w:id");
|
|
13832
13846
|
if (commentId) {
|
|
13833
13847
|
const key = `c${commentId}`;
|
|
13834
|
-
activeComments.delete(key);
|
|
13835
|
-
const opened = state.openComments.get(key);
|
|
13848
|
+
context.activeComments.delete(key);
|
|
13849
|
+
const opened = context.state.openComments.get(key);
|
|
13836
13850
|
if (opened) {
|
|
13837
|
-
state.commentAnchors.set(key, {
|
|
13851
|
+
context.state.commentAnchors.set(key, {
|
|
13838
13852
|
startBlockId: opened.blockId,
|
|
13839
13853
|
startOffset: opened.offset,
|
|
13840
|
-
endBlockId:
|
|
13841
|
-
endOffset:
|
|
13854
|
+
endBlockId: context.blockId,
|
|
13855
|
+
endOffset: context.offsetRef.value
|
|
13842
13856
|
});
|
|
13843
|
-
state.openComments.delete(key);
|
|
13857
|
+
context.state.openComments.delete(key);
|
|
13844
13858
|
}
|
|
13845
13859
|
}
|
|
13846
13860
|
continue;
|
|
13847
13861
|
}
|
|
13848
13862
|
if (child.tag === "w:r") {
|
|
13849
|
-
const run = readRun(view, child, activeComments,
|
|
13863
|
+
const run = readRun(context.view, child, context.activeComments, trackedChange, hyperlink, context.state);
|
|
13850
13864
|
if (run) {
|
|
13851
13865
|
if (run.type === "text")
|
|
13852
|
-
|
|
13853
|
-
paragraph.runs.push(run);
|
|
13866
|
+
context.offsetRef.value += run.text.length;
|
|
13867
|
+
context.paragraph.runs.push(run);
|
|
13854
13868
|
}
|
|
13855
13869
|
continue;
|
|
13856
13870
|
}
|
|
@@ -13861,46 +13875,40 @@ function readParagraph(view, node, id, state) {
|
|
|
13861
13875
|
date: child.getAttribute("w:date") ?? "",
|
|
13862
13876
|
revisionId: child.getAttribute("w:id") ?? ""
|
|
13863
13877
|
};
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
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
|
-
}
|
|
13892
|
-
if (inner.tag !== "w:r")
|
|
13893
|
-
continue;
|
|
13894
|
-
const run = readRun(view, inner, activeComments, change, state);
|
|
13895
|
-
if (!run)
|
|
13896
|
-
continue;
|
|
13897
|
-
if (run.type === "text")
|
|
13898
|
-
offset += run.text.length;
|
|
13899
|
-
paragraph.runs.push(run);
|
|
13900
|
-
}
|
|
13878
|
+
walkRunContainer(context, child, change, hyperlink);
|
|
13879
|
+
continue;
|
|
13880
|
+
}
|
|
13881
|
+
if (child.tag === "w:hyperlink") {
|
|
13882
|
+
const link = readHyperlinkProperties(context.view, child, container.children, context.state);
|
|
13883
|
+
walkRunContainer(context, child, trackedChange, link);
|
|
13901
13884
|
}
|
|
13902
13885
|
}
|
|
13903
|
-
|
|
13886
|
+
}
|
|
13887
|
+
function readHyperlinkProperties(view, node, parent, state) {
|
|
13888
|
+
const id = `link${state.hyperlinkIndex++}`;
|
|
13889
|
+
const link = { id };
|
|
13890
|
+
const relationshipId = node.getAttribute("r:id");
|
|
13891
|
+
if (relationshipId) {
|
|
13892
|
+
const relationship = view.hyperlinksByRelationshipId.get(relationshipId);
|
|
13893
|
+
if (relationship?.url)
|
|
13894
|
+
link.url = relationship.url;
|
|
13895
|
+
}
|
|
13896
|
+
const anchor = node.getAttribute("w:anchor");
|
|
13897
|
+
if (anchor)
|
|
13898
|
+
link.anchor = anchor;
|
|
13899
|
+
const tooltip = node.getAttribute("w:tooltip");
|
|
13900
|
+
if (tooltip)
|
|
13901
|
+
link.tooltip = tooltip;
|
|
13902
|
+
if (!link.url && !link.anchor && !link.tooltip) {
|
|
13903
|
+
state.hyperlinkIndex--;
|
|
13904
|
+
return;
|
|
13905
|
+
}
|
|
13906
|
+
view.hyperlinkById.set(id, {
|
|
13907
|
+
node,
|
|
13908
|
+
parent,
|
|
13909
|
+
...relationshipId ? { relationshipId } : {}
|
|
13910
|
+
});
|
|
13911
|
+
return link;
|
|
13904
13912
|
}
|
|
13905
13913
|
function applyParagraphProperties(paragraph, paragraphProperties) {
|
|
13906
13914
|
const styleNode = paragraphProperties.findChild("w:pStyle");
|
|
@@ -13925,7 +13933,7 @@ function applyParagraphProperties(paragraph, paragraphProperties) {
|
|
|
13925
13933
|
paragraph.list = { level, numId: id };
|
|
13926
13934
|
}
|
|
13927
13935
|
}
|
|
13928
|
-
function readRun(view, node, activeComments, trackedChange, state) {
|
|
13936
|
+
function readRun(view, node, activeComments, trackedChange, hyperlink, state) {
|
|
13929
13937
|
const runProperties = node.findChild("w:rPr");
|
|
13930
13938
|
for (const child of node.children) {
|
|
13931
13939
|
if (child.tag === "w:drawing") {
|
|
@@ -13956,6 +13964,8 @@ function readRun(view, node, activeComments, trackedChange, state) {
|
|
|
13956
13964
|
run.comments = [...activeComments];
|
|
13957
13965
|
if (trackedChange)
|
|
13958
13966
|
run.trackedChange = trackedChange;
|
|
13967
|
+
if (hyperlink)
|
|
13968
|
+
run.hyperlink = hyperlink;
|
|
13959
13969
|
return run;
|
|
13960
13970
|
}
|
|
13961
13971
|
function applyRunProperties(run, runProperties) {
|
|
@@ -14060,22 +14070,30 @@ function readCellBlocks(view, cell, tableId, rowIndex, columnIndex, state) {
|
|
|
14060
14070
|
}
|
|
14061
14071
|
return blocks;
|
|
14062
14072
|
}
|
|
14063
|
-
function
|
|
14073
|
+
function readRelationships(view) {
|
|
14064
14074
|
const relationships = XmlNode2.findRoot(view.relationshipsTree, "Relationships");
|
|
14065
14075
|
if (!relationships)
|
|
14066
14076
|
return;
|
|
14067
14077
|
for (const child of relationships.children) {
|
|
14068
14078
|
if (child.tag !== "Relationship")
|
|
14069
14079
|
continue;
|
|
14070
|
-
|
|
14071
|
-
continue;
|
|
14080
|
+
const type = child.getAttribute("Type");
|
|
14072
14081
|
const relationshipId = child.getAttribute("Id");
|
|
14073
14082
|
const target = child.getAttribute("Target");
|
|
14074
14083
|
if (!relationshipId || !target)
|
|
14075
14084
|
continue;
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14085
|
+
if (type === RELATIONSHIP_NAMESPACE_IMAGE) {
|
|
14086
|
+
const partName = target.startsWith("/") ? target.slice(1) : `word/${target}`;
|
|
14087
|
+
const contentType = lookupContentType(view, partName);
|
|
14088
|
+
view.imagesByRelationshipId.set(relationshipId, {
|
|
14089
|
+
partName,
|
|
14090
|
+
contentType
|
|
14091
|
+
});
|
|
14092
|
+
continue;
|
|
14093
|
+
}
|
|
14094
|
+
if (type === RELATIONSHIP_NAMESPACE_HYPERLINK) {
|
|
14095
|
+
view.hyperlinksByRelationshipId.set(relationshipId, { url: target });
|
|
14096
|
+
}
|
|
14079
14097
|
}
|
|
14080
14098
|
}
|
|
14081
14099
|
function lookupContentType(view, partName) {
|
|
@@ -14204,7 +14222,7 @@ function readCommentsExtended(view) {
|
|
|
14204
14222
|
}
|
|
14205
14223
|
return out;
|
|
14206
14224
|
}
|
|
14207
|
-
var RELATIONSHIP_NAMESPACE_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
14225
|
+
var RELATIONSHIP_NAMESPACE_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", RELATIONSHIP_NAMESPACE_HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
|
14208
14226
|
var init_read = __esm(() => {
|
|
14209
14227
|
init_parser();
|
|
14210
14228
|
});
|
|
@@ -14232,7 +14250,9 @@ async function openDocView(path) {
|
|
|
14232
14250
|
blockReferences: new Map,
|
|
14233
14251
|
commentReferences: new Map,
|
|
14234
14252
|
imagesByRelationshipId: new Map,
|
|
14235
|
-
imageById: new Map
|
|
14253
|
+
imageById: new Map,
|
|
14254
|
+
hyperlinksByRelationshipId: new Map,
|
|
14255
|
+
hyperlinkById: new Map
|
|
14236
14256
|
};
|
|
14237
14257
|
view.doc = buildDoc(view, pkg.path);
|
|
14238
14258
|
return view;
|
|
@@ -14319,6 +14339,10 @@ function parseLocator(input) {
|
|
|
14319
14339
|
const imageMatch = trimmed.match(IMAGE_RE);
|
|
14320
14340
|
if (imageMatch)
|
|
14321
14341
|
return { kind: "image", imageId: `img${imageMatch[1]}` };
|
|
14342
|
+
const linkMatch = trimmed.match(LINK_RE);
|
|
14343
|
+
if (linkMatch) {
|
|
14344
|
+
return { kind: "hyperlink", hyperlinkId: `link${linkMatch[1]}` };
|
|
14345
|
+
}
|
|
14322
14346
|
const cellMatch = trimmed.match(CELL_RE);
|
|
14323
14347
|
if (cellMatch) {
|
|
14324
14348
|
const [, tableIndex, rowIndex, columnIndex, rest] = cellMatch;
|
|
@@ -14367,7 +14391,7 @@ function validateOffsets(input, start, end, crossBlock) {
|
|
|
14367
14391
|
throw new LocatorParseError(input, "end offset precedes start");
|
|
14368
14392
|
}
|
|
14369
14393
|
}
|
|
14370
|
-
var LocatorParseError, BLOCK_RE, SPAN_RE, RANGE_RE, COMMENT_RE, IMAGE_RE, CELL_RE;
|
|
14394
|
+
var LocatorParseError, BLOCK_RE, SPAN_RE, RANGE_RE, COMMENT_RE, IMAGE_RE, LINK_RE, CELL_RE;
|
|
14371
14395
|
var init_parse = __esm(() => {
|
|
14372
14396
|
LocatorParseError = class LocatorParseError extends Error {
|
|
14373
14397
|
input;
|
|
@@ -14382,6 +14406,7 @@ var init_parse = __esm(() => {
|
|
|
14382
14406
|
RANGE_RE = /^p(\d+):(\d+)-p(\d+):(\d+)$/;
|
|
14383
14407
|
COMMENT_RE = /^c(\d+)$/;
|
|
14384
14408
|
IMAGE_RE = /^img(\d+)$/;
|
|
14409
|
+
LINK_RE = /^link(\d+)$/;
|
|
14385
14410
|
CELL_RE = /^t(\d+):r(\d+)c(\d+)(?::(.+))?$/;
|
|
14386
14411
|
});
|
|
14387
14412
|
|
|
@@ -14431,6 +14456,39 @@ var init_locators = __esm(() => {
|
|
|
14431
14456
|
init_resolve();
|
|
14432
14457
|
});
|
|
14433
14458
|
|
|
14459
|
+
// src/core/relationships.ts
|
|
14460
|
+
function mintRelationshipId(relationshipsRoot) {
|
|
14461
|
+
let highest = 0;
|
|
14462
|
+
for (const child of relationshipsRoot.children) {
|
|
14463
|
+
if (child.tag !== "Relationship")
|
|
14464
|
+
continue;
|
|
14465
|
+
const id = child.getAttribute("Id");
|
|
14466
|
+
if (!id)
|
|
14467
|
+
continue;
|
|
14468
|
+
const match = id.match(/^rId(\d+)$/);
|
|
14469
|
+
if (match?.[1]) {
|
|
14470
|
+
const number = Number(match[1]);
|
|
14471
|
+
if (number > highest)
|
|
14472
|
+
highest = number;
|
|
14473
|
+
}
|
|
14474
|
+
}
|
|
14475
|
+
return `rId${highest + 1}`;
|
|
14476
|
+
}
|
|
14477
|
+
function addHyperlinkRelationship(relationshipsRoot, url) {
|
|
14478
|
+
const id = mintRelationshipId(relationshipsRoot);
|
|
14479
|
+
relationshipsRoot.children.push(new XmlNode2("Relationship", {
|
|
14480
|
+
Id: id,
|
|
14481
|
+
Type: HYPERLINK_RELATIONSHIP_TYPE,
|
|
14482
|
+
Target: url,
|
|
14483
|
+
TargetMode: "External"
|
|
14484
|
+
}));
|
|
14485
|
+
return id;
|
|
14486
|
+
}
|
|
14487
|
+
var HYPERLINK_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
|
14488
|
+
var init_relationships = __esm(() => {
|
|
14489
|
+
init_parser();
|
|
14490
|
+
});
|
|
14491
|
+
|
|
14434
14492
|
// src/core/track-changes/index.ts
|
|
14435
14493
|
function isTrackChangesEnabled(view) {
|
|
14436
14494
|
if (!view.settingsTree)
|
|
@@ -14607,7 +14665,8 @@ var init_jsx = __esm(() => {
|
|
|
14607
14665
|
"comments",
|
|
14608
14666
|
"comment",
|
|
14609
14667
|
"settings",
|
|
14610
|
-
"trackChanges"
|
|
14668
|
+
"trackChanges",
|
|
14669
|
+
"hyperlink"
|
|
14611
14670
|
];
|
|
14612
14671
|
R_TAGS = ["embed", "link", "id"];
|
|
14613
14672
|
A_TAGS = [
|
|
@@ -14722,6 +14781,7 @@ var init_core = __esm(() => {
|
|
|
14722
14781
|
init_locators();
|
|
14723
14782
|
init_package();
|
|
14724
14783
|
init_parser();
|
|
14784
|
+
init_relationships();
|
|
14725
14785
|
init_track_changes();
|
|
14726
14786
|
init_emit();
|
|
14727
14787
|
});
|
|
@@ -14755,6 +14815,7 @@ function exitCodeFor(code) {
|
|
|
14755
14815
|
case "BLOCK_NOT_FOUND":
|
|
14756
14816
|
case "COMMENT_NOT_FOUND":
|
|
14757
14817
|
case "IMAGE_NOT_FOUND":
|
|
14818
|
+
case "HYPERLINK_NOT_FOUND":
|
|
14758
14819
|
case "MATCH_NOT_FOUND":
|
|
14759
14820
|
return EXIT.NOT_FOUND;
|
|
14760
14821
|
case "NOT_A_ZIP":
|
|
@@ -16629,12 +16690,107 @@ var init_find = __esm(() => {
|
|
|
16629
16690
|
init_respond();
|
|
16630
16691
|
});
|
|
16631
16692
|
|
|
16632
|
-
// src/cli/
|
|
16633
|
-
|
|
16634
|
-
|
|
16693
|
+
// src/cli/hyperlinks/wrap.tsx
|
|
16694
|
+
function wrapSpanInHyperlink(paragraph, span, relationshipId) {
|
|
16695
|
+
if (span.start >= span.end) {
|
|
16696
|
+
throw new HyperlinkWrapError(`Empty or inverted span ${span.start}-${span.end}`);
|
|
16697
|
+
}
|
|
16698
|
+
const newChildren = [];
|
|
16699
|
+
const wrappedRuns = [];
|
|
16700
|
+
let offset = 0;
|
|
16701
|
+
let placed = false;
|
|
16702
|
+
const placeWrapper = () => {
|
|
16703
|
+
if (placed || wrappedRuns.length === 0)
|
|
16704
|
+
return;
|
|
16705
|
+
newChildren.push(hyperlinkWrapper(relationshipId, wrappedRuns));
|
|
16706
|
+
wrappedRuns.length = 0;
|
|
16707
|
+
placed = true;
|
|
16708
|
+
};
|
|
16709
|
+
for (const child of paragraph.children) {
|
|
16710
|
+
if (child.tag === "w:r") {
|
|
16711
|
+
const length = runTextLength(child);
|
|
16712
|
+
const runStart = offset;
|
|
16713
|
+
const runEnd = offset + length;
|
|
16714
|
+
offset = runEnd;
|
|
16715
|
+
if (runEnd <= span.start || runStart >= span.end) {
|
|
16716
|
+
placeWrapper();
|
|
16717
|
+
newChildren.push(child);
|
|
16718
|
+
continue;
|
|
16719
|
+
}
|
|
16720
|
+
const sliceStartInRun = Math.max(0, span.start - runStart);
|
|
16721
|
+
const sliceEndInRun = Math.min(length, span.end - runStart);
|
|
16722
|
+
if (sliceStartInRun > 0) {
|
|
16723
|
+
newChildren.push(sliceRun(child, 0, sliceStartInRun));
|
|
16724
|
+
}
|
|
16725
|
+
wrappedRuns.push(sliceRun(child, sliceStartInRun, sliceEndInRun));
|
|
16726
|
+
if (runEnd >= span.end)
|
|
16727
|
+
placeWrapper();
|
|
16728
|
+
if (sliceEndInRun < length) {
|
|
16729
|
+
newChildren.push(sliceRun(child, sliceEndInRun, length));
|
|
16730
|
+
}
|
|
16731
|
+
continue;
|
|
16732
|
+
}
|
|
16733
|
+
if (child.tag === "w:hyperlink") {
|
|
16734
|
+
const hyperlinkLength = sumInnerRunLengths(child);
|
|
16735
|
+
const hyperlinkStart = offset;
|
|
16736
|
+
const hyperlinkEnd = offset + hyperlinkLength;
|
|
16737
|
+
offset = hyperlinkEnd;
|
|
16738
|
+
if (hyperlinkEnd <= span.start || hyperlinkStart >= span.end) {
|
|
16739
|
+
placeWrapper();
|
|
16740
|
+
newChildren.push(child);
|
|
16741
|
+
continue;
|
|
16742
|
+
}
|
|
16743
|
+
throw new HyperlinkWrapError(`Span ${span.start}-${span.end} overlaps an existing hyperlink at ${hyperlinkStart}-${hyperlinkEnd}; nested hyperlinks are not allowed`);
|
|
16744
|
+
}
|
|
16745
|
+
if (child.tag === "w:ins" || child.tag === "w:del") {
|
|
16746
|
+
const innerLength = sumInnerRunLengths(child);
|
|
16747
|
+
const wrapperStart = offset;
|
|
16748
|
+
const wrapperEnd = offset + innerLength;
|
|
16749
|
+
offset = wrapperEnd;
|
|
16750
|
+
if (wrapperEnd <= span.start || wrapperStart >= span.end) {
|
|
16751
|
+
placeWrapper();
|
|
16752
|
+
newChildren.push(child);
|
|
16753
|
+
continue;
|
|
16754
|
+
}
|
|
16755
|
+
throw new HyperlinkWrapError(`Span ${span.start}-${span.end} crosses a tracked-change wrapper at ${wrapperStart}-${wrapperEnd}; resolve or accept the change first`);
|
|
16756
|
+
}
|
|
16757
|
+
newChildren.push(child);
|
|
16758
|
+
}
|
|
16759
|
+
placeWrapper();
|
|
16760
|
+
paragraph.children = newChildren;
|
|
16761
|
+
}
|
|
16762
|
+
function hyperlinkWrapper(relationshipId, runs) {
|
|
16763
|
+
return /* @__PURE__ */ jsxDEV(w.hyperlink, {
|
|
16764
|
+
"r:id": relationshipId,
|
|
16765
|
+
children: runs
|
|
16766
|
+
}, undefined, false, undefined, this);
|
|
16767
|
+
}
|
|
16768
|
+
function sumInnerRunLengths(wrapper) {
|
|
16769
|
+
let total = 0;
|
|
16770
|
+
for (const inner of wrapper.children) {
|
|
16771
|
+
if (inner.tag === "w:r")
|
|
16772
|
+
total += runTextLength(inner);
|
|
16773
|
+
}
|
|
16774
|
+
return total;
|
|
16775
|
+
}
|
|
16776
|
+
var HyperlinkWrapError;
|
|
16777
|
+
var init_wrap = __esm(() => {
|
|
16778
|
+
init_jsx();
|
|
16779
|
+
init_parser();
|
|
16780
|
+
init_jsx_dev_runtime();
|
|
16781
|
+
HyperlinkWrapError = class HyperlinkWrapError extends Error {
|
|
16782
|
+
constructor(message) {
|
|
16783
|
+
super(message);
|
|
16784
|
+
this.name = "HyperlinkWrapError";
|
|
16785
|
+
}
|
|
16786
|
+
};
|
|
16787
|
+
});
|
|
16788
|
+
|
|
16789
|
+
// src/cli/hyperlinks/add.tsx
|
|
16790
|
+
var exports_add2 = {};
|
|
16791
|
+
__export(exports_add2, {
|
|
16635
16792
|
run: () => run11
|
|
16636
16793
|
});
|
|
16637
|
-
import { join } from "path";
|
|
16638
16794
|
import { parseArgs as parseArgs10 } from "util";
|
|
16639
16795
|
async function run11(args) {
|
|
16640
16796
|
let parsed;
|
|
@@ -16643,8 +16799,10 @@ async function run11(args) {
|
|
|
16643
16799
|
args,
|
|
16644
16800
|
allowPositionals: true,
|
|
16645
16801
|
options: {
|
|
16646
|
-
|
|
16647
|
-
|
|
16802
|
+
at: { type: "string" },
|
|
16803
|
+
url: { type: "string" },
|
|
16804
|
+
output: { type: "string", short: "o" },
|
|
16805
|
+
"dry-run": { type: "boolean" },
|
|
16648
16806
|
help: { type: "boolean", short: "h" }
|
|
16649
16807
|
}
|
|
16650
16808
|
});
|
|
@@ -16659,109 +16817,100 @@ async function run11(args) {
|
|
|
16659
16817
|
const path = parsed.positionals[0];
|
|
16660
16818
|
if (!path)
|
|
16661
16819
|
return fail("USAGE", "Missing FILE argument", HELP11);
|
|
16662
|
-
const
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16820
|
+
const atInput = parsed.values.at;
|
|
16821
|
+
const url = parsed.values.url;
|
|
16822
|
+
if (!atInput)
|
|
16823
|
+
return fail("USAGE", "Missing --at LOCATOR", HELP11);
|
|
16824
|
+
if (!url)
|
|
16825
|
+
return fail("USAGE", "Missing --url URL", HELP11);
|
|
16826
|
+
let locator;
|
|
16827
|
+
try {
|
|
16828
|
+
locator = parseLocator(atInput);
|
|
16829
|
+
} catch (error) {
|
|
16830
|
+
if (error instanceof LocatorParseError) {
|
|
16831
|
+
return fail("INVALID_LOCATOR", error.message);
|
|
16832
|
+
}
|
|
16833
|
+
throw error;
|
|
16834
|
+
}
|
|
16835
|
+
const target = locatorToBlockTarget(locator);
|
|
16836
|
+
if (!target?.span) {
|
|
16837
|
+
return fail("INVALID_LOCATOR", "hyperlinks add requires a span locator like pN:S-E or tT:rRcC:pK:S-E");
|
|
16838
|
+
}
|
|
16666
16839
|
const view = await openOrFail(path);
|
|
16667
16840
|
if (typeof view === "number")
|
|
16668
16841
|
return view;
|
|
16669
|
-
await
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
const
|
|
16673
|
-
if (
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
const extension = extensionFor(image.contentType, reference.partName);
|
|
16683
|
-
const fileName = `${image.hash}.${extension}`;
|
|
16684
|
-
const outputPath = join(outputDir, fileName);
|
|
16685
|
-
if (!seenHashes.has(image.hash)) {
|
|
16686
|
-
const bytes = await view.pkg.readBytes(reference.partName);
|
|
16687
|
-
await Bun.write(outputPath, bytes);
|
|
16688
|
-
seenHashes.add(image.hash);
|
|
16689
|
-
}
|
|
16690
|
-
manifest.push({
|
|
16691
|
-
id: image.id,
|
|
16692
|
-
path: outputPath,
|
|
16693
|
-
bytes: (await Bun.file(outputPath).arrayBuffer()).byteLength
|
|
16842
|
+
const paragraphRef = await resolveBlockOrFail(view, target.blockId);
|
|
16843
|
+
if (typeof paragraphRef === "number")
|
|
16844
|
+
return paragraphRef;
|
|
16845
|
+
const outputPath = parsed.values.output;
|
|
16846
|
+
if (parsed.values["dry-run"]) {
|
|
16847
|
+
await respond({
|
|
16848
|
+
ok: true,
|
|
16849
|
+
operation: "hyperlinks.add",
|
|
16850
|
+
dryRun: true,
|
|
16851
|
+
path,
|
|
16852
|
+
at: atInput,
|
|
16853
|
+
url,
|
|
16854
|
+
...outputPath ? { output: outputPath } : {}
|
|
16694
16855
|
});
|
|
16856
|
+
return EXIT.OK;
|
|
16695
16857
|
}
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
|
|
16706
|
-
continue;
|
|
16707
|
-
}
|
|
16708
|
-
if (block.type === "table") {
|
|
16709
|
-
for (const row of block.rows) {
|
|
16710
|
-
for (const cell of row.cells) {
|
|
16711
|
-
collectImages(cell.blocks, out);
|
|
16712
|
-
}
|
|
16713
|
-
}
|
|
16858
|
+
const relationships = XmlNode2.findRoot(view.relationshipsTree, "Relationships");
|
|
16859
|
+
if (!relationships) {
|
|
16860
|
+
return fail("UNHANDLED", "Missing <Relationships> root in document rels");
|
|
16861
|
+
}
|
|
16862
|
+
const relationshipId = addHyperlinkRelationship(relationships, url);
|
|
16863
|
+
try {
|
|
16864
|
+
wrapSpanInHyperlink(paragraphRef.node, target.span, relationshipId);
|
|
16865
|
+
} catch (error) {
|
|
16866
|
+
if (error instanceof HyperlinkWrapError) {
|
|
16867
|
+
return fail("USAGE", error.message);
|
|
16714
16868
|
}
|
|
16869
|
+
throw error;
|
|
16715
16870
|
}
|
|
16871
|
+
view.hyperlinksByRelationshipId.set(relationshipId, { url });
|
|
16872
|
+
await saveDocView(view, outputPath);
|
|
16873
|
+
await respond({
|
|
16874
|
+
ok: true,
|
|
16875
|
+
operation: "hyperlinks.add",
|
|
16876
|
+
path: outputPath ?? path,
|
|
16877
|
+
at: atInput,
|
|
16878
|
+
url
|
|
16879
|
+
});
|
|
16880
|
+
return EXIT.OK;
|
|
16716
16881
|
}
|
|
16717
|
-
|
|
16718
|
-
const fromType = EXTENSION_BY_CONTENT_TYPE[contentType.toLowerCase()];
|
|
16719
|
-
if (fromType)
|
|
16720
|
-
return fromType;
|
|
16721
|
-
const fromName = partName.split(".").pop()?.toLowerCase();
|
|
16722
|
-
return fromName ?? "bin";
|
|
16723
|
-
}
|
|
16724
|
-
var HELP11 = `docx images extract \u2014 dump image bytes to a directory
|
|
16882
|
+
var HELP11 = `docx hyperlinks add \u2014 wrap an existing span in a hyperlink
|
|
16725
16883
|
|
|
16726
16884
|
Usage:
|
|
16727
|
-
docx
|
|
16885
|
+
docx hyperlinks add FILE --at LOCATOR --url URL [options]
|
|
16728
16886
|
|
|
16729
16887
|
Required:
|
|
16730
|
-
--
|
|
16888
|
+
--at LOCATOR Where to wrap. Supports:
|
|
16889
|
+
pN:S-E chars S..E of pN
|
|
16890
|
+
tT:rRcC:pK:S-E chars S..E of a cell paragraph
|
|
16891
|
+
--url URL Target URL
|
|
16731
16892
|
|
|
16732
16893
|
Optional:
|
|
16733
|
-
--
|
|
16894
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
16895
|
+
--dry-run Print what would change; do not write the file
|
|
16734
16896
|
-h, --help Show this help
|
|
16735
16897
|
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
to written paths.
|
|
16898
|
+
The span must lie inside a single paragraph and must not overlap an existing
|
|
16899
|
+
hyperlink or a tracked-change wrapper.
|
|
16739
16900
|
|
|
16740
16901
|
Examples:
|
|
16741
|
-
docx
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
var init_extract = __esm(() => {
|
|
16902
|
+
docx hyperlinks add doc.docx --at p3:5-20 --url https://example.com
|
|
16903
|
+
`;
|
|
16904
|
+
var init_add2 = __esm(() => {
|
|
16745
16905
|
init_core();
|
|
16906
|
+
init_parser();
|
|
16746
16907
|
init_respond();
|
|
16747
|
-
|
|
16748
|
-
"image/png": "png",
|
|
16749
|
-
"image/jpeg": "jpg",
|
|
16750
|
-
"image/jpg": "jpg",
|
|
16751
|
-
"image/gif": "gif",
|
|
16752
|
-
"image/svg+xml": "svg",
|
|
16753
|
-
"image/webp": "webp",
|
|
16754
|
-
"image/tiff": "tif",
|
|
16755
|
-
"image/bmp": "bmp",
|
|
16756
|
-
"image/x-emf": "emf",
|
|
16757
|
-
"image/x-wmf": "wmf",
|
|
16758
|
-
"image/vnd.microsoft.icon": "ico"
|
|
16759
|
-
};
|
|
16908
|
+
init_wrap();
|
|
16760
16909
|
});
|
|
16761
16910
|
|
|
16762
|
-
// src/cli/
|
|
16763
|
-
var
|
|
16764
|
-
__export(
|
|
16911
|
+
// src/cli/hyperlinks/delete.ts
|
|
16912
|
+
var exports_delete3 = {};
|
|
16913
|
+
__export(exports_delete3, {
|
|
16765
16914
|
run: () => run12
|
|
16766
16915
|
});
|
|
16767
16916
|
import { parseArgs as parseArgs11 } from "util";
|
|
@@ -16772,6 +16921,9 @@ async function run12(args) {
|
|
|
16772
16921
|
args,
|
|
16773
16922
|
allowPositionals: true,
|
|
16774
16923
|
options: {
|
|
16924
|
+
at: { type: "string" },
|
|
16925
|
+
output: { type: "string", short: "o" },
|
|
16926
|
+
"dry-run": { type: "boolean" },
|
|
16775
16927
|
help: { type: "boolean", short: "h" }
|
|
16776
16928
|
}
|
|
16777
16929
|
});
|
|
@@ -16786,34 +16938,560 @@ async function run12(args) {
|
|
|
16786
16938
|
const path = parsed.positionals[0];
|
|
16787
16939
|
if (!path)
|
|
16788
16940
|
return fail("USAGE", "Missing FILE argument", HELP12);
|
|
16941
|
+
const targetId = parsed.values.at;
|
|
16942
|
+
if (!targetId)
|
|
16943
|
+
return fail("USAGE", "Missing --at LINK_ID", HELP12);
|
|
16789
16944
|
const view = await openOrFail(path);
|
|
16790
16945
|
if (typeof view === "number")
|
|
16791
16946
|
return view;
|
|
16792
|
-
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16947
|
+
const reference = view.hyperlinkById.get(targetId);
|
|
16948
|
+
if (!reference) {
|
|
16949
|
+
return fail("HYPERLINK_NOT_FOUND", `Hyperlink not found: ${targetId}`);
|
|
16950
|
+
}
|
|
16951
|
+
const oldUrl = reference.relationshipId ? view.hyperlinksByRelationshipId.get(reference.relationshipId)?.url : undefined;
|
|
16952
|
+
const outputPath = parsed.values.output;
|
|
16953
|
+
if (parsed.values["dry-run"]) {
|
|
16954
|
+
await respond({
|
|
16955
|
+
ok: true,
|
|
16956
|
+
operation: "hyperlinks.delete",
|
|
16957
|
+
dryRun: true,
|
|
16958
|
+
path,
|
|
16959
|
+
hyperlinkId: targetId,
|
|
16960
|
+
from: oldUrl,
|
|
16961
|
+
...outputPath ? { output: outputPath } : {}
|
|
16962
|
+
});
|
|
16963
|
+
return EXIT.OK;
|
|
16964
|
+
}
|
|
16965
|
+
const index = reference.parent.indexOf(reference.node);
|
|
16966
|
+
if (index === -1) {
|
|
16967
|
+
return fail("HYPERLINK_NOT_FOUND", `Hyperlink reference is stale (parent does not contain it): ${targetId}`);
|
|
16968
|
+
}
|
|
16969
|
+
reference.parent.splice(index, 1, ...reference.node.children);
|
|
16970
|
+
view.hyperlinkById.delete(targetId);
|
|
16971
|
+
if (reference.relationshipId) {
|
|
16972
|
+
const remaining = countHyperlinkUsages(view.documentTree, reference.relationshipId);
|
|
16973
|
+
if (remaining === 0) {
|
|
16974
|
+
pruneRelationship(view.relationshipsTree, reference.relationshipId);
|
|
16975
|
+
view.hyperlinksByRelationshipId.delete(reference.relationshipId);
|
|
16976
|
+
}
|
|
16977
|
+
}
|
|
16978
|
+
await saveDocView(view, outputPath);
|
|
16979
|
+
await respond({
|
|
16980
|
+
ok: true,
|
|
16981
|
+
operation: "hyperlinks.delete",
|
|
16982
|
+
path: outputPath ?? path,
|
|
16983
|
+
hyperlinkId: targetId,
|
|
16984
|
+
from: oldUrl
|
|
16985
|
+
});
|
|
16986
|
+
return EXIT.OK;
|
|
16987
|
+
}
|
|
16988
|
+
function countHyperlinkUsages(documentTree, relationshipId) {
|
|
16989
|
+
let count = 0;
|
|
16990
|
+
for (const root of documentTree) {
|
|
16991
|
+
count += countInNode(root, relationshipId);
|
|
16992
|
+
}
|
|
16993
|
+
return count;
|
|
16994
|
+
}
|
|
16995
|
+
function countInNode(node, relationshipId) {
|
|
16996
|
+
let count = 0;
|
|
16997
|
+
if (node.tag === "w:hyperlink" && node.getAttribute("r:id") === relationshipId) {
|
|
16998
|
+
count++;
|
|
16999
|
+
}
|
|
17000
|
+
for (const child of node.children) {
|
|
17001
|
+
count += countInNode(child, relationshipId);
|
|
17002
|
+
}
|
|
17003
|
+
return count;
|
|
17004
|
+
}
|
|
17005
|
+
function pruneRelationship(relationshipsTree, relationshipId) {
|
|
17006
|
+
const relationships = XmlNode2.findRoot(relationshipsTree, "Relationships");
|
|
17007
|
+
if (!relationships)
|
|
17008
|
+
return;
|
|
17009
|
+
relationships.children = relationships.children.filter((child) => !(child.tag === "Relationship" && child.getAttribute("Id") === relationshipId));
|
|
17010
|
+
}
|
|
17011
|
+
var HELP12 = `docx hyperlinks delete \u2014 unwrap a hyperlink (keep the text)
|
|
17012
|
+
|
|
17013
|
+
Usage:
|
|
17014
|
+
docx hyperlinks delete FILE --at LINK_ID [options]
|
|
17015
|
+
|
|
17016
|
+
Required:
|
|
17017
|
+
--at LINK_ID Existing hyperlink to remove (e.g., link0)
|
|
17018
|
+
|
|
17019
|
+
Optional:
|
|
17020
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
17021
|
+
--dry-run Print what would change; do not write the file
|
|
17022
|
+
-h, --help Show this help
|
|
17023
|
+
|
|
17024
|
+
The display text stays in place; only the <w:hyperlink> wrapper is removed.
|
|
17025
|
+
If the underlying relationship is no longer referenced, it is pruned from the
|
|
17026
|
+
rels file too.
|
|
17027
|
+
|
|
17028
|
+
Examples:
|
|
17029
|
+
docx hyperlinks delete doc.docx --at link0
|
|
17030
|
+
`;
|
|
17031
|
+
var init_delete3 = __esm(() => {
|
|
17032
|
+
init_core();
|
|
17033
|
+
init_parser();
|
|
17034
|
+
init_respond();
|
|
17035
|
+
});
|
|
17036
|
+
|
|
17037
|
+
// src/cli/hyperlinks/list.ts
|
|
17038
|
+
var exports_list2 = {};
|
|
17039
|
+
__export(exports_list2, {
|
|
17040
|
+
run: () => run13
|
|
17041
|
+
});
|
|
17042
|
+
import { parseArgs as parseArgs12 } from "util";
|
|
17043
|
+
async function run13(args) {
|
|
17044
|
+
let parsed;
|
|
17045
|
+
try {
|
|
17046
|
+
parsed = parseArgs12({
|
|
17047
|
+
args,
|
|
17048
|
+
allowPositionals: true,
|
|
17049
|
+
options: {
|
|
17050
|
+
help: { type: "boolean", short: "h" }
|
|
17051
|
+
}
|
|
17052
|
+
});
|
|
17053
|
+
} catch (parseError) {
|
|
17054
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17055
|
+
return fail("USAGE", message, HELP13);
|
|
17056
|
+
}
|
|
17057
|
+
if (parsed.values.help) {
|
|
17058
|
+
await writeStdout(HELP13);
|
|
17059
|
+
return EXIT.OK;
|
|
17060
|
+
}
|
|
17061
|
+
const path = parsed.positionals[0];
|
|
17062
|
+
if (!path)
|
|
17063
|
+
return fail("USAGE", "Missing FILE argument", HELP13);
|
|
17064
|
+
const view = await openOrFail(path);
|
|
17065
|
+
if (typeof view === "number")
|
|
17066
|
+
return view;
|
|
17067
|
+
const entries = new Map;
|
|
17068
|
+
collectHyperlinks(view.doc.blocks, entries);
|
|
17069
|
+
await respond([...entries.values()]);
|
|
17070
|
+
return EXIT.OK;
|
|
17071
|
+
}
|
|
17072
|
+
function collectHyperlinks(blocks, entries) {
|
|
17073
|
+
for (const block of blocks) {
|
|
17074
|
+
if (block.type === "paragraph") {
|
|
17075
|
+
for (const run14 of block.runs) {
|
|
17076
|
+
if (run14.type !== "text" || !run14.hyperlink)
|
|
17077
|
+
continue;
|
|
17078
|
+
addToEntry(entries, run14.hyperlink, block.id, run14.text);
|
|
17079
|
+
}
|
|
17080
|
+
continue;
|
|
17081
|
+
}
|
|
17082
|
+
if (block.type === "table") {
|
|
17083
|
+
for (const row of block.rows) {
|
|
17084
|
+
for (const cell of row.cells) {
|
|
17085
|
+
collectHyperlinks(cell.blocks, entries);
|
|
17086
|
+
}
|
|
17087
|
+
}
|
|
17088
|
+
}
|
|
17089
|
+
}
|
|
17090
|
+
}
|
|
17091
|
+
function addToEntry(entries, hyperlink, blockId, text) {
|
|
17092
|
+
const existing = entries.get(hyperlink.id);
|
|
17093
|
+
if (existing) {
|
|
17094
|
+
existing.text += text;
|
|
17095
|
+
return;
|
|
17096
|
+
}
|
|
17097
|
+
const entry = {
|
|
17098
|
+
id: hyperlink.id,
|
|
17099
|
+
text,
|
|
17100
|
+
blockId
|
|
17101
|
+
};
|
|
17102
|
+
if (hyperlink.url)
|
|
17103
|
+
entry.url = hyperlink.url;
|
|
17104
|
+
if (hyperlink.anchor)
|
|
17105
|
+
entry.anchor = hyperlink.anchor;
|
|
17106
|
+
if (hyperlink.tooltip)
|
|
17107
|
+
entry.tooltip = hyperlink.tooltip;
|
|
17108
|
+
entries.set(hyperlink.id, entry);
|
|
17109
|
+
}
|
|
17110
|
+
var HELP13 = `docx hyperlinks list \u2014 print hyperlink manifest as JSON
|
|
17111
|
+
|
|
17112
|
+
Usage:
|
|
17113
|
+
docx hyperlinks list FILE [options]
|
|
17114
|
+
|
|
17115
|
+
Options:
|
|
17116
|
+
-h, --help Show this help
|
|
17117
|
+
|
|
17118
|
+
Each entry has: id, url (or anchor), tooltip (if set), text (display text),
|
|
17119
|
+
and blockId (the paragraph containing the link).
|
|
17120
|
+
|
|
17121
|
+
Examples:
|
|
17122
|
+
docx hyperlinks list doc.docx | jq -c '.[] | {id, url, text}'
|
|
17123
|
+
`;
|
|
17124
|
+
var init_list2 = __esm(() => {
|
|
17125
|
+
init_respond();
|
|
17126
|
+
});
|
|
17127
|
+
|
|
17128
|
+
// src/cli/hyperlinks/replace.ts
|
|
17129
|
+
var exports_replace = {};
|
|
17130
|
+
__export(exports_replace, {
|
|
17131
|
+
run: () => run14
|
|
17132
|
+
});
|
|
17133
|
+
import { parseArgs as parseArgs13 } from "util";
|
|
17134
|
+
async function run14(args) {
|
|
17135
|
+
let parsed;
|
|
17136
|
+
try {
|
|
17137
|
+
parsed = parseArgs13({
|
|
17138
|
+
args,
|
|
17139
|
+
allowPositionals: true,
|
|
17140
|
+
options: {
|
|
17141
|
+
at: { type: "string" },
|
|
17142
|
+
with: { type: "string" },
|
|
17143
|
+
output: { type: "string", short: "o" },
|
|
17144
|
+
"dry-run": { type: "boolean" },
|
|
17145
|
+
help: { type: "boolean", short: "h" }
|
|
17146
|
+
}
|
|
17147
|
+
});
|
|
17148
|
+
} catch (parseError) {
|
|
17149
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17150
|
+
return fail("USAGE", message, HELP14);
|
|
17151
|
+
}
|
|
17152
|
+
if (parsed.values.help) {
|
|
17153
|
+
await writeStdout(HELP14);
|
|
17154
|
+
return EXIT.OK;
|
|
17155
|
+
}
|
|
17156
|
+
const path = parsed.positionals[0];
|
|
17157
|
+
if (!path)
|
|
17158
|
+
return fail("USAGE", "Missing FILE argument", HELP14);
|
|
17159
|
+
const targetId = parsed.values.at;
|
|
17160
|
+
if (!targetId)
|
|
17161
|
+
return fail("USAGE", "Missing --at LINK_ID", HELP14);
|
|
17162
|
+
const newUrl = parsed.values.with;
|
|
17163
|
+
if (!newUrl)
|
|
17164
|
+
return fail("USAGE", "Missing --with URL", HELP14);
|
|
17165
|
+
const view = await openOrFail(path);
|
|
17166
|
+
if (typeof view === "number")
|
|
17167
|
+
return view;
|
|
17168
|
+
const reference = view.hyperlinkById.get(targetId);
|
|
17169
|
+
if (!reference) {
|
|
17170
|
+
return fail("HYPERLINK_NOT_FOUND", `Hyperlink not found: ${targetId}`);
|
|
17171
|
+
}
|
|
17172
|
+
const relationships = XmlNode2.findRoot(view.relationshipsTree, "Relationships");
|
|
17173
|
+
if (!relationships) {
|
|
17174
|
+
return fail("UNHANDLED", "Missing <Relationships> root in document rels");
|
|
17175
|
+
}
|
|
17176
|
+
const existingId = reference.relationshipId;
|
|
17177
|
+
const oldUrl = existingId ? view.hyperlinksByRelationshipId.get(existingId)?.url : undefined;
|
|
17178
|
+
const sharedCount = existingId ? countHyperlinkUsages2(view.documentTree, existingId) : 0;
|
|
17179
|
+
const willAllocateNew = !existingId || sharedCount > 1;
|
|
17180
|
+
const outputPath = parsed.values.output;
|
|
17181
|
+
if (parsed.values["dry-run"]) {
|
|
17182
|
+
await respond({
|
|
17183
|
+
ok: true,
|
|
17184
|
+
operation: "hyperlinks.replace",
|
|
17185
|
+
dryRun: true,
|
|
17186
|
+
path,
|
|
17187
|
+
hyperlinkId: targetId,
|
|
17188
|
+
from: oldUrl,
|
|
17189
|
+
to: newUrl,
|
|
17190
|
+
sharedRelationship: sharedCount > 1,
|
|
17191
|
+
...outputPath ? { output: outputPath } : {}
|
|
17192
|
+
});
|
|
17193
|
+
return EXIT.OK;
|
|
17194
|
+
}
|
|
17195
|
+
if (willAllocateNew) {
|
|
17196
|
+
const newRelationshipId = addHyperlinkRelationship(relationships, newUrl);
|
|
17197
|
+
reference.node.setAttribute("r:id", newRelationshipId);
|
|
17198
|
+
reference.relationshipId = newRelationshipId;
|
|
17199
|
+
view.hyperlinksByRelationshipId.set(newRelationshipId, { url: newUrl });
|
|
17200
|
+
} else if (existingId) {
|
|
17201
|
+
updateRelationshipTarget(relationships, existingId, newUrl);
|
|
17202
|
+
view.hyperlinksByRelationshipId.set(existingId, { url: newUrl });
|
|
17203
|
+
}
|
|
17204
|
+
await saveDocView(view, outputPath);
|
|
17205
|
+
await respond({
|
|
17206
|
+
ok: true,
|
|
17207
|
+
operation: "hyperlinks.replace",
|
|
17208
|
+
path: outputPath ?? path,
|
|
17209
|
+
hyperlinkId: targetId,
|
|
17210
|
+
from: oldUrl,
|
|
17211
|
+
to: newUrl
|
|
17212
|
+
});
|
|
17213
|
+
return EXIT.OK;
|
|
17214
|
+
}
|
|
17215
|
+
function updateRelationshipTarget(relationships, relationshipId, newTarget) {
|
|
17216
|
+
for (const child of relationships.children) {
|
|
17217
|
+
if (child.tag !== "Relationship")
|
|
17218
|
+
continue;
|
|
17219
|
+
if (child.getAttribute("Id") === relationshipId) {
|
|
17220
|
+
child.setAttribute("Target", newTarget);
|
|
17221
|
+
return;
|
|
17222
|
+
}
|
|
17223
|
+
}
|
|
17224
|
+
}
|
|
17225
|
+
function countHyperlinkUsages2(documentTree, relationshipId) {
|
|
17226
|
+
let count = 0;
|
|
17227
|
+
for (const root of documentTree) {
|
|
17228
|
+
count += countInNode2(root, relationshipId);
|
|
17229
|
+
}
|
|
17230
|
+
return count;
|
|
17231
|
+
}
|
|
17232
|
+
function countInNode2(node, relationshipId) {
|
|
17233
|
+
let count = 0;
|
|
17234
|
+
if (node.tag === "w:hyperlink" && node.getAttribute("r:id") === relationshipId) {
|
|
17235
|
+
count++;
|
|
17236
|
+
}
|
|
17237
|
+
for (const child of node.children) {
|
|
17238
|
+
count += countInNode2(child, relationshipId);
|
|
17239
|
+
}
|
|
17240
|
+
return count;
|
|
17241
|
+
}
|
|
17242
|
+
var HELP14 = `docx hyperlinks replace \u2014 change a hyperlink's URL
|
|
17243
|
+
|
|
17244
|
+
Usage:
|
|
17245
|
+
docx hyperlinks replace FILE --at LINK_ID --with URL [options]
|
|
17246
|
+
|
|
17247
|
+
Required:
|
|
17248
|
+
--at LINK_ID Existing hyperlink to update (e.g., link0)
|
|
17249
|
+
--with URL New target URL
|
|
17250
|
+
|
|
17251
|
+
Optional:
|
|
17252
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
17253
|
+
--dry-run Print what would change; do not write the file
|
|
17254
|
+
-h, --help Show this help
|
|
17255
|
+
|
|
17256
|
+
Replaces only the targeted hyperlink. If multiple hyperlinks shared the same
|
|
17257
|
+
underlying relationship, a new relationship is allocated so the others are
|
|
17258
|
+
unaffected.
|
|
17259
|
+
|
|
17260
|
+
Examples:
|
|
17261
|
+
docx hyperlinks replace doc.docx --at link0 --with https://example.com
|
|
17262
|
+
`;
|
|
17263
|
+
var init_replace = __esm(() => {
|
|
17264
|
+
init_core();
|
|
17265
|
+
init_parser();
|
|
17266
|
+
init_respond();
|
|
17267
|
+
});
|
|
17268
|
+
|
|
17269
|
+
// src/cli/hyperlinks/index.ts
|
|
17270
|
+
var exports_hyperlinks = {};
|
|
17271
|
+
__export(exports_hyperlinks, {
|
|
17272
|
+
run: () => run15
|
|
17273
|
+
});
|
|
17274
|
+
async function run15(args) {
|
|
17275
|
+
const verb = args[0];
|
|
17276
|
+
if (!verb || verb === "--help" || verb === "-h" || verb === "help") {
|
|
17277
|
+
await writeStdout(HELP15);
|
|
17278
|
+
return verb ? 0 : 2;
|
|
17279
|
+
}
|
|
17280
|
+
const loader = SUBCOMMANDS2[verb];
|
|
17281
|
+
if (!loader) {
|
|
17282
|
+
return fail("USAGE", `Unknown hyperlinks subcommand: ${verb}`, 'Run "docx hyperlinks --help".');
|
|
17283
|
+
}
|
|
17284
|
+
const module_ = await loader();
|
|
17285
|
+
return module_.run(args.slice(1));
|
|
17286
|
+
}
|
|
17287
|
+
var SUBCOMMANDS2, HELP15 = `docx hyperlinks \u2014 manage hyperlinks
|
|
17288
|
+
|
|
17289
|
+
Usage:
|
|
17290
|
+
docx hyperlinks <verb> FILE [options]
|
|
17291
|
+
|
|
17292
|
+
Verbs:
|
|
17293
|
+
add Wrap an existing span in a hyperlink
|
|
17294
|
+
delete Unwrap a hyperlink (keep the text)
|
|
17295
|
+
list Print hyperlink manifest as JSON
|
|
17296
|
+
replace Change a hyperlink's URL
|
|
17297
|
+
|
|
17298
|
+
Run "docx hyperlinks <verb> --help" for verb-specific help.
|
|
17299
|
+
`;
|
|
17300
|
+
var init_hyperlinks = __esm(() => {
|
|
17301
|
+
init_respond();
|
|
17302
|
+
SUBCOMMANDS2 = {
|
|
17303
|
+
add: () => Promise.resolve().then(() => (init_add2(), exports_add2)),
|
|
17304
|
+
delete: () => Promise.resolve().then(() => (init_delete3(), exports_delete3)),
|
|
17305
|
+
list: () => Promise.resolve().then(() => (init_list2(), exports_list2)),
|
|
17306
|
+
replace: () => Promise.resolve().then(() => (init_replace(), exports_replace))
|
|
17307
|
+
};
|
|
17308
|
+
});
|
|
17309
|
+
|
|
17310
|
+
// src/cli/images/extract.ts
|
|
17311
|
+
var exports_extract = {};
|
|
17312
|
+
__export(exports_extract, {
|
|
17313
|
+
run: () => run16
|
|
17314
|
+
});
|
|
17315
|
+
import { join } from "path";
|
|
17316
|
+
import { parseArgs as parseArgs14 } from "util";
|
|
17317
|
+
async function run16(args) {
|
|
17318
|
+
let parsed;
|
|
17319
|
+
try {
|
|
17320
|
+
parsed = parseArgs14({
|
|
17321
|
+
args,
|
|
17322
|
+
allowPositionals: true,
|
|
17323
|
+
options: {
|
|
17324
|
+
to: { type: "string" },
|
|
17325
|
+
id: { type: "string" },
|
|
17326
|
+
help: { type: "boolean", short: "h" }
|
|
17327
|
+
}
|
|
17328
|
+
});
|
|
17329
|
+
} catch (parseError) {
|
|
17330
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17331
|
+
return fail("USAGE", message, HELP16);
|
|
17332
|
+
}
|
|
17333
|
+
if (parsed.values.help) {
|
|
17334
|
+
await writeStdout(HELP16);
|
|
17335
|
+
return EXIT.OK;
|
|
17336
|
+
}
|
|
17337
|
+
const path = parsed.positionals[0];
|
|
17338
|
+
if (!path)
|
|
17339
|
+
return fail("USAGE", "Missing FILE argument", HELP16);
|
|
17340
|
+
const outputDir = parsed.values.to;
|
|
17341
|
+
if (!outputDir)
|
|
17342
|
+
return fail("USAGE", "Missing --to DIR", HELP16);
|
|
17343
|
+
const targetId = parsed.values.id;
|
|
17344
|
+
const view = await openOrFail(path);
|
|
17345
|
+
if (typeof view === "number")
|
|
17346
|
+
return view;
|
|
17347
|
+
await enrichImageHashes(view);
|
|
17348
|
+
const allImages = [];
|
|
17349
|
+
collectImages(view.doc.blocks, allImages);
|
|
17350
|
+
const targets = targetId ? allImages.filter((image) => image.id === targetId) : allImages;
|
|
17351
|
+
if (targetId && targets.length === 0) {
|
|
17352
|
+
return fail("IMAGE_NOT_FOUND", `Image not found: ${targetId}`);
|
|
17353
|
+
}
|
|
17354
|
+
const manifest = [];
|
|
17355
|
+
const seenHashes = new Set;
|
|
17356
|
+
for (const image of targets) {
|
|
17357
|
+
const reference = view.imageById.get(image.id);
|
|
17358
|
+
if (!reference)
|
|
17359
|
+
continue;
|
|
17360
|
+
const extension = extensionFor(image.contentType, reference.partName);
|
|
17361
|
+
const fileName = `${image.hash}.${extension}`;
|
|
17362
|
+
const outputPath = join(outputDir, fileName);
|
|
17363
|
+
if (!seenHashes.has(image.hash)) {
|
|
17364
|
+
const bytes = await view.pkg.readBytes(reference.partName);
|
|
17365
|
+
await Bun.write(outputPath, bytes);
|
|
17366
|
+
seenHashes.add(image.hash);
|
|
17367
|
+
}
|
|
17368
|
+
manifest.push({
|
|
17369
|
+
id: image.id,
|
|
17370
|
+
path: outputPath,
|
|
17371
|
+
bytes: (await Bun.file(outputPath).arrayBuffer()).byteLength
|
|
17372
|
+
});
|
|
17373
|
+
}
|
|
17374
|
+
await respond({ ok: true, operation: "images.extract", path, manifest });
|
|
17375
|
+
return EXIT.OK;
|
|
17376
|
+
}
|
|
17377
|
+
function collectImages(blocks, out) {
|
|
17378
|
+
for (const block of blocks) {
|
|
17379
|
+
if (block.type === "paragraph") {
|
|
17380
|
+
for (const run17 of block.runs) {
|
|
17381
|
+
if (run17.type === "image")
|
|
17382
|
+
out.push(run17);
|
|
17383
|
+
}
|
|
17384
|
+
continue;
|
|
17385
|
+
}
|
|
17386
|
+
if (block.type === "table") {
|
|
17387
|
+
for (const row of block.rows) {
|
|
17388
|
+
for (const cell of row.cells) {
|
|
17389
|
+
collectImages(cell.blocks, out);
|
|
17390
|
+
}
|
|
17391
|
+
}
|
|
17392
|
+
}
|
|
17393
|
+
}
|
|
17394
|
+
}
|
|
17395
|
+
function extensionFor(contentType, partName) {
|
|
17396
|
+
const fromType = EXTENSION_BY_CONTENT_TYPE[contentType.toLowerCase()];
|
|
17397
|
+
if (fromType)
|
|
17398
|
+
return fromType;
|
|
17399
|
+
const fromName = partName.split(".").pop()?.toLowerCase();
|
|
17400
|
+
return fromName ?? "bin";
|
|
17401
|
+
}
|
|
17402
|
+
var HELP16 = `docx images extract \u2014 dump image bytes to a directory
|
|
17403
|
+
|
|
17404
|
+
Usage:
|
|
17405
|
+
docx images extract FILE --to DIR [options]
|
|
17406
|
+
|
|
17407
|
+
Required:
|
|
17408
|
+
--to DIR Output directory (created if missing)
|
|
17409
|
+
|
|
17410
|
+
Optional:
|
|
17411
|
+
--id IMG_ID Extract a single image (default: extract all)
|
|
17412
|
+
-h, --help Show this help
|
|
17413
|
+
|
|
17414
|
+
Files are named <hash>.<ext> where hash is the sha256 of the image bytes
|
|
17415
|
+
and ext is derived from contentType. Returns a manifest mapping image ids
|
|
17416
|
+
to written paths.
|
|
17417
|
+
|
|
17418
|
+
Examples:
|
|
17419
|
+
docx images extract doc.docx --to ./media
|
|
17420
|
+
docx images extract doc.docx --to ./media --id img2
|
|
17421
|
+
`, EXTENSION_BY_CONTENT_TYPE;
|
|
17422
|
+
var init_extract = __esm(() => {
|
|
17423
|
+
init_core();
|
|
17424
|
+
init_respond();
|
|
17425
|
+
EXTENSION_BY_CONTENT_TYPE = {
|
|
17426
|
+
"image/png": "png",
|
|
17427
|
+
"image/jpeg": "jpg",
|
|
17428
|
+
"image/jpg": "jpg",
|
|
17429
|
+
"image/gif": "gif",
|
|
17430
|
+
"image/svg+xml": "svg",
|
|
17431
|
+
"image/webp": "webp",
|
|
17432
|
+
"image/tiff": "tif",
|
|
17433
|
+
"image/bmp": "bmp",
|
|
17434
|
+
"image/x-emf": "emf",
|
|
17435
|
+
"image/x-wmf": "wmf",
|
|
17436
|
+
"image/vnd.microsoft.icon": "ico"
|
|
17437
|
+
};
|
|
17438
|
+
});
|
|
17439
|
+
|
|
17440
|
+
// src/cli/images/list.ts
|
|
17441
|
+
var exports_list3 = {};
|
|
17442
|
+
__export(exports_list3, {
|
|
17443
|
+
run: () => run17
|
|
17444
|
+
});
|
|
17445
|
+
import { parseArgs as parseArgs15 } from "util";
|
|
17446
|
+
async function run17(args) {
|
|
17447
|
+
let parsed;
|
|
17448
|
+
try {
|
|
17449
|
+
parsed = parseArgs15({
|
|
17450
|
+
args,
|
|
17451
|
+
allowPositionals: true,
|
|
17452
|
+
options: {
|
|
17453
|
+
help: { type: "boolean", short: "h" }
|
|
17454
|
+
}
|
|
17455
|
+
});
|
|
17456
|
+
} catch (parseError) {
|
|
17457
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17458
|
+
return fail("USAGE", message, HELP17);
|
|
17459
|
+
}
|
|
17460
|
+
if (parsed.values.help) {
|
|
17461
|
+
await writeStdout(HELP17);
|
|
17462
|
+
return EXIT.OK;
|
|
17463
|
+
}
|
|
17464
|
+
const path = parsed.positionals[0];
|
|
17465
|
+
if (!path)
|
|
17466
|
+
return fail("USAGE", "Missing FILE argument", HELP17);
|
|
17467
|
+
const view = await openOrFail(path);
|
|
17468
|
+
if (typeof view === "number")
|
|
17469
|
+
return view;
|
|
17470
|
+
await enrichImageHashes(view);
|
|
17471
|
+
const images = [];
|
|
17472
|
+
collectImages2(view.doc.blocks, images);
|
|
17473
|
+
await respond(images);
|
|
17474
|
+
return EXIT.OK;
|
|
17475
|
+
}
|
|
17476
|
+
function collectImages2(blocks, out) {
|
|
17477
|
+
for (const block of blocks) {
|
|
17478
|
+
if (block.type === "paragraph") {
|
|
17479
|
+
for (const run18 of block.runs) {
|
|
17480
|
+
if (run18.type === "image")
|
|
17481
|
+
out.push(run18);
|
|
17482
|
+
}
|
|
17483
|
+
continue;
|
|
17484
|
+
}
|
|
17485
|
+
if (block.type === "table") {
|
|
17486
|
+
for (const row of block.rows) {
|
|
17487
|
+
for (const cell of row.cells) {
|
|
17488
|
+
collectImages2(cell.blocks, out);
|
|
17489
|
+
}
|
|
17490
|
+
}
|
|
17491
|
+
}
|
|
16814
17492
|
}
|
|
16815
17493
|
}
|
|
16816
|
-
var
|
|
17494
|
+
var HELP17 = `docx images list \u2014 print image manifest as JSON
|
|
16817
17495
|
|
|
16818
17496
|
Usage:
|
|
16819
17497
|
docx images list FILE [options]
|
|
@@ -16824,21 +17502,21 @@ Options:
|
|
|
16824
17502
|
Examples:
|
|
16825
17503
|
docx images list doc.docx | jq -c '.[] | {id, contentType, hash}'
|
|
16826
17504
|
`;
|
|
16827
|
-
var
|
|
17505
|
+
var init_list3 = __esm(() => {
|
|
16828
17506
|
init_core();
|
|
16829
17507
|
init_respond();
|
|
16830
17508
|
});
|
|
16831
17509
|
|
|
16832
17510
|
// src/cli/images/replace.ts
|
|
16833
|
-
var
|
|
16834
|
-
__export(
|
|
16835
|
-
run: () =>
|
|
17511
|
+
var exports_replace2 = {};
|
|
17512
|
+
__export(exports_replace2, {
|
|
17513
|
+
run: () => run18
|
|
16836
17514
|
});
|
|
16837
|
-
import { parseArgs as
|
|
16838
|
-
async function
|
|
17515
|
+
import { parseArgs as parseArgs16 } from "util";
|
|
17516
|
+
async function run18(args) {
|
|
16839
17517
|
let parsed;
|
|
16840
17518
|
try {
|
|
16841
|
-
parsed =
|
|
17519
|
+
parsed = parseArgs16({
|
|
16842
17520
|
args,
|
|
16843
17521
|
allowPositionals: true,
|
|
16844
17522
|
options: {
|
|
@@ -16851,21 +17529,21 @@ async function run13(args) {
|
|
|
16851
17529
|
});
|
|
16852
17530
|
} catch (parseError) {
|
|
16853
17531
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
16854
|
-
return fail("USAGE", message,
|
|
17532
|
+
return fail("USAGE", message, HELP18);
|
|
16855
17533
|
}
|
|
16856
17534
|
if (parsed.values.help) {
|
|
16857
|
-
await writeStdout(
|
|
17535
|
+
await writeStdout(HELP18);
|
|
16858
17536
|
return EXIT.OK;
|
|
16859
17537
|
}
|
|
16860
17538
|
const path = parsed.positionals[0];
|
|
16861
17539
|
if (!path)
|
|
16862
|
-
return fail("USAGE", "Missing FILE argument",
|
|
17540
|
+
return fail("USAGE", "Missing FILE argument", HELP18);
|
|
16863
17541
|
const targetId = parsed.values.at;
|
|
16864
17542
|
if (!targetId)
|
|
16865
|
-
return fail("USAGE", "Missing --at IMG_ID",
|
|
17543
|
+
return fail("USAGE", "Missing --at IMG_ID", HELP18);
|
|
16866
17544
|
const sourcePath = parsed.values.with;
|
|
16867
17545
|
if (!sourcePath)
|
|
16868
|
-
return fail("USAGE", "Missing --with PATH",
|
|
17546
|
+
return fail("USAGE", "Missing --with PATH", HELP18);
|
|
16869
17547
|
const sourceFile = Bun.file(sourcePath);
|
|
16870
17548
|
if (!await sourceFile.exists()) {
|
|
16871
17549
|
return fail("FILE_NOT_FOUND", `Replacement file not found: ${sourcePath}`);
|
|
@@ -16904,7 +17582,7 @@ async function run13(args) {
|
|
|
16904
17582
|
} else {
|
|
16905
17583
|
view.pkg.writeBytes(newPartName, bytes);
|
|
16906
17584
|
view.pkg.deletePart(originalPartName);
|
|
16907
|
-
|
|
17585
|
+
updateRelationshipTarget2(view.relationshipsTree, reference.relationshipId, relativeTargetFor(newPartName));
|
|
16908
17586
|
ensureContentTypeDefault(view.contentTypesTree, newExtension, newMimeType);
|
|
16909
17587
|
reference.partName = newPartName;
|
|
16910
17588
|
reference.contentType = newMimeType;
|
|
@@ -16932,7 +17610,7 @@ function renameExtension(partName, newExtension) {
|
|
|
16932
17610
|
function relativeTargetFor(partName) {
|
|
16933
17611
|
return partName.startsWith("word/") ? partName.slice("word/".length) : partName;
|
|
16934
17612
|
}
|
|
16935
|
-
function
|
|
17613
|
+
function updateRelationshipTarget2(relationshipsTree, relationshipId, newTarget) {
|
|
16936
17614
|
const relationships = XmlNode2.findRoot(relationshipsTree, "Relationships");
|
|
16937
17615
|
if (!relationships)
|
|
16938
17616
|
return;
|
|
@@ -16957,7 +17635,7 @@ function ensureContentTypeDefault(contentTypesTree, extension, mimeType) {
|
|
|
16957
17635
|
}
|
|
16958
17636
|
types.children.push(new XmlNode2("Default", { Extension: extension, ContentType: mimeType }));
|
|
16959
17637
|
}
|
|
16960
|
-
var
|
|
17638
|
+
var HELP18 = `docx images replace \u2014 swap an image's bytes
|
|
16961
17639
|
|
|
16962
17640
|
Usage:
|
|
16963
17641
|
docx images replace FILE --at IMG_ID --with PATH [options]
|
|
@@ -16979,7 +17657,7 @@ Examples:
|
|
|
16979
17657
|
docx images replace doc.docx --at img2 --with ./new-photo.png
|
|
16980
17658
|
docx images replace doc.docx --at img0 --with ./diagram.svg
|
|
16981
17659
|
`, EXTENSION_BY_MIME;
|
|
16982
|
-
var
|
|
17660
|
+
var init_replace2 = __esm(() => {
|
|
16983
17661
|
init_core();
|
|
16984
17662
|
init_parser();
|
|
16985
17663
|
init_respond();
|
|
@@ -17000,22 +17678,22 @@ var init_replace = __esm(() => {
|
|
|
17000
17678
|
// src/cli/images/index.ts
|
|
17001
17679
|
var exports_images = {};
|
|
17002
17680
|
__export(exports_images, {
|
|
17003
|
-
run: () =>
|
|
17681
|
+
run: () => run19
|
|
17004
17682
|
});
|
|
17005
|
-
async function
|
|
17683
|
+
async function run19(args) {
|
|
17006
17684
|
const verb = args[0];
|
|
17007
17685
|
if (!verb || verb === "--help" || verb === "-h" || verb === "help") {
|
|
17008
|
-
await writeStdout(
|
|
17686
|
+
await writeStdout(HELP19);
|
|
17009
17687
|
return verb ? 0 : 2;
|
|
17010
17688
|
}
|
|
17011
|
-
const loader =
|
|
17689
|
+
const loader = SUBCOMMANDS3[verb];
|
|
17012
17690
|
if (!loader) {
|
|
17013
17691
|
return fail("USAGE", `Unknown images subcommand: ${verb}`, 'Run "docx images --help".');
|
|
17014
17692
|
}
|
|
17015
17693
|
const module_ = await loader();
|
|
17016
17694
|
return module_.run(args.slice(1));
|
|
17017
17695
|
}
|
|
17018
|
-
var
|
|
17696
|
+
var SUBCOMMANDS3, HELP19 = `docx images \u2014 manage embedded images
|
|
17019
17697
|
|
|
17020
17698
|
Usage:
|
|
17021
17699
|
docx images <verb> FILE [options]
|
|
@@ -17029,10 +17707,10 @@ Run "docx images <verb> --help" for verb-specific help.
|
|
|
17029
17707
|
`;
|
|
17030
17708
|
var init_images = __esm(() => {
|
|
17031
17709
|
init_respond();
|
|
17032
|
-
|
|
17710
|
+
SUBCOMMANDS3 = {
|
|
17033
17711
|
extract: () => Promise.resolve().then(() => (init_extract(), exports_extract)),
|
|
17034
|
-
list: () => Promise.resolve().then(() => (
|
|
17035
|
-
replace: () => Promise.resolve().then(() => (
|
|
17712
|
+
list: () => Promise.resolve().then(() => (init_list3(), exports_list3)),
|
|
17713
|
+
replace: () => Promise.resolve().then(() => (init_replace2(), exports_replace2))
|
|
17036
17714
|
};
|
|
17037
17715
|
});
|
|
17038
17716
|
|
|
@@ -17097,6 +17775,14 @@ export type TextRun = {
|
|
|
17097
17775
|
sizeHalfPoints?: number;
|
|
17098
17776
|
comments?: string[];
|
|
17099
17777
|
trackedChange?: TrackedChange;
|
|
17778
|
+
hyperlink?: Hyperlink;
|
|
17779
|
+
};
|
|
17780
|
+
|
|
17781
|
+
export type Hyperlink = {
|
|
17782
|
+
id: string;
|
|
17783
|
+
url?: string;
|
|
17784
|
+
anchor?: string;
|
|
17785
|
+
tooltip?: string;
|
|
17100
17786
|
};
|
|
17101
17787
|
|
|
17102
17788
|
export type ImageRun = {
|
|
@@ -17148,13 +17834,13 @@ var init_types = () => {};
|
|
|
17148
17834
|
// src/cli/info/schema.ts
|
|
17149
17835
|
var exports_schema = {};
|
|
17150
17836
|
__export(exports_schema, {
|
|
17151
|
-
run: () =>
|
|
17837
|
+
run: () => run20
|
|
17152
17838
|
});
|
|
17153
|
-
import { parseArgs as
|
|
17154
|
-
async function
|
|
17839
|
+
import { parseArgs as parseArgs17 } from "util";
|
|
17840
|
+
async function run20(args) {
|
|
17155
17841
|
let parsed;
|
|
17156
17842
|
try {
|
|
17157
|
-
parsed =
|
|
17843
|
+
parsed = parseArgs17({
|
|
17158
17844
|
args,
|
|
17159
17845
|
allowPositionals: true,
|
|
17160
17846
|
options: {
|
|
@@ -17165,10 +17851,10 @@ async function run15(args) {
|
|
|
17165
17851
|
});
|
|
17166
17852
|
} catch (parseError) {
|
|
17167
17853
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17168
|
-
return fail("USAGE", message,
|
|
17854
|
+
return fail("USAGE", message, HELP20);
|
|
17169
17855
|
}
|
|
17170
17856
|
if (parsed.values.help) {
|
|
17171
|
-
await writeStdout(
|
|
17857
|
+
await writeStdout(HELP20);
|
|
17172
17858
|
return EXIT.OK;
|
|
17173
17859
|
}
|
|
17174
17860
|
if (parsed.values.ts) {
|
|
@@ -17178,7 +17864,7 @@ async function run15(args) {
|
|
|
17178
17864
|
await respond(JSON_SCHEMA);
|
|
17179
17865
|
return EXIT.OK;
|
|
17180
17866
|
}
|
|
17181
|
-
var
|
|
17867
|
+
var HELP20 = `docx schema \u2014 print the AST type definitions
|
|
17182
17868
|
|
|
17183
17869
|
Usage:
|
|
17184
17870
|
docx schema [options]
|
|
@@ -17275,7 +17961,18 @@ var init_schema = __esm(() => {
|
|
|
17275
17961
|
date: { type: "string" },
|
|
17276
17962
|
revisionId: { type: "string" }
|
|
17277
17963
|
}
|
|
17278
|
-
}
|
|
17964
|
+
},
|
|
17965
|
+
hyperlink: { $ref: "#/$defs/Hyperlink" }
|
|
17966
|
+
}
|
|
17967
|
+
},
|
|
17968
|
+
Hyperlink: {
|
|
17969
|
+
type: "object",
|
|
17970
|
+
required: ["id"],
|
|
17971
|
+
properties: {
|
|
17972
|
+
id: { type: "string" },
|
|
17973
|
+
url: { type: "string" },
|
|
17974
|
+
anchor: { type: "string" },
|
|
17975
|
+
tooltip: { type: "string" }
|
|
17279
17976
|
}
|
|
17280
17977
|
},
|
|
17281
17978
|
ImageRun: {
|
|
@@ -17370,13 +18067,13 @@ var init_schema = __esm(() => {
|
|
|
17370
18067
|
// src/cli/info/locators.ts
|
|
17371
18068
|
var exports_locators = {};
|
|
17372
18069
|
__export(exports_locators, {
|
|
17373
|
-
run: () =>
|
|
18070
|
+
run: () => run21
|
|
17374
18071
|
});
|
|
17375
|
-
import { parseArgs as
|
|
17376
|
-
async function
|
|
18072
|
+
import { parseArgs as parseArgs18 } from "util";
|
|
18073
|
+
async function run21(args) {
|
|
17377
18074
|
let parsed;
|
|
17378
18075
|
try {
|
|
17379
|
-
parsed =
|
|
18076
|
+
parsed = parseArgs18({
|
|
17380
18077
|
args,
|
|
17381
18078
|
allowPositionals: true,
|
|
17382
18079
|
options: {
|
|
@@ -17386,10 +18083,10 @@ async function run16(args) {
|
|
|
17386
18083
|
});
|
|
17387
18084
|
} catch (parseError) {
|
|
17388
18085
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17389
|
-
return fail("USAGE", message,
|
|
18086
|
+
return fail("USAGE", message, HELP21);
|
|
17390
18087
|
}
|
|
17391
18088
|
if (parsed.values.help) {
|
|
17392
|
-
await writeStdout(
|
|
18089
|
+
await writeStdout(HELP21);
|
|
17393
18090
|
return EXIT.OK;
|
|
17394
18091
|
}
|
|
17395
18092
|
if (parsed.values.json) {
|
|
@@ -17400,7 +18097,7 @@ async function run16(args) {
|
|
|
17400
18097
|
await writeStdout(REFERENCE);
|
|
17401
18098
|
return EXIT.OK;
|
|
17402
18099
|
}
|
|
17403
|
-
var
|
|
18100
|
+
var HELP21 = `docx locators \u2014 print the locator grammar reference
|
|
17404
18101
|
|
|
17405
18102
|
Usage:
|
|
17406
18103
|
docx locators [options]
|
|
@@ -17426,6 +18123,7 @@ Range locators (across blocks):
|
|
|
17426
18123
|
Entity locators:
|
|
17427
18124
|
cN Comment id (e.g., c0)
|
|
17428
18125
|
imgN Image id (e.g., img2)
|
|
18126
|
+
linkN Hyperlink id (e.g., link0)
|
|
17429
18127
|
tN:rRcC Cell at row R, column C of table tN
|
|
17430
18128
|
|
|
17431
18129
|
Examples:
|
|
@@ -17434,6 +18132,7 @@ Examples:
|
|
|
17434
18132
|
p3:5-p5:10 -> from char 5 of p3 to char 10 of p5
|
|
17435
18133
|
c1 -> comment c1
|
|
17436
18134
|
img0 -> image img0
|
|
18135
|
+
link0 -> hyperlink link0
|
|
17437
18136
|
t0:r1c2 -> cell at row 1, col 2 of table t0
|
|
17438
18137
|
t0:r1c2:p0 -> first paragraph of that cell
|
|
17439
18138
|
t0:r1c2:p0:5-10 -> chars 5..10 of that paragraph
|
|
@@ -17466,6 +18165,7 @@ var init_locators2 = __esm(() => {
|
|
|
17466
18165
|
entityLocators: {
|
|
17467
18166
|
comment: { syntax: "cN", example: "c1" },
|
|
17468
18167
|
image: { syntax: "imgN", example: "img0" },
|
|
18168
|
+
hyperlink: { syntax: "linkN", example: "link0" },
|
|
17469
18169
|
cell: { syntax: "tN:rRcC", example: "t0:r1c2" },
|
|
17470
18170
|
nestedCell: { syntax: "tN:rRcC:pK", example: "t0:r1c2:p0" }
|
|
17471
18171
|
},
|
|
@@ -17480,22 +18180,22 @@ var init_locators2 = __esm(() => {
|
|
|
17480
18180
|
// src/cli/info/index.ts
|
|
17481
18181
|
var exports_info = {};
|
|
17482
18182
|
__export(exports_info, {
|
|
17483
|
-
run: () =>
|
|
18183
|
+
run: () => run22
|
|
17484
18184
|
});
|
|
17485
|
-
async function
|
|
18185
|
+
async function run22(args) {
|
|
17486
18186
|
const topic = args[0];
|
|
17487
18187
|
if (!topic || topic === "--help" || topic === "-h" || topic === "help") {
|
|
17488
|
-
await writeStdout(
|
|
18188
|
+
await writeStdout(HELP22);
|
|
17489
18189
|
return topic ? 0 : 2;
|
|
17490
18190
|
}
|
|
17491
|
-
const loader =
|
|
18191
|
+
const loader = SUBCOMMANDS4[topic];
|
|
17492
18192
|
if (!loader) {
|
|
17493
18193
|
return fail("USAGE", `Unknown info topic: ${topic}`, 'Run "docx info --help".');
|
|
17494
18194
|
}
|
|
17495
18195
|
const module_ = await loader();
|
|
17496
18196
|
return module_.run(args.slice(1));
|
|
17497
18197
|
}
|
|
17498
|
-
var
|
|
18198
|
+
var SUBCOMMANDS4, HELP22 = `docx info \u2014 print reference material about the CLI
|
|
17499
18199
|
|
|
17500
18200
|
Usage:
|
|
17501
18201
|
docx info <topic> [options]
|
|
@@ -17508,7 +18208,7 @@ Run "docx info <topic> --help" for topic-specific help.
|
|
|
17508
18208
|
`;
|
|
17509
18209
|
var init_info = __esm(() => {
|
|
17510
18210
|
init_respond();
|
|
17511
|
-
|
|
18211
|
+
SUBCOMMANDS4 = {
|
|
17512
18212
|
schema: () => Promise.resolve().then(() => (init_schema(), exports_schema)),
|
|
17513
18213
|
locators: () => Promise.resolve().then(() => (init_locators2(), exports_locators))
|
|
17514
18214
|
};
|
|
@@ -17517,13 +18217,13 @@ var init_info = __esm(() => {
|
|
|
17517
18217
|
// src/cli/insert/index.tsx
|
|
17518
18218
|
var exports_insert = {};
|
|
17519
18219
|
__export(exports_insert, {
|
|
17520
|
-
run: () =>
|
|
18220
|
+
run: () => run23
|
|
17521
18221
|
});
|
|
17522
|
-
import { parseArgs as
|
|
17523
|
-
async function
|
|
18222
|
+
import { parseArgs as parseArgs19 } from "util";
|
|
18223
|
+
async function run23(args) {
|
|
17524
18224
|
let parsed;
|
|
17525
18225
|
try {
|
|
17526
|
-
parsed =
|
|
18226
|
+
parsed = parseArgs19({
|
|
17527
18227
|
args,
|
|
17528
18228
|
allowPositionals: true,
|
|
17529
18229
|
options: {
|
|
@@ -17536,6 +18236,7 @@ async function run18(args) {
|
|
|
17536
18236
|
color: { type: "string" },
|
|
17537
18237
|
bold: { type: "boolean" },
|
|
17538
18238
|
italic: { type: "boolean" },
|
|
18239
|
+
url: { type: "string" },
|
|
17539
18240
|
output: { type: "string", short: "o" },
|
|
17540
18241
|
"dry-run": { type: "boolean" },
|
|
17541
18242
|
help: { type: "boolean", short: "h" }
|
|
@@ -17543,30 +18244,34 @@ async function run18(args) {
|
|
|
17543
18244
|
});
|
|
17544
18245
|
} catch (parseError) {
|
|
17545
18246
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
17546
|
-
return fail("USAGE", message,
|
|
18247
|
+
return fail("USAGE", message, HELP23);
|
|
17547
18248
|
}
|
|
17548
18249
|
if (parsed.values.help) {
|
|
17549
|
-
await writeStdout(
|
|
18250
|
+
await writeStdout(HELP23);
|
|
17550
18251
|
return EXIT.OK;
|
|
17551
18252
|
}
|
|
17552
18253
|
const path = parsed.positionals[0];
|
|
17553
18254
|
if (!path)
|
|
17554
|
-
return fail("USAGE", "Missing FILE argument",
|
|
18255
|
+
return fail("USAGE", "Missing FILE argument", HELP23);
|
|
17555
18256
|
const after = parsed.values.after;
|
|
17556
18257
|
const before = parsed.values.before;
|
|
17557
18258
|
if (!after && !before) {
|
|
17558
|
-
return fail("USAGE", "Missing locator: pass --after or --before",
|
|
18259
|
+
return fail("USAGE", "Missing locator: pass --after or --before", HELP23);
|
|
17559
18260
|
}
|
|
17560
18261
|
if (after && before) {
|
|
17561
|
-
return fail("USAGE", "Pass either --after or --before, not both",
|
|
18262
|
+
return fail("USAGE", "Pass either --after or --before, not both", HELP23);
|
|
17562
18263
|
}
|
|
17563
18264
|
const text = parsed.values.text;
|
|
17564
18265
|
const runsJson = parsed.values.runs;
|
|
18266
|
+
const url = parsed.values.url;
|
|
17565
18267
|
if (!text && !runsJson) {
|
|
17566
|
-
return fail("USAGE", "Missing content: pass --text or --runs",
|
|
18268
|
+
return fail("USAGE", "Missing content: pass --text or --runs", HELP23);
|
|
17567
18269
|
}
|
|
17568
18270
|
if (text && runsJson) {
|
|
17569
|
-
return fail("USAGE", "Pass either --text or --runs, not both",
|
|
18271
|
+
return fail("USAGE", "Pass either --text or --runs, not both", HELP23);
|
|
18272
|
+
}
|
|
18273
|
+
if (url && !text) {
|
|
18274
|
+
return fail("USAGE", "--url requires --text", HELP23);
|
|
17570
18275
|
}
|
|
17571
18276
|
const paragraphOptions = {};
|
|
17572
18277
|
const styleValue = parsed.values.style;
|
|
@@ -17596,6 +18301,8 @@ async function run18(args) {
|
|
|
17596
18301
|
...parsed.values.bold ? { bold: true } : {},
|
|
17597
18302
|
...parsed.values.italic ? { italic: true } : {}
|
|
17598
18303
|
}, undefined, false, undefined, this);
|
|
18304
|
+
if (url)
|
|
18305
|
+
wrapFirstRunInHyperlink(view, paragraphNode, url);
|
|
17599
18306
|
} else {
|
|
17600
18307
|
let runsValue;
|
|
17601
18308
|
try {
|
|
@@ -17644,6 +18351,29 @@ async function run18(args) {
|
|
|
17644
18351
|
});
|
|
17645
18352
|
return EXIT.OK;
|
|
17646
18353
|
}
|
|
18354
|
+
function wrapFirstRunInHyperlink(view, paragraph, url) {
|
|
18355
|
+
const relationships = XmlNode2.findRoot(view.relationshipsTree, "Relationships");
|
|
18356
|
+
if (!relationships) {
|
|
18357
|
+
throw new Error("Missing <Relationships> root in document rels");
|
|
18358
|
+
}
|
|
18359
|
+
const relationshipId = addHyperlinkRelationship(relationships, url);
|
|
18360
|
+
view.hyperlinksByRelationshipId.set(relationshipId, { url });
|
|
18361
|
+
const newChildren = [];
|
|
18362
|
+
let wrapped = false;
|
|
18363
|
+
for (const child of paragraph.children) {
|
|
18364
|
+
if (!wrapped && child.tag === "w:r") {
|
|
18365
|
+
const wrapper = /* @__PURE__ */ jsxDEV(w.hyperlink, {
|
|
18366
|
+
"r:id": relationshipId,
|
|
18367
|
+
children: child
|
|
18368
|
+
}, undefined, false, undefined, this);
|
|
18369
|
+
newChildren.push(wrapper);
|
|
18370
|
+
wrapped = true;
|
|
18371
|
+
continue;
|
|
18372
|
+
}
|
|
18373
|
+
newChildren.push(child);
|
|
18374
|
+
}
|
|
18375
|
+
paragraph.children = newChildren;
|
|
18376
|
+
}
|
|
17647
18377
|
function applyTrackedInsertion(paragraph, view) {
|
|
17648
18378
|
const allocator = createRevisionAllocator(view);
|
|
17649
18379
|
const baseMeta = { author: resolveAuthor(), date: resolveDate() };
|
|
@@ -17670,58 +18400,197 @@ function applyTrackedInsertion(paragraph, view) {
|
|
|
17670
18400
|
flush();
|
|
17671
18401
|
newChildren.push(child);
|
|
17672
18402
|
}
|
|
17673
|
-
flush();
|
|
17674
|
-
paragraph.children = newChildren;
|
|
17675
|
-
markParagraphMarkAs(paragraph, "ins", mintMeta());
|
|
18403
|
+
flush();
|
|
18404
|
+
paragraph.children = newChildren;
|
|
18405
|
+
markParagraphMarkAs(paragraph, "ins", mintMeta());
|
|
18406
|
+
}
|
|
18407
|
+
var HELP23 = `docx insert \u2014 insert a paragraph at a locator
|
|
18408
|
+
|
|
18409
|
+
Usage:
|
|
18410
|
+
docx insert FILE [options]
|
|
18411
|
+
|
|
18412
|
+
Locator (one required):
|
|
18413
|
+
--after LOCATOR Insert after the block at LOCATOR (e.g., p3)
|
|
18414
|
+
--before LOCATOR Insert before the block at LOCATOR
|
|
18415
|
+
|
|
18416
|
+
Content (one required):
|
|
18417
|
+
--text TEXT Insert a paragraph with this text
|
|
18418
|
+
--runs JSON Insert a paragraph with custom runs (Run[] JSON)
|
|
18419
|
+
|
|
18420
|
+
Paragraph options:
|
|
18421
|
+
--style NAME Apply paragraph style (e.g., Heading1)
|
|
18422
|
+
--alignment ALIGN left | center | right | justify
|
|
18423
|
+
|
|
18424
|
+
Run options (only with --text):
|
|
18425
|
+
--color HEX Run color, hex (e.g., 800080 for purple)
|
|
18426
|
+
--bold Bold
|
|
18427
|
+
--italic Italic
|
|
18428
|
+
--url URL Wrap the inserted text in a hyperlink to URL
|
|
18429
|
+
|
|
18430
|
+
-o, --output PATH Write to PATH instead of overwriting FILE
|
|
18431
|
+
--dry-run Print what would be inserted; do not write the file
|
|
18432
|
+
-h, --help Show this help
|
|
18433
|
+
|
|
18434
|
+
Examples:
|
|
18435
|
+
docx insert doc.docx --after p3 --text "Section header" --style Heading2
|
|
18436
|
+
docx insert doc.docx --before p0 --text "ALERT" --color CC0000 --bold
|
|
18437
|
+
docx insert doc.docx --after p2 --runs '[{"type":"text","text":"X","bold":true}]'
|
|
18438
|
+
docx insert doc.docx --after p3 --text "click here" --url https://example.com
|
|
18439
|
+
`;
|
|
18440
|
+
var init_insert = __esm(() => {
|
|
18441
|
+
init_core();
|
|
18442
|
+
init_jsx();
|
|
18443
|
+
init_parser();
|
|
18444
|
+
init_respond();
|
|
18445
|
+
init_emit2();
|
|
18446
|
+
init_jsx_dev_runtime();
|
|
18447
|
+
});
|
|
18448
|
+
|
|
18449
|
+
// src/cli/outline/build.ts
|
|
18450
|
+
function buildOutline(doc, options = {}) {
|
|
18451
|
+
const stylePrefix = options.stylePrefix ?? "Heading";
|
|
18452
|
+
const root = {
|
|
18453
|
+
id: "",
|
|
18454
|
+
locator: "",
|
|
18455
|
+
level: 0,
|
|
18456
|
+
style: "",
|
|
18457
|
+
text: "",
|
|
18458
|
+
children: []
|
|
18459
|
+
};
|
|
18460
|
+
const stack = [root];
|
|
18461
|
+
for (const paragraph of headingParagraphs(doc.blocks, stylePrefix)) {
|
|
18462
|
+
const level = headingLevel(paragraph.style, stylePrefix);
|
|
18463
|
+
if (level === null)
|
|
18464
|
+
continue;
|
|
18465
|
+
while ((stack[stack.length - 1]?.level ?? 0) >= level)
|
|
18466
|
+
stack.pop();
|
|
18467
|
+
const parent = stack[stack.length - 1] ?? root;
|
|
18468
|
+
const entry = {
|
|
18469
|
+
id: paragraph.id,
|
|
18470
|
+
locator: paragraph.id,
|
|
18471
|
+
level,
|
|
18472
|
+
style: paragraph.style ?? "",
|
|
18473
|
+
text: paragraphText(paragraph),
|
|
18474
|
+
children: []
|
|
18475
|
+
};
|
|
18476
|
+
parent.children.push(entry);
|
|
18477
|
+
stack.push(entry);
|
|
18478
|
+
}
|
|
18479
|
+
return root.children;
|
|
18480
|
+
}
|
|
18481
|
+
function headingLevel(style, stylePrefix) {
|
|
18482
|
+
if (!style)
|
|
18483
|
+
return null;
|
|
18484
|
+
if (!style.startsWith(stylePrefix))
|
|
18485
|
+
return null;
|
|
18486
|
+
const remainder = style.slice(stylePrefix.length).trim();
|
|
18487
|
+
if (remainder === "")
|
|
18488
|
+
return 1;
|
|
18489
|
+
const parsed = Number(remainder);
|
|
18490
|
+
if (!Number.isInteger(parsed) || parsed < 1)
|
|
18491
|
+
return null;
|
|
18492
|
+
return parsed;
|
|
18493
|
+
}
|
|
18494
|
+
function* headingParagraphs(blocks, stylePrefix) {
|
|
18495
|
+
for (const block of blocks) {
|
|
18496
|
+
if (block.type !== "paragraph")
|
|
18497
|
+
continue;
|
|
18498
|
+
if (headingLevel(block.style, stylePrefix) === null)
|
|
18499
|
+
continue;
|
|
18500
|
+
yield block;
|
|
18501
|
+
}
|
|
18502
|
+
}
|
|
18503
|
+
function paragraphText(paragraph) {
|
|
18504
|
+
let out = "";
|
|
18505
|
+
for (const run24 of paragraph.runs) {
|
|
18506
|
+
if (run24.type === "text")
|
|
18507
|
+
out += run24.text;
|
|
18508
|
+
}
|
|
18509
|
+
return out;
|
|
18510
|
+
}
|
|
18511
|
+
|
|
18512
|
+
// src/cli/outline/index.ts
|
|
18513
|
+
var exports_outline = {};
|
|
18514
|
+
__export(exports_outline, {
|
|
18515
|
+
run: () => run24
|
|
18516
|
+
});
|
|
18517
|
+
import { parseArgs as parseArgs20 } from "util";
|
|
18518
|
+
async function run24(args) {
|
|
18519
|
+
let parsed;
|
|
18520
|
+
try {
|
|
18521
|
+
parsed = parseArgs20({
|
|
18522
|
+
args,
|
|
18523
|
+
allowPositionals: true,
|
|
18524
|
+
options: {
|
|
18525
|
+
"style-prefix": { type: "string" },
|
|
18526
|
+
help: { type: "boolean", short: "h" }
|
|
18527
|
+
}
|
|
18528
|
+
});
|
|
18529
|
+
} catch (parseError) {
|
|
18530
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
18531
|
+
return fail("USAGE", message, HELP24);
|
|
18532
|
+
}
|
|
18533
|
+
if (parsed.values.help) {
|
|
18534
|
+
await writeStdout(HELP24);
|
|
18535
|
+
return EXIT.OK;
|
|
18536
|
+
}
|
|
18537
|
+
const path = parsed.positionals[0];
|
|
18538
|
+
if (!path)
|
|
18539
|
+
return fail("USAGE", "Missing FILE argument", HELP24);
|
|
18540
|
+
const stylePrefix = parsed.values["style-prefix"] ?? "Heading";
|
|
18541
|
+
if (stylePrefix.length === 0) {
|
|
18542
|
+
return fail("USAGE", "--style-prefix cannot be empty");
|
|
18543
|
+
}
|
|
18544
|
+
const view = await openOrFail(path);
|
|
18545
|
+
if (typeof view === "number")
|
|
18546
|
+
return view;
|
|
18547
|
+
const outline = buildOutline(view.doc, { stylePrefix });
|
|
18548
|
+
await respond({
|
|
18549
|
+
ok: true,
|
|
18550
|
+
operation: "outline",
|
|
18551
|
+
path,
|
|
18552
|
+
stylePrefix,
|
|
18553
|
+
outline
|
|
18554
|
+
});
|
|
18555
|
+
return EXIT.OK;
|
|
17676
18556
|
}
|
|
17677
|
-
var
|
|
18557
|
+
var HELP24 = `docx outline \u2014 list headings as a hierarchical tree
|
|
17678
18558
|
|
|
17679
18559
|
Usage:
|
|
17680
|
-
docx
|
|
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)
|
|
18560
|
+
docx outline FILE [options]
|
|
17689
18561
|
|
|
17690
|
-
|
|
17691
|
-
--style
|
|
17692
|
-
--
|
|
18562
|
+
Options:
|
|
18563
|
+
--style-prefix S paragraph-style prefix that marks a heading (default: "Heading")
|
|
18564
|
+
-h, --help show this help
|
|
17693
18565
|
|
|
17694
|
-
|
|
17695
|
-
|
|
17696
|
-
|
|
17697
|
-
|
|
18566
|
+
Walks top-level paragraphs whose style starts with the prefix and parses the
|
|
18567
|
+
trailing number as the heading level (e.g. "Heading1" \u2192 1, "Heading 2" \u2192 2).
|
|
18568
|
+
Paragraphs nested inside table cells are skipped \u2014 outlines reflect the
|
|
18569
|
+
document's structural skeleton, not embedded labels. Lower levels nest under
|
|
18570
|
+
higher ones; missing intermediate levels nest directly (H1 \u2192 H3 is fine).
|
|
17698
18571
|
|
|
17699
|
-
|
|
17700
|
-
|
|
17701
|
-
-h, --help Show this help
|
|
18572
|
+
Output is JSON: an array of entries, each shaped like
|
|
18573
|
+
{ id, locator, level, style, text, children }.
|
|
17702
18574
|
|
|
17703
18575
|
Examples:
|
|
17704
|
-
docx
|
|
17705
|
-
docx
|
|
17706
|
-
docx
|
|
18576
|
+
docx outline doc.docx
|
|
18577
|
+
docx outline doc.docx --style-prefix "Section"
|
|
18578
|
+
docx outline doc.docx | jq '.outline[].text'
|
|
17707
18579
|
`;
|
|
17708
|
-
var
|
|
17709
|
-
init_core();
|
|
18580
|
+
var init_outline = __esm(() => {
|
|
17710
18581
|
init_respond();
|
|
17711
|
-
init_emit2();
|
|
17712
|
-
init_jsx_dev_runtime();
|
|
17713
18582
|
});
|
|
17714
18583
|
|
|
17715
18584
|
// src/cli/read/index.ts
|
|
17716
18585
|
var exports_read = {};
|
|
17717
18586
|
__export(exports_read, {
|
|
17718
|
-
run: () =>
|
|
18587
|
+
run: () => run25
|
|
17719
18588
|
});
|
|
17720
|
-
import { parseArgs as
|
|
17721
|
-
async function
|
|
18589
|
+
import { parseArgs as parseArgs21 } from "util";
|
|
18590
|
+
async function run25(args) {
|
|
17722
18591
|
let parsed;
|
|
17723
18592
|
try {
|
|
17724
|
-
parsed =
|
|
18593
|
+
parsed = parseArgs21({
|
|
17725
18594
|
args,
|
|
17726
18595
|
allowPositionals: true,
|
|
17727
18596
|
options: {
|
|
@@ -17730,15 +18599,15 @@ async function run19(args) {
|
|
|
17730
18599
|
}
|
|
17731
18600
|
});
|
|
17732
18601
|
} catch (e) {
|
|
17733
|
-
return fail("USAGE", e instanceof Error ? e.message : String(e),
|
|
18602
|
+
return fail("USAGE", e instanceof Error ? e.message : String(e), HELP25);
|
|
17734
18603
|
}
|
|
17735
18604
|
if (parsed.values.help) {
|
|
17736
|
-
await writeStdout(
|
|
18605
|
+
await writeStdout(HELP25);
|
|
17737
18606
|
return EXIT.OK;
|
|
17738
18607
|
}
|
|
17739
18608
|
const path = parsed.positionals[0];
|
|
17740
18609
|
if (!path)
|
|
17741
|
-
return fail("USAGE", "Missing FILE argument",
|
|
18610
|
+
return fail("USAGE", "Missing FILE argument", HELP25);
|
|
17742
18611
|
const view = await openOrFail(path);
|
|
17743
18612
|
if (typeof view === "number")
|
|
17744
18613
|
return view;
|
|
@@ -17746,7 +18615,7 @@ async function run19(args) {
|
|
|
17746
18615
|
await respond(view.doc);
|
|
17747
18616
|
return EXIT.OK;
|
|
17748
18617
|
}
|
|
17749
|
-
var
|
|
18618
|
+
var HELP25 = `docx read \u2014 print AST as JSON
|
|
17750
18619
|
|
|
17751
18620
|
Usage:
|
|
17752
18621
|
docx read FILE [options]
|
|
@@ -17804,7 +18673,7 @@ function collectRunSlots(paragraph) {
|
|
|
17804
18673
|
offset += length;
|
|
17805
18674
|
continue;
|
|
17806
18675
|
}
|
|
17807
|
-
if (child.tag === "w:ins" || child.tag === "w:del") {
|
|
18676
|
+
if (child.tag === "w:ins" || child.tag === "w:del" || child.tag === "w:hyperlink") {
|
|
17808
18677
|
for (const inner of child.children) {
|
|
17809
18678
|
if (inner.tag !== "w:r")
|
|
17810
18679
|
continue;
|
|
@@ -17829,14 +18698,14 @@ function rebuildContainer(container, baseOffset, span, replacement, runPropertie
|
|
|
17829
18698
|
if (placed)
|
|
17830
18699
|
return;
|
|
17831
18700
|
placed = true;
|
|
17832
|
-
const
|
|
18701
|
+
const run26 = /* @__PURE__ */ jsxDEV(ReplacementRun, {
|
|
17833
18702
|
runProperties,
|
|
17834
18703
|
text: replacement
|
|
17835
18704
|
}, undefined, false, undefined, this);
|
|
17836
18705
|
newChildren.push(tracked && isParagraph ? /* @__PURE__ */ jsxDEV(Ins, {
|
|
17837
18706
|
meta: mintMeta(tracked),
|
|
17838
|
-
children:
|
|
17839
|
-
}, undefined, false, undefined, this) :
|
|
18707
|
+
children: run26
|
|
18708
|
+
}, undefined, false, undefined, this) : run26);
|
|
17840
18709
|
};
|
|
17841
18710
|
for (const child of container.children) {
|
|
17842
18711
|
if (child.tag === "w:r") {
|
|
@@ -17872,7 +18741,7 @@ function rebuildContainer(container, baseOffset, span, replacement, runPropertie
|
|
|
17872
18741
|
}
|
|
17873
18742
|
continue;
|
|
17874
18743
|
}
|
|
17875
|
-
if (isParagraph && (child.tag === "w:ins" || child.tag === "w:del")) {
|
|
18744
|
+
if (isParagraph && (child.tag === "w:ins" || child.tag === "w:del" || child.tag === "w:hyperlink")) {
|
|
17876
18745
|
let innerLength = 0;
|
|
17877
18746
|
for (const inner of child.children) {
|
|
17878
18747
|
if (inner.tag === "w:r")
|
|
@@ -17896,14 +18765,14 @@ function rebuildAcrossBoundaries(paragraph, span, replacement, runProperties, tr
|
|
|
17896
18765
|
if (placed)
|
|
17897
18766
|
return;
|
|
17898
18767
|
placed = true;
|
|
17899
|
-
const
|
|
18768
|
+
const run26 = /* @__PURE__ */ jsxDEV(ReplacementRun, {
|
|
17900
18769
|
runProperties,
|
|
17901
18770
|
text: replacement
|
|
17902
18771
|
}, undefined, false, undefined, this);
|
|
17903
18772
|
newChildren.push(tracked ? /* @__PURE__ */ jsxDEV(Ins, {
|
|
17904
18773
|
meta: mintMeta(tracked),
|
|
17905
|
-
children:
|
|
17906
|
-
}, undefined, false, undefined, this) :
|
|
18774
|
+
children: run26
|
|
18775
|
+
}, undefined, false, undefined, this) : run26);
|
|
17907
18776
|
};
|
|
17908
18777
|
for (const child of paragraph.children) {
|
|
17909
18778
|
if (child.tag === "w:r") {
|
|
@@ -17940,7 +18809,7 @@ function rebuildAcrossBoundaries(paragraph, span, replacement, runProperties, tr
|
|
|
17940
18809
|
continue;
|
|
17941
18810
|
}
|
|
17942
18811
|
if (child.tag === "w:ins" || child.tag === "w:del") {
|
|
17943
|
-
const innerLength =
|
|
18812
|
+
const innerLength = sumInnerRunLengths2(child);
|
|
17944
18813
|
const wrapperStart = offset;
|
|
17945
18814
|
const wrapperEnd = offset + innerLength;
|
|
17946
18815
|
offset = wrapperEnd;
|
|
@@ -17956,6 +18825,25 @@ function rebuildAcrossBoundaries(paragraph, span, replacement, runProperties, tr
|
|
|
17956
18825
|
splitWrapperAcrossSpan(child, wrapperStart, span, tracked, newChildren, placeReplacement);
|
|
17957
18826
|
continue;
|
|
17958
18827
|
}
|
|
18828
|
+
if (child.tag === "w:hyperlink") {
|
|
18829
|
+
const innerLength = sumInnerRunLengths2(child);
|
|
18830
|
+
const wrapperStart = offset;
|
|
18831
|
+
const wrapperEnd = offset + innerLength;
|
|
18832
|
+
offset = wrapperEnd;
|
|
18833
|
+
if (wrapperEnd <= span.start) {
|
|
18834
|
+
newChildren.push(child);
|
|
18835
|
+
continue;
|
|
18836
|
+
}
|
|
18837
|
+
if (wrapperStart >= span.end) {
|
|
18838
|
+
placeReplacement();
|
|
18839
|
+
newChildren.push(child);
|
|
18840
|
+
continue;
|
|
18841
|
+
}
|
|
18842
|
+
splitHyperlinkAcrossSpan(child, wrapperStart, span, runProperties, replacement, tracked, newChildren, placeReplacement, () => {
|
|
18843
|
+
placed = true;
|
|
18844
|
+
});
|
|
18845
|
+
continue;
|
|
18846
|
+
}
|
|
17959
18847
|
newChildren.push(child);
|
|
17960
18848
|
}
|
|
17961
18849
|
if (!placed)
|
|
@@ -18016,7 +18904,62 @@ function splitWrapperAcrossSpan(wrapper, wrapperStart, span, tracked, out, place
|
|
|
18016
18904
|
out.push(postWrapper);
|
|
18017
18905
|
}
|
|
18018
18906
|
}
|
|
18019
|
-
function
|
|
18907
|
+
function splitHyperlinkAcrossSpan(wrapper, wrapperStart, span, runProperties, replacement, tracked, out, placeReplacement, markReplacementPlaced) {
|
|
18908
|
+
const wrapperEnd = wrapperStart + wrapper.children.reduce((total, inner) => inner.tag === "w:r" ? total + runTextLength(inner) : total, 0);
|
|
18909
|
+
const startsInside = span.start > wrapperStart && span.start < wrapperEnd;
|
|
18910
|
+
const preInner = [];
|
|
18911
|
+
const postInner = [];
|
|
18912
|
+
let innerOffset = wrapperStart;
|
|
18913
|
+
for (const inner of wrapper.children) {
|
|
18914
|
+
if (inner.tag !== "w:r") {
|
|
18915
|
+
preInner.push(inner);
|
|
18916
|
+
continue;
|
|
18917
|
+
}
|
|
18918
|
+
const length = runTextLength(inner);
|
|
18919
|
+
const runStart = innerOffset;
|
|
18920
|
+
const runEnd = innerOffset + length;
|
|
18921
|
+
innerOffset = runEnd;
|
|
18922
|
+
if (runEnd <= span.start) {
|
|
18923
|
+
preInner.push(inner);
|
|
18924
|
+
continue;
|
|
18925
|
+
}
|
|
18926
|
+
if (runStart >= span.end) {
|
|
18927
|
+
postInner.push(inner);
|
|
18928
|
+
continue;
|
|
18929
|
+
}
|
|
18930
|
+
const sliceStartInRun = Math.max(0, span.start - runStart);
|
|
18931
|
+
const sliceEndInRun = Math.min(length, span.end - runStart);
|
|
18932
|
+
if (sliceStartInRun > 0)
|
|
18933
|
+
preInner.push(sliceRun(inner, 0, sliceStartInRun));
|
|
18934
|
+
if (sliceEndInRun < length) {
|
|
18935
|
+
postInner.push(sliceRun(inner, sliceEndInRun, length));
|
|
18936
|
+
}
|
|
18937
|
+
}
|
|
18938
|
+
if (startsInside) {
|
|
18939
|
+
const innerReplacement = /* @__PURE__ */ jsxDEV(ReplacementRun, {
|
|
18940
|
+
runProperties,
|
|
18941
|
+
text: replacement
|
|
18942
|
+
}, undefined, false, undefined, this);
|
|
18943
|
+
preInner.push(tracked ? /* @__PURE__ */ jsxDEV(Ins, {
|
|
18944
|
+
meta: mintMeta(tracked),
|
|
18945
|
+
children: innerReplacement
|
|
18946
|
+
}, undefined, false, undefined, this) : innerReplacement);
|
|
18947
|
+
markReplacementPlaced();
|
|
18948
|
+
}
|
|
18949
|
+
if (preInner.length > 0) {
|
|
18950
|
+
const preWrapper = new XmlNode2("w:hyperlink", { ...wrapper.attributes });
|
|
18951
|
+
preWrapper.children = preInner;
|
|
18952
|
+
out.push(preWrapper);
|
|
18953
|
+
}
|
|
18954
|
+
if (!startsInside)
|
|
18955
|
+
placeReplacement();
|
|
18956
|
+
if (postInner.length > 0) {
|
|
18957
|
+
const postWrapper = new XmlNode2("w:hyperlink", { ...wrapper.attributes });
|
|
18958
|
+
postWrapper.children = postInner;
|
|
18959
|
+
out.push(postWrapper);
|
|
18960
|
+
}
|
|
18961
|
+
}
|
|
18962
|
+
function sumInnerRunLengths2(wrapper) {
|
|
18020
18963
|
let total = 0;
|
|
18021
18964
|
for (const inner of wrapper.children) {
|
|
18022
18965
|
if (inner.tag === "w:r")
|
|
@@ -18027,8 +18970,8 @@ function sumInnerRunLengths(wrapper) {
|
|
|
18027
18970
|
function mintMeta(tracked) {
|
|
18028
18971
|
return { ...tracked.meta, revisionId: tracked.allocator.next() };
|
|
18029
18972
|
}
|
|
18030
|
-
function convertRunTextToDelText(
|
|
18031
|
-
for (const child of
|
|
18973
|
+
function convertRunTextToDelText(run26) {
|
|
18974
|
+
for (const child of run26.children) {
|
|
18032
18975
|
if (child.tag === "w:t")
|
|
18033
18976
|
child.tag = "w:delText";
|
|
18034
18977
|
}
|
|
@@ -18055,15 +18998,15 @@ var init_replace_span = __esm(() => {
|
|
|
18055
18998
|
});
|
|
18056
18999
|
|
|
18057
19000
|
// src/cli/replace/index.ts
|
|
18058
|
-
var
|
|
18059
|
-
__export(
|
|
18060
|
-
run: () =>
|
|
19001
|
+
var exports_replace3 = {};
|
|
19002
|
+
__export(exports_replace3, {
|
|
19003
|
+
run: () => run26
|
|
18061
19004
|
});
|
|
18062
|
-
import { parseArgs as
|
|
18063
|
-
async function
|
|
19005
|
+
import { parseArgs as parseArgs22 } from "util";
|
|
19006
|
+
async function run26(args) {
|
|
18064
19007
|
let parsed;
|
|
18065
19008
|
try {
|
|
18066
|
-
parsed =
|
|
19009
|
+
parsed = parseArgs22({
|
|
18067
19010
|
args,
|
|
18068
19011
|
allowPositionals: true,
|
|
18069
19012
|
options: {
|
|
@@ -18078,21 +19021,21 @@ async function run20(args) {
|
|
|
18078
19021
|
});
|
|
18079
19022
|
} catch (parseError) {
|
|
18080
19023
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
18081
|
-
return fail("USAGE", message,
|
|
19024
|
+
return fail("USAGE", message, HELP26);
|
|
18082
19025
|
}
|
|
18083
19026
|
if (parsed.values.help) {
|
|
18084
|
-
await writeStdout(
|
|
19027
|
+
await writeStdout(HELP26);
|
|
18085
19028
|
return EXIT.OK;
|
|
18086
19029
|
}
|
|
18087
19030
|
const path = parsed.positionals[0];
|
|
18088
19031
|
const pattern = parsed.positionals[1];
|
|
18089
19032
|
const replacement = parsed.positionals[2];
|
|
18090
19033
|
if (!path)
|
|
18091
|
-
return fail("USAGE", "Missing FILE argument",
|
|
19034
|
+
return fail("USAGE", "Missing FILE argument", HELP26);
|
|
18092
19035
|
if (pattern == null)
|
|
18093
|
-
return fail("USAGE", "Missing PATTERN argument",
|
|
19036
|
+
return fail("USAGE", "Missing PATTERN argument", HELP26);
|
|
18094
19037
|
if (replacement == null) {
|
|
18095
|
-
return fail("USAGE", "Missing REPLACEMENT argument",
|
|
19038
|
+
return fail("USAGE", "Missing REPLACEMENT argument", HELP26);
|
|
18096
19039
|
}
|
|
18097
19040
|
const ignoreCase = Boolean(parsed.values["ignore-case"]);
|
|
18098
19041
|
const useRegex = Boolean(parsed.values.regex);
|
|
@@ -18194,7 +19137,7 @@ async function run20(args) {
|
|
|
18194
19137
|
});
|
|
18195
19138
|
return EXIT.OK;
|
|
18196
19139
|
}
|
|
18197
|
-
var
|
|
19140
|
+
var HELP26 = `docx replace \u2014 substitute text spans (sed for docx)
|
|
18198
19141
|
|
|
18199
19142
|
Usage:
|
|
18200
19143
|
docx replace FILE PATTERN REPLACEMENT [options]
|
|
@@ -18228,7 +19171,7 @@ Examples:
|
|
|
18228
19171
|
docx replace doc.docx "(\\w+) (\\w+)" "$2 $1" --regex --all
|
|
18229
19172
|
docx replace doc.docx "wordy phrase" "tighter phrase" --all --dry-run
|
|
18230
19173
|
`;
|
|
18231
|
-
var
|
|
19174
|
+
var init_replace3 = __esm(() => {
|
|
18232
19175
|
init_core();
|
|
18233
19176
|
init_respond();
|
|
18234
19177
|
init_replace_span();
|
|
@@ -18237,13 +19180,13 @@ var init_replace2 = __esm(() => {
|
|
|
18237
19180
|
// src/cli/track-changes/index.tsx
|
|
18238
19181
|
var exports_track_changes = {};
|
|
18239
19182
|
__export(exports_track_changes, {
|
|
18240
|
-
run: () =>
|
|
19183
|
+
run: () => run27
|
|
18241
19184
|
});
|
|
18242
|
-
import { parseArgs as
|
|
18243
|
-
async function
|
|
19185
|
+
import { parseArgs as parseArgs23 } from "util";
|
|
19186
|
+
async function run27(args) {
|
|
18244
19187
|
let parsed;
|
|
18245
19188
|
try {
|
|
18246
|
-
parsed =
|
|
19189
|
+
parsed = parseArgs23({
|
|
18247
19190
|
args,
|
|
18248
19191
|
allowPositionals: true,
|
|
18249
19192
|
options: {
|
|
@@ -18254,18 +19197,18 @@ async function run21(args) {
|
|
|
18254
19197
|
});
|
|
18255
19198
|
} catch (parseError) {
|
|
18256
19199
|
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
18257
|
-
return fail("USAGE", message,
|
|
19200
|
+
return fail("USAGE", message, HELP27);
|
|
18258
19201
|
}
|
|
18259
19202
|
if (parsed.values.help) {
|
|
18260
|
-
await writeStdout(
|
|
19203
|
+
await writeStdout(HELP27);
|
|
18261
19204
|
return EXIT.OK;
|
|
18262
19205
|
}
|
|
18263
19206
|
const path = parsed.positionals[0];
|
|
18264
19207
|
if (!path)
|
|
18265
|
-
return fail("USAGE", "Missing FILE argument",
|
|
19208
|
+
return fail("USAGE", "Missing FILE argument", HELP27);
|
|
18266
19209
|
const mode = parsed.positionals[1];
|
|
18267
19210
|
if (mode !== "on" && mode !== "off") {
|
|
18268
|
-
return fail("USAGE", `Expected "on" or "off", got: ${mode ?? "<nothing>"}`,
|
|
19211
|
+
return fail("USAGE", `Expected "on" or "off", got: ${mode ?? "<nothing>"}`, HELP27);
|
|
18269
19212
|
}
|
|
18270
19213
|
const view = await openOrFail(path);
|
|
18271
19214
|
if (typeof view === "number")
|
|
@@ -18317,7 +19260,7 @@ async function run21(args) {
|
|
|
18317
19260
|
});
|
|
18318
19261
|
return EXIT.OK;
|
|
18319
19262
|
}
|
|
18320
|
-
var
|
|
19263
|
+
var HELP27 = `docx track-changes \u2014 toggle the document's tracked-changes mode
|
|
18321
19264
|
|
|
18322
19265
|
Usage:
|
|
18323
19266
|
docx track-changes FILE on|off [options]
|
|
@@ -18344,10 +19287,297 @@ var init_track_changes2 = __esm(() => {
|
|
|
18344
19287
|
init_respond();
|
|
18345
19288
|
init_jsx_dev_runtime();
|
|
18346
19289
|
});
|
|
19290
|
+
|
|
19291
|
+
// src/cli/wc/count.ts
|
|
19292
|
+
function countWords(text) {
|
|
19293
|
+
const matches = text.match(/\S+/g);
|
|
19294
|
+
return matches?.length ?? 0;
|
|
19295
|
+
}
|
|
19296
|
+
function paragraphText2(paragraph) {
|
|
19297
|
+
let out = "";
|
|
19298
|
+
for (const run28 of paragraph.runs) {
|
|
19299
|
+
if (run28.type === "text")
|
|
19300
|
+
out += run28.text;
|
|
19301
|
+
}
|
|
19302
|
+
return out;
|
|
19303
|
+
}
|
|
19304
|
+
function countWordsInBlocks(blocks) {
|
|
19305
|
+
let total = 0;
|
|
19306
|
+
for (const block of blocks) {
|
|
19307
|
+
if (block.type === "paragraph") {
|
|
19308
|
+
total += countWords(paragraphText2(block));
|
|
19309
|
+
} else if (block.type === "table") {
|
|
19310
|
+
for (const row of block.rows) {
|
|
19311
|
+
for (const cell of row.cells) {
|
|
19312
|
+
total += countWordsInBlocks(cell.blocks);
|
|
19313
|
+
}
|
|
19314
|
+
}
|
|
19315
|
+
}
|
|
19316
|
+
}
|
|
19317
|
+
return total;
|
|
19318
|
+
}
|
|
19319
|
+
function findBlockById(blocks, blockId) {
|
|
19320
|
+
for (const block of blocks) {
|
|
19321
|
+
if (block.id === blockId)
|
|
19322
|
+
return block;
|
|
19323
|
+
if (block.type === "table") {
|
|
19324
|
+
for (const row of block.rows) {
|
|
19325
|
+
for (const cell of row.cells) {
|
|
19326
|
+
const inner = findBlockById(cell.blocks, blockId);
|
|
19327
|
+
if (inner)
|
|
19328
|
+
return inner;
|
|
19329
|
+
}
|
|
19330
|
+
}
|
|
19331
|
+
}
|
|
19332
|
+
}
|
|
19333
|
+
return null;
|
|
19334
|
+
}
|
|
19335
|
+
function countWordsInParagraphSpan(paragraph, start, end) {
|
|
19336
|
+
const text = paragraphText2(paragraph);
|
|
19337
|
+
const clampedEnd = Math.min(Math.max(end, 0), text.length);
|
|
19338
|
+
const clampedStart = Math.min(Math.max(start, 0), clampedEnd);
|
|
19339
|
+
return countWords(text.slice(clampedStart, clampedEnd));
|
|
19340
|
+
}
|
|
19341
|
+
function countWordsInRange(paragraphsInOrder, startBlockId, startOffset, endBlockId, endOffset) {
|
|
19342
|
+
const startIndex = paragraphsInOrder.findIndex((p) => p.id === startBlockId);
|
|
19343
|
+
const endIndex = paragraphsInOrder.findIndex((p) => p.id === endBlockId);
|
|
19344
|
+
if (startIndex === -1 || endIndex === -1)
|
|
19345
|
+
return 0;
|
|
19346
|
+
if (endIndex < startIndex)
|
|
19347
|
+
return 0;
|
|
19348
|
+
if (startIndex === endIndex) {
|
|
19349
|
+
const paragraph = paragraphsInOrder[startIndex];
|
|
19350
|
+
if (!paragraph)
|
|
19351
|
+
return 0;
|
|
19352
|
+
return countWordsInParagraphSpan(paragraph, startOffset, endOffset);
|
|
19353
|
+
}
|
|
19354
|
+
let total = 0;
|
|
19355
|
+
const first = paragraphsInOrder[startIndex];
|
|
19356
|
+
if (first) {
|
|
19357
|
+
total += countWordsInParagraphSpan(first, startOffset, paragraphText2(first).length);
|
|
19358
|
+
}
|
|
19359
|
+
for (let index = startIndex + 1;index < endIndex; index++) {
|
|
19360
|
+
const middle = paragraphsInOrder[index];
|
|
19361
|
+
if (middle)
|
|
19362
|
+
total += countWords(paragraphText2(middle));
|
|
19363
|
+
}
|
|
19364
|
+
const last = paragraphsInOrder[endIndex];
|
|
19365
|
+
if (last)
|
|
19366
|
+
total += countWordsInParagraphSpan(last, 0, endOffset);
|
|
19367
|
+
return total;
|
|
19368
|
+
}
|
|
19369
|
+
function flattenParagraphs(blocks) {
|
|
19370
|
+
const out = [];
|
|
19371
|
+
for (const block of blocks) {
|
|
19372
|
+
if (block.type === "paragraph") {
|
|
19373
|
+
out.push(block);
|
|
19374
|
+
continue;
|
|
19375
|
+
}
|
|
19376
|
+
if (block.type === "table") {
|
|
19377
|
+
for (const row of block.rows) {
|
|
19378
|
+
for (const cell of row.cells) {
|
|
19379
|
+
out.push(...flattenParagraphs(cell.blocks));
|
|
19380
|
+
}
|
|
19381
|
+
}
|
|
19382
|
+
}
|
|
19383
|
+
}
|
|
19384
|
+
return out;
|
|
19385
|
+
}
|
|
19386
|
+
|
|
19387
|
+
// src/cli/wc/index.ts
|
|
19388
|
+
var exports_wc = {};
|
|
19389
|
+
__export(exports_wc, {
|
|
19390
|
+
run: () => run28
|
|
19391
|
+
});
|
|
19392
|
+
import { parseArgs as parseArgs24 } from "util";
|
|
19393
|
+
async function run28(args) {
|
|
19394
|
+
let parsed;
|
|
19395
|
+
try {
|
|
19396
|
+
parsed = parseArgs24({
|
|
19397
|
+
args,
|
|
19398
|
+
allowPositionals: true,
|
|
19399
|
+
options: {
|
|
19400
|
+
help: { type: "boolean", short: "h" }
|
|
19401
|
+
}
|
|
19402
|
+
});
|
|
19403
|
+
} catch (parseError) {
|
|
19404
|
+
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
19405
|
+
return fail("USAGE", message, HELP28);
|
|
19406
|
+
}
|
|
19407
|
+
if (parsed.values.help) {
|
|
19408
|
+
await writeStdout(HELP28);
|
|
19409
|
+
return EXIT.OK;
|
|
19410
|
+
}
|
|
19411
|
+
const path = parsed.positionals[0];
|
|
19412
|
+
const locatorInput = parsed.positionals[1];
|
|
19413
|
+
if (!path)
|
|
19414
|
+
return fail("USAGE", "Missing FILE argument", HELP28);
|
|
19415
|
+
const view = await openOrFail(path);
|
|
19416
|
+
if (typeof view === "number")
|
|
19417
|
+
return view;
|
|
19418
|
+
if (!locatorInput) {
|
|
19419
|
+
await respond({
|
|
19420
|
+
ok: true,
|
|
19421
|
+
operation: "wc",
|
|
19422
|
+
path,
|
|
19423
|
+
scope: "document",
|
|
19424
|
+
words: countWordsInBlocks(view.doc.blocks)
|
|
19425
|
+
});
|
|
19426
|
+
return EXIT.OK;
|
|
19427
|
+
}
|
|
19428
|
+
let locator;
|
|
19429
|
+
try {
|
|
19430
|
+
locator = parseLocator(locatorInput);
|
|
19431
|
+
} catch (error) {
|
|
19432
|
+
if (error instanceof LocatorParseError) {
|
|
19433
|
+
return fail("INVALID_LOCATOR", error.message);
|
|
19434
|
+
}
|
|
19435
|
+
throw error;
|
|
19436
|
+
}
|
|
19437
|
+
if (locator.kind === "comment" || locator.kind === "image") {
|
|
19438
|
+
return fail("USAGE", `Locator ${locatorInput} addresses a ${locator.kind}, not text`, "wc accepts paragraph, span, range, table, and cell locators.");
|
|
19439
|
+
}
|
|
19440
|
+
const blocks = view.doc.blocks;
|
|
19441
|
+
if (locator.kind === "block") {
|
|
19442
|
+
const block = findBlockById(blocks, locator.blockId);
|
|
19443
|
+
if (!block) {
|
|
19444
|
+
return fail("BLOCK_NOT_FOUND", `Block not found: ${locator.blockId}`);
|
|
19445
|
+
}
|
|
19446
|
+
const words = block.type === "paragraph" ? countWords(paragraphText2(block)) : block.type === "table" ? countWordsInBlocks([block]) : 0;
|
|
19447
|
+
await respond({
|
|
19448
|
+
ok: true,
|
|
19449
|
+
operation: "wc",
|
|
19450
|
+
path,
|
|
19451
|
+
locator: locatorInput,
|
|
19452
|
+
scope: block.type,
|
|
19453
|
+
words
|
|
19454
|
+
});
|
|
19455
|
+
return EXIT.OK;
|
|
19456
|
+
}
|
|
19457
|
+
if (locator.kind === "blockSpan") {
|
|
19458
|
+
const block = findBlockById(blocks, locator.blockId);
|
|
19459
|
+
if (!block || block.type !== "paragraph") {
|
|
19460
|
+
return fail("BLOCK_NOT_FOUND", `Paragraph not found: ${locator.blockId}`);
|
|
19461
|
+
}
|
|
19462
|
+
await respond({
|
|
19463
|
+
ok: true,
|
|
19464
|
+
operation: "wc",
|
|
19465
|
+
path,
|
|
19466
|
+
locator: locatorInput,
|
|
19467
|
+
scope: "paragraphSpan",
|
|
19468
|
+
words: countWordsInParagraphSpan(block, locator.start, locator.end)
|
|
19469
|
+
});
|
|
19470
|
+
return EXIT.OK;
|
|
19471
|
+
}
|
|
19472
|
+
if (locator.kind === "range") {
|
|
19473
|
+
const paragraphs = flattenParagraphs(blocks);
|
|
19474
|
+
const startExists = paragraphs.some((p) => p.id === locator.start.blockId);
|
|
19475
|
+
const endExists = paragraphs.some((p) => p.id === locator.end.blockId);
|
|
19476
|
+
if (!startExists) {
|
|
19477
|
+
return fail("BLOCK_NOT_FOUND", `Paragraph not found: ${locator.start.blockId}`);
|
|
19478
|
+
}
|
|
19479
|
+
if (!endExists) {
|
|
19480
|
+
return fail("BLOCK_NOT_FOUND", `Paragraph not found: ${locator.end.blockId}`);
|
|
19481
|
+
}
|
|
19482
|
+
await respond({
|
|
19483
|
+
ok: true,
|
|
19484
|
+
operation: "wc",
|
|
19485
|
+
path,
|
|
19486
|
+
locator: locatorInput,
|
|
19487
|
+
scope: "range",
|
|
19488
|
+
words: countWordsInRange(paragraphs, locator.start.blockId, locator.start.offset, locator.end.blockId, locator.end.offset)
|
|
19489
|
+
});
|
|
19490
|
+
return EXIT.OK;
|
|
19491
|
+
}
|
|
19492
|
+
if (locator.kind === "cell") {
|
|
19493
|
+
const cellPath = `${locator.tableId}:r${locator.row}c${locator.col}`;
|
|
19494
|
+
if (!locator.inner) {
|
|
19495
|
+
const cellBlocks = findCellBlocks(blocks, locator);
|
|
19496
|
+
if (!cellBlocks) {
|
|
19497
|
+
return fail("BLOCK_NOT_FOUND", `Cell not found: ${cellPath}`);
|
|
19498
|
+
}
|
|
19499
|
+
await respond({
|
|
19500
|
+
ok: true,
|
|
19501
|
+
operation: "wc",
|
|
19502
|
+
path,
|
|
19503
|
+
locator: locatorInput,
|
|
19504
|
+
scope: "cell",
|
|
19505
|
+
words: countWordsInBlocks(cellBlocks)
|
|
19506
|
+
});
|
|
19507
|
+
return EXIT.OK;
|
|
19508
|
+
}
|
|
19509
|
+
const innerBlockId = locator.inner.kind === "block" ? locator.inner.blockId : locator.inner.kind === "blockSpan" ? locator.inner.blockId : null;
|
|
19510
|
+
if (!innerBlockId) {
|
|
19511
|
+
return fail("USAGE", `Unsupported inner locator for cell: ${locatorInput}`, "Inside a cell, wc accepts pK or pK:S-E only.");
|
|
19512
|
+
}
|
|
19513
|
+
const composedId = `${cellPath}:${innerBlockId}`;
|
|
19514
|
+
const block = findBlockById(blocks, composedId);
|
|
19515
|
+
if (!block || block.type !== "paragraph") {
|
|
19516
|
+
return fail("BLOCK_NOT_FOUND", `Paragraph not found: ${composedId}`);
|
|
19517
|
+
}
|
|
19518
|
+
const words = locator.inner.kind === "blockSpan" ? countWordsInParagraphSpan(block, locator.inner.start, locator.inner.end) : countWords(paragraphText2(block));
|
|
19519
|
+
await respond({
|
|
19520
|
+
ok: true,
|
|
19521
|
+
operation: "wc",
|
|
19522
|
+
path,
|
|
19523
|
+
locator: locatorInput,
|
|
19524
|
+
scope: locator.inner.kind === "blockSpan" ? "paragraphSpan" : "paragraph",
|
|
19525
|
+
words
|
|
19526
|
+
});
|
|
19527
|
+
return EXIT.OK;
|
|
19528
|
+
}
|
|
19529
|
+
return fail("USAGE", `Unsupported locator: ${locatorInput}`);
|
|
19530
|
+
}
|
|
19531
|
+
function findCellBlocks(blocks, locator) {
|
|
19532
|
+
const block = findBlockById(blocks, locator.tableId);
|
|
19533
|
+
if (!block || block.type !== "table")
|
|
19534
|
+
return null;
|
|
19535
|
+
const row = block.rows[locator.row];
|
|
19536
|
+
if (!row)
|
|
19537
|
+
return null;
|
|
19538
|
+
const cell = row.cells[locator.col];
|
|
19539
|
+
if (!cell)
|
|
19540
|
+
return null;
|
|
19541
|
+
return cell.blocks;
|
|
19542
|
+
}
|
|
19543
|
+
var HELP28 = `docx wc \u2014 count words in a document or a locator-addressed slice
|
|
19544
|
+
|
|
19545
|
+
Usage:
|
|
19546
|
+
docx wc FILE [LOCATOR] [options]
|
|
19547
|
+
|
|
19548
|
+
Locators (optional; default: whole document):
|
|
19549
|
+
pN whole paragraph N
|
|
19550
|
+
pN:S-E chars S..E within paragraph N
|
|
19551
|
+
pN:S-pM:E cross-paragraph range
|
|
19552
|
+
tN whole table
|
|
19553
|
+
tN:rRcC whole cell
|
|
19554
|
+
tN:rRcC:pK paragraph K inside that cell
|
|
19555
|
+
tN:rRcC:pK:S-E span within a cell paragraph
|
|
19556
|
+
|
|
19557
|
+
Options:
|
|
19558
|
+
-h, --help show this help
|
|
19559
|
+
|
|
19560
|
+
Counting is whitespace-segmented (\\S+) over the joined paragraph text. Hidden
|
|
19561
|
+
content like images/breaks/tabs contributes no words. Tracked-change deletions
|
|
19562
|
+
are counted alongside insertions because their characters are still part of the
|
|
19563
|
+
on-disk text; accept or reject changes first if you need a delta on the
|
|
19564
|
+
"accepted" view.
|
|
19565
|
+
|
|
19566
|
+
Examples:
|
|
19567
|
+
docx wc doc.docx
|
|
19568
|
+
docx wc doc.docx p3
|
|
19569
|
+
docx wc doc.docx p3:0-120
|
|
19570
|
+
docx wc doc.docx p5:10-p9:42
|
|
19571
|
+
docx wc doc.docx t0:r1c0
|
|
19572
|
+
`;
|
|
19573
|
+
var init_wc = __esm(() => {
|
|
19574
|
+
init_core();
|
|
19575
|
+
init_respond();
|
|
19576
|
+
});
|
|
18347
19577
|
// package.json
|
|
18348
19578
|
var package_default = {
|
|
18349
19579
|
name: "bun-docx",
|
|
18350
|
-
version: "0.
|
|
19580
|
+
version: "0.5.0",
|
|
18351
19581
|
description: "CLI for AI agents (Claude, Codex) to read, edit, and comment on .docx files with full format fidelity.",
|
|
18352
19582
|
keywords: [
|
|
18353
19583
|
"docx",
|
|
@@ -18422,8 +19652,11 @@ Commands:
|
|
|
18422
19652
|
delete FILE Remove a block or run range
|
|
18423
19653
|
find FILE QUERY Find text spans, return locators
|
|
18424
19654
|
replace FILE PATTERN REPL Substitute text spans (sed for docx)
|
|
19655
|
+
wc FILE [LOCATOR] Count words in the doc or a slice
|
|
19656
|
+
outline FILE List headings as a hierarchical tree
|
|
18425
19657
|
comments \u2026 Add, reply, resolve, delete, list comments
|
|
18426
19658
|
images \u2026 Extract, replace, list images
|
|
19659
|
+
hyperlinks \u2026 List, replace hyperlinks
|
|
18427
19660
|
track-changes FILE on|off Toggle tracked-changes mode
|
|
18428
19661
|
info \u2026 Reference material (schema, locators)
|
|
18429
19662
|
|
|
@@ -18450,12 +19683,15 @@ var COMMANDS = {
|
|
|
18450
19683
|
delete: () => Promise.resolve().then(() => (init_delete2(), exports_delete2)),
|
|
18451
19684
|
edit: () => Promise.resolve().then(() => (init_edit(), exports_edit)),
|
|
18452
19685
|
find: () => Promise.resolve().then(() => (init_find(), exports_find)),
|
|
19686
|
+
hyperlinks: () => Promise.resolve().then(() => (init_hyperlinks(), exports_hyperlinks)),
|
|
18453
19687
|
images: () => Promise.resolve().then(() => (init_images(), exports_images)),
|
|
18454
19688
|
info: () => Promise.resolve().then(() => (init_info(), exports_info)),
|
|
18455
19689
|
insert: () => Promise.resolve().then(() => (init_insert(), exports_insert)),
|
|
19690
|
+
outline: () => Promise.resolve().then(() => (init_outline(), exports_outline)),
|
|
18456
19691
|
read: () => Promise.resolve().then(() => (init_read2(), exports_read)),
|
|
18457
|
-
replace: () => Promise.resolve().then(() => (
|
|
18458
|
-
"track-changes": () => Promise.resolve().then(() => (init_track_changes2(), exports_track_changes))
|
|
19692
|
+
replace: () => Promise.resolve().then(() => (init_replace3(), exports_replace3)),
|
|
19693
|
+
"track-changes": () => Promise.resolve().then(() => (init_track_changes2(), exports_track_changes)),
|
|
19694
|
+
wc: () => Promise.resolve().then(() => (init_wc(), exports_wc))
|
|
18459
19695
|
};
|
|
18460
19696
|
async function main(argv) {
|
|
18461
19697
|
const args = argv.slice(2);
|