@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
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, InitializeRequestSchema, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, InitializeRequestSchema, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
4
4
|
import { getSystemInfo, getOSSpecificGuidance, getPathGuidance, getDevelopmentToolGuidance } from './utils/system-info.js';
|
|
5
5
|
// Get system information once at startup
|
|
@@ -21,6 +21,8 @@ import { handleWelcomePageOnboarding } from './utils/welcome-onboarding.js';
|
|
|
21
21
|
import { VERSION } from './version.js';
|
|
22
22
|
import { capture, capture_call_tool } from "./utils/capture.js";
|
|
23
23
|
import { logToStderr, logger } from './utils/logger.js';
|
|
24
|
+
import { buildUiToolMeta, FILE_PREVIEW_RESOURCE_URI } from './ui/contracts.js';
|
|
25
|
+
import { listUiResources, readUiResource } from './ui/resources.js';
|
|
24
26
|
// Store startup messages to send after initialization
|
|
25
27
|
const deferredMessages = [];
|
|
26
28
|
function deferLog(level, message) {
|
|
@@ -47,11 +49,18 @@ export const server = new Server({
|
|
|
47
49
|
});
|
|
48
50
|
// Add handler for resources/list method
|
|
49
51
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
50
|
-
// Return an empty list of resources
|
|
51
52
|
return {
|
|
52
|
-
resources:
|
|
53
|
+
resources: listUiResources(),
|
|
53
54
|
};
|
|
54
55
|
});
|
|
56
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
57
|
+
const { uri } = request.params;
|
|
58
|
+
const response = await readUiResource(uri);
|
|
59
|
+
if (response) {
|
|
60
|
+
return response;
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
63
|
+
});
|
|
55
64
|
// Add handler for prompts/list method
|
|
56
65
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
57
66
|
// Return an empty list of prompts
|
|
@@ -233,10 +242,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
233
242
|
- PDF: Extracts text content as markdown with page structure
|
|
234
243
|
* offset/length work as page pagination (0-based)
|
|
235
244
|
* Includes embedded images when available
|
|
245
|
+
- DOCX (.docx): Two modes depending on parameters:
|
|
246
|
+
* DEFAULT (no offset/length): Returns a text-bearing outline — shows paragraphs with text,
|
|
247
|
+
tables with cell content, styles, image refs. Skips shapes/drawings/SVG noise.
|
|
248
|
+
Each element shows its body index [0], [1], etc.
|
|
249
|
+
* WITH offset/length: Returns raw pretty-printed XML with line pagination.
|
|
250
|
+
Use this to drill into specific sections or see the actual XML for editing.
|
|
251
|
+
* EDITING WORKFLOW: 1) read_file to get outline, 2) read_file with offset/length
|
|
252
|
+
to see raw XML around what you want to edit, 3) edit_block with old_string/new_string
|
|
253
|
+
using XML fragments copied from the read output.
|
|
254
|
+
* IMPORTANT: offset MUST be non-zero to get raw XML (use offset=1 to start from line 1).
|
|
255
|
+
offset=0 always returns the outline regardless of length.
|
|
256
|
+
* For BULK changes (translation, mass replacements): use start_process with Python
|
|
257
|
+
zipfile module to find/replace all <w:t> elements at once.
|
|
236
258
|
|
|
237
259
|
${PATH_GUIDANCE}
|
|
238
260
|
${CMD_PREFIX_DESCRIPTION}`,
|
|
239
261
|
inputSchema: zodToJsonSchema(ReadFileArgsSchema),
|
|
262
|
+
_meta: buildUiToolMeta(FILE_PREVIEW_RESOURCE_URI, true),
|
|
240
263
|
annotations: {
|
|
241
264
|
title: "Read File or URL",
|
|
242
265
|
readOnlyHint: true,
|
|
@@ -269,6 +292,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
269
292
|
Write or append to file contents.
|
|
270
293
|
|
|
271
294
|
IMPORTANT: DO NOT use this tool to create PDF files. Use 'write_pdf' for all PDF creation tasks.
|
|
295
|
+
DO NOT use this tool to edit DOCX files. Use 'edit_block' with old_string/new_string instead.
|
|
296
|
+
To CREATE a new DOCX, use write_file with .docx extension — text content with markdown headings (#, ##, ###) is converted to styled DOCX paragraphs.
|
|
272
297
|
|
|
273
298
|
CHUNKING IS STANDARD PRACTICE: Always write files in chunks of 25-30 lines maximum.
|
|
274
299
|
This is the normal, recommended way to write files - not an emergency measure.
|
|
@@ -647,6 +672,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
647
672
|
- new_string: Replacement text
|
|
648
673
|
- expected_replacements: Optional number of replacements (default: 1)
|
|
649
674
|
|
|
675
|
+
DOCX FILES (.docx) - XML Find/Replace mode:
|
|
676
|
+
Takes same parameters as text files (old_string, new_string, expected_replacements).
|
|
677
|
+
Operates on the pretty-printed XML inside the DOCX — the same XML you see from
|
|
678
|
+
read_file with offset/length. Copy XML fragments from read output as old_string.
|
|
679
|
+
After editing, the XML is repacked into a valid DOCX.
|
|
680
|
+
Also searches headers/footers if not found in document body.
|
|
681
|
+
Examples:
|
|
682
|
+
- Replace text: old_string="<w:t>Old Text</w:t>" new_string="<w:t>New Text</w:t>"
|
|
683
|
+
- Change style: old_string='<w:pStyle w:val="Normal"/>' new_string='<w:pStyle w:val="Heading1"/>'
|
|
684
|
+
- Add content: include surrounding XML context in old_string, add new elements in new_string
|
|
685
|
+
|
|
650
686
|
By default, replaces only ONE occurrence of the search text.
|
|
651
687
|
To replace multiple occurrences, provide expected_replacements with
|
|
652
688
|
the exact number of matches expected.
|
|
@@ -1179,6 +1215,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1179
1215
|
};
|
|
1180
1216
|
}
|
|
1181
1217
|
break;
|
|
1218
|
+
case "track_ui_event":
|
|
1219
|
+
try {
|
|
1220
|
+
result = await handlers.handleTrackUiEvent(args);
|
|
1221
|
+
}
|
|
1222
|
+
catch (error) {
|
|
1223
|
+
capture('server_request_error', { message: `Error in track_ui_event handler: ${error}` });
|
|
1224
|
+
result = {
|
|
1225
|
+
content: [{ type: "text", text: `Error: Failed to track UI event` }],
|
|
1226
|
+
isError: true,
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
break;
|
|
1182
1230
|
case "give_feedback_to_desktop_commander":
|
|
1183
1231
|
try {
|
|
1184
1232
|
result = await giveFeedbackToDesktopCommander(args);
|
|
@@ -1265,12 +1313,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1265
1313
|
// Add tool call to history (exclude only get_recent_tool_calls to prevent recursion)
|
|
1266
1314
|
const duration = Date.now() - startTime;
|
|
1267
1315
|
const EXCLUDED_TOOLS = [
|
|
1268
|
-
'get_recent_tool_calls'
|
|
1316
|
+
'get_recent_tool_calls',
|
|
1317
|
+
'track_ui_event'
|
|
1269
1318
|
];
|
|
1270
1319
|
if (!EXCLUDED_TOOLS.includes(name)) {
|
|
1271
1320
|
toolHistory.addCall(name, args, result, duration);
|
|
1272
1321
|
}
|
|
1273
1322
|
// Track success or failure based on result
|
|
1323
|
+
if (name === 'track_ui_event') {
|
|
1324
|
+
return result;
|
|
1325
|
+
}
|
|
1274
1326
|
if (result.isError) {
|
|
1275
1327
|
await usageTracker.trackFailure(name);
|
|
1276
1328
|
console.log(`[FEEDBACK DEBUG] Tool ${name} failed, not checking feedback`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image builder — creates w:drawing elements and manages image relationships.
|
|
3
|
+
*/
|
|
4
|
+
import PizZip from 'pizzip';
|
|
5
|
+
import type { DocxContentImage, InsertImageOp } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build an image element and add it to the ZIP archive.
|
|
8
|
+
*
|
|
9
|
+
* @param doc The XML document
|
|
10
|
+
* @param zip The DOCX ZIP archive
|
|
11
|
+
* @param spec The image specification (from content or operation)
|
|
12
|
+
* @returns A w:p element containing the image drawing
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildImageElement(doc: Document, zip: PizZip, spec: DocxContentImage | InsertImageOp): Promise<Element>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image builder — creates w:drawing elements and manages image relationships.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { DOMParser } from '@xmldom/xmldom';
|
|
7
|
+
import { addImageRelationship, ensureContentType } from '../relationships.js';
|
|
8
|
+
import { escapeXmlAttr } from './utils.js';
|
|
9
|
+
import { pixelsToEmu, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, NAMESPACES } from '../constants.js';
|
|
10
|
+
/**
|
|
11
|
+
* Build an image element and add it to the ZIP archive.
|
|
12
|
+
*
|
|
13
|
+
* @param doc The XML document
|
|
14
|
+
* @param zip The DOCX ZIP archive
|
|
15
|
+
* @param spec The image specification (from content or operation)
|
|
16
|
+
* @returns A w:p element containing the image drawing
|
|
17
|
+
*/
|
|
18
|
+
export async function buildImageElement(doc, zip, spec) {
|
|
19
|
+
// Validate image exists
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(spec.imagePath);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`Image file not found: ${spec.imagePath}`);
|
|
25
|
+
}
|
|
26
|
+
// Read image
|
|
27
|
+
const imgBuffer = await fs.readFile(spec.imagePath);
|
|
28
|
+
const ext = path.extname(spec.imagePath).toLowerCase();
|
|
29
|
+
const baseName = path.basename(spec.imagePath);
|
|
30
|
+
// Find next available media filename
|
|
31
|
+
let mediaIndex = 1;
|
|
32
|
+
while (zip.file(`word/media/image${mediaIndex}${ext}`)) {
|
|
33
|
+
mediaIndex++;
|
|
34
|
+
}
|
|
35
|
+
const mediaFileName = `image${mediaIndex}${ext}`;
|
|
36
|
+
// Add image to ZIP
|
|
37
|
+
zip.file(`word/media/${mediaFileName}`, imgBuffer);
|
|
38
|
+
// Add relationship
|
|
39
|
+
const rId = addImageRelationship(zip, mediaFileName);
|
|
40
|
+
// Ensure Content_Types entry
|
|
41
|
+
ensureContentType(zip, ext);
|
|
42
|
+
// Compute dimensions (EMU)
|
|
43
|
+
const widthPx = spec.width ?? DEFAULT_IMAGE_WIDTH;
|
|
44
|
+
const heightPx = spec.height ?? DEFAULT_IMAGE_HEIGHT;
|
|
45
|
+
const widthEmu = pixelsToEmu(widthPx);
|
|
46
|
+
const heightEmu = pixelsToEmu(heightPx);
|
|
47
|
+
// Build drawing XML
|
|
48
|
+
const altText = spec.altText ?? baseName;
|
|
49
|
+
const drawingXmlStr = buildDrawingXml(rId, widthEmu, heightEmu, altText, mediaFileName);
|
|
50
|
+
// Parse drawing XML into a paragraph
|
|
51
|
+
const drawingFragment = new DOMParser().parseFromString(`<w:p xmlns:w="${NAMESPACES.W}">` +
|
|
52
|
+
`<w:r>${drawingXmlStr}</w:r></w:p>`, 'application/xml');
|
|
53
|
+
return doc.importNode(drawingFragment.documentElement, true);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build the inline w:drawing XML for an image reference.
|
|
57
|
+
*/
|
|
58
|
+
function buildDrawingXml(rId, widthEmu, heightEmu, altText, fileName) {
|
|
59
|
+
return (`<w:drawing xmlns:w="${NAMESPACES.W}">` +
|
|
60
|
+
`<wp:inline distT="0" distB="0" distL="0" distR="0" ` +
|
|
61
|
+
`xmlns:wp="${NAMESPACES.WP}">` +
|
|
62
|
+
`<wp:extent cx="${widthEmu}" cy="${heightEmu}"/>` +
|
|
63
|
+
`<wp:docPr id="1" name="${fileName}" descr="${escapeXmlAttr(altText)}"/>` +
|
|
64
|
+
`<a:graphic xmlns:a="${NAMESPACES.A}">` +
|
|
65
|
+
`<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">` +
|
|
66
|
+
`<pic:pic xmlns:pic="${NAMESPACES.PIC}">` +
|
|
67
|
+
`<pic:nvPicPr>` +
|
|
68
|
+
`<pic:cNvPr id="0" name="${fileName}" descr="${escapeXmlAttr(altText)}"/>` +
|
|
69
|
+
`<pic:cNvPicPr/>` +
|
|
70
|
+
`</pic:nvPicPr>` +
|
|
71
|
+
`<pic:blipFill>` +
|
|
72
|
+
`<a:blip r:embed="${rId}" xmlns:r="${NAMESPACES.R}"/>` +
|
|
73
|
+
`<a:stretch><a:fillRect/></a:stretch>` +
|
|
74
|
+
`</pic:blipFill>` +
|
|
75
|
+
`<pic:spPr>` +
|
|
76
|
+
`<a:xfrm><a:off x="0" y="0"/><a:ext cx="${widthEmu}" cy="${heightEmu}"/></a:xfrm>` +
|
|
77
|
+
`<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>` +
|
|
78
|
+
`</pic:spPr>` +
|
|
79
|
+
`</pic:pic>` +
|
|
80
|
+
`</a:graphicData>` +
|
|
81
|
+
`</a:graphic>` +
|
|
82
|
+
`</wp:inline>` +
|
|
83
|
+
`</w:drawing>`);
|
|
84
|
+
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DOCX
|
|
3
|
-
*
|
|
2
|
+
* DOCX element builders — Single Responsibility: build XML elements
|
|
3
|
+
* for paragraphs, tables, and images.
|
|
4
|
+
*
|
|
5
|
+
* These builders are shared between create.ts and ops/ modules to
|
|
6
|
+
* eliminate code duplication and ensure consistency.
|
|
4
7
|
*/
|
|
5
|
-
export
|
|
8
|
+
export { buildParagraph } from './paragraph.js';
|
|
9
|
+
export { buildTable } from './table.js';
|
|
10
|
+
export { buildImageElement } from './image.js';
|
|
11
|
+
export { escapeXml, escapeXmlAttr } from './utils.js';
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DOCX
|
|
3
|
-
*
|
|
2
|
+
* DOCX element builders — Single Responsibility: build XML elements
|
|
3
|
+
* for paragraphs, tables, and images.
|
|
4
|
+
*
|
|
5
|
+
* These builders are shared between create.ts and ops/ modules to
|
|
6
|
+
* eliminate code duplication and ensure consistency.
|
|
4
7
|
*/
|
|
5
|
-
export
|
|
8
|
+
export { buildParagraph } from './paragraph.js';
|
|
9
|
+
export { buildTable } from './table.js';
|
|
10
|
+
export { buildImageElement } from './image.js';
|
|
11
|
+
export { escapeXml, escapeXmlAttr } from './utils.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paragraph builder — creates w:p elements with optional styles.
|
|
3
|
+
*/
|
|
4
|
+
import type { DocxContentParagraph } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Build a paragraph element from content structure.
|
|
7
|
+
*
|
|
8
|
+
* @param doc The XML document
|
|
9
|
+
* @param item The paragraph content item
|
|
10
|
+
* @returns A w:p element
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildParagraph(doc: Document, item: DocxContentParagraph): Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paragraph builder — creates w:p elements with optional styles.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Build a paragraph element from content structure.
|
|
6
|
+
*
|
|
7
|
+
* @param doc The XML document
|
|
8
|
+
* @param item The paragraph content item
|
|
9
|
+
* @returns A w:p element
|
|
10
|
+
*/
|
|
11
|
+
export function buildParagraph(doc, item) {
|
|
12
|
+
const p = doc.createElement('w:p');
|
|
13
|
+
// Set style if provided
|
|
14
|
+
if (item.style) {
|
|
15
|
+
const pPr = doc.createElement('w:pPr');
|
|
16
|
+
const pStyle = doc.createElement('w:pStyle');
|
|
17
|
+
pStyle.setAttribute('w:val', item.style);
|
|
18
|
+
pPr.appendChild(pStyle);
|
|
19
|
+
p.appendChild(pPr);
|
|
20
|
+
}
|
|
21
|
+
// Add text run
|
|
22
|
+
const r = doc.createElement('w:r');
|
|
23
|
+
const t = doc.createElement('w:t');
|
|
24
|
+
t.setAttribute('xml:space', 'preserve');
|
|
25
|
+
t.textContent = item.text;
|
|
26
|
+
r.appendChild(t);
|
|
27
|
+
p.appendChild(r);
|
|
28
|
+
return p;
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table builder — creates w:tbl elements with headers, rows, and styling.
|
|
3
|
+
* Supports multiple paragraphs per cell with different styles.
|
|
4
|
+
*/
|
|
5
|
+
import type { DocxContentTable, InsertTableOp } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build a table element from content structure or operation.
|
|
8
|
+
* Supports cells with multiple paragraphs, each with its own style.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildTable(doc: Document, spec: DocxContentTable | InsertTableOp): Element;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table builder — creates w:tbl elements with headers, rows, and styling.
|
|
3
|
+
* Supports multiple paragraphs per cell with different styles.
|
|
4
|
+
*/
|
|
5
|
+
import { buildParagraph } from './paragraph.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build a table element from content structure or operation.
|
|
8
|
+
* Supports cells with multiple paragraphs, each with its own style.
|
|
9
|
+
*/
|
|
10
|
+
export function buildTable(doc, spec) {
|
|
11
|
+
const tbl = doc.createElement('w:tbl');
|
|
12
|
+
// Table properties
|
|
13
|
+
const tblPr = doc.createElement('w:tblPr');
|
|
14
|
+
if (spec.style) {
|
|
15
|
+
const tblStyle = doc.createElement('w:tblStyle');
|
|
16
|
+
tblStyle.setAttribute('w:val', spec.style);
|
|
17
|
+
tblPr.appendChild(tblStyle);
|
|
18
|
+
}
|
|
19
|
+
const tblW = doc.createElement('w:tblW');
|
|
20
|
+
tblW.setAttribute('w:w', '0');
|
|
21
|
+
tblW.setAttribute('w:type', 'auto');
|
|
22
|
+
tblPr.appendChild(tblW);
|
|
23
|
+
// Table borders
|
|
24
|
+
const tblBorders = doc.createElement('w:tblBorders');
|
|
25
|
+
for (const side of ['top', 'left', 'bottom', 'right', 'insideH', 'insideV']) {
|
|
26
|
+
const border = doc.createElement(`w:${side}`);
|
|
27
|
+
border.setAttribute('w:val', 'single');
|
|
28
|
+
border.setAttribute('w:sz', '4');
|
|
29
|
+
border.setAttribute('w:space', '0');
|
|
30
|
+
border.setAttribute('w:color', '000000');
|
|
31
|
+
tblBorders.appendChild(border);
|
|
32
|
+
}
|
|
33
|
+
tblPr.appendChild(tblBorders);
|
|
34
|
+
tbl.appendChild(tblPr);
|
|
35
|
+
// Determine column count
|
|
36
|
+
const colCount = spec.headers
|
|
37
|
+
? spec.headers.length
|
|
38
|
+
: spec.rows.length > 0
|
|
39
|
+
? spec.rows[0].length
|
|
40
|
+
: 0;
|
|
41
|
+
// Table grid
|
|
42
|
+
if (colCount > 0) {
|
|
43
|
+
const tblGrid = doc.createElement('w:tblGrid');
|
|
44
|
+
for (let c = 0; c < colCount; c++) {
|
|
45
|
+
const gridCol = doc.createElement('w:gridCol');
|
|
46
|
+
const w = spec.colWidths?.[c] ?? Math.floor(9000 / colCount);
|
|
47
|
+
gridCol.setAttribute('w:w', String(w));
|
|
48
|
+
tblGrid.appendChild(gridCol);
|
|
49
|
+
}
|
|
50
|
+
tbl.appendChild(tblGrid);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a cell from content.
|
|
54
|
+
* Content can be:
|
|
55
|
+
* - A string: creates one paragraph with that text
|
|
56
|
+
* - An array of DocxContentParagraph: creates multiple paragraphs, each with its own style
|
|
57
|
+
*/
|
|
58
|
+
const buildCell = (content, isHeader, widthTwips) => {
|
|
59
|
+
const tc = doc.createElement('w:tc');
|
|
60
|
+
// Cell properties (width)
|
|
61
|
+
if (widthTwips) {
|
|
62
|
+
const tcPr = doc.createElement('w:tcPr');
|
|
63
|
+
const tcW = doc.createElement('w:tcW');
|
|
64
|
+
tcW.setAttribute('w:w', String(widthTwips));
|
|
65
|
+
tcW.setAttribute('w:type', 'dxa');
|
|
66
|
+
tcPr.appendChild(tcW);
|
|
67
|
+
tc.appendChild(tcPr);
|
|
68
|
+
}
|
|
69
|
+
// Handle content: string or array of paragraphs
|
|
70
|
+
if (typeof content === 'string') {
|
|
71
|
+
// Simple case: single paragraph
|
|
72
|
+
const p = doc.createElement('w:p');
|
|
73
|
+
const r = doc.createElement('w:r');
|
|
74
|
+
// Header cells get bold
|
|
75
|
+
if (isHeader) {
|
|
76
|
+
const rPr = doc.createElement('w:rPr');
|
|
77
|
+
const b = doc.createElement('w:b');
|
|
78
|
+
rPr.appendChild(b);
|
|
79
|
+
r.appendChild(rPr);
|
|
80
|
+
}
|
|
81
|
+
const t = doc.createElement('w:t');
|
|
82
|
+
t.setAttribute('xml:space', 'preserve');
|
|
83
|
+
t.textContent = content;
|
|
84
|
+
r.appendChild(t);
|
|
85
|
+
p.appendChild(r);
|
|
86
|
+
tc.appendChild(p);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Complex case: multiple paragraphs with different styles
|
|
90
|
+
for (const paraSpec of content) {
|
|
91
|
+
const p = buildParagraph(doc, paraSpec);
|
|
92
|
+
// If header and first paragraph, ensure bold on runs
|
|
93
|
+
if (isHeader) {
|
|
94
|
+
const runs = p.getElementsByTagName('w:r');
|
|
95
|
+
for (let i = 0; i < runs.length; i++) {
|
|
96
|
+
const run = runs.item(i);
|
|
97
|
+
let rPr = run.getElementsByTagName('w:rPr').item(0);
|
|
98
|
+
if (!rPr) {
|
|
99
|
+
rPr = doc.createElement('w:rPr');
|
|
100
|
+
if (run.firstChild) {
|
|
101
|
+
run.insertBefore(rPr, run.firstChild);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
run.appendChild(rPr);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Add bold if not already present
|
|
108
|
+
if (!rPr.getElementsByTagName('w:b').length) {
|
|
109
|
+
const b = doc.createElement('w:b');
|
|
110
|
+
rPr.appendChild(b);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
tc.appendChild(p);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return tc;
|
|
118
|
+
};
|
|
119
|
+
// Header row
|
|
120
|
+
if (spec.headers && spec.headers.length > 0) {
|
|
121
|
+
const tr = doc.createElement('w:tr');
|
|
122
|
+
for (let i = 0; i < spec.headers.length; i++) {
|
|
123
|
+
const width = spec.colWidths?.[i];
|
|
124
|
+
tr.appendChild(buildCell(spec.headers[i], true, width));
|
|
125
|
+
}
|
|
126
|
+
tbl.appendChild(tr);
|
|
127
|
+
}
|
|
128
|
+
// Data rows
|
|
129
|
+
for (const row of spec.rows) {
|
|
130
|
+
const tr = doc.createElement('w:tr');
|
|
131
|
+
for (let i = 0; i < row.length; i++) {
|
|
132
|
+
const width = spec.colWidths?.[i];
|
|
133
|
+
tr.appendChild(buildCell(row[i], false, width));
|
|
134
|
+
}
|
|
135
|
+
tbl.appendChild(tr);
|
|
136
|
+
}
|
|
137
|
+
return tbl;
|
|
138
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML escaping utilities.
|
|
3
|
+
*/
|
|
4
|
+
export function escapeXml(s) {
|
|
5
|
+
return s
|
|
6
|
+
.replace(/&/g, '&')
|
|
7
|
+
.replace(/</g, '<')
|
|
8
|
+
.replace(/>/g, '>')
|
|
9
|
+
.replace(/"/g, '"')
|
|
10
|
+
.replace(/'/g, ''');
|
|
11
|
+
}
|
|
12
|
+
export function escapeXmlAttr(s) {
|
|
13
|
+
return s
|
|
14
|
+
.replace(/&/g, '&')
|
|
15
|
+
.replace(/"/g, '"')
|
|
16
|
+
.replace(/</g, '<')
|
|
17
|
+
.replace(/>/g, '>');
|
|
18
|
+
}
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DOCX
|
|
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
|
-
export declare const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
readonly
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
readonly footer: 720;
|
|
24
|
-
readonly gutter: 0;
|
|
25
|
-
};
|
|
26
|
-
readonly footer: true;
|
|
27
|
-
readonly pageNumber: false;
|
|
4
|
+
export declare const IMAGE_MIME_TYPES: Record<string, string>;
|
|
5
|
+
export declare function getMimeType(ext: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Convert pixels to EMU (English Metric Units).
|
|
8
|
+
* 1 inch = 914400 EMU, 1 px ≈ 9525 EMU (at 96 DPI)
|
|
9
|
+
*/
|
|
10
|
+
export declare const PX_TO_EMU = 9525;
|
|
11
|
+
export declare function pixelsToEmu(px: number): number;
|
|
12
|
+
export declare const NAMESPACES: {
|
|
13
|
+
readonly W: "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14
|
+
readonly WP: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
|
|
15
|
+
readonly A: "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
16
|
+
readonly PIC: "http://schemas.openxmlformats.org/drawingml/2006/picture";
|
|
17
|
+
readonly R: "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
18
|
+
readonly RELS: "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
28
19
|
};
|
|
29
|
-
export declare const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
readonly
|
|
20
|
+
export declare const DEFAULT_IMAGE_WIDTH = 300;
|
|
21
|
+
export declare const DEFAULT_IMAGE_HEIGHT = 200;
|
|
22
|
+
export declare const DOCX_PATHS: {
|
|
23
|
+
readonly CONTENT_TYPES: "[Content_Types].xml";
|
|
24
|
+
readonly DOCUMENT_XML: "word/document.xml";
|
|
25
|
+
readonly DOCUMENT_RELS: "word/_rels/document.xml.rels";
|
|
26
|
+
readonly ROOT_RELS: "_rels/.rels";
|
|
27
|
+
readonly STYLES_XML: "word/styles.xml";
|
|
28
|
+
readonly SETTINGS_XML: "word/settings.xml";
|
|
29
|
+
readonly WEB_SETTINGS_XML: "word/webSettings.xml";
|
|
30
|
+
readonly FONT_TABLE_XML: "word/fontTable.xml";
|
|
31
|
+
readonly MEDIA_FOLDER: "word/media";
|
|
33
32
|
};
|
|
34
|
-
export declare const CORE_PROPERTIES_PATH = "docProps/core.xml";
|
|
35
|
-
export declare const IMAGE_MIME_TYPES: Readonly<Record<string, string>>;
|
|
36
|
-
export declare const HTML_WRAPPER_TEMPLATE = "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n</head>\n<body>\n{content}\n</body>\n</html>";
|