@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.
- package/README.md +3 -0
- package/dist/handlers/filesystem-handlers.js +58 -11
- package/dist/handlers/history-handlers.d.ts +7 -0
- package/dist/handlers/history-handlers.js +33 -1
- package/dist/remote-device/remote-channel.d.ts +8 -3
- package/dist/remote-device/remote-channel.js +68 -21
- package/dist/search-manager.d.ts +13 -0
- package/dist/search-manager.js +146 -0
- package/dist/server.js +56 -4
- package/dist/test-docx.d.ts +1 -0
- package/dist/tools/docx/builders/image.d.ts +14 -0
- package/dist/tools/docx/builders/image.js +84 -0
- package/dist/tools/docx/builders/index.d.ts +9 -3
- package/dist/tools/docx/builders/index.js +9 -3
- package/dist/tools/docx/builders/paragraph.d.ts +12 -0
- package/dist/tools/docx/builders/paragraph.js +29 -0
- package/dist/tools/docx/builders/table.d.ts +10 -0
- package/dist/tools/docx/builders/table.js +138 -0
- package/dist/tools/docx/builders/utils.d.ts +5 -0
- package/dist/tools/docx/builders/utils.js +18 -0
- package/dist/tools/docx/constants.d.ts +28 -32
- package/dist/tools/docx/constants.js +56 -52
- package/dist/tools/docx/create.d.ts +21 -0
- package/dist/tools/docx/create.js +386 -0
- package/dist/tools/docx/dom.d.ts +139 -0
- package/dist/tools/docx/dom.js +448 -0
- package/dist/tools/docx/index.d.ts +8 -12
- package/dist/tools/docx/index.js +8 -14
- package/dist/tools/docx/modify.d.ts +28 -0
- package/dist/tools/docx/modify.js +271 -0
- package/dist/tools/docx/ops/delete-paragraph-at-body-index.d.ts +11 -0
- package/dist/tools/docx/ops/delete-paragraph-at-body-index.js +23 -0
- package/dist/tools/docx/ops/header-replace-text-exact.d.ts +13 -0
- package/dist/tools/docx/ops/header-replace-text-exact.js +55 -0
- package/dist/tools/docx/ops/index.d.ts +17 -0
- package/dist/tools/docx/ops/index.js +70 -0
- package/dist/tools/docx/ops/insert-image-after-text.d.ts +24 -0
- package/dist/tools/docx/ops/insert-image-after-text.js +128 -0
- package/dist/tools/docx/ops/insert-paragraph-after-text.d.ts +12 -0
- package/dist/tools/docx/ops/insert-paragraph-after-text.js +74 -0
- package/dist/tools/docx/ops/insert-table-after-text.d.ts +19 -0
- package/dist/tools/docx/ops/insert-table-after-text.js +57 -0
- package/dist/tools/docx/ops/replace-hyperlink-url.d.ts +12 -0
- package/dist/tools/docx/ops/replace-hyperlink-url.js +37 -0
- package/dist/tools/docx/ops/replace-paragraph-at-body-index.d.ts +9 -0
- package/dist/tools/docx/ops/replace-paragraph-at-body-index.js +25 -0
- package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +21 -0
- package/dist/tools/docx/ops/replace-paragraph-text-exact.js +36 -0
- package/dist/tools/docx/ops/replace-table-cell-text.d.ts +25 -0
- package/dist/tools/docx/ops/replace-table-cell-text.js +85 -0
- package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +9 -0
- package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +24 -0
- package/dist/tools/docx/ops/set-color-for-style.d.ts +13 -0
- package/dist/tools/docx/ops/set-color-for-style.js +31 -0
- package/dist/tools/docx/ops/set-paragraph-style-at-body-index.d.ts +8 -0
- package/dist/tools/docx/ops/set-paragraph-style-at-body-index.js +57 -0
- package/dist/tools/docx/ops/table-set-cell-text.d.ts +9 -0
- package/dist/tools/docx/ops/table-set-cell-text.js +40 -0
- package/dist/tools/docx/read.d.ts +27 -0
- package/dist/tools/docx/read.js +308 -0
- package/dist/tools/docx/relationships.d.ts +22 -0
- package/dist/tools/docx/relationships.js +76 -0
- package/dist/tools/docx/types.d.ts +202 -103
- package/dist/tools/docx/types.js +2 -5
- package/dist/tools/docx/validate.d.ts +33 -0
- package/dist/tools/docx/validate.js +49 -0
- package/dist/tools/docx/write.d.ts +17 -0
- package/dist/tools/docx/write.js +88 -0
- package/dist/tools/docx/xml-view-test.d.ts +1 -0
- package/dist/tools/docx/xml-view-test.js +63 -0
- package/dist/tools/docx/xml-view.d.ts +56 -0
- package/dist/tools/docx/xml-view.js +169 -0
- package/dist/tools/docx/zip.d.ts +21 -0
- package/dist/tools/docx/zip.js +35 -0
- package/dist/tools/edit.js +57 -27
- package/dist/tools/schemas.d.ts +13 -0
- package/dist/tools/schemas.js +6 -1
- package/dist/types.d.ts +10 -0
- package/dist/ui/contracts.d.ts +14 -0
- package/dist/ui/contracts.js +18 -0
- package/dist/ui/file-preview/index.html +16 -0
- package/dist/ui/file-preview/preview-runtime.js +13983 -0
- package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
- package/dist/ui/file-preview/shared/preview-file-types.js +57 -0
- package/dist/ui/file-preview/src/app.d.ts +4 -0
- package/dist/ui/file-preview/src/app.js +800 -0
- package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
- package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
- package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
- package/dist/ui/file-preview/src/components/highlighting.js +54 -0
- package/dist/ui/file-preview/src/components/html-renderer.d.ts +9 -0
- package/dist/ui/file-preview/src/components/html-renderer.js +63 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.js +21 -0
- package/dist/ui/file-preview/src/components/toolbar.d.ts +6 -0
- package/dist/ui/file-preview/src/components/toolbar.js +75 -0
- package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
- package/dist/ui/file-preview/src/image-preview.js +21 -0
- package/dist/ui/file-preview/src/main.d.ts +1 -0
- package/dist/ui/file-preview/src/main.js +5 -0
- package/dist/ui/file-preview/src/types.d.ts +1 -0
- package/dist/ui/file-preview/src/types.js +1 -0
- package/dist/ui/file-preview/styles.css +764 -0
- package/dist/ui/resources.d.ts +21 -0
- package/dist/ui/resources.js +72 -0
- package/dist/ui/shared/escape-html.d.ts +4 -0
- package/dist/ui/shared/escape-html.js +11 -0
- package/dist/ui/shared/host-lifecycle.d.ts +16 -0
- package/dist/ui/shared/host-lifecycle.js +35 -0
- package/dist/ui/shared/rpc-client.d.ts +14 -0
- package/dist/ui/shared/rpc-client.js +72 -0
- package/dist/ui/shared/theme-adaptation.d.ts +10 -0
- package/dist/ui/shared/theme-adaptation.js +118 -0
- package/dist/ui/shared/tool-header.d.ts +9 -0
- package/dist/ui/shared/tool-header.js +25 -0
- package/dist/ui/shared/tool-shell.d.ts +16 -0
- package/dist/ui/shared/tool-shell.js +65 -0
- package/dist/ui/shared/widget-state.d.ts +28 -0
- package/dist/ui/shared/widget-state.js +60 -0
- package/dist/utils/capture.d.ts +1 -0
- package/dist/utils/capture.js +176 -8
- package/dist/utils/files/base.d.ts +3 -1
- package/dist/utils/files/docx.d.ts +28 -22
- package/dist/utils/files/docx.js +630 -196
- package/dist/utils/files/factory.d.ts +6 -5
- package/dist/utils/files/factory.js +18 -6
- package/dist/utils/files/text.js +9 -1
- package/dist/utils/system-info.js +1 -1
- package/dist/utils/usageTracker.js +5 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -2
|
@@ -1,114 +1,213 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DOCX
|
|
3
|
-
*
|
|
4
|
-
* Centralised type definitions for every DOCX operation in the module.
|
|
5
|
-
*
|
|
6
|
-
* @module docx/types
|
|
2
|
+
* Type definitions for DOCX operations.
|
|
3
|
+
* Single source of truth for every type used across the DOCX module.
|
|
7
4
|
*/
|
|
8
|
-
/** Default font and size extracted from the original DOCX, used to preserve styles on re-creation. */
|
|
9
|
-
export interface DocxDocumentDefaults {
|
|
10
|
-
/** Default font family (e.g. 'Calibri', 'Times New Roman') */
|
|
11
|
-
font: string;
|
|
12
|
-
/** Default font size in points (e.g. 11, 12) */
|
|
13
|
-
fontSize: number;
|
|
14
|
-
}
|
|
15
5
|
export interface DocxMetadata {
|
|
16
6
|
title?: string;
|
|
17
7
|
author?: string;
|
|
18
8
|
subject?: string;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
creator?: string;
|
|
10
|
+
paragraphCount: number;
|
|
11
|
+
wordCount: number;
|
|
12
|
+
}
|
|
13
|
+
export interface DocxParagraph {
|
|
14
|
+
index: number;
|
|
15
|
+
text: string;
|
|
16
|
+
hasText: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface DocxRun {
|
|
19
|
+
text: string;
|
|
20
|
+
bold?: boolean;
|
|
21
|
+
italic?: boolean;
|
|
22
|
+
color?: string;
|
|
23
|
+
fontSize?: number;
|
|
24
|
+
fontName?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface DocxModification {
|
|
27
|
+
type: 'replace' | 'insert' | 'delete' | 'style';
|
|
28
|
+
paragraphIndex?: number;
|
|
29
|
+
findText?: string;
|
|
30
|
+
replaceText?: string;
|
|
31
|
+
insertText?: string;
|
|
32
|
+
style?: {
|
|
33
|
+
color?: string;
|
|
34
|
+
bold?: boolean;
|
|
35
|
+
italic?: boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface ParagraphOutline {
|
|
39
|
+
bodyChildIndex: number;
|
|
40
|
+
paragraphIndex: number;
|
|
41
|
+
style: string | null;
|
|
42
|
+
text: string;
|
|
43
|
+
}
|
|
44
|
+
export interface TableOutline {
|
|
45
|
+
bodyChildIndex: number;
|
|
46
|
+
tableIndex: number;
|
|
47
|
+
style: string | null;
|
|
48
|
+
headers?: string[];
|
|
49
|
+
rows: string[][];
|
|
50
|
+
}
|
|
51
|
+
export interface ImageOutline {
|
|
52
|
+
bodyChildIndex: number;
|
|
53
|
+
imageIndex: number;
|
|
54
|
+
mediaPath: string;
|
|
55
|
+
rId: string;
|
|
31
56
|
altText?: string;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
export interface
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
export interface
|
|
65
|
-
type: '
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
57
|
+
}
|
|
58
|
+
export interface ReadDocxResult {
|
|
59
|
+
path: string;
|
|
60
|
+
paragraphs: ParagraphOutline[];
|
|
61
|
+
tables: TableOutline[];
|
|
62
|
+
images: ImageOutline[];
|
|
63
|
+
stylesSeen: string[];
|
|
64
|
+
counts: {
|
|
65
|
+
tables: number;
|
|
66
|
+
images: number;
|
|
67
|
+
bodyChildren: number;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface WriteDocxStats {
|
|
71
|
+
tablesBefore: number;
|
|
72
|
+
tablesAfter: number;
|
|
73
|
+
bodyChildrenBefore: number;
|
|
74
|
+
bodyChildrenAfter: number;
|
|
75
|
+
bodySignatureBefore: string;
|
|
76
|
+
bodySignatureAfter: string;
|
|
77
|
+
}
|
|
78
|
+
export interface WriteDocxResult {
|
|
79
|
+
outputPath: string;
|
|
80
|
+
results: OpResult[];
|
|
81
|
+
stats: WriteDocxStats;
|
|
82
|
+
warnings: string[];
|
|
83
|
+
}
|
|
84
|
+
export interface BodySnapshot {
|
|
85
|
+
bodyChildCount: number;
|
|
86
|
+
tableCount: number;
|
|
87
|
+
signature: string;
|
|
88
|
+
}
|
|
89
|
+
export interface ReplaceParagraphTextExactOp {
|
|
90
|
+
type: 'replace_paragraph_text_exact';
|
|
91
|
+
from: string;
|
|
92
|
+
to: string;
|
|
93
|
+
}
|
|
94
|
+
export interface ReplaceParagraphAtBodyIndexOp {
|
|
95
|
+
type: 'replace_paragraph_at_body_index';
|
|
96
|
+
bodyChildIndex: number;
|
|
97
|
+
to: string;
|
|
98
|
+
}
|
|
99
|
+
export interface SetColorForStyleOp {
|
|
100
|
+
type: 'set_color_for_style';
|
|
101
|
+
style: string;
|
|
102
|
+
color: string;
|
|
103
|
+
}
|
|
104
|
+
export interface SetColorForParagraphExactOp {
|
|
105
|
+
type: 'set_color_for_paragraph_exact';
|
|
106
|
+
text: string;
|
|
107
|
+
color: string;
|
|
108
|
+
}
|
|
109
|
+
export interface SetParagraphStyleAtBodyIndexOp {
|
|
110
|
+
type: 'set_paragraph_style_at_body_index';
|
|
111
|
+
bodyChildIndex: number;
|
|
112
|
+
style: string;
|
|
113
|
+
}
|
|
114
|
+
export interface InsertParagraphAfterTextOp {
|
|
115
|
+
type: 'insert_paragraph_after_text';
|
|
116
|
+
after: string;
|
|
117
|
+
text: string;
|
|
118
|
+
style?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface DeleteParagraphAtBodyIndexOp {
|
|
121
|
+
type: 'delete_paragraph_at_body_index';
|
|
122
|
+
bodyChildIndex: number;
|
|
123
|
+
}
|
|
124
|
+
export interface TableSetCellTextOp {
|
|
125
|
+
type: 'table_set_cell_text';
|
|
126
|
+
tableIndex: number;
|
|
127
|
+
row: number;
|
|
128
|
+
col: number;
|
|
129
|
+
text: string;
|
|
130
|
+
}
|
|
131
|
+
export interface ReplaceTableCellTextOp {
|
|
132
|
+
type: 'replace_table_cell_text';
|
|
133
|
+
from: string;
|
|
134
|
+
to: string;
|
|
135
|
+
}
|
|
136
|
+
export interface ReplaceHyperlinkUrlOp {
|
|
137
|
+
type: 'replace_hyperlink_url';
|
|
138
|
+
oldUrl: string;
|
|
139
|
+
newUrl: string;
|
|
140
|
+
}
|
|
141
|
+
export interface HeaderReplaceTextExactOp {
|
|
142
|
+
type: 'header_replace_text_exact';
|
|
143
|
+
from: string;
|
|
144
|
+
to: string;
|
|
145
|
+
}
|
|
146
|
+
export interface InsertTableOp {
|
|
147
|
+
type: 'insert_table';
|
|
148
|
+
/** Exact trimmed text of the paragraph to insert AFTER. Mutually exclusive with `before`. */
|
|
149
|
+
after?: string;
|
|
150
|
+
/** Exact trimmed text of the paragraph to insert BEFORE. Mutually exclusive with `after`. */
|
|
151
|
+
before?: string;
|
|
152
|
+
/** Optional header row (bold cells) */
|
|
153
|
+
headers?: string[];
|
|
154
|
+
/** Data rows — each row is an array of cell strings */
|
|
155
|
+
rows: string[][];
|
|
156
|
+
/** Optional column widths in twips (1/20 pt). Defaults to auto. */
|
|
157
|
+
colWidths?: number[];
|
|
158
|
+
/** Optional table style id (e.g. 'TableGrid') */
|
|
159
|
+
style?: string;
|
|
160
|
+
}
|
|
161
|
+
export interface InsertImageOp {
|
|
162
|
+
type: 'insert_image';
|
|
163
|
+
/** Exact trimmed text of the paragraph to insert AFTER. Mutually exclusive with `before`. */
|
|
164
|
+
after?: string;
|
|
165
|
+
/** Exact trimmed text of the paragraph to insert BEFORE. Mutually exclusive with `after`. */
|
|
166
|
+
before?: string;
|
|
167
|
+
/** Absolute or relative path to the image file */
|
|
84
168
|
imagePath: string;
|
|
169
|
+
/** Image width in pixels (default 300) */
|
|
170
|
+
width?: number;
|
|
171
|
+
/** Image height in pixels (default 200) */
|
|
172
|
+
height?: number;
|
|
173
|
+
/** Alt text for accessibility */
|
|
85
174
|
altText?: string;
|
|
175
|
+
}
|
|
176
|
+
export type DocxOp = ReplaceParagraphTextExactOp | ReplaceParagraphAtBodyIndexOp | SetColorForStyleOp | SetColorForParagraphExactOp | SetParagraphStyleAtBodyIndexOp | InsertParagraphAfterTextOp | DeleteParagraphAtBodyIndexOp | TableSetCellTextOp | ReplaceTableCellTextOp | ReplaceHyperlinkUrlOp | HeaderReplaceTextExactOp | InsertTableOp | InsertImageOp;
|
|
177
|
+
export interface OpResult {
|
|
178
|
+
op: DocxOp;
|
|
179
|
+
status: 'applied' | 'skipped';
|
|
180
|
+
matched: number;
|
|
181
|
+
reason?: string;
|
|
182
|
+
}
|
|
183
|
+
export interface DocxContentParagraph {
|
|
184
|
+
type: 'paragraph';
|
|
185
|
+
text: string;
|
|
186
|
+
style?: string | null;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Cell content can be:
|
|
190
|
+
* - A string (simple case, creates one paragraph)
|
|
191
|
+
* - An array of paragraphs (allows multiple paragraphs with different styles per cell)
|
|
192
|
+
*/
|
|
193
|
+
export type DocxTableCellContent = string | DocxContentParagraph[];
|
|
194
|
+
export interface DocxContentTable {
|
|
195
|
+
type: 'table';
|
|
196
|
+
/** Header cells - can be strings or arrays of paragraphs */
|
|
197
|
+
headers?: DocxTableCellContent[];
|
|
198
|
+
/** Data rows - each cell can be a string or array of paragraphs */
|
|
199
|
+
rows: DocxTableCellContent[][];
|
|
200
|
+
colWidths?: number[];
|
|
201
|
+
style?: string;
|
|
202
|
+
}
|
|
203
|
+
export interface DocxContentImage {
|
|
204
|
+
type: 'image';
|
|
205
|
+
imagePath: string;
|
|
86
206
|
width?: number;
|
|
87
207
|
height?: number;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
export interface
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
export interface DocxInsertHtmlOperation {
|
|
96
|
-
type: 'insertHtml';
|
|
97
|
-
html: string;
|
|
98
|
-
selector?: string;
|
|
99
|
-
position?: 'before' | 'after' | 'inside';
|
|
100
|
-
}
|
|
101
|
-
export interface DocxReplaceHtmlOperation {
|
|
102
|
-
type: 'replaceHtml';
|
|
103
|
-
selector: string;
|
|
104
|
-
html: string;
|
|
105
|
-
replaceAll?: boolean;
|
|
106
|
-
}
|
|
107
|
-
export interface DocxUpdateHtmlOperation {
|
|
108
|
-
type: 'updateHtml';
|
|
109
|
-
selector: string;
|
|
110
|
-
html?: string;
|
|
111
|
-
attributes?: Record<string, string>;
|
|
112
|
-
updateAll?: boolean;
|
|
113
|
-
}
|
|
114
|
-
export type DocxOperation = DocxReplaceTextOperation | DocxAppendMarkdownOperation | DocxInsertTableOperation | DocxInsertImageOperation | DocxAppendHtmlOperation | DocxInsertHtmlOperation | DocxReplaceHtmlOperation | DocxUpdateHtmlOperation;
|
|
208
|
+
altText?: string;
|
|
209
|
+
}
|
|
210
|
+
export type DocxContentItem = DocxContentParagraph | DocxContentTable | DocxContentImage;
|
|
211
|
+
export interface DocxContentStructure {
|
|
212
|
+
items: DocxContentItem[];
|
|
213
|
+
}
|
package/dist/tools/docx/types.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DOCX
|
|
3
|
-
*
|
|
4
|
-
* Centralised type definitions for every DOCX operation in the module.
|
|
5
|
-
*
|
|
6
|
-
* @module docx/types
|
|
2
|
+
* Type definitions for DOCX operations.
|
|
3
|
+
* Single source of truth for every type used across the DOCX module.
|
|
7
4
|
*/
|
|
8
5
|
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant validation for DOCX write operations.
|
|
3
|
+
*
|
|
4
|
+
* Single Responsibility: capture a structural snapshot of w:body and
|
|
5
|
+
* compare before / after snapshots to guarantee no accidental breakage.
|
|
6
|
+
*/
|
|
7
|
+
import type { BodySnapshot } from './types.js';
|
|
8
|
+
export interface ValidationOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Expected change in w:body direct child count.
|
|
11
|
+
* Positive = inserts, negative = deletes.
|
|
12
|
+
* Default 0 (no structural changes expected).
|
|
13
|
+
*/
|
|
14
|
+
expectedChildDelta?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Expected change in w:tbl count.
|
|
17
|
+
* Default 0 (no table additions/removals expected).
|
|
18
|
+
*/
|
|
19
|
+
expectedTableDelta?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Take a snapshot of the body's structural invariants. */
|
|
22
|
+
export declare function captureSnapshot(body: Element): BodySnapshot;
|
|
23
|
+
/**
|
|
24
|
+
* Compare before / after snapshots.
|
|
25
|
+
* Throws a descriptive error if any invariant has been violated,
|
|
26
|
+
* preventing the output file from being written.
|
|
27
|
+
*
|
|
28
|
+
* When `expectedChildDelta` is non-zero (structural ops like insert or
|
|
29
|
+
* delete), signature validation is skipped because the body structure
|
|
30
|
+
* is *expected* to change. Child count is still validated against
|
|
31
|
+
* the expected delta, and table count must remain unchanged.
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateInvariants(before: BodySnapshot, after: BodySnapshot, options?: ValidationOptions): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant validation for DOCX write operations.
|
|
3
|
+
*
|
|
4
|
+
* Single Responsibility: capture a structural snapshot of w:body and
|
|
5
|
+
* compare before / after snapshots to guarantee no accidental breakage.
|
|
6
|
+
*/
|
|
7
|
+
import { getBodyChildren, bodySignature, countTables } from './dom.js';
|
|
8
|
+
// ─── Capture ─────────────────────────────────────────────────────────
|
|
9
|
+
/** Take a snapshot of the body's structural invariants. */
|
|
10
|
+
export function captureSnapshot(body) {
|
|
11
|
+
const children = getBodyChildren(body);
|
|
12
|
+
return {
|
|
13
|
+
bodyChildCount: children.length,
|
|
14
|
+
tableCount: countTables(children),
|
|
15
|
+
signature: bodySignature(children),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// ─── Validate ────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Compare before / after snapshots.
|
|
21
|
+
* Throws a descriptive error if any invariant has been violated,
|
|
22
|
+
* preventing the output file from being written.
|
|
23
|
+
*
|
|
24
|
+
* When `expectedChildDelta` is non-zero (structural ops like insert or
|
|
25
|
+
* delete), signature validation is skipped because the body structure
|
|
26
|
+
* is *expected* to change. Child count is still validated against
|
|
27
|
+
* the expected delta, and table count must remain unchanged.
|
|
28
|
+
*/
|
|
29
|
+
export function validateInvariants(before, after, options) {
|
|
30
|
+
const delta = options?.expectedChildDelta ?? 0;
|
|
31
|
+
const tableDelta = options?.expectedTableDelta ?? 0;
|
|
32
|
+
const expectedChildCount = before.bodyChildCount + delta;
|
|
33
|
+
const expectedTableCount = before.tableCount + tableDelta;
|
|
34
|
+
const errors = [];
|
|
35
|
+
if (expectedChildCount !== after.bodyChildCount) {
|
|
36
|
+
errors.push(`Body child count mismatch: expected ${expectedChildCount} (before ${before.bodyChildCount} + delta ${delta}), got ${after.bodyChildCount}`);
|
|
37
|
+
}
|
|
38
|
+
if (expectedTableCount !== after.tableCount) {
|
|
39
|
+
errors.push(`Table count mismatch: expected ${expectedTableCount} (before ${before.tableCount} + delta ${tableDelta}), got ${after.tableCount}`);
|
|
40
|
+
}
|
|
41
|
+
// Only enforce signature stability when no structural ops changed the body
|
|
42
|
+
if (delta === 0 && before.signature !== after.signature) {
|
|
43
|
+
errors.push(`Body signature changed:\n before: ${before.signature}\n after: ${after.signature}`);
|
|
44
|
+
}
|
|
45
|
+
if (errors.length > 0) {
|
|
46
|
+
throw new Error('DOCX structural validation failed — output NOT written.\n' +
|
|
47
|
+
errors.join('\n'));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* writeDocxPatched - the patch-based "update" orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Single Responsibility: coordinate the full update pipeline:
|
|
5
|
+
* 1. Open DOCX ZIP
|
|
6
|
+
* 2. Parse word/document.xml
|
|
7
|
+
* 3. Snapshot before
|
|
8
|
+
* 4. Apply operations (pass zip for ops that touch auxiliary files)
|
|
9
|
+
* 5. Snapshot after
|
|
10
|
+
* 6. Validate invariants (accounting for structural deltas)
|
|
11
|
+
* 7. Serialize and save
|
|
12
|
+
*
|
|
13
|
+
* Each step delegates to a single-purpose module, keeping this file
|
|
14
|
+
* a pure orchestrator with no direct DOM/XML/ZIP logic.
|
|
15
|
+
*/
|
|
16
|
+
import type { DocxOp, WriteDocxResult } from './types.js';
|
|
17
|
+
export declare function writeDocxPatched(inputPath: string, outputPath: string, ops: DocxOp[]): Promise<WriteDocxResult>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* writeDocxPatched - the patch-based "update" orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Single Responsibility: coordinate the full update pipeline:
|
|
5
|
+
* 1. Open DOCX ZIP
|
|
6
|
+
* 2. Parse word/document.xml
|
|
7
|
+
* 3. Snapshot before
|
|
8
|
+
* 4. Apply operations (pass zip for ops that touch auxiliary files)
|
|
9
|
+
* 5. Snapshot after
|
|
10
|
+
* 6. Validate invariants (accounting for structural deltas)
|
|
11
|
+
* 7. Serialize and save
|
|
12
|
+
*
|
|
13
|
+
* Each step delegates to a single-purpose module, keeping this file
|
|
14
|
+
* a pure orchestrator with no direct DOM/XML/ZIP logic.
|
|
15
|
+
*/
|
|
16
|
+
import { loadDocxZip, getDocumentXml, saveDocxZip } from './zip.js';
|
|
17
|
+
import { parseXml, serializeXml, getBody } from './dom.js';
|
|
18
|
+
import { captureSnapshot, validateInvariants } from './validate.js';
|
|
19
|
+
import { applyOp } from './ops/index.js';
|
|
20
|
+
/** Structural op types that add/remove body children. */
|
|
21
|
+
const STRUCTURAL_INSERT_OPS = new Set([
|
|
22
|
+
'insert_paragraph_after_text',
|
|
23
|
+
'insert_table',
|
|
24
|
+
'insert_image',
|
|
25
|
+
]);
|
|
26
|
+
const STRUCTURAL_DELETE_OPS = new Set(['delete_paragraph_at_body_index']);
|
|
27
|
+
export async function writeDocxPatched(inputPath, outputPath, ops) {
|
|
28
|
+
// 1. Load ZIP
|
|
29
|
+
const zip = await loadDocxZip(inputPath);
|
|
30
|
+
// 2. Parse document.xml
|
|
31
|
+
const xmlStr = getDocumentXml(zip);
|
|
32
|
+
const doc = parseXml(xmlStr);
|
|
33
|
+
const body = getBody(doc);
|
|
34
|
+
// 3. Before-snapshot
|
|
35
|
+
const before = captureSnapshot(body);
|
|
36
|
+
// 4. Apply ops — pass zip for ops that modify auxiliary files
|
|
37
|
+
const results = [];
|
|
38
|
+
const warnings = [];
|
|
39
|
+
for (const op of ops) {
|
|
40
|
+
try {
|
|
41
|
+
const result = applyOp(body, op, zip);
|
|
42
|
+
results.push(result);
|
|
43
|
+
if (result.status === 'skipped') {
|
|
44
|
+
warnings.push(`Op ${op.type} skipped: ${result.reason ?? 'unknown'}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
49
|
+
warnings.push(`Op ${op.type} failed: ${msg}`);
|
|
50
|
+
results.push({
|
|
51
|
+
op,
|
|
52
|
+
status: 'skipped',
|
|
53
|
+
matched: 0,
|
|
54
|
+
reason: `error: ${msg}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 5. Compute expected structural delta from applied ops
|
|
59
|
+
let expectedChildDelta = 0;
|
|
60
|
+
let expectedTableDelta = 0;
|
|
61
|
+
for (const r of results) {
|
|
62
|
+
if (r.status !== 'applied')
|
|
63
|
+
continue;
|
|
64
|
+
if (STRUCTURAL_INSERT_OPS.has(r.op.type))
|
|
65
|
+
expectedChildDelta += 1;
|
|
66
|
+
if (STRUCTURAL_DELETE_OPS.has(r.op.type))
|
|
67
|
+
expectedChildDelta -= 1;
|
|
68
|
+
if (r.op.type === 'insert_table')
|
|
69
|
+
expectedTableDelta += 1;
|
|
70
|
+
}
|
|
71
|
+
// 6. After-snapshot
|
|
72
|
+
const after = captureSnapshot(body);
|
|
73
|
+
// 7. Validate — throws if structural invariants are broken
|
|
74
|
+
validateInvariants(before, after, { expectedChildDelta, expectedTableDelta });
|
|
75
|
+
// 8. Serialize and save (document.xml + any zip-level changes)
|
|
76
|
+
const newXml = serializeXml(doc);
|
|
77
|
+
await saveDocxZip(zip, newXml, outputPath);
|
|
78
|
+
// 9. Build stats
|
|
79
|
+
const stats = {
|
|
80
|
+
tablesBefore: before.tableCount,
|
|
81
|
+
tablesAfter: after.tableCount,
|
|
82
|
+
bodyChildrenBefore: before.bodyChildCount,
|
|
83
|
+
bodyChildrenAfter: after.bodyChildCount,
|
|
84
|
+
bodySignatureBefore: before.signature,
|
|
85
|
+
bodySignatureAfter: after.signature,
|
|
86
|
+
};
|
|
87
|
+
return { outputPath, results, stats, warnings };
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quick test: extract XML, show context, then edit a checkbox.
|
|
3
|
+
*/
|
|
4
|
+
import { extractDocxXml, editDocxXml } from './xml-view.js';
|
|
5
|
+
async function main() {
|
|
6
|
+
const input = '/Users/eduardsruzga/Downloads/dcox/Wedding Photo Checklist.docx';
|
|
7
|
+
const output = '/Users/eduardsruzga/Downloads/dcox/Wedding Photo Checklist - XML Edit Test.docx';
|
|
8
|
+
// 1. Extract and show stats
|
|
9
|
+
const view = await extractDocxXml(input);
|
|
10
|
+
console.log(`Extracted XML: ${view.lineCount} lines, ${view.rawSize} raw chars`);
|
|
11
|
+
// 2. Show context around "Bride hairstyling" with EXACT indentation
|
|
12
|
+
const lines = view.xml.split('\n');
|
|
13
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14
|
+
if (lines[i].includes('Bride hairstyling')) {
|
|
15
|
+
console.log(`\nFound "Bride hairstyling" at line ${i + 1}:`);
|
|
16
|
+
for (let j = Math.max(0, i - 20); j <= Math.min(lines.length - 1, i + 5); j++) {
|
|
17
|
+
console.log(`${String(j + 1).padStart(4)}|${lines[j]}`);
|
|
18
|
+
}
|
|
19
|
+
// 3. Build the old/new strings from ACTUAL lines
|
|
20
|
+
// The ☐ should be around line i-20 to i-15
|
|
21
|
+
// Find it by scanning backwards
|
|
22
|
+
let checkboxLine = -1;
|
|
23
|
+
for (let j = i - 1; j >= Math.max(0, i - 25); j--) {
|
|
24
|
+
if (lines[j].includes('☐')) {
|
|
25
|
+
checkboxLine = j;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
console.log(`\nCheckbox ☐ found at line ${checkboxLine + 1}: "${lines[checkboxLine]}"`);
|
|
30
|
+
// Use just enough context: the checkbox line + next line to make it unique
|
|
31
|
+
const oldStr = lines[checkboxLine];
|
|
32
|
+
const newStr = oldStr.replace('☐', '☑');
|
|
33
|
+
console.log(`\nold: "${oldStr}"`);
|
|
34
|
+
console.log(`new: "${newStr}"`);
|
|
35
|
+
// Check uniqueness
|
|
36
|
+
const matches = lines.filter(l => l === oldStr).length;
|
|
37
|
+
console.log(`Exact line matches in document: ${matches}`);
|
|
38
|
+
if (matches > 1) {
|
|
39
|
+
// Expand context until we get exactly 1 match
|
|
40
|
+
for (let ctx = 3; ctx <= 30; ctx++) {
|
|
41
|
+
const contextOld = lines.slice(checkboxLine, checkboxLine + ctx).join('\n');
|
|
42
|
+
const contextMatches = view.xml.split(contextOld).length - 1;
|
|
43
|
+
if (contextMatches === 1) {
|
|
44
|
+
console.log(`\nUnique match found with ${ctx}-line context`);
|
|
45
|
+
const contextNew = contextOld.replace('☐', '☑');
|
|
46
|
+
const result = await editDocxXml(input, output, contextOld, contextNew);
|
|
47
|
+
console.log(`Edit result:`, result);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.log(`${ctx} lines of context: ${contextMatches} matches`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const result = await editDocxXml(input, output, oldStr, newStr);
|
|
57
|
+
console.log(`\nEdit result:`, result);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX XML View
|
|
3
|
+
*
|
|
4
|
+
* Exposes word/document.xml from a DOCX file as pretty-printed XML that can be
|
|
5
|
+
* read with offset/length pagination and edited with find/replace, just like a
|
|
6
|
+
* text file. All formatting is preserved because we operate on the actual XML.
|
|
7
|
+
*
|
|
8
|
+
* Round-trip: DOCX → unzip → pretty-print → edit → compact → repack → DOCX
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Pretty-print XML by splitting tags onto separate lines with indentation.
|
|
12
|
+
*
|
|
13
|
+
* Preserves content inside text nodes exactly. Self-closing tags, inline
|
|
14
|
+
* open+close tags (e.g. <w:t>text</w:t>), and pure closing tags are all
|
|
15
|
+
* handled so that compact→pretty→compact is lossless.
|
|
16
|
+
*/
|
|
17
|
+
export declare function prettyPrintXml(xml: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Compact pretty-printed XML back to a single line.
|
|
20
|
+
*
|
|
21
|
+
* CRITICAL: Must not introduce or remove whitespace inside <w:t> text nodes
|
|
22
|
+
* or break xml:space="preserve" semantics. We achieve this by only stripping
|
|
23
|
+
* leading indentation (which we added) and joining lines. The original XML
|
|
24
|
+
* had no newlines between tags, so this restores the original form.
|
|
25
|
+
*/
|
|
26
|
+
export declare function compactXml(prettyXml: string): string;
|
|
27
|
+
export interface DocxXmlReadResult {
|
|
28
|
+
/** Pretty-printed XML content */
|
|
29
|
+
content: string;
|
|
30
|
+
/** Total number of lines */
|
|
31
|
+
lineCount: number;
|
|
32
|
+
/** Source DOCX path */
|
|
33
|
+
path: string;
|
|
34
|
+
/** Size of the raw (compact) XML in chars */
|
|
35
|
+
rawSize: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the document.xml from a DOCX as pretty-printed, line-based XML.
|
|
39
|
+
* Supports offset/length pagination just like text file reading.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readDocxXml(filePath: string, offset?: number, length?: number): Promise<DocxXmlReadResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Apply a find/replace edit to a DOCX file's XML and write a new DOCX.
|
|
44
|
+
*
|
|
45
|
+
* The edit operates on the pretty-printed XML so that line-based context
|
|
46
|
+
* from read_file can be used directly as the search string. After editing,
|
|
47
|
+
* the XML is compacted back and repacked into the DOCX zip.
|
|
48
|
+
*
|
|
49
|
+
* @returns result with status, match count, and output path
|
|
50
|
+
*/
|
|
51
|
+
export declare function editDocxXml(inputPath: string, outputPath: string, oldStr: string, newStr: string, expectedReplacements?: number): Promise<{
|
|
52
|
+
status: 'applied' | 'no_match' | 'unexpected_count';
|
|
53
|
+
matchCount: number;
|
|
54
|
+
outputPath: string;
|
|
55
|
+
message?: string;
|
|
56
|
+
}>;
|