@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,14 +1,13 @@
1
1
  /**
2
2
  * DOCX File Handler
3
- * Implements FileHandler interface for Microsoft Word documents
3
+ * Implements FileHandler interface for DOCX documents
4
+ * Handles reading, writing, and modifying DOCX files while preserving formatting
4
5
  */
5
6
  import fs from 'fs/promises';
6
- import path from 'path';
7
- import { parseDocxToHtml, createDocxFromHtml, editDocxWithOperations, DocxError } from '../../tools/docx/index.js';
8
- import { convertToHtmlIfNeeded, generateOutputPath } from '../../tools/docx/utils/index.js';
7
+ import { readDocx, getDocxMetadata, modifyDocxContent } from '../../tools/docx/index.js';
9
8
  /**
10
9
  * File handler for DOCX documents
11
- * Extracts text as markdown with embedded images
10
+ * Extracts text and metadata, supports paragraph-based pagination
12
11
  */
13
12
  export class DocxFileHandler {
14
13
  constructor() {
@@ -22,54 +21,30 @@ export class DocxFileHandler {
22
21
  return this.extensions.some(e => ext.endsWith(e));
23
22
  }
24
23
  /**
25
- * Read DOCX content extracts text as styled HTML (with embedded images).
26
- * Uses direct DOCX XML parsing for style preservation, with mammoth.js fallback.
24
+ * Read DOCX content - returns body XML for LLM modification
27
25
  */
28
26
  async read(path, options) {
27
+ const { offset = 0, length } = options ?? {};
29
28
  try {
30
- const docxResult = await parseDocxToHtml(path, {
31
- includeImages: true,
32
- preserveFormatting: true
29
+ const result = await readDocx(path, {
30
+ offset,
31
+ length
33
32
  });
34
- // Format the content for MCP response
35
- let content = docxResult.html;
36
- // Add status message if requested (default: true)
37
- const includeStatusMessage = options?.includeStatusMessage !== false;
38
- if (includeStatusMessage) {
39
- const statusParts = [];
40
- if (docxResult.metadata.title) {
41
- statusParts.push(`Title: "${docxResult.metadata.title}"`);
42
- }
43
- if (docxResult.metadata.author) {
44
- statusParts.push(`Author: ${docxResult.metadata.author}`);
45
- }
46
- if (docxResult.images.length > 0) {
47
- statusParts.push(`${docxResult.images.length} embedded images`);
48
- }
49
- if (docxResult.sections) {
50
- const headings = docxResult.sections.filter(s => s.type === 'heading').length;
51
- statusParts.push(`${headings} headings`);
52
- }
53
- if (statusParts.length > 0) {
54
- content = `[DOCX: ${statusParts.join(', ')}]\n\n${content}`;
55
- }
56
- }
33
+ // Return body XML as content - LLMs can modify this and write it back
57
34
  return {
58
- content,
59
- mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
35
+ content: result.bodyXml,
36
+ mimeType: 'application/xml',
60
37
  metadata: {
61
38
  isDocx: true,
62
- title: docxResult.metadata.title,
63
- author: docxResult.metadata.author,
64
- subject: docxResult.metadata.subject,
65
- description: docxResult.metadata.description,
66
- creationDate: docxResult.metadata.creationDate,
67
- modificationDate: docxResult.metadata.modificationDate,
68
- lastModifiedBy: docxResult.metadata.lastModifiedBy,
69
- revision: docxResult.metadata.revision,
70
- fileSize: docxResult.metadata.fileSize,
71
- images: docxResult.images,
72
- sections: docxResult.sections
39
+ author: result.metadata.author,
40
+ title: result.metadata.title,
41
+ subject: result.metadata.subject,
42
+ creator: result.metadata.creator,
43
+ paragraphCount: result.metadata.paragraphCount,
44
+ wordCount: result.metadata.wordCount,
45
+ paragraphs: result.paragraphs,
46
+ // Include extracted text for reference
47
+ extractedText: result.text
73
48
  }
74
49
  };
75
50
  }
@@ -86,98 +61,42 @@ export class DocxFileHandler {
86
61
  }
87
62
  }
88
63
  /**
89
- * Write DOCX file.
90
- *
91
- * - String content + 'rewrite': create new DOCX from HTML/markdown.
92
- * - String content + 'append': append to existing DOCX → writes to {name}_v1.docx.
93
- * - Array content: apply DocxOperation[] edits → writes to {name}_v1.docx.
94
- * Original file is always preserved.
64
+ * Write DOCX - NOT SUPPORTED via write_file
65
+ * Use write_docx tool instead to preserve styles
95
66
  */
96
- async write(path, content, mode = 'rewrite') {
97
- const baseDir = path ? this.getBaseDir(path) : process.cwd();
98
- // String content treat as HTML (or markdown which will be converted)
99
- if (typeof content === 'string') {
100
- if (mode === 'append') {
101
- // Append HTML/markdown — write to _v1 file, preserve original
102
- const targetPath = await generateOutputPath(path);
103
- const operations = [{
104
- type: 'appendMarkdown',
105
- markdown: content
106
- }];
107
- const buffer = await editDocxWithOperations(path, operations, { baseDir });
108
- await fs.writeFile(targetPath, buffer);
109
- }
110
- else {
111
- const html = convertToHtmlIfNeeded(content);
112
- const buffer = await createDocxFromHtml(html, { baseDir });
113
- await fs.writeFile(path, buffer);
114
- }
115
- return;
116
- }
117
- // Array content → treat as DocxOperation[], write to _v1 file, preserve original
118
- if (Array.isArray(content)) {
119
- try {
120
- await fs.access(path);
121
- }
122
- catch {
123
- throw new Error(`Cannot modify DOCX: source file does not exist: ${path}. Use string content to create a new DOCX file.`);
124
- }
125
- const targetPath = await generateOutputPath(path);
126
- const operations = content;
127
- const buffer = await editDocxWithOperations(path, operations, { baseDir });
128
- await fs.writeFile(targetPath, buffer);
129
- return;
130
- }
131
- throw new Error('Unsupported content type for DOCX write. Expected HTML/markdown string or array of operations.');
67
+ async write(path, content, mode) {
68
+ throw new Error('DOCX files cannot be written using write_file tool. ' +
69
+ 'Use write_docx tool instead to create or modify DOCX files while preserving styles and formatting.');
132
70
  }
133
71
  /**
134
- * Edit DOCX by applying high-level operations.
135
- * Writes to {name}_v1.docx unless `options.outputPath` is provided.
72
+ * Edit DOCX by applying modifications
136
73
  */
137
74
  async editRange(path, range, content, options) {
138
- const baseDir = this.getBaseDir(path);
139
- // Use provided outputPath, otherwise write to _v1 file
140
- const outputPath = options?.outputPath ?? await generateOutputPath(path);
141
- try {
142
- await fs.access(path);
143
- }
144
- catch {
145
- return {
146
- success: false,
147
- editsApplied: 0,
148
- errors: [{
149
- location: range,
150
- error: `Cannot edit DOCX: source file does not exist: ${path}`
151
- }]
152
- };
153
- }
154
- let operations;
155
- if (typeof content === 'string') {
156
- // Treat string content as HTML/markdown to append (will be converted to HTML internally)
157
- operations = [{
158
- type: 'appendMarkdown',
159
- markdown: content
160
- }];
161
- }
162
- else if (Array.isArray(content)) {
163
- operations = content;
164
- }
165
- else {
166
- return {
167
- success: false,
168
- editsApplied: 0,
169
- errors: [{
170
- location: range,
171
- error: 'Unsupported content type for DOCX edit. Expected HTML/markdown string or array of operations.'
172
- }]
173
- };
174
- }
175
75
  try {
176
- const buffer = await editDocxWithOperations(path, operations, { baseDir, outputPath });
177
- await fs.writeFile(outputPath, buffer);
76
+ // Parse content as modifications
77
+ let modifications = [];
78
+ if (Array.isArray(content)) {
79
+ modifications = content;
80
+ }
81
+ else if (typeof content === 'string') {
82
+ // Try to parse as JSON
83
+ try {
84
+ modifications = JSON.parse(content);
85
+ }
86
+ catch {
87
+ // If not JSON, treat as single replace operation
88
+ modifications = [{
89
+ type: 'replace',
90
+ findText: range,
91
+ replaceText: content
92
+ }];
93
+ }
94
+ }
95
+ const outputPath = options?.outputPath || path;
96
+ await modifyDocxContent(path, outputPath, modifications);
178
97
  return {
179
98
  success: true,
180
- editsApplied: operations.length
99
+ editsApplied: modifications.length
181
100
  };
182
101
  }
183
102
  catch (error) {
@@ -185,61 +104,42 @@ export class DocxFileHandler {
185
104
  return {
186
105
  success: false,
187
106
  editsApplied: 0,
188
- errors: [{
189
- location: range,
190
- error: errorMessage
191
- }]
107
+ errors: [{ location: range, error: errorMessage }]
192
108
  };
193
109
  }
194
110
  }
195
111
  /**
196
- * Get DOCX file information including metadata.
112
+ * Get DOCX file information
197
113
  */
198
114
  async getInfo(path) {
115
+ const stats = await fs.stat(path);
116
+ // Get DOCX metadata
117
+ let metadata = { isDocx: true };
199
118
  try {
200
- const stats = await fs.stat(path);
201
- let metadata = { isDocx: true };
202
- try {
203
- const docxResult = await parseDocxToHtml(path, {
204
- includeImages: false,
205
- preserveFormatting: false,
206
- });
207
- metadata = {
208
- isDocx: true,
209
- title: docxResult.metadata.title,
210
- author: docxResult.metadata.author,
211
- subject: docxResult.metadata.subject,
212
- description: docxResult.metadata.description,
213
- creationDate: docxResult.metadata.creationDate,
214
- modificationDate: docxResult.metadata.modificationDate,
215
- lastModifiedBy: docxResult.metadata.lastModifiedBy,
216
- revision: docxResult.metadata.revision,
217
- imageCount: docxResult.images.length,
218
- sectionCount: docxResult.sections?.length,
219
- };
220
- }
221
- catch {
222
- // Non-critical — return basic info if parsing fails
223
- }
224
- return {
225
- size: stats.size,
226
- created: stats.birthtime,
227
- modified: stats.mtime,
228
- accessed: stats.atime,
229
- isDirectory: false,
230
- isFile: true,
231
- permissions: (stats.mode & 0o777).toString(8),
232
- fileType: 'binary',
233
- metadata
119
+ const docxMetadata = await getDocxMetadata(path);
120
+ metadata = {
121
+ isDocx: true,
122
+ title: docxMetadata.title,
123
+ author: docxMetadata.author,
124
+ subject: docxMetadata.subject,
125
+ creator: docxMetadata.creator,
126
+ paragraphCount: docxMetadata.paragraphCount,
127
+ wordCount: docxMetadata.wordCount
234
128
  };
235
129
  }
236
- catch (error) {
237
- const message = error instanceof Error ? error.message : String(error);
238
- throw new DocxError(`Failed to get DOCX file info: ${message}`, 'GET_INFO_FAILED', { path });
130
+ catch {
131
+ // If we can't parse, just return basic info
239
132
  }
240
- }
241
- /** Get base directory for resolving relative image paths. */
242
- getBaseDir(docxPath) {
243
- return docxPath ? path.dirname(docxPath) : process.cwd();
133
+ return {
134
+ size: stats.size,
135
+ created: stats.birthtime,
136
+ modified: stats.mtime,
137
+ accessed: stats.atime,
138
+ isDirectory: false,
139
+ isFile: true,
140
+ permissions: (stats.mode & 0o777).toString(8),
141
+ fileType: 'binary',
142
+ metadata
143
+ };
244
144
  }
245
145
  }
@@ -87,7 +87,15 @@ export class TextFileHandler {
87
87
  * Made static and public for use by other modules (e.g., writeFile telemetry in filesystem.ts)
88
88
  */
89
89
  static countLines(content) {
90
- return content.split('\n').length;
90
+ if (content === '')
91
+ return 0;
92
+ // A file with N lines has N-1 newline characters.
93
+ // If the file ends with a trailing newline, don't count the empty string after it.
94
+ const lines = content.split('\n');
95
+ if (lines[lines.length - 1] === '') {
96
+ return lines.length - 1;
97
+ }
98
+ return lines.length;
91
99
  }
92
100
  /**
93
101
  * Get file line count (for files under size limit)
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.35";
1
+ export declare const VERSION = "0.2.36";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.2.35';
1
+ export const VERSION = '0.2.36';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.2.35",
3
+ "version": "0.2.36",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "mcpName": "io.github.wonderwhy-er/desktop-commander",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  "bump": "node scripts/sync-version.js --bump",
32
32
  "bump:minor": "node scripts/sync-version.js --bump --minor",
33
33
  "bump:major": "node scripts/sync-version.js --bump --major",
34
- "build": "tsc && shx cp setup-claude-server.js uninstall-claude-server.js track-installation.js dist/ && shx chmod +x dist/*.js && shx mkdir -p dist/data && shx cp src/data/onboarding-prompts.json dist/data/ && shx mkdir -p dist/remote-device/scripts && shx cp src/remote-device/scripts/blocking-offline-update.js dist/remote-device/scripts/",
34
+ "build": "tsc && shx cp setup-claude-server.js uninstall-claude-server.js track-installation.js dist/ && shx chmod +x dist/*.js && shx mkdir -p dist/data && shx cp src/data/onboarding-prompts.json dist/data/ && shx mkdir -p dist/remote-device/scripts && shx cp src/remote-device/scripts/blocking-offline-update.js dist/remote-device/scripts/ && node scripts/build-ui-runtime.cjs file-preview",
35
35
  "watch": "tsc --watch",
36
36
  "start": "node dist/index.js",
37
37
  "start:debug": "node --inspect-brk=9229 dist/index.js",
@@ -90,7 +90,9 @@
90
90
  "fastest-levenshtein": "^1.0.16",
91
91
  "file-type": "^21.1.1",
92
92
  "glob": "^10.3.10",
93
+ "highlight.js": "^11.11.1",
93
94
  "isbinaryfile": "^5.0.4",
95
+ "markdown-it": "^14.1.0",
94
96
  "md-to-pdf": "^5.2.5",
95
97
  "open": "^10.2.0",
96
98
  "pdf-lib": "^1.17.1",
@@ -110,6 +112,7 @@
110
112
  "@anthropic-ai/mcpb": "^1.2.0",
111
113
  "@types/node": "^20.17.24",
112
114
  "commander": "^13.1.0",
115
+ "esbuild": "^0.27.2",
113
116
  "nexe": "^5.0.0-beta.4",
114
117
  "nodemon": "^3.0.2",
115
118
  "shx": "^0.3.4",