@wonderwhy-er/desktop-commander 0.2.35 → 0.2.36

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 (115) hide show
  1. package/README.md +2 -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/server.js +30 -4
  6. package/dist/tools/docx/builders/image.d.ts +14 -0
  7. package/dist/tools/docx/builders/image.js +84 -0
  8. package/dist/tools/docx/builders/index.d.ts +9 -3
  9. package/dist/tools/docx/builders/index.js +9 -3
  10. package/dist/tools/docx/builders/paragraph.d.ts +12 -0
  11. package/dist/tools/docx/builders/paragraph.js +29 -0
  12. package/dist/tools/docx/builders/table.d.ts +8 -0
  13. package/dist/tools/docx/builders/table.js +94 -0
  14. package/dist/tools/docx/builders/utils.d.ts +5 -0
  15. package/dist/tools/docx/builders/utils.js +18 -0
  16. package/dist/tools/docx/constants.d.ts +28 -32
  17. package/dist/tools/docx/constants.js +56 -52
  18. package/dist/tools/docx/create.d.ts +21 -0
  19. package/dist/tools/docx/create.js +386 -0
  20. package/dist/tools/docx/dom.d.ts +66 -0
  21. package/dist/tools/docx/dom.js +228 -0
  22. package/dist/tools/docx/index.d.ts +8 -12
  23. package/dist/tools/docx/index.js +8 -14
  24. package/dist/tools/docx/modify.d.ts +28 -0
  25. package/dist/tools/docx/modify.js +271 -0
  26. package/dist/tools/docx/ops/delete-paragraph-at-body-index.d.ts +11 -0
  27. package/dist/tools/docx/ops/delete-paragraph-at-body-index.js +23 -0
  28. package/dist/tools/docx/ops/header-replace-text-exact.d.ts +13 -0
  29. package/dist/tools/docx/ops/header-replace-text-exact.js +55 -0
  30. package/dist/tools/docx/ops/index.d.ts +17 -0
  31. package/dist/tools/docx/ops/index.js +67 -0
  32. package/dist/tools/docx/ops/insert-image-after-text.d.ts +24 -0
  33. package/dist/tools/docx/ops/insert-image-after-text.js +128 -0
  34. package/dist/tools/docx/ops/insert-paragraph-after-text.d.ts +12 -0
  35. package/dist/tools/docx/ops/insert-paragraph-after-text.js +74 -0
  36. package/dist/tools/docx/ops/insert-table-after-text.d.ts +19 -0
  37. package/dist/tools/docx/ops/insert-table-after-text.js +57 -0
  38. package/dist/tools/docx/ops/replace-hyperlink-url.d.ts +12 -0
  39. package/dist/tools/docx/ops/replace-hyperlink-url.js +37 -0
  40. package/dist/tools/docx/ops/replace-paragraph-at-body-index.d.ts +9 -0
  41. package/dist/tools/docx/ops/replace-paragraph-at-body-index.js +25 -0
  42. package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +9 -0
  43. package/dist/tools/docx/ops/replace-paragraph-text-exact.js +21 -0
  44. package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +8 -0
  45. package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +23 -0
  46. package/dist/tools/docx/ops/set-color-for-style.d.ts +9 -0
  47. package/dist/tools/docx/ops/set-color-for-style.js +27 -0
  48. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.d.ts +8 -0
  49. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.js +57 -0
  50. package/dist/tools/docx/ops/table-set-cell-text.d.ts +9 -0
  51. package/dist/tools/docx/ops/table-set-cell-text.js +72 -0
  52. package/dist/tools/docx/read.d.ts +27 -0
  53. package/dist/tools/docx/read.js +188 -0
  54. package/dist/tools/docx/relationships.d.ts +22 -0
  55. package/dist/tools/docx/relationships.js +76 -0
  56. package/dist/tools/docx/types.d.ts +174 -104
  57. package/dist/tools/docx/types.js +2 -5
  58. package/dist/tools/docx/validate.d.ts +33 -0
  59. package/dist/tools/docx/validate.js +49 -0
  60. package/dist/tools/docx/write.d.ts +17 -0
  61. package/dist/tools/docx/write.js +88 -0
  62. package/dist/tools/docx/zip.d.ts +21 -0
  63. package/dist/tools/docx/zip.js +35 -0
  64. package/dist/tools/schemas.d.ts +13 -0
  65. package/dist/tools/schemas.js +5 -0
  66. package/dist/types.d.ts +10 -0
  67. package/dist/ui/contracts.d.ts +14 -0
  68. package/dist/ui/contracts.js +18 -0
  69. package/dist/ui/file-preview/index.html +16 -0
  70. package/dist/ui/file-preview/preview-runtime.js +13977 -0
  71. package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
  72. package/dist/ui/file-preview/shared/preview-file-types.js +57 -0
  73. package/dist/ui/file-preview/src/app.d.ts +4 -0
  74. package/dist/ui/file-preview/src/app.js +800 -0
  75. package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
  76. package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
  77. package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
  78. package/dist/ui/file-preview/src/components/highlighting.js +54 -0
  79. package/dist/ui/file-preview/src/components/html-renderer.d.ts +9 -0
  80. package/dist/ui/file-preview/src/components/html-renderer.js +63 -0
  81. package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
  82. package/dist/ui/file-preview/src/components/markdown-renderer.js +21 -0
  83. package/dist/ui/file-preview/src/components/toolbar.d.ts +6 -0
  84. package/dist/ui/file-preview/src/components/toolbar.js +75 -0
  85. package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
  86. package/dist/ui/file-preview/src/image-preview.js +21 -0
  87. package/dist/ui/file-preview/src/main.d.ts +1 -0
  88. package/dist/ui/file-preview/src/main.js +5 -0
  89. package/dist/ui/file-preview/src/types.d.ts +1 -0
  90. package/dist/ui/file-preview/src/types.js +1 -0
  91. package/dist/ui/file-preview/styles.css +764 -0
  92. package/dist/ui/resources.d.ts +21 -0
  93. package/dist/ui/resources.js +72 -0
  94. package/dist/ui/shared/escape-html.d.ts +4 -0
  95. package/dist/ui/shared/escape-html.js +11 -0
  96. package/dist/ui/shared/host-lifecycle.d.ts +16 -0
  97. package/dist/ui/shared/host-lifecycle.js +35 -0
  98. package/dist/ui/shared/rpc-client.d.ts +14 -0
  99. package/dist/ui/shared/rpc-client.js +72 -0
  100. package/dist/ui/shared/theme-adaptation.d.ts +10 -0
  101. package/dist/ui/shared/theme-adaptation.js +118 -0
  102. package/dist/ui/shared/tool-header.d.ts +9 -0
  103. package/dist/ui/shared/tool-header.js +25 -0
  104. package/dist/ui/shared/tool-shell.d.ts +16 -0
  105. package/dist/ui/shared/tool-shell.js +65 -0
  106. package/dist/ui/shared/widget-state.d.ts +28 -0
  107. package/dist/ui/shared/widget-state.js +60 -0
  108. package/dist/utils/capture.d.ts +1 -0
  109. package/dist/utils/capture.js +6 -0
  110. package/dist/utils/files/docx.d.ts +8 -15
  111. package/dist/utils/files/docx.js +76 -176
  112. package/dist/utils/files/text.js +9 -1
  113. package/dist/version.d.ts +1 -1
  114. package/dist/version.js +1 -1
  115. package/package.json +5 -2
@@ -1,114 +1,184 @@
1
1
  /**
2
- * DOCX Type Definitions
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
- description?: string;
20
- creationDate?: Date;
21
- modificationDate?: Date;
22
- lastModifiedBy?: string;
23
- revision?: string;
24
- fileSize?: number;
25
- }
26
- export interface DocxImage {
27
- id: string;
28
- /** Base64-encoded image data */
29
- data: string;
30
- mimeType: string;
31
- altText?: string;
32
- originalSize?: number;
33
- }
34
- export interface DocxSection {
35
- type: 'heading' | 'paragraph' | 'list' | 'table' | 'image';
36
- content: string;
37
- level?: number;
38
- images?: DocxImage[];
39
- }
40
- export interface DocxParseResult {
41
- html: string;
42
- metadata: DocxMetadata;
43
- images: DocxImage[];
44
- sections?: DocxSection[];
45
- /** Original document defaults — passed through the pipeline to preserve styles during editing. */
46
- documentDefaults?: DocxDocumentDefaults;
47
- }
48
- export interface DocxParseOptions {
49
- includeImages?: boolean;
50
- preserveFormatting?: boolean;
51
- styleMap?: string[];
52
- }
53
- export interface DocxBuildOptions {
54
- baseDir?: string;
55
- includeImages?: boolean;
56
- preserveFormatting?: boolean;
57
- /** Original document defaults to preserve font/size when converting HTML → DOCX. */
58
- documentDefaults?: DocxDocumentDefaults;
59
- }
60
- export interface DocxEditOptions extends DocxBuildOptions {
61
- outputPath?: string;
62
- styleMap?: string[];
63
- }
64
- export interface DocxReplaceTextOperation {
65
- type: 'replaceText';
66
- search: string;
67
- replace: string;
68
- matchCase?: boolean;
69
- global?: boolean;
70
- }
71
- export interface DocxAppendMarkdownOperation {
72
- type: 'appendMarkdown';
73
- markdown: string;
74
- }
75
- export interface DocxInsertTableOperation {
76
- type: 'insertTable';
77
- markdownTable?: string;
78
- rows?: string[][];
79
- selector?: string;
80
- position?: 'before' | 'after' | 'inside';
81
- }
82
- export interface DocxInsertImageOperation {
83
- type: 'insertImage';
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 ReadDocxResult {
45
+ path: string;
46
+ paragraphs: ParagraphOutline[];
47
+ stylesSeen: string[];
48
+ counts: {
49
+ tables: number;
50
+ images: number;
51
+ bodyChildren: number;
52
+ };
53
+ }
54
+ export interface WriteDocxStats {
55
+ tablesBefore: number;
56
+ tablesAfter: number;
57
+ bodyChildrenBefore: number;
58
+ bodyChildrenAfter: number;
59
+ bodySignatureBefore: string;
60
+ bodySignatureAfter: string;
61
+ }
62
+ export interface WriteDocxResult {
63
+ outputPath: string;
64
+ results: OpResult[];
65
+ stats: WriteDocxStats;
66
+ warnings: string[];
67
+ }
68
+ export interface BodySnapshot {
69
+ bodyChildCount: number;
70
+ tableCount: number;
71
+ signature: string;
72
+ }
73
+ export interface ReplaceParagraphTextExactOp {
74
+ type: 'replace_paragraph_text_exact';
75
+ from: string;
76
+ to: string;
77
+ }
78
+ export interface ReplaceParagraphAtBodyIndexOp {
79
+ type: 'replace_paragraph_at_body_index';
80
+ bodyChildIndex: number;
81
+ to: string;
82
+ }
83
+ export interface SetColorForStyleOp {
84
+ type: 'set_color_for_style';
85
+ style: string;
86
+ color: string;
87
+ }
88
+ export interface SetColorForParagraphExactOp {
89
+ type: 'set_color_for_paragraph_exact';
90
+ text: string;
91
+ color: string;
92
+ }
93
+ export interface SetParagraphStyleAtBodyIndexOp {
94
+ type: 'set_paragraph_style_at_body_index';
95
+ bodyChildIndex: number;
96
+ style: string;
97
+ }
98
+ export interface InsertParagraphAfterTextOp {
99
+ type: 'insert_paragraph_after_text';
100
+ after: string;
101
+ text: string;
102
+ style?: string;
103
+ }
104
+ export interface DeleteParagraphAtBodyIndexOp {
105
+ type: 'delete_paragraph_at_body_index';
106
+ bodyChildIndex: number;
107
+ }
108
+ export interface TableSetCellTextOp {
109
+ type: 'table_set_cell_text';
110
+ tableIndex: number;
111
+ row: number;
112
+ col: number;
113
+ text: string;
114
+ }
115
+ export interface ReplaceHyperlinkUrlOp {
116
+ type: 'replace_hyperlink_url';
117
+ oldUrl: string;
118
+ newUrl: string;
119
+ }
120
+ export interface HeaderReplaceTextExactOp {
121
+ type: 'header_replace_text_exact';
122
+ from: string;
123
+ to: string;
124
+ }
125
+ export interface InsertTableOp {
126
+ type: 'insert_table';
127
+ /** Exact trimmed text of the paragraph to insert AFTER. Mutually exclusive with `before`. */
128
+ after?: string;
129
+ /** Exact trimmed text of the paragraph to insert BEFORE. Mutually exclusive with `after`. */
130
+ before?: string;
131
+ /** Optional header row (bold cells) */
132
+ headers?: string[];
133
+ /** Data rows — each row is an array of cell strings */
134
+ rows: string[][];
135
+ /** Optional column widths in twips (1/20 pt). Defaults to auto. */
136
+ colWidths?: number[];
137
+ /** Optional table style id (e.g. 'TableGrid') */
138
+ style?: string;
139
+ }
140
+ export interface InsertImageOp {
141
+ type: 'insert_image';
142
+ /** Exact trimmed text of the paragraph to insert AFTER. Mutually exclusive with `before`. */
143
+ after?: string;
144
+ /** Exact trimmed text of the paragraph to insert BEFORE. Mutually exclusive with `after`. */
145
+ before?: string;
146
+ /** Absolute or relative path to the image file */
84
147
  imagePath: string;
148
+ /** Image width in pixels (default 300) */
149
+ width?: number;
150
+ /** Image height in pixels (default 200) */
151
+ height?: number;
152
+ /** Alt text for accessibility */
85
153
  altText?: string;
154
+ }
155
+ export type DocxOp = ReplaceParagraphTextExactOp | ReplaceParagraphAtBodyIndexOp | SetColorForStyleOp | SetColorForParagraphExactOp | SetParagraphStyleAtBodyIndexOp | InsertParagraphAfterTextOp | DeleteParagraphAtBodyIndexOp | TableSetCellTextOp | ReplaceHyperlinkUrlOp | HeaderReplaceTextExactOp | InsertTableOp | InsertImageOp;
156
+ export interface OpResult {
157
+ op: DocxOp;
158
+ status: 'applied' | 'skipped';
159
+ matched: number;
160
+ reason?: string;
161
+ }
162
+ export interface DocxContentParagraph {
163
+ type: 'paragraph';
164
+ text: string;
165
+ style?: string | null;
166
+ }
167
+ export interface DocxContentTable {
168
+ type: 'table';
169
+ headers?: string[];
170
+ rows: string[][];
171
+ colWidths?: number[];
172
+ style?: string;
173
+ }
174
+ export interface DocxContentImage {
175
+ type: 'image';
176
+ imagePath: string;
86
177
  width?: number;
87
178
  height?: number;
88
- selector?: string;
89
- position?: 'before' | 'after' | 'inside';
90
- }
91
- export interface DocxAppendHtmlOperation {
92
- type: 'appendHtml';
93
- html: string;
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;
179
+ altText?: string;
180
+ }
181
+ export type DocxContentItem = DocxContentParagraph | DocxContentTable | DocxContentImage;
182
+ export interface DocxContentStructure {
183
+ items: DocxContentItem[];
184
+ }
@@ -1,8 +1,5 @@
1
1
  /**
2
- * DOCX Type Definitions
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,21 @@
1
+ /**
2
+ * DOCX ZIP I/O — Single Responsibility: file ↔ zip ↔ xml.
3
+ *
4
+ * Every other module depends on these three functions for disk I/O;
5
+ * none of them touch the file system directly.
6
+ */
7
+ import PizZip from 'pizzip';
8
+ /**
9
+ * Read a .docx file from disk and return a PizZip instance.
10
+ */
11
+ export declare function loadDocxZip(filePath: string): Promise<PizZip>;
12
+ /**
13
+ * Extract the raw XML string from word/document.xml inside the zip.
14
+ * Throws if the entry is missing.
15
+ */
16
+ export declare function getDocumentXml(zip: PizZip): string;
17
+ /**
18
+ * Replace word/document.xml in the zip with new XML,
19
+ * then write the whole archive to outputPath.
20
+ */
21
+ export declare function saveDocxZip(zip: PizZip, newDocumentXml: string, outputPath: string): Promise<void>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * DOCX ZIP I/O — Single Responsibility: file ↔ zip ↔ xml.
3
+ *
4
+ * Every other module depends on these three functions for disk I/O;
5
+ * none of them touch the file system directly.
6
+ */
7
+ import fs from 'fs/promises';
8
+ import PizZip from 'pizzip';
9
+ /**
10
+ * Read a .docx file from disk and return a PizZip instance.
11
+ */
12
+ export async function loadDocxZip(filePath) {
13
+ const buf = await fs.readFile(filePath);
14
+ return new PizZip(buf);
15
+ }
16
+ /**
17
+ * Extract the raw XML string from word/document.xml inside the zip.
18
+ * Throws if the entry is missing.
19
+ */
20
+ export function getDocumentXml(zip) {
21
+ const entry = zip.file('word/document.xml');
22
+ if (!entry) {
23
+ throw new Error('Invalid DOCX: missing word/document.xml');
24
+ }
25
+ return entry.asText();
26
+ }
27
+ /**
28
+ * Replace word/document.xml in the zip with new XML,
29
+ * then write the whole archive to outputPath.
30
+ */
31
+ export async function saveDocxZip(zip, newDocumentXml, outputPath) {
32
+ zip.file('word/document.xml', newDocumentXml);
33
+ const buf = zip.generate({ type: 'nodebuffer' });
34
+ await fs.writeFile(outputPath, buf);
35
+ }
@@ -401,3 +401,16 @@ export declare const GetRecentToolCallsArgsSchema: z.ZodObject<{
401
401
  toolName?: string | undefined;
402
402
  since?: string | undefined;
403
403
  }>;
404
+ export declare const TrackUiEventArgsSchema: z.ZodObject<{
405
+ event: z.ZodString;
406
+ component: z.ZodDefault<z.ZodOptional<z.ZodString>>;
407
+ params: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>>;
408
+ }, "strip", z.ZodTypeAny, {
409
+ params: Record<string, string | number | boolean | null>;
410
+ event: string;
411
+ component: string;
412
+ }, {
413
+ event: string;
414
+ params?: Record<string, string | number | boolean | null> | undefined;
415
+ component?: string | undefined;
416
+ }>;
@@ -175,3 +175,8 @@ export const GetRecentToolCallsArgsSchema = z.object({
175
175
  toolName: z.string().optional(),
176
176
  since: z.string().datetime().optional(),
177
177
  });
178
+ export const TrackUiEventArgsSchema = z.object({
179
+ event: z.string().min(1).max(80),
180
+ component: z.string().optional().default('file_preview'),
181
+ params: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional().default({}),
182
+ });
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ChildProcess } from 'child_process';
2
2
  import { FilteredStdioServerTransport } from './custom-stdio.js';
3
+ import type { PreviewFileType } from './ui/file-preview/shared/preview-file-types.js';
3
4
  declare global {
4
5
  var mcpTransport: FilteredStdioServerTransport | undefined;
5
6
  var disableOnboarding: boolean | undefined;
@@ -60,8 +61,17 @@ export interface ServerResponseContent {
60
61
  data?: string;
61
62
  mimeType?: string;
62
63
  }
64
+ export interface FilePreviewStructuredContent {
65
+ fileName: string;
66
+ filePath: string;
67
+ fileType: PreviewFileType;
68
+ content: string;
69
+ imageData?: string;
70
+ mimeType?: string;
71
+ }
63
72
  export interface ServerResult {
64
73
  content: ServerResponseContent[];
74
+ structuredContent?: FilePreviewStructuredContent | Record<string, unknown>;
65
75
  isError?: boolean;
66
76
  _meta?: Record<string, unknown>;
67
77
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Central constants and shape contracts for UI resource identifiers. It gives one source of truth for URIs/tool metadata shared between server handlers and UI loaders.
3
+ */
4
+ export declare const FILE_PREVIEW_RESOURCE_URI = "ui://desktop-commander/file-preview";
5
+ export declare const CONFIG_EDITOR_RESOURCE_URI = "ui://desktop-commander/config-editor";
6
+ export interface UiToolMeta extends Record<string, unknown> {
7
+ 'ui/resourceUri': string;
8
+ 'openai/outputTemplate': string;
9
+ ui: {
10
+ resourceUri: string;
11
+ };
12
+ 'openai/widgetAccessible'?: boolean;
13
+ }
14
+ export declare function buildUiToolMeta(resourceUri: string, widgetAccessible?: boolean): UiToolMeta;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Central constants and shape contracts for UI resource identifiers. It gives one source of truth for URIs/tool metadata shared between server handlers and UI loaders.
3
+ */
4
+ export const FILE_PREVIEW_RESOURCE_URI = 'ui://desktop-commander/file-preview';
5
+ export const CONFIG_EDITOR_RESOURCE_URI = 'ui://desktop-commander/config-editor';
6
+ export function buildUiToolMeta(resourceUri, widgetAccessible = false) {
7
+ const meta = {
8
+ 'ui/resourceUri': resourceUri,
9
+ 'openai/outputTemplate': resourceUri,
10
+ ui: {
11
+ resourceUri,
12
+ },
13
+ };
14
+ if (widgetAccessible) {
15
+ meta['openai/widgetAccessible'] = true;
16
+ }
17
+ return meta;
18
+ }
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <!--
3
+ Host page for the File Preview MCP UI. It establishes the root structure and script/style loading required to render text, markdown, and HTML previews safely.
4
+ -->
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
9
+ <title>Desktop Commander File Preview</title>
10
+ <link rel="stylesheet" href="./styles.css" />
11
+ </head>
12
+ <body>
13
+ <div id="app"></div>
14
+ <script src="./preview-runtime.js"></script>
15
+ </body>
16
+ </html>