@wonderwhy-er/desktop-commander 0.2.35 → 0.2.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +3 -0
  2. package/dist/handlers/filesystem-handlers.js +58 -11
  3. package/dist/handlers/history-handlers.d.ts +7 -0
  4. package/dist/handlers/history-handlers.js +33 -1
  5. package/dist/remote-device/remote-channel.d.ts +8 -3
  6. package/dist/remote-device/remote-channel.js +68 -21
  7. package/dist/search-manager.d.ts +13 -0
  8. package/dist/search-manager.js +146 -0
  9. package/dist/server.js +56 -4
  10. package/dist/test-docx.d.ts +1 -0
  11. package/dist/tools/docx/builders/image.d.ts +14 -0
  12. package/dist/tools/docx/builders/image.js +84 -0
  13. package/dist/tools/docx/builders/index.d.ts +9 -3
  14. package/dist/tools/docx/builders/index.js +9 -3
  15. package/dist/tools/docx/builders/paragraph.d.ts +12 -0
  16. package/dist/tools/docx/builders/paragraph.js +29 -0
  17. package/dist/tools/docx/builders/table.d.ts +10 -0
  18. package/dist/tools/docx/builders/table.js +138 -0
  19. package/dist/tools/docx/builders/utils.d.ts +5 -0
  20. package/dist/tools/docx/builders/utils.js +18 -0
  21. package/dist/tools/docx/constants.d.ts +28 -32
  22. package/dist/tools/docx/constants.js +56 -52
  23. package/dist/tools/docx/create.d.ts +21 -0
  24. package/dist/tools/docx/create.js +386 -0
  25. package/dist/tools/docx/dom.d.ts +139 -0
  26. package/dist/tools/docx/dom.js +448 -0
  27. package/dist/tools/docx/index.d.ts +8 -12
  28. package/dist/tools/docx/index.js +8 -14
  29. package/dist/tools/docx/modify.d.ts +28 -0
  30. package/dist/tools/docx/modify.js +271 -0
  31. package/dist/tools/docx/ops/delete-paragraph-at-body-index.d.ts +11 -0
  32. package/dist/tools/docx/ops/delete-paragraph-at-body-index.js +23 -0
  33. package/dist/tools/docx/ops/header-replace-text-exact.d.ts +13 -0
  34. package/dist/tools/docx/ops/header-replace-text-exact.js +55 -0
  35. package/dist/tools/docx/ops/index.d.ts +17 -0
  36. package/dist/tools/docx/ops/index.js +70 -0
  37. package/dist/tools/docx/ops/insert-image-after-text.d.ts +24 -0
  38. package/dist/tools/docx/ops/insert-image-after-text.js +128 -0
  39. package/dist/tools/docx/ops/insert-paragraph-after-text.d.ts +12 -0
  40. package/dist/tools/docx/ops/insert-paragraph-after-text.js +74 -0
  41. package/dist/tools/docx/ops/insert-table-after-text.d.ts +19 -0
  42. package/dist/tools/docx/ops/insert-table-after-text.js +57 -0
  43. package/dist/tools/docx/ops/replace-hyperlink-url.d.ts +12 -0
  44. package/dist/tools/docx/ops/replace-hyperlink-url.js +37 -0
  45. package/dist/tools/docx/ops/replace-paragraph-at-body-index.d.ts +9 -0
  46. package/dist/tools/docx/ops/replace-paragraph-at-body-index.js +25 -0
  47. package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +21 -0
  48. package/dist/tools/docx/ops/replace-paragraph-text-exact.js +36 -0
  49. package/dist/tools/docx/ops/replace-table-cell-text.d.ts +25 -0
  50. package/dist/tools/docx/ops/replace-table-cell-text.js +85 -0
  51. package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +9 -0
  52. package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +24 -0
  53. package/dist/tools/docx/ops/set-color-for-style.d.ts +13 -0
  54. package/dist/tools/docx/ops/set-color-for-style.js +31 -0
  55. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.d.ts +8 -0
  56. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.js +57 -0
  57. package/dist/tools/docx/ops/table-set-cell-text.d.ts +9 -0
  58. package/dist/tools/docx/ops/table-set-cell-text.js +40 -0
  59. package/dist/tools/docx/read.d.ts +27 -0
  60. package/dist/tools/docx/read.js +308 -0
  61. package/dist/tools/docx/relationships.d.ts +22 -0
  62. package/dist/tools/docx/relationships.js +76 -0
  63. package/dist/tools/docx/types.d.ts +202 -103
  64. package/dist/tools/docx/types.js +2 -5
  65. package/dist/tools/docx/validate.d.ts +33 -0
  66. package/dist/tools/docx/validate.js +49 -0
  67. package/dist/tools/docx/write.d.ts +17 -0
  68. package/dist/tools/docx/write.js +88 -0
  69. package/dist/tools/docx/xml-view-test.d.ts +1 -0
  70. package/dist/tools/docx/xml-view-test.js +63 -0
  71. package/dist/tools/docx/xml-view.d.ts +56 -0
  72. package/dist/tools/docx/xml-view.js +169 -0
  73. package/dist/tools/docx/zip.d.ts +21 -0
  74. package/dist/tools/docx/zip.js +35 -0
  75. package/dist/tools/edit.js +57 -27
  76. package/dist/tools/schemas.d.ts +13 -0
  77. package/dist/tools/schemas.js +6 -1
  78. package/dist/types.d.ts +10 -0
  79. package/dist/ui/contracts.d.ts +14 -0
  80. package/dist/ui/contracts.js +18 -0
  81. package/dist/ui/file-preview/index.html +16 -0
  82. package/dist/ui/file-preview/preview-runtime.js +13983 -0
  83. package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
  84. package/dist/ui/file-preview/shared/preview-file-types.js +57 -0
  85. package/dist/ui/file-preview/src/app.d.ts +4 -0
  86. package/dist/ui/file-preview/src/app.js +800 -0
  87. package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
  88. package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
  89. package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
  90. package/dist/ui/file-preview/src/components/highlighting.js +54 -0
  91. package/dist/ui/file-preview/src/components/html-renderer.d.ts +9 -0
  92. package/dist/ui/file-preview/src/components/html-renderer.js +63 -0
  93. package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
  94. package/dist/ui/file-preview/src/components/markdown-renderer.js +21 -0
  95. package/dist/ui/file-preview/src/components/toolbar.d.ts +6 -0
  96. package/dist/ui/file-preview/src/components/toolbar.js +75 -0
  97. package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
  98. package/dist/ui/file-preview/src/image-preview.js +21 -0
  99. package/dist/ui/file-preview/src/main.d.ts +1 -0
  100. package/dist/ui/file-preview/src/main.js +5 -0
  101. package/dist/ui/file-preview/src/types.d.ts +1 -0
  102. package/dist/ui/file-preview/src/types.js +1 -0
  103. package/dist/ui/file-preview/styles.css +764 -0
  104. package/dist/ui/resources.d.ts +21 -0
  105. package/dist/ui/resources.js +72 -0
  106. package/dist/ui/shared/escape-html.d.ts +4 -0
  107. package/dist/ui/shared/escape-html.js +11 -0
  108. package/dist/ui/shared/host-lifecycle.d.ts +16 -0
  109. package/dist/ui/shared/host-lifecycle.js +35 -0
  110. package/dist/ui/shared/rpc-client.d.ts +14 -0
  111. package/dist/ui/shared/rpc-client.js +72 -0
  112. package/dist/ui/shared/theme-adaptation.d.ts +10 -0
  113. package/dist/ui/shared/theme-adaptation.js +118 -0
  114. package/dist/ui/shared/tool-header.d.ts +9 -0
  115. package/dist/ui/shared/tool-header.js +25 -0
  116. package/dist/ui/shared/tool-shell.d.ts +16 -0
  117. package/dist/ui/shared/tool-shell.js +65 -0
  118. package/dist/ui/shared/widget-state.d.ts +28 -0
  119. package/dist/ui/shared/widget-state.js +60 -0
  120. package/dist/utils/capture.d.ts +1 -0
  121. package/dist/utils/capture.js +176 -8
  122. package/dist/utils/files/base.d.ts +3 -1
  123. package/dist/utils/files/docx.d.ts +28 -22
  124. package/dist/utils/files/docx.js +630 -196
  125. package/dist/utils/files/factory.d.ts +6 -5
  126. package/dist/utils/files/factory.js +18 -6
  127. package/dist/utils/files/text.js +9 -1
  128. package/dist/utils/system-info.js +1 -1
  129. package/dist/utils/usageTracker.js +5 -0
  130. package/dist/version.d.ts +1 -1
  131. package/dist/version.js +1 -1
  132. package/package.json +6 -2
@@ -0,0 +1,448 @@
1
+ /**
2
+ * DOM utilities for DOCX XML manipulation.
3
+ *
4
+ * Single Responsibility: XML parsing, navigation, and minimal element
5
+ * mutation. No file I/O — every function works on in-memory DOM nodes.
6
+ *
7
+ * Uses @xmldom/xmldom for parsing and serialisation so that the
8
+ * document-order of nodes is always preserved.
9
+ */
10
+ import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
11
+ // ═══════════════════════════════════════════════════════════════════════
12
+ // XML parse / serialize
13
+ // ═══════════════════════════════════════════════════════════════════════
14
+ export function parseXml(xmlStr) {
15
+ return new DOMParser().parseFromString(xmlStr, 'application/xml');
16
+ }
17
+ export function serializeXml(doc) {
18
+ return new XMLSerializer().serializeToString(doc);
19
+ }
20
+ // ═══════════════════════════════════════════════════════════════════════
21
+ // Generic DOM helpers
22
+ // ═══════════════════════════════════════════════════════════════════════
23
+ /**
24
+ * Convert any NodeList / HTMLCollection-like object into a real array.
25
+ */
26
+ export function nodeListToArray(nl) {
27
+ const arr = [];
28
+ for (let i = 0; i < nl.length; i++) {
29
+ const n = nl.item(i);
30
+ if (n)
31
+ arr.push(n);
32
+ }
33
+ return arr;
34
+ }
35
+ // ═══════════════════════════════════════════════════════════════════════
36
+ // Body access
37
+ // ═══════════════════════════════════════════════════════════════════════
38
+ /** Return the single <w:body> element from a parsed document.xml DOM. */
39
+ export function getBody(doc) {
40
+ const body = doc.getElementsByTagName('w:body').item(0);
41
+ if (!body)
42
+ throw new Error('Invalid DOCX DOM: missing <w:body>');
43
+ return body;
44
+ }
45
+ /**
46
+ * Return ALL direct element children of w:body **in document order**.
47
+ * Includes w:p, w:tbl, w:sdt, w:sectPr, etc.
48
+ */
49
+ export function getBodyChildren(body) {
50
+ const out = [];
51
+ for (const node of nodeListToArray(body.childNodes)) {
52
+ if (node.nodeType === 1)
53
+ out.push(node);
54
+ }
55
+ return out;
56
+ }
57
+ /**
58
+ * Return ALL top‑level tables that are logically in the body, including those
59
+ * wrapped in structured document tags (w:sdt / w:sdtContent).
60
+ *
61
+ * Previous logic only saw tables that were direct children of <w:body>. That
62
+ * meant tables inside SDTs were invisible to table operations and readDocxOutline.
63
+ * This helper walks the body tree and collects any <w:tbl> that appears as a
64
+ * *first‑class* block (we do not recurse into tables themselves, so nested
65
+ * tables are not double‑counted).
66
+ */
67
+ export function getAllBodyTables(body) {
68
+ const result = [];
69
+ function collectFromNode(node) {
70
+ const name = node.nodeName;
71
+ if (name === 'w:tbl') {
72
+ result.push(node);
73
+ return; // don't recurse into tables to avoid nested counting
74
+ }
75
+ // If this is a structured document tag, look into its content container.
76
+ if (name === 'w:sdt') {
77
+ const sdtContent = findDirectChild(node, 'w:sdtContent');
78
+ if (sdtContent) {
79
+ for (const child of nodeListToArray(sdtContent.childNodes)) {
80
+ if (child.nodeType === 1)
81
+ collectFromNode(child);
82
+ }
83
+ }
84
+ return;
85
+ }
86
+ // Generic container: recurse into element children.
87
+ for (const child of nodeListToArray(node.childNodes)) {
88
+ if (child.nodeType === 1)
89
+ collectFromNode(child);
90
+ }
91
+ }
92
+ for (const child of getBodyChildren(body)) {
93
+ collectFromNode(child);
94
+ }
95
+ return result;
96
+ }
97
+ // ═══════════════════════════════════════════════════════════════════════
98
+ // Body signature
99
+ // ═══════════════════════════════════════════════════════════════════════
100
+ /**
101
+ * Build a compact signature string from the body children array.
102
+ * Maps each node's qualified name to a short local name:
103
+ * w:p → p, w:tbl → tbl, w:sdt → sdt, w:sectPr → sectPr, …
104
+ * Returns e.g. "p,tbl,p,p,sectPr".
105
+ */
106
+ export function bodySignature(children) {
107
+ return children
108
+ .map((ch) => {
109
+ const name = ch.nodeName;
110
+ const idx = name.indexOf(':');
111
+ return idx >= 0 ? name.substring(idx + 1) : name;
112
+ })
113
+ .join(',');
114
+ }
115
+ // ═══════════════════════════════════════════════════════════════════════
116
+ // Paragraph text helpers
117
+ // ═══════════════════════════════════════════════════════════════════════
118
+ /** Concatenate text from every <w:t> descendant of a paragraph. */
119
+ export function getParagraphText(p) {
120
+ const tNodes = p.getElementsByTagName('w:t');
121
+ let out = '';
122
+ for (let i = 0; i < tNodes.length; i++) {
123
+ out += tNodes.item(i)?.textContent ?? '';
124
+ }
125
+ return out;
126
+ }
127
+ /** Read the style id from w:pPr/w:pStyle/@w:val, or null if absent. */
128
+ export function getParagraphStyle(p) {
129
+ for (const child of nodeListToArray(p.childNodes)) {
130
+ if (child.nodeType === 1 && child.nodeName === 'w:pPr') {
131
+ const pPr = child;
132
+ for (const prChild of nodeListToArray(pPr.childNodes)) {
133
+ if (prChild.nodeType === 1 && prChild.nodeName === 'w:pStyle') {
134
+ return prChild.getAttribute('w:val');
135
+ }
136
+ }
137
+ return null;
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ // ═══════════════════════════════════════════════════════════════════════
143
+ // Table content extraction
144
+ // ═══════════════════════════════════════════════════════════════════════
145
+ /**
146
+ * Extract all text content from a table cell (w:tc).
147
+ * Returns the concatenated text from all paragraphs in the cell.
148
+ */
149
+ export function getCellText(tc) {
150
+ const paragraphs = tc.getElementsByTagName('w:p');
151
+ const texts = [];
152
+ for (let i = 0; i < paragraphs.length; i++) {
153
+ const p = paragraphs.item(i);
154
+ if (p) {
155
+ const text = getParagraphText(p).trim();
156
+ if (text)
157
+ texts.push(text);
158
+ }
159
+ }
160
+ return texts.join(' '); // Join multiple paragraphs in cell with space
161
+ }
162
+ /**
163
+ * Extract all rows from a table (w:tbl).
164
+ * Returns an array of rows, where each row is an array of cell text strings.
165
+ * First row is treated as header if it exists.
166
+ */
167
+ export function getTableContent(tbl) {
168
+ const rows = [];
169
+ for (const child of nodeListToArray(tbl.childNodes)) {
170
+ if (child.nodeType === 1 && child.nodeName === 'w:tr') {
171
+ rows.push(child);
172
+ }
173
+ }
174
+ if (rows.length === 0) {
175
+ return { rows: [] };
176
+ }
177
+ // Extract cells from each row
178
+ const tableRows = [];
179
+ for (const row of rows) {
180
+ const cells = [];
181
+ for (const child of nodeListToArray(row.childNodes)) {
182
+ if (child.nodeType === 1 && child.nodeName === 'w:tc') {
183
+ cells.push(getCellText(child));
184
+ }
185
+ }
186
+ if (cells.length > 0) {
187
+ tableRows.push(cells);
188
+ }
189
+ }
190
+ // First row might be header - check if it has bold formatting
191
+ // For simplicity, we'll treat first row as potential header
192
+ // User can determine this based on style or content
193
+ if (tableRows.length > 0) {
194
+ const firstRow = tableRows[0];
195
+ const restRows = tableRows.slice(1);
196
+ return {
197
+ headers: firstRow.length > 0 ? firstRow : undefined,
198
+ rows: restRows.length > 0 ? restRows : [],
199
+ };
200
+ }
201
+ return { rows: tableRows };
202
+ }
203
+ /**
204
+ * Get table style from w:tblPr/w:tblStyle/@w:val, or null if absent.
205
+ */
206
+ export function getTableStyle(tbl) {
207
+ const tblPr = tbl.getElementsByTagName('w:tblPr').item(0);
208
+ if (!tblPr)
209
+ return null;
210
+ const tblStyle = tblPr.getElementsByTagName('w:tblStyle').item(0);
211
+ if (!tblStyle)
212
+ return null;
213
+ return tblStyle.getAttribute('w:val');
214
+ }
215
+ // ═══════════════════════════════════════════════════════════════════════
216
+ // Image reference extraction
217
+ // ═══════════════════════════════════════════════════════════════════════
218
+ /**
219
+ * Extract image reference from a w:drawing element.
220
+ * Returns the relationship ID (rId) and media file path if found.
221
+ */
222
+ export function getImageReference(drawing) {
223
+ // Find a:blip/@r:embed to get the relationship ID
224
+ const blip = drawing.getElementsByTagName('a:blip').item(0);
225
+ if (!blip)
226
+ return { rId: null, mediaPath: null };
227
+ const rId = blip.getAttribute('r:embed');
228
+ if (!rId)
229
+ return { rId: null, mediaPath: null };
230
+ // Media path will be resolved from relationships file
231
+ // For now, return the rId - the caller will resolve it from rels
232
+ return { rId, mediaPath: null };
233
+ }
234
+ // ═══════════════════════════════════════════════════════════════════════
235
+ // Minimal text replacement
236
+ // ═══════════════════════════════════════════════════════════════════════
237
+ /**
238
+ * Replace the text of a paragraph with minimal DOM changes.
239
+ * Sets the FIRST w:t to `text`, clears every subsequent w:t.
240
+ * Sets xml:space="preserve" so leading/trailing spaces survive.
241
+ * Does NOT remove/recreate runs or remove paragraph properties.
242
+ *
243
+ * WARNING: This function does NOT preserve multiple runs with different styles.
244
+ * Use setParagraphTextPreservingStyles() for cells with multiple styled runs.
245
+ */
246
+ export function setParagraphTextMinimal(p, text) {
247
+ const tNodes = p.getElementsByTagName('w:t');
248
+ if (tNodes.length === 0)
249
+ return;
250
+ const first = tNodes.item(0);
251
+ first.textContent = text;
252
+ first.setAttribute('xml:space', 'preserve');
253
+ for (let i = 1; i < tNodes.length; i++) {
254
+ tNodes.item(i).textContent = '';
255
+ }
256
+ }
257
+ /**
258
+ * Replace paragraph text while preserving all run styles.
259
+ *
260
+ * This function preserves the structure of all runs (w:r) and their
261
+ * properties (w:rPr), distributing the new text across existing runs.
262
+ *
263
+ * Strategy:
264
+ * 1. Collect all runs with their properties
265
+ * 2. Distribute new text across runs (preserving run count and styles)
266
+ * 3. If new text is longer, extend the last run
267
+ * 4. If new text is shorter, clear excess runs but keep their structure
268
+ */
269
+ export function setParagraphTextPreservingStyles(p, text) {
270
+ const doc = p.ownerDocument;
271
+ if (!doc)
272
+ return;
273
+ // Work on ALL <w:t> descendants, not just direct children.
274
+ // This covers runs inside hyperlinks, smart tags, etc.
275
+ const tNodes = p.getElementsByTagName('w:t');
276
+ if (tNodes.length === 0) {
277
+ // No text nodes exist - create a minimal run + text.
278
+ const r = doc.createElement('w:r');
279
+ const t = doc.createElement('w:t');
280
+ t.setAttribute('xml:space', 'preserve');
281
+ t.textContent = text;
282
+ r.appendChild(t);
283
+ p.appendChild(r);
284
+ return;
285
+ }
286
+ // First text node gets the NEW text.
287
+ const first = tNodes.item(0);
288
+ first.textContent = text;
289
+ first.setAttribute('xml:space', 'preserve');
290
+ // All other text nodes are cleared, but their runs (and w:rPr) remain,
291
+ // so formatting structures are preserved while old text disappears.
292
+ for (let i = 1; i < tNodes.length; i++) {
293
+ const t = tNodes.item(i);
294
+ if (t)
295
+ t.textContent = '';
296
+ }
297
+ }
298
+ /**
299
+ * Replace cell text while preserving ALL paragraphs and their styles.
300
+ *
301
+ * This function works at the cell level:
302
+ * - Preserves ALL paragraphs in the cell (doesn't remove any)
303
+ * - Updates text in the first paragraph while preserving its styles
304
+ * - Keeps all other paragraphs intact with their original text and styles
305
+ *
306
+ * This ensures that cells with multiple paragraphs (each with different
307
+ * styles, font sizes, etc.) maintain their structure after text replacement.
308
+ *
309
+ * Example: If a cell has:
310
+ * - Paragraph 1: "LAWN AND LANDSCAPE" (Heading1 style, large font, red color)
311
+ * - Paragraph 2: "Take your weekends back..." (Normal style, smaller font, black color)
312
+ *
313
+ * Replacing with "EARTH AND MOUNTAIN" will:
314
+ * - Update paragraph 1 to "EARTH AND MOUNTAIN" (preserving Heading1 style, large font, red color)
315
+ * - Keep paragraph 2 completely intact with its original text and style
316
+ */
317
+ export function setCellTextPreservingStyles(tc, text) {
318
+ // Convert NodeList to array to avoid live NodeList issues
319
+ const paragraphs = nodeListToArray(tc.getElementsByTagName('w:p'));
320
+ if (paragraphs.length === 0) {
321
+ // Cell has no paragraphs - create one
322
+ const doc = tc.ownerDocument;
323
+ if (!doc)
324
+ return;
325
+ const p = doc.createElement('w:p');
326
+ const r = doc.createElement('w:r');
327
+ const t = doc.createElement('w:t');
328
+ t.setAttribute('xml:space', 'preserve');
329
+ t.textContent = text;
330
+ r.appendChild(t);
331
+ p.appendChild(r);
332
+ tc.appendChild(p);
333
+ return;
334
+ }
335
+ // Update first paragraph with new text, preserving all its styles
336
+ // This function preserves all runs and their properties (colors, bold, italic, etc.)
337
+ setParagraphTextPreservingStyles(paragraphs[0], text);
338
+ // CRITICAL: All other paragraphs remain completely untouched
339
+ // They keep their original text, styles, runs, and all formatting
340
+ // This ensures multi-paragraph cells maintain their full structure
341
+ }
342
+ // ═══════════════════════════════════════════════════════════════════════
343
+ // Run-level formatting helpers
344
+ // ═══════════════════════════════════════════════════════════════════════
345
+ /**
346
+ * Ensure a <w:r> element has w:rPr/w:color[@w:val=hex].
347
+ * Creates w:rPr and w:color if they don't exist.
348
+ * Only touches the colour — leaves every other run property intact.
349
+ */
350
+ export function ensureRunColor(run, hex) {
351
+ const doc = run.ownerDocument;
352
+ if (!doc)
353
+ return;
354
+ let rPr = findDirectChild(run, 'w:rPr');
355
+ if (!rPr) {
356
+ rPr = doc.createElement('w:rPr');
357
+ if (run.firstChild) {
358
+ run.insertBefore(rPr, run.firstChild);
359
+ }
360
+ else {
361
+ run.appendChild(rPr);
362
+ }
363
+ }
364
+ let colorEl = findDirectChild(rPr, 'w:color');
365
+ if (!colorEl) {
366
+ colorEl = doc.createElement('w:color');
367
+ rPr.appendChild(colorEl);
368
+ }
369
+ colorEl.setAttribute('w:val', hex);
370
+ }
371
+ /**
372
+ * Apply run-level colour to every <w:r> in a paragraph.
373
+ */
374
+ export function colorParagraphRuns(p, color) {
375
+ const runs = nodeListToArray(p.getElementsByTagName('w:r'));
376
+ for (const r of runs) {
377
+ ensureRunColor(r, color);
378
+ }
379
+ }
380
+ /**
381
+ * Apply bold / italic / color to every <w:r> in a paragraph.
382
+ * Preserves all existing w:rPr children; only modifies specified props.
383
+ */
384
+ export function styleParagraphRuns(p, style) {
385
+ const doc = p.ownerDocument;
386
+ if (!doc)
387
+ return;
388
+ const runs = nodeListToArray(p.getElementsByTagName('w:r'));
389
+ for (const r of runs) {
390
+ let rPr = findDirectChild(r, 'w:rPr');
391
+ if (!rPr) {
392
+ rPr = doc.createElement('w:rPr');
393
+ if (r.firstChild) {
394
+ r.insertBefore(rPr, r.firstChild);
395
+ }
396
+ else {
397
+ r.appendChild(rPr);
398
+ }
399
+ }
400
+ if (style.color) {
401
+ let colorNode = findDirectChild(rPr, 'w:color');
402
+ if (!colorNode) {
403
+ colorNode = doc.createElement('w:color');
404
+ rPr.appendChild(colorNode);
405
+ }
406
+ colorNode.setAttribute('w:val', style.color);
407
+ }
408
+ if (style.bold !== undefined) {
409
+ toggleElement(doc, rPr, 'w:b', style.bold);
410
+ }
411
+ if (style.italic !== undefined) {
412
+ toggleElement(doc, rPr, 'w:i', style.italic);
413
+ }
414
+ }
415
+ }
416
+ // ═══════════════════════════════════════════════════════════════════════
417
+ // Counting helpers
418
+ // ═══════════════════════════════════════════════════════════════════════
419
+ /** Count direct w:tbl children of body. */
420
+ export function countTables(children) {
421
+ return children.filter((ch) => ch.nodeName === 'w:tbl').length;
422
+ }
423
+ /** Count <w:drawing> descendants (rough image count). */
424
+ export function countImages(body) {
425
+ return body.getElementsByTagName('w:drawing').length;
426
+ }
427
+ // ═══════════════════════════════════════════════════════════════════════
428
+ // Private helpers (DRY: used by multiple public functions)
429
+ // ═══════════════════════════════════════════════════════════════════════
430
+ /** Find the first direct child element with the given nodeName. */
431
+ function findDirectChild(parent, nodeName) {
432
+ for (const child of nodeListToArray(parent.childNodes)) {
433
+ if (child.nodeType === 1 && child.nodeName === nodeName) {
434
+ return child;
435
+ }
436
+ }
437
+ return null;
438
+ }
439
+ /** Add or remove a simple flag element (e.g. w:b, w:i) inside a parent. */
440
+ function toggleElement(doc, parent, nodeName, enabled) {
441
+ const existing = findDirectChild(parent, nodeName);
442
+ if (enabled && !existing) {
443
+ parent.appendChild(doc.createElement(nodeName));
444
+ }
445
+ else if (!enabled && existing) {
446
+ parent.removeChild(existing);
447
+ }
448
+ }
@@ -1,14 +1,10 @@
1
1
  /**
2
- * DOCX Operations LibraryPublic API
3
- *
4
- * Re-exports only the symbols that external consumers need.
5
- * Internal modules (styled-html-parser, validators, converters, etc.)
6
- * are consumed by sibling files and are NOT part of the public surface.
7
- *
8
- * @module docx
2
+ * DOCX file manipulation tools barrel exports.
9
3
  */
10
- export { parseDocxToHtml } from './html.js';
11
- export { createDocxFromHtml } from './builders/html-builder.js';
12
- export { editDocxWithOperations } from './operations/index.js';
13
- export type { DocxParseResult, DocxMetadata, DocxImage, DocxSection, DocxOperation, DocxDocumentDefaults, } from './types.js';
14
- export { DocxError, DocxErrorCode } from './errors.js';
4
+ export { readDocxOutline } from './read.js';
5
+ export { writeDocxPatched } from './write.js';
6
+ export { createDocxNew } from './create.js';
7
+ export type { DocxContentStructure, DocxContentItem, DocxContentParagraph, DocxContentTable, DocxContentImage, DocxTableCellContent, } from './types.js';
8
+ export { readDocx, extractTextFromDocx, getDocxMetadata, extractBodyXml } from './read.js';
9
+ export { writeDocx, modifyDocxContent, replaceBodyXml } from './modify.js';
10
+ export type { DocxMetadata, DocxParagraph, DocxRun, DocxModification, ParagraphOutline, TableOutline, ImageOutline, ReadDocxResult, WriteDocxStats, WriteDocxResult, BodySnapshot, DocxOp, OpResult, } from './types.js';
@@ -1,16 +1,10 @@
1
1
  /**
2
- * DOCX Operations LibraryPublic API
3
- *
4
- * Re-exports only the symbols that external consumers need.
5
- * Internal modules (styled-html-parser, validators, converters, etc.)
6
- * are consumed by sibling files and are NOT part of the public surface.
7
- *
8
- * @module docx
2
+ * DOCX file manipulation tools barrel exports.
9
3
  */
10
- // ── Reading ─────────────────────────────────────────────────────────────────
11
- export { parseDocxToHtml } from './html.js';
12
- // ── Writing / Editing ───────────────────────────────────────────────────────
13
- export { createDocxFromHtml } from './builders/html-builder.js';
14
- export { editDocxWithOperations } from './operations/index.js';
15
- // ── Errors ──────────────────────────────────────────────────────────────────
16
- export { DocxError, DocxErrorCode } from './errors.js';
4
+ // Patch-based tools (read_docx / write_docx)
5
+ export { readDocxOutline } from './read.js';
6
+ export { writeDocxPatched } from './write.js';
7
+ export { createDocxNew } from './create.js';
8
+ // Legacy functions (used by read_file, write_file, edit_block handlers)
9
+ export { readDocx, extractTextFromDocx, getDocxMetadata, extractBodyXml } from './read.js';
10
+ export { writeDocx, modifyDocxContent, replaceBodyXml } from './modify.js';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Legacy DOCX modification operations.
3
+ *
4
+ * These functions support the older write_file / edit_block paths that
5
+ * modify DOCX via simple operations (replace, insert, delete, style).
6
+ * They are distinct from the new patch-based writeDocxPatched pipeline.
7
+ *
8
+ * Single Responsibility: create / modify DOCX content using the legacy
9
+ * DocxModification interface. Delegates XML parsing and element
10
+ * manipulation to the shared dom.ts module.
11
+ */
12
+ import type { DocxModification } from './types.js';
13
+ /**
14
+ * Open an existing DOCX, apply an ordered list of modifications to
15
+ * word/document.xml, and write the result to outputPath.
16
+ * Every other file in the ZIP (styles, images, rels, …) is preserved.
17
+ */
18
+ export declare function modifyDocxContent(inputPath: string, outputPath: string, modifications: DocxModification[]): Promise<void>;
19
+ /**
20
+ * Replace the entire w:body content of a DOCX with new body XML.
21
+ * Used by the body-XML replacement mode of write_file.
22
+ */
23
+ export declare function replaceBodyXml(inputPath: string, outputPath: string, newBodyXml: string): Promise<void>;
24
+ /**
25
+ * Create a brand-new minimal DOCX from a plain-text string.
26
+ * Double-newlines are treated as paragraph separators.
27
+ */
28
+ export declare function writeDocx(outputPath: string, content: string | DocxModification[]): Promise<void>;