@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
@@ -1,57 +1,61 @@
1
1
  /**
2
- * DOCX Constants
3
- *
4
- * Centralised constants shared across the DOCX module.
5
- *
6
- * @module docx/constants
2
+ * DOCX constants — shared values used across the module.
7
3
  */
8
- // ─── Conversion Defaults ─────────────────────────────────────────────────────
9
- export const DEFAULT_CONVERSION_OPTIONS = {
10
- includeImages: true,
11
- preserveFormatting: true,
12
- styleMap: [],
13
- };
14
- // ─── Build (html-to-docx) Defaults ──────────────────────────────────────────
15
- export const DEFAULT_BUILD_OPTIONS = {
16
- font: 'Calibri',
17
- fontSize: 11,
18
- orientation: 'portrait',
19
- margins: {
20
- top: 1440,
21
- right: 1440,
22
- bottom: 1440,
23
- left: 1440,
24
- header: 720,
25
- footer: 720,
26
- gutter: 0,
27
- },
28
- footer: true,
29
- pageNumber: false,
4
+ // ═══════════════════════════════════════════════════════════════════════
5
+ // Image MIME types
6
+ // ═══════════════════════════════════════════════════════════════════════
7
+ export const IMAGE_MIME_TYPES = {
8
+ '.png': 'image/png',
9
+ '.jpg': 'image/jpeg',
10
+ '.jpeg': 'image/jpeg',
11
+ '.gif': 'image/gif',
12
+ '.bmp': 'image/bmp',
13
+ '.tiff': 'image/tiff',
14
+ '.tif': 'image/tiff',
15
+ '.svg': 'image/svg+xml',
16
+ '.webp': 'image/webp',
30
17
  };
31
- // ─── DOCX Internal XML Namespaces ────────────────────────────────────────────
32
- export const DOCX_NAMESPACES = {
33
- DUBLIN_CORE: 'dc',
34
- CUSTOM_PROPERTIES: 'cp',
35
- DCTERMS: 'dcterms',
18
+ export function getMimeType(ext) {
19
+ return IMAGE_MIME_TYPES[ext.toLowerCase()] ?? 'application/octet-stream';
20
+ }
21
+ // ═══════════════════════════════════════════════════════════════════════
22
+ // EMU conversion (English Metric Units)
23
+ // ═══════════════════════════════════════════════════════════════════════
24
+ /**
25
+ * Convert pixels to EMU (English Metric Units).
26
+ * 1 inch = 914400 EMU, 1 px ≈ 9525 EMU (at 96 DPI)
27
+ */
28
+ export const PX_TO_EMU = 9525;
29
+ export function pixelsToEmu(px) {
30
+ return px * PX_TO_EMU;
31
+ }
32
+ // ═══════════════════════════════════════════════════════════════════════
33
+ // XML namespaces
34
+ // ═══════════════════════════════════════════════════════════════════════
35
+ export const NAMESPACES = {
36
+ W: 'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
37
+ WP: 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing',
38
+ A: 'http://schemas.openxmlformats.org/drawingml/2006/main',
39
+ PIC: 'http://schemas.openxmlformats.org/drawingml/2006/picture',
40
+ R: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
41
+ RELS: 'http://schemas.openxmlformats.org/package/2006/relationships',
36
42
  };
37
- // ─── DOCX Archive Paths ─────────────────────────────────────────────────────
38
- export const CORE_PROPERTIES_PATH = 'docProps/core.xml';
39
- // ─── Image MIME Types (extension → MIME) ─────────────────────────────────────
40
- export const IMAGE_MIME_TYPES = {
41
- jpg: 'image/jpeg',
42
- jpeg: 'image/jpeg',
43
- gif: 'image/gif',
44
- bmp: 'image/bmp',
45
- webp: 'image/webp',
46
- png: 'image/png',
43
+ // ═══════════════════════════════════════════════════════════════════════
44
+ // Default values
45
+ // ═══════════════════════════════════════════════════════════════════════
46
+ export const DEFAULT_IMAGE_WIDTH = 300;
47
+ export const DEFAULT_IMAGE_HEIGHT = 200;
48
+ // ═══════════════════════════════════════════════════════════════════════
49
+ // File paths
50
+ // ═══════════════════════════════════════════════════════════════════════
51
+ export const DOCX_PATHS = {
52
+ CONTENT_TYPES: '[Content_Types].xml',
53
+ DOCUMENT_XML: 'word/document.xml',
54
+ DOCUMENT_RELS: 'word/_rels/document.xml.rels',
55
+ ROOT_RELS: '_rels/.rels',
56
+ STYLES_XML: 'word/styles.xml',
57
+ SETTINGS_XML: 'word/settings.xml',
58
+ WEB_SETTINGS_XML: 'word/webSettings.xml',
59
+ FONT_TABLE_XML: 'word/fontTable.xml',
60
+ MEDIA_FOLDER: 'word/media',
47
61
  };
48
- // ─── HTML Wrapper Template ───────────────────────────────────────────────────
49
- export const HTML_WRAPPER_TEMPLATE = `<!DOCTYPE html>
50
- <html>
51
- <head>
52
- <meta charset="UTF-8">
53
- </head>
54
- <body>
55
- {content}
56
- </body>
57
- </html>`;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * createDocxNew - Create a brand-new professional DOCX file from scratch.
3
+ *
4
+ * Single Responsibility: Build a complete, professional DOCX structure
5
+ * with proper styles, document defaults, and content from styled DOM structure.
6
+ *
7
+ * This function does NOT read any existing files - it creates everything
8
+ * from scratch with a professional structure.
9
+ */
10
+ import type { DocxContentStructure, WriteDocxResult } from './types.js';
11
+ /**
12
+ * Create a new professional DOCX file from content structure.
13
+ *
14
+ * This function creates a complete DOCX structure from scratch with:
15
+ * - Professional styles (Normal, Heading1-9, ListParagraph, TableGrid)
16
+ * - Document defaults (Calibri font, proper spacing)
17
+ * - Complete ZIP structure
18
+ *
19
+ * Then builds content from the provided structure.
20
+ */
21
+ export declare function createDocxNew(outputPath: string, content: DocxContentStructure): Promise<WriteDocxResult>;
@@ -0,0 +1,386 @@
1
+ /**
2
+ * createDocxNew - Create a brand-new professional DOCX file from scratch.
3
+ *
4
+ * Single Responsibility: Build a complete, professional DOCX structure
5
+ * with proper styles, document defaults, and content from styled DOM structure.
6
+ *
7
+ * This function does NOT read any existing files - it creates everything
8
+ * from scratch with a professional structure.
9
+ */
10
+ import fs from 'fs/promises';
11
+ import PizZip from 'pizzip';
12
+ import { parseXml, serializeXml, getBody } from './dom.js';
13
+ import { buildParagraph, buildTable, buildImageElement } from './builders/index.js';
14
+ /**
15
+ * Create a professional DOCX ZIP structure with:
16
+ * - Complete styles.xml (Normal, Heading1-9, etc.)
17
+ * - Document defaults (fonts, spacing, colors)
18
+ * - Proper relationships
19
+ * - Content types
20
+ * - Empty document body ready for content
21
+ */
22
+ function createProfessionalDocxZip() {
23
+ const zip = new PizZip();
24
+ // ─── [Content_Types].xml ──────────────────────────────────────────
25
+ zip.file('[Content_Types].xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
27
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
28
+ <Default Extension="xml" ContentType="application/xml"/>
29
+ <Default Extension="png" ContentType="image/png"/>
30
+ <Default Extension="jpg" ContentType="image/jpeg"/>
31
+ <Default Extension="jpeg" ContentType="image/jpeg"/>
32
+ <Default Extension="gif" ContentType="image/gif"/>
33
+ <Default Extension="bmp" ContentType="image/bmp"/>
34
+ <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
35
+ <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
36
+ </Types>`);
37
+ // ─── _rels/.rels ──────────────────────────────────────────────────
38
+ zip.folder('_rels')?.file('.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
39
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
40
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
41
+ </Relationships>`);
42
+ // ─── word/_rels/document.xml.rels ────────────────────────────────
43
+ zip.folder('word')?.folder('_rels')?.file('document.xml.rels', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
44
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
45
+ </Relationships>`);
46
+ // ─── word/styles.xml ───────────────────────────────────────────────
47
+ zip.file('word/styles.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
48
+ <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
49
+ <!-- Document Defaults -->
50
+ <w:docDefaults>
51
+ <w:rPrDefault>
52
+ <w:rPr>
53
+ <w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:eastAsia="Calibri" w:cs="Calibri"/>
54
+ <w:sz w:val="22"/>
55
+ <w:szCs w:val="22"/>
56
+ <w:color w:val="000000"/>
57
+ </w:rPr>
58
+ </w:rPrDefault>
59
+ <w:pPrDefault>
60
+ <w:pPr>
61
+ <w:spacing w:after="200" w:line="276" w:lineRule="auto"/>
62
+ </w:pPr>
63
+ </w:pPrDefault>
64
+ </w:docDefaults>
65
+
66
+ <!-- Normal Style -->
67
+ <w:style w:type="paragraph" w:styleId="Normal">
68
+ <w:name w:val="Normal"/>
69
+ <w:qFormat/>
70
+ <w:pPr>
71
+ <w:spacing w:after="200" w:line="276" w:lineRule="auto"/>
72
+ </w:pPr>
73
+ <w:rPr>
74
+ <w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:eastAsia="Calibri" w:cs="Calibri"/>
75
+ <w:sz w:val="22"/>
76
+ <w:szCs w:val="22"/>
77
+ <w:color w:val="000000"/>
78
+ </w:rPr>
79
+ </w:style>
80
+
81
+ <!-- Heading Styles -->
82
+ <w:style w:type="paragraph" w:styleId="Heading1">
83
+ <w:name w:val="Heading 1"/>
84
+ <w:basedOn w:val="Normal"/>
85
+ <w:next w:val="Normal"/>
86
+ <w:qFormat/>
87
+ <w:pPr>
88
+ <w:keepNext/>
89
+ <w:spacing w:before="480" w:after="0"/>
90
+ <w:outlineLvl w:val="0"/>
91
+ </w:pPr>
92
+ <w:rPr>
93
+ <w:rFonts w:ascii="Calibri Light" w:hAnsi="Calibri Light" w:eastAsia="Calibri Light" w:cs="Calibri Light"/>
94
+ <w:b/>
95
+ <w:bCs/>
96
+ <w:sz w:val="32"/>
97
+ <w:szCs w:val="32"/>
98
+ <w:color w:val="2F5496"/>
99
+ </w:rPr>
100
+ </w:style>
101
+
102
+ <w:style w:type="paragraph" w:styleId="Heading2">
103
+ <w:name w:val="Heading 2"/>
104
+ <w:basedOn w:val="Normal"/>
105
+ <w:next w:val="Normal"/>
106
+ <w:qFormat/>
107
+ <w:pPr>
108
+ <w:keepNext/>
109
+ <w:spacing w:before="240" w:after="0"/>
110
+ <w:outlineLvl w:val="1"/>
111
+ </w:pPr>
112
+ <w:rPr>
113
+ <w:rFonts w:ascii="Calibri Light" w:hAnsi="Calibri Light" w:eastAsia="Calibri Light" w:cs="Calibri Light"/>
114
+ <w:b/>
115
+ <w:bCs/>
116
+ <w:sz w:val="28"/>
117
+ <w:szCs w:val="28"/>
118
+ <w:color w:val="2F5496"/>
119
+ </w:rPr>
120
+ </w:style>
121
+
122
+ <w:style w:type="paragraph" w:styleId="Heading3">
123
+ <w:name w:val="Heading 3"/>
124
+ <w:basedOn w:val="Normal"/>
125
+ <w:next w:val="Normal"/>
126
+ <w:qFormat/>
127
+ <w:pPr>
128
+ <w:keepNext/>
129
+ <w:spacing w:before="240" w:after="0"/>
130
+ <w:outlineLvl w:val="2"/>
131
+ </w:pPr>
132
+ <w:rPr>
133
+ <w:b/>
134
+ <w:bCs/>
135
+ <w:sz w:val="24"/>
136
+ <w:szCs w:val="24"/>
137
+ <w:color w:val="1F3763"/>
138
+ </w:rPr>
139
+ </w:style>
140
+
141
+ <w:style w:type="paragraph" w:styleId="Heading4">
142
+ <w:name w:val="Heading 4"/>
143
+ <w:basedOn w:val="Normal"/>
144
+ <w:next w:val="Normal"/>
145
+ <w:qFormat/>
146
+ <w:pPr>
147
+ <w:keepNext/>
148
+ <w:spacing w:before="240" w:after="0"/>
149
+ <w:outlineLvl w:val="3"/>
150
+ </w:pPr>
151
+ <w:rPr>
152
+ <w:b/>
153
+ <w:bCs/>
154
+ <w:sz w:val="22"/>
155
+ <w:szCs w:val="22"/>
156
+ <w:color w:val="1F3763"/>
157
+ </w:rPr>
158
+ </w:style>
159
+
160
+ <w:style w:type="paragraph" w:styleId="Heading5">
161
+ <w:name w:val="Heading 5"/>
162
+ <w:basedOn w:val="Normal"/>
163
+ <w:next w:val="Normal"/>
164
+ <w:qFormat/>
165
+ <w:pPr>
166
+ <w:keepNext/>
167
+ <w:spacing w:before="240" w:after="0"/>
168
+ <w:outlineLvl w:val="4"/>
169
+ </w:pPr>
170
+ <w:rPr>
171
+ <w:b/>
172
+ <w:bCs/>
173
+ <w:sz w:val="22"/>
174
+ <w:szCs w:val="22"/>
175
+ <w:color w:val="1F3763"/>
176
+ </w:rPr>
177
+ </w:style>
178
+
179
+ <w:style w:type="paragraph" w:styleId="Heading6">
180
+ <w:name w:val="Heading 6"/>
181
+ <w:basedOn w:val="Normal"/>
182
+ <w:next w:val="Normal"/>
183
+ <w:qFormat/>
184
+ <w:pPr>
185
+ <w:keepNext/>
186
+ <w:spacing w:before="240" w:after="0"/>
187
+ <w:outlineLvl w:val="5"/>
188
+ </w:pPr>
189
+ <w:rPr>
190
+ <w:b/>
191
+ <w:bCs/>
192
+ <w:sz w:val="22"/>
193
+ <w:szCs w:val="22"/>
194
+ <w:color w:val="1F3763"/>
195
+ </w:rPr>
196
+ </w:style>
197
+
198
+ <w:style w:type="paragraph" w:styleId="Heading7">
199
+ <w:name w:val="Heading 7"/>
200
+ <w:basedOn w:val="Normal"/>
201
+ <w:next w:val="Normal"/>
202
+ <w:qFormat/>
203
+ <w:pPr>
204
+ <w:keepNext/>
205
+ <w:spacing w:before="240" w:after="0"/>
206
+ <w:outlineLvl w:val="6"/>
207
+ </w:pPr>
208
+ <w:rPr>
209
+ <w:b/>
210
+ <w:bCs/>
211
+ <w:sz w:val="22"/>
212
+ <w:szCs w:val="22"/>
213
+ <w:color w:val="1F3763"/>
214
+ </w:rPr>
215
+ </w:style>
216
+
217
+ <w:style w:type="paragraph" w:styleId="Heading8">
218
+ <w:name w:val="Heading 8"/>
219
+ <w:basedOn w:val="Normal"/>
220
+ <w:next w:val="Normal"/>
221
+ <w:qFormat/>
222
+ <w:pPr>
223
+ <w:keepNext/>
224
+ <w:spacing w:before="240" w:after="0"/>
225
+ <w:outlineLvl w:val="7"/>
226
+ </w:pPr>
227
+ <w:rPr>
228
+ <w:b/>
229
+ <w:bCs/>
230
+ <w:sz w:val="22"/>
231
+ <w:szCs w:val="22"/>
232
+ <w:color w:val="1F3763"/>
233
+ </w:rPr>
234
+ </w:style>
235
+
236
+ <w:style w:type="paragraph" w:styleId="Heading9">
237
+ <w:name w:val="Heading 9"/>
238
+ <w:basedOn w:val="Normal"/>
239
+ <w:next w:val="Normal"/>
240
+ <w:qFormat/>
241
+ <w:pPr>
242
+ <w:keepNext/>
243
+ <w:spacing w:before="240" w:after="0"/>
244
+ <w:outlineLvl w:val="8"/>
245
+ </w:pPr>
246
+ <w:rPr>
247
+ <w:b/>
248
+ <w:bCs/>
249
+ <w:sz w:val="22"/>
250
+ <w:szCs w:val="22"/>
251
+ <w:color w:val="1F3763"/>
252
+ </w:rPr>
253
+ </w:style>
254
+
255
+ <!-- List Styles -->
256
+ <w:style w:type="paragraph" w:styleId="ListParagraph">
257
+ <w:name w:val="List Paragraph"/>
258
+ <w:basedOn w:val="Normal"/>
259
+ <w:qFormat/>
260
+ <w:pPr>
261
+ <w:ind w:left="720"/>
262
+ </w:pPr>
263
+ </w:style>
264
+
265
+ <!-- Table Styles -->
266
+ <w:style w:type="table" w:styleId="TableGrid">
267
+ <w:name w:val="Table Grid"/>
268
+ <w:basedOn w:val="NormalTable"/>
269
+ <w:qFormat/>
270
+ <w:tblPr>
271
+ <w:tblBorders>
272
+ <w:top w:val="single" w:sz="4" w:space="0" w:color="000000"/>
273
+ <w:left w:val="single" w:sz="4" w:space="0" w:color="000000"/>
274
+ <w:bottom w:val="single" w:sz="4" w:space="0" w:color="000000"/>
275
+ <w:right w:val="single" w:sz="4" w:space="0" w:color="000000"/>
276
+ <w:insideH w:val="single" w:sz="4" w:space="0" w:color="000000"/>
277
+ <w:insideV w:val="single" w:sz="4" w:space="0" w:color="000000"/>
278
+ </w:tblBorders>
279
+ </w:tblPr>
280
+ </w:style>
281
+ </w:styles>`);
282
+ // ─── word/document.xml ─────────────────────────────────────────────
283
+ zip.file('word/document.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
284
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
285
+ <w:body>
286
+ </w:body>
287
+ </w:document>`);
288
+ // ─── word/settings.xml ────────────────────────────────────────────
289
+ zip.file('word/settings.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
290
+ <w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
291
+ <w:defaultTabStop w:val="720"/>
292
+ <w:compat>
293
+ <w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/>
294
+ </w:compat>
295
+ </w:settings>`);
296
+ // ─── word/webSettings.xml ────────────────────────────────────────
297
+ zip.file('word/webSettings.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
298
+ <w:webSettings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
299
+ <w:optimizeForBrowser/>
300
+ </w:webSettings>`);
301
+ // ─── word/fontTable.xml ───────────────────────────────────────────
302
+ zip.file('word/fontTable.xml', `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
303
+ <w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
304
+ <w:font w:name="Calibri">
305
+ <w:panose1 w:val="020F0502020204030204"/>
306
+ <w:charset w:val="00"/>
307
+ <w:family w:val="swiss"/>
308
+ <w:pitch w:val="variable"/>
309
+ <w:sig w:usb0="E00002FF" w:usb1="4000ACFF" w:usb2="00000001" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000"/>
310
+ </w:font>
311
+ <w:font w:name="Calibri Light">
312
+ <w:panose1 w:val="020F0302020204030204"/>
313
+ <w:charset w:val="00"/>
314
+ <w:family w:val="swiss"/>
315
+ <w:pitch w:val="variable"/>
316
+ <w:sig w:usb0="E00002FF" w:usb1="4000ACFF" w:usb2="00000001" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000"/>
317
+ </w:font>
318
+ <w:font w:name="Times New Roman">
319
+ <w:panose1 w:val="02020603050405020304"/>
320
+ <w:charset w:val="00"/>
321
+ <w:family w:val="roman"/>
322
+ <w:pitch w:val="variable"/>
323
+ <w:sig w:usb0="E0002AFF" w:usb1="C0007841" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/>
324
+ </w:font>
325
+ </w:fonts>`);
326
+ // ─── Create media folder ───────────────────────────────────────────
327
+ zip.folder('word')?.folder('media');
328
+ return zip;
329
+ }
330
+ // ─── Content builders are now in ./builders/index.js ───────────────────
331
+ /**
332
+ * Create a new professional DOCX file from content structure.
333
+ *
334
+ * This function creates a complete DOCX structure from scratch with:
335
+ * - Professional styles (Normal, Heading1-9, ListParagraph, TableGrid)
336
+ * - Document defaults (Calibri font, proper spacing)
337
+ * - Complete ZIP structure
338
+ *
339
+ * Then builds content from the provided structure.
340
+ */
341
+ export async function createDocxNew(outputPath, content) {
342
+ // 1. Create professional DOCX ZIP structure
343
+ const zip = createProfessionalDocxZip();
344
+ // 2. Parse empty document.xml
345
+ const xmlStr = zip.file('word/document.xml').asText();
346
+ const doc = parseXml(xmlStr);
347
+ const body = getBody(doc);
348
+ // 3. Build content from structure
349
+ let tableCount = 0;
350
+ for (const item of content.items) {
351
+ if (item.type === 'paragraph') {
352
+ const p = buildParagraph(doc, item);
353
+ body.appendChild(p);
354
+ }
355
+ else if (item.type === 'table') {
356
+ const tbl = buildTable(doc, item);
357
+ body.appendChild(tbl);
358
+ tableCount++;
359
+ }
360
+ else if (item.type === 'image') {
361
+ const imgP = await buildImageElement(doc, zip, item);
362
+ body.appendChild(imgP);
363
+ }
364
+ }
365
+ // 4. Serialize and save
366
+ const newXml = serializeXml(doc);
367
+ zip.file('word/document.xml', newXml);
368
+ const buf = zip.generate({ type: 'nodebuffer' });
369
+ await fs.writeFile(outputPath, buf);
370
+ // 5. Build stats
371
+ const bodyChildCount = content.items.length;
372
+ const stats = {
373
+ tablesBefore: 0,
374
+ tablesAfter: tableCount,
375
+ bodyChildrenBefore: 0,
376
+ bodyChildrenAfter: bodyChildCount,
377
+ bodySignatureBefore: '',
378
+ bodySignatureAfter: '', // Could compute if needed
379
+ };
380
+ return {
381
+ outputPath,
382
+ results: [], // No ops were applied
383
+ stats,
384
+ warnings: [],
385
+ };
386
+ }
@@ -0,0 +1,139 @@
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
+ export declare function parseXml(xmlStr: string): Document;
11
+ export declare function serializeXml(doc: Document): string;
12
+ /**
13
+ * Convert any NodeList / HTMLCollection-like object into a real array.
14
+ */
15
+ export declare function nodeListToArray<T extends Node = Node>(nl: NodeListOf<T> | NodeList | {
16
+ length: number;
17
+ item(index: number): T | null;
18
+ }): T[];
19
+ /** Return the single <w:body> element from a parsed document.xml DOM. */
20
+ export declare function getBody(doc: Document): Element;
21
+ /**
22
+ * Return ALL direct element children of w:body **in document order**.
23
+ * Includes w:p, w:tbl, w:sdt, w:sectPr, etc.
24
+ */
25
+ export declare function getBodyChildren(body: Element): Element[];
26
+ /**
27
+ * Return ALL top‑level tables that are logically in the body, including those
28
+ * wrapped in structured document tags (w:sdt / w:sdtContent).
29
+ *
30
+ * Previous logic only saw tables that were direct children of <w:body>. That
31
+ * meant tables inside SDTs were invisible to table operations and readDocxOutline.
32
+ * This helper walks the body tree and collects any <w:tbl> that appears as a
33
+ * *first‑class* block (we do not recurse into tables themselves, so nested
34
+ * tables are not double‑counted).
35
+ */
36
+ export declare function getAllBodyTables(body: Element): Element[];
37
+ /**
38
+ * Build a compact signature string from the body children array.
39
+ * Maps each node's qualified name to a short local name:
40
+ * w:p → p, w:tbl → tbl, w:sdt → sdt, w:sectPr → sectPr, …
41
+ * Returns e.g. "p,tbl,p,p,sectPr".
42
+ */
43
+ export declare function bodySignature(children: Element[]): string;
44
+ /** Concatenate text from every <w:t> descendant of a paragraph. */
45
+ export declare function getParagraphText(p: Element): string;
46
+ /** Read the style id from w:pPr/w:pStyle/@w:val, or null if absent. */
47
+ export declare function getParagraphStyle(p: Element): string | null;
48
+ /**
49
+ * Extract all text content from a table cell (w:tc).
50
+ * Returns the concatenated text from all paragraphs in the cell.
51
+ */
52
+ export declare function getCellText(tc: Element): string;
53
+ /**
54
+ * Extract all rows from a table (w:tbl).
55
+ * Returns an array of rows, where each row is an array of cell text strings.
56
+ * First row is treated as header if it exists.
57
+ */
58
+ export declare function getTableContent(tbl: Element): {
59
+ headers?: string[];
60
+ rows: string[][];
61
+ };
62
+ /**
63
+ * Get table style from w:tblPr/w:tblStyle/@w:val, or null if absent.
64
+ */
65
+ export declare function getTableStyle(tbl: Element): string | null;
66
+ /**
67
+ * Extract image reference from a w:drawing element.
68
+ * Returns the relationship ID (rId) and media file path if found.
69
+ */
70
+ export declare function getImageReference(drawing: Element): {
71
+ rId: string | null;
72
+ mediaPath: string | null;
73
+ };
74
+ /**
75
+ * Replace the text of a paragraph with minimal DOM changes.
76
+ * Sets the FIRST w:t to `text`, clears every subsequent w:t.
77
+ * Sets xml:space="preserve" so leading/trailing spaces survive.
78
+ * Does NOT remove/recreate runs or remove paragraph properties.
79
+ *
80
+ * WARNING: This function does NOT preserve multiple runs with different styles.
81
+ * Use setParagraphTextPreservingStyles() for cells with multiple styled runs.
82
+ */
83
+ export declare function setParagraphTextMinimal(p: Element, text: string): void;
84
+ /**
85
+ * Replace paragraph text while preserving all run styles.
86
+ *
87
+ * This function preserves the structure of all runs (w:r) and their
88
+ * properties (w:rPr), distributing the new text across existing runs.
89
+ *
90
+ * Strategy:
91
+ * 1. Collect all runs with their properties
92
+ * 2. Distribute new text across runs (preserving run count and styles)
93
+ * 3. If new text is longer, extend the last run
94
+ * 4. If new text is shorter, clear excess runs but keep their structure
95
+ */
96
+ export declare function setParagraphTextPreservingStyles(p: Element, text: string): void;
97
+ /**
98
+ * Replace cell text while preserving ALL paragraphs and their styles.
99
+ *
100
+ * This function works at the cell level:
101
+ * - Preserves ALL paragraphs in the cell (doesn't remove any)
102
+ * - Updates text in the first paragraph while preserving its styles
103
+ * - Keeps all other paragraphs intact with their original text and styles
104
+ *
105
+ * This ensures that cells with multiple paragraphs (each with different
106
+ * styles, font sizes, etc.) maintain their structure after text replacement.
107
+ *
108
+ * Example: If a cell has:
109
+ * - Paragraph 1: "LAWN AND LANDSCAPE" (Heading1 style, large font, red color)
110
+ * - Paragraph 2: "Take your weekends back..." (Normal style, smaller font, black color)
111
+ *
112
+ * Replacing with "EARTH AND MOUNTAIN" will:
113
+ * - Update paragraph 1 to "EARTH AND MOUNTAIN" (preserving Heading1 style, large font, red color)
114
+ * - Keep paragraph 2 completely intact with its original text and style
115
+ */
116
+ export declare function setCellTextPreservingStyles(tc: Element, text: string): void;
117
+ /**
118
+ * Ensure a <w:r> element has w:rPr/w:color[@w:val=hex].
119
+ * Creates w:rPr and w:color if they don't exist.
120
+ * Only touches the colour — leaves every other run property intact.
121
+ */
122
+ export declare function ensureRunColor(run: Element, hex: string): void;
123
+ /**
124
+ * Apply run-level colour to every <w:r> in a paragraph.
125
+ */
126
+ export declare function colorParagraphRuns(p: Element, color: string): void;
127
+ /**
128
+ * Apply bold / italic / color to every <w:r> in a paragraph.
129
+ * Preserves all existing w:rPr children; only modifies specified props.
130
+ */
131
+ export declare function styleParagraphRuns(p: Element, style: {
132
+ color?: string;
133
+ bold?: boolean;
134
+ italic?: boolean;
135
+ }): void;
136
+ /** Count direct w:tbl children of body. */
137
+ export declare function countTables(children: Element[]): number;
138
+ /** Count <w:drawing> descendants (rough image count). */
139
+ export declare function countImages(body: Element): number;