@wonderwhy-er/desktop-commander 0.2.36 → 0.2.38

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 (94) hide show
  1. package/README.md +240 -100
  2. package/dist/command-manager.js +6 -3
  3. package/dist/config-field-definitions.d.ts +41 -0
  4. package/dist/config-field-definitions.js +37 -0
  5. package/dist/config-manager.d.ts +2 -0
  6. package/dist/config-manager.js +22 -2
  7. package/dist/handlers/filesystem-handlers.js +6 -11
  8. package/dist/handlers/macos-control-handlers.d.ts +16 -0
  9. package/dist/handlers/macos-control-handlers.js +81 -0
  10. package/dist/lib.d.ts +10 -0
  11. package/dist/lib.js +10 -0
  12. package/dist/remote-device/remote-channel.d.ts +8 -3
  13. package/dist/remote-device/remote-channel.js +68 -21
  14. package/dist/search-manager.d.ts +13 -0
  15. package/dist/search-manager.js +146 -0
  16. package/dist/server.js +29 -1
  17. package/dist/test-docx.d.ts +1 -0
  18. package/dist/tools/config.d.ts +71 -0
  19. package/dist/tools/config.js +117 -2
  20. package/dist/tools/docx/builders/table.d.ts +2 -0
  21. package/dist/tools/docx/builders/table.js +60 -16
  22. package/dist/tools/docx/dom.d.ts +74 -1
  23. package/dist/tools/docx/dom.js +221 -1
  24. package/dist/tools/docx/index.d.ts +2 -2
  25. package/dist/tools/docx/ops/index.js +3 -0
  26. package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +15 -3
  27. package/dist/tools/docx/ops/replace-paragraph-text-exact.js +25 -10
  28. package/dist/tools/docx/ops/replace-table-cell-text.d.ts +25 -0
  29. package/dist/tools/docx/ops/replace-table-cell-text.js +85 -0
  30. package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +2 -1
  31. package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +9 -8
  32. package/dist/tools/docx/ops/set-color-for-style.d.ts +4 -0
  33. package/dist/tools/docx/ops/set-color-for-style.js +11 -7
  34. package/dist/tools/docx/ops/table-set-cell-text.js +8 -40
  35. package/dist/tools/docx/read.d.ts +2 -2
  36. package/dist/tools/docx/read.js +137 -17
  37. package/dist/tools/docx/types.d.ts +32 -3
  38. package/dist/tools/docx/xml-view-test.d.ts +1 -0
  39. package/dist/tools/docx/xml-view-test.js +63 -0
  40. package/dist/tools/docx/xml-view.d.ts +56 -0
  41. package/dist/tools/docx/xml-view.js +169 -0
  42. package/dist/tools/edit.js +57 -27
  43. package/dist/tools/macos-control/ax-adapter.d.ts +55 -0
  44. package/dist/tools/macos-control/ax-adapter.js +438 -0
  45. package/dist/tools/macos-control/cdp-adapter.d.ts +23 -0
  46. package/dist/tools/macos-control/cdp-adapter.js +402 -0
  47. package/dist/tools/macos-control/orchestrator.d.ts +77 -0
  48. package/dist/tools/macos-control/orchestrator.js +136 -0
  49. package/dist/tools/macos-control/role-aliases.d.ts +5 -0
  50. package/dist/tools/macos-control/role-aliases.js +34 -0
  51. package/dist/tools/macos-control/types.d.ts +129 -0
  52. package/dist/tools/macos-control/types.js +1 -0
  53. package/dist/tools/schemas.d.ts +3 -0
  54. package/dist/tools/schemas.js +2 -1
  55. package/dist/types.d.ts +0 -1
  56. package/dist/ui/config-editor/config-editor-runtime.js +14181 -0
  57. package/dist/ui/config-editor/index.html +13 -0
  58. package/dist/ui/config-editor/src/app.d.ts +43 -0
  59. package/dist/ui/config-editor/src/app.js +840 -0
  60. package/dist/ui/config-editor/src/array-modal.d.ts +19 -0
  61. package/dist/ui/config-editor/src/array-modal.js +185 -0
  62. package/dist/ui/config-editor/src/main.d.ts +1 -0
  63. package/dist/ui/config-editor/src/main.js +2 -0
  64. package/dist/ui/config-editor/styles.css +586 -0
  65. package/dist/ui/file-preview/preview-runtime.js +13337 -752
  66. package/dist/ui/file-preview/shared/preview-file-types.js +3 -1
  67. package/dist/ui/file-preview/src/app.d.ts +5 -1
  68. package/dist/ui/file-preview/src/app.js +114 -200
  69. package/dist/ui/file-preview/src/components/html-renderer.d.ts +1 -5
  70. package/dist/ui/file-preview/src/components/html-renderer.js +11 -27
  71. package/dist/ui/file-preview/styles.css +117 -83
  72. package/dist/ui/resources.d.ts +7 -0
  73. package/dist/ui/resources.js +16 -2
  74. package/dist/ui/shared/compact-row.d.ts +11 -0
  75. package/dist/ui/shared/compact-row.js +18 -0
  76. package/dist/ui/shared/host-context.d.ts +15 -0
  77. package/dist/ui/shared/host-context.js +51 -0
  78. package/dist/ui/shared/tool-bridge.d.ts +30 -0
  79. package/dist/ui/shared/tool-bridge.js +137 -0
  80. package/dist/ui/shared/tool-shell.d.ts +9 -0
  81. package/dist/ui/shared/tool-shell.js +46 -4
  82. package/dist/ui/shared/ui-event-tracker.d.ts +9 -0
  83. package/dist/ui/shared/ui-event-tracker.js +27 -0
  84. package/dist/utils/capture.js +173 -11
  85. package/dist/utils/files/base.d.ts +3 -1
  86. package/dist/utils/files/docx.d.ts +28 -15
  87. package/dist/utils/files/docx.js +622 -88
  88. package/dist/utils/files/factory.d.ts +6 -5
  89. package/dist/utils/files/factory.js +18 -6
  90. package/dist/utils/system-info.js +1 -1
  91. package/dist/utils/usageTracker.js +5 -0
  92. package/dist/version.d.ts +1 -1
  93. package/dist/version.js +1 -1
  94. package/package.json +8 -3
@@ -6,6 +6,77 @@ export declare function getConfig(): Promise<{
6
6
  type: string;
7
7
  text: string;
8
8
  }[];
9
+ structuredContent: {
10
+ config: {
11
+ currentClient: {
12
+ name: string;
13
+ version: string;
14
+ };
15
+ featureFlags: Record<string, any>;
16
+ systemInfo: {
17
+ memory: {
18
+ rss: string;
19
+ heapTotal: string;
20
+ heapUsed: string;
21
+ external: string;
22
+ arrayBuffers: string;
23
+ };
24
+ platform: string;
25
+ platformName: string;
26
+ defaultShell: string;
27
+ pathSeparator: string;
28
+ isWindows: boolean;
29
+ isMacOS: boolean;
30
+ isLinux: boolean;
31
+ docker: import("../utils/system-info.js").ContainerInfo;
32
+ isDXT: boolean;
33
+ nodeInfo?: {
34
+ version: string;
35
+ path: string;
36
+ npmVersion?: string;
37
+ };
38
+ pythonInfo?: {
39
+ available: boolean;
40
+ command: string;
41
+ version?: string;
42
+ };
43
+ processInfo: {
44
+ pid: number;
45
+ arch: string;
46
+ platform: string;
47
+ versions: NodeJS.ProcessVersions;
48
+ };
49
+ examplePaths: {
50
+ home: string;
51
+ temp: string;
52
+ absolute: string;
53
+ accessible?: string[];
54
+ };
55
+ };
56
+ blockedCommands?: string[];
57
+ defaultShell?: string;
58
+ allowedDirectories?: string[];
59
+ telemetryEnabled?: boolean;
60
+ fileWriteLineLimit?: number;
61
+ fileReadLineLimit?: number;
62
+ clientId?: string;
63
+ };
64
+ uiHints: {
65
+ availableShells: string[];
66
+ };
67
+ entries: {
68
+ key: "defaultShell" | "blockedCommands" | "allowedDirectories" | "telemetryEnabled" | "fileReadLineLimit" | "fileWriteLineLimit";
69
+ value: unknown;
70
+ valueType: "string" | "number" | "boolean" | "array";
71
+ editable: boolean;
72
+ }[];
73
+ };
74
+ } | {
75
+ content: {
76
+ type: string;
77
+ text: string;
78
+ }[];
79
+ structuredContent?: undefined;
9
80
  }>;
10
81
  /**
11
82
  * Set a specific config value
@@ -3,6 +3,74 @@ import { SetConfigValueArgsSchema } from './schemas.js';
3
3
  import { getSystemInfo } from '../utils/system-info.js';
4
4
  import { currentClient } from '../server.js';
5
5
  import { featureFlagManager } from '../utils/feature-flags.js';
6
+ import { access, readFile } from 'node:fs/promises';
7
+ import { constants as fsConstants } from 'node:fs';
8
+ import { CONFIG_FIELD_DEFINITIONS, CONFIG_FIELD_KEYS, isConfigFieldKey, } from '../config-field-definitions.js';
9
+ const ALLOWED_CONFIG_KEYS = new Set(CONFIG_FIELD_KEYS);
10
+ async function pathExists(pathValue) {
11
+ try {
12
+ await access(pathValue, fsConstants.X_OK);
13
+ return true;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ async function detectAvailableShells(systemInfo) {
20
+ const detected = new Set();
21
+ const add = (shell) => {
22
+ if (shell.trim().length > 0) {
23
+ detected.add(shell.trim());
24
+ }
25
+ };
26
+ add(systemInfo.defaultShell);
27
+ if (systemInfo.isWindows) {
28
+ add(process.env.ComSpec ?? '');
29
+ const systemRoot = process.env.SystemRoot ?? 'C:\\Windows';
30
+ const candidates = [
31
+ `${systemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,
32
+ `${systemRoot}\\System32\\cmd.exe`,
33
+ `${systemRoot}\\System32\\bash.exe`,
34
+ 'powershell.exe',
35
+ 'pwsh.exe',
36
+ 'cmd.exe',
37
+ 'bash.exe',
38
+ ];
39
+ for (const shell of candidates) {
40
+ if (shell.includes('\\')) {
41
+ if (await pathExists(shell)) {
42
+ add(shell);
43
+ }
44
+ }
45
+ else {
46
+ add(shell);
47
+ }
48
+ }
49
+ return [...detected];
50
+ }
51
+ add(process.env.SHELL ?? '');
52
+ const shellFiles = ['/etc/shells'];
53
+ for (const shellFile of shellFiles) {
54
+ try {
55
+ const content = await readFile(shellFile, 'utf8');
56
+ content
57
+ .split(/\r?\n/)
58
+ .map((line) => line.trim())
59
+ .filter((line) => line.length > 0 && !line.startsWith('#'))
60
+ .forEach(add);
61
+ }
62
+ catch {
63
+ // Best-effort discovery only.
64
+ }
65
+ }
66
+ const fallbackCandidates = ['/bin/zsh', '/bin/bash', '/bin/sh', '/usr/bin/fish'];
67
+ for (const shell of fallbackCandidates) {
68
+ if (await pathExists(shell)) {
69
+ add(shell);
70
+ }
71
+ }
72
+ return [...detected];
73
+ }
6
74
  /**
7
75
  * Get the entire config including system information
8
76
  */
@@ -30,12 +98,29 @@ export async function getConfig() {
30
98
  memory
31
99
  }
32
100
  };
101
+ const availableShells = await detectAvailableShells(systemInfo);
33
102
  console.error(`getConfig result: ${JSON.stringify(configWithSystemInfo, null, 2)}`);
34
103
  return {
35
104
  content: [{
36
105
  type: "text",
37
106
  text: `Current configuration:\n${JSON.stringify(configWithSystemInfo, null, 2)}`
38
107
  }],
108
+ structuredContent: {
109
+ config: configWithSystemInfo,
110
+ uiHints: {
111
+ availableShells,
112
+ },
113
+ entries: CONFIG_FIELD_KEYS.map((key) => {
114
+ const definition = CONFIG_FIELD_DEFINITIONS[key];
115
+ const value = configWithSystemInfo[key];
116
+ return {
117
+ key,
118
+ value,
119
+ valueType: definition.valueType,
120
+ editable: true,
121
+ };
122
+ }),
123
+ },
39
124
  };
40
125
  }
41
126
  catch (error) {
@@ -67,7 +152,17 @@ export async function setConfigValue(args) {
67
152
  isError: true
68
153
  };
69
154
  }
155
+ if (!isConfigFieldKey(parsed.data.key)) {
156
+ return {
157
+ content: [{
158
+ type: "text",
159
+ text: `Key "${parsed.data.key}" is not configurable via this tool. Allowed keys: ${[...ALLOWED_CONFIG_KEYS].join(', ')}`
160
+ }],
161
+ isError: true
162
+ };
163
+ }
70
164
  try {
165
+ const fieldDefinition = CONFIG_FIELD_DEFINITIONS[parsed.data.key];
71
166
  // Parse string values that should be arrays or objects
72
167
  let valueToStore = parsed.data.value;
73
168
  // If the value is a string that looks like an array or object, try to parse it
@@ -82,8 +177,7 @@ export async function setConfigValue(args) {
82
177
  }
83
178
  }
84
179
  // Special handling for known array configuration keys
85
- if ((parsed.data.key === 'allowedDirectories' || parsed.data.key === 'blockedCommands') &&
86
- !Array.isArray(valueToStore)) {
180
+ if (fieldDefinition.valueType === 'array' && !Array.isArray(valueToStore)) {
87
181
  if (typeof valueToStore === 'string') {
88
182
  const originalString = valueToStore;
89
183
  try {
@@ -108,6 +202,27 @@ export async function setConfigValue(args) {
108
202
  valueToStore = [String(valueToStore)];
109
203
  }
110
204
  }
205
+ // Harden boolean fields against stringly-typed inputs like "false".
206
+ if (fieldDefinition.valueType === 'boolean') {
207
+ if (typeof valueToStore === 'string') {
208
+ const normalized = valueToStore.trim().toLowerCase();
209
+ if (normalized === 'true') {
210
+ valueToStore = true;
211
+ }
212
+ else if (normalized === 'false') {
213
+ valueToStore = false;
214
+ }
215
+ }
216
+ if (typeof valueToStore !== 'boolean') {
217
+ return {
218
+ content: [{
219
+ type: "text",
220
+ text: `Value for ${parsed.data.key} must be boolean true/false.`
221
+ }],
222
+ isError: true
223
+ };
224
+ }
225
+ }
111
226
  await configManager.setValue(parsed.data.key, valueToStore);
112
227
  // Get the updated configuration to show the user
113
228
  const updatedConfig = await configManager.getConfig();
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Table builder — creates w:tbl elements with headers, rows, and styling.
3
+ * Supports multiple paragraphs per cell with different styles.
3
4
  */
4
5
  import type { DocxContentTable, InsertTableOp } from '../types.js';
5
6
  /**
6
7
  * Build a table element from content structure or operation.
8
+ * Supports cells with multiple paragraphs, each with its own style.
7
9
  */
8
10
  export declare function buildTable(doc: Document, spec: DocxContentTable | InsertTableOp): Element;
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Table builder — creates w:tbl elements with headers, rows, and styling.
3
+ * Supports multiple paragraphs per cell with different styles.
3
4
  */
5
+ import { buildParagraph } from './paragraph.js';
4
6
  /**
5
7
  * Build a table element from content structure or operation.
8
+ * Supports cells with multiple paragraphs, each with its own style.
6
9
  */
7
10
  export function buildTable(doc, spec) {
8
11
  const tbl = doc.createElement('w:tbl');
@@ -29,12 +32,13 @@ export function buildTable(doc, spec) {
29
32
  }
30
33
  tblPr.appendChild(tblBorders);
31
34
  tbl.appendChild(tblPr);
32
- // Table grid
35
+ // Determine column count
33
36
  const colCount = spec.headers
34
37
  ? spec.headers.length
35
38
  : spec.rows.length > 0
36
39
  ? spec.rows[0].length
37
40
  : 0;
41
+ // Table grid
38
42
  if (colCount > 0) {
39
43
  const tblGrid = doc.createElement('w:tblGrid');
40
44
  for (let c = 0; c < colCount; c++) {
@@ -45,9 +49,15 @@ export function buildTable(doc, spec) {
45
49
  }
46
50
  tbl.appendChild(tblGrid);
47
51
  }
48
- // Helper to build a cell
49
- const buildCell = (text, isHeader, widthTwips) => {
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) => {
50
59
  const tc = doc.createElement('w:tc');
60
+ // Cell properties (width)
51
61
  if (widthTwips) {
52
62
  const tcPr = doc.createElement('w:tcPr');
53
63
  const tcW = doc.createElement('w:tcW');
@@ -56,20 +66,54 @@ export function buildTable(doc, spec) {
56
66
  tcPr.appendChild(tcW);
57
67
  tc.appendChild(tcPr);
58
68
  }
59
- const p = doc.createElement('w:p');
60
- const r = doc.createElement('w:r');
61
- if (isHeader) {
62
- const rPr = doc.createElement('w:rPr');
63
- const b = doc.createElement('w:b');
64
- rPr.appendChild(b);
65
- r.appendChild(rPr);
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
+ }
66
116
  }
67
- const t = doc.createElement('w:t');
68
- t.setAttribute('xml:space', 'preserve');
69
- t.textContent = text;
70
- r.appendChild(t);
71
- p.appendChild(r);
72
- tc.appendChild(p);
73
117
  return tc;
74
118
  };
75
119
  // Header row
@@ -23,6 +23,17 @@ export declare function getBody(doc: Document): Element;
23
23
  * Includes w:p, w:tbl, w:sdt, w:sectPr, etc.
24
24
  */
25
25
  export declare function getBodyChildren(body: Element): Element[];
26
+ /**
27
+ * Return ALL top‑level tables that are logically in the body, including those
28
+ * wrapped in structured document tags (w:sdt / w:sdtContent).
29
+ *
30
+ * Previous logic only saw tables that were direct children of <w:body>. That
31
+ * meant tables inside SDTs were invisible to table operations and readDocxOutline.
32
+ * This helper walks the body tree and collects any <w:tbl> that appears as a
33
+ * *first‑class* block (we do not recurse into tables themselves, so nested
34
+ * tables are not double‑counted).
35
+ */
36
+ export declare function getAllBodyTables(body: Element): Element[];
26
37
  /**
27
38
  * Build a compact signature string from the body children array.
28
39
  * Maps each node's qualified name to a short local name:
@@ -34,13 +45,75 @@ export declare function bodySignature(children: Element[]): string;
34
45
  export declare function getParagraphText(p: Element): string;
35
46
  /** Read the style id from w:pPr/w:pStyle/@w:val, or null if absent. */
36
47
  export declare function getParagraphStyle(p: Element): string | null;
48
+ /**
49
+ * Extract all text content from a table cell (w:tc).
50
+ * Returns the concatenated text from all paragraphs in the cell.
51
+ */
52
+ export declare function getCellText(tc: Element): string;
53
+ /**
54
+ * Extract all rows from a table (w:tbl).
55
+ * Returns an array of rows, where each row is an array of cell text strings.
56
+ * First row is treated as header if it exists.
57
+ */
58
+ export declare function getTableContent(tbl: Element): {
59
+ headers?: string[];
60
+ rows: string[][];
61
+ };
62
+ /**
63
+ * Get table style from w:tblPr/w:tblStyle/@w:val, or null if absent.
64
+ */
65
+ export declare function getTableStyle(tbl: Element): string | null;
66
+ /**
67
+ * Extract image reference from a w:drawing element.
68
+ * Returns the relationship ID (rId) and media file path if found.
69
+ */
70
+ export declare function getImageReference(drawing: Element): {
71
+ rId: string | null;
72
+ mediaPath: string | null;
73
+ };
37
74
  /**
38
75
  * Replace the text of a paragraph with minimal DOM changes.
39
76
  * Sets the FIRST w:t to `text`, clears every subsequent w:t.
40
77
  * Sets xml:space="preserve" so leading/trailing spaces survive.
41
- * Does NOT recreate runs or remove paragraph properties.
78
+ * Does NOT remove/recreate runs or remove paragraph properties.
79
+ *
80
+ * WARNING: This function does NOT preserve multiple runs with different styles.
81
+ * Use setParagraphTextPreservingStyles() for cells with multiple styled runs.
42
82
  */
43
83
  export declare function setParagraphTextMinimal(p: Element, text: string): void;
84
+ /**
85
+ * Replace paragraph text while preserving all run styles.
86
+ *
87
+ * This function preserves the structure of all runs (w:r) and their
88
+ * properties (w:rPr), distributing the new text across existing runs.
89
+ *
90
+ * Strategy:
91
+ * 1. Collect all runs with their properties
92
+ * 2. Distribute new text across runs (preserving run count and styles)
93
+ * 3. If new text is longer, extend the last run
94
+ * 4. If new text is shorter, clear excess runs but keep their structure
95
+ */
96
+ export declare function setParagraphTextPreservingStyles(p: Element, text: string): void;
97
+ /**
98
+ * Replace cell text while preserving ALL paragraphs and their styles.
99
+ *
100
+ * This function works at the cell level:
101
+ * - Preserves ALL paragraphs in the cell (doesn't remove any)
102
+ * - Updates text in the first paragraph while preserving its styles
103
+ * - Keeps all other paragraphs intact with their original text and styles
104
+ *
105
+ * This ensures that cells with multiple paragraphs (each with different
106
+ * styles, font sizes, etc.) maintain their structure after text replacement.
107
+ *
108
+ * Example: If a cell has:
109
+ * - Paragraph 1: "LAWN AND LANDSCAPE" (Heading1 style, large font, red color)
110
+ * - Paragraph 2: "Take your weekends back..." (Normal style, smaller font, black color)
111
+ *
112
+ * Replacing with "EARTH AND MOUNTAIN" will:
113
+ * - Update paragraph 1 to "EARTH AND MOUNTAIN" (preserving Heading1 style, large font, red color)
114
+ * - Keep paragraph 2 completely intact with its original text and style
115
+ */
116
+ export declare function setCellTextPreservingStyles(tc: Element, text: string): void;
44
117
  /**
45
118
  * Ensure a <w:r> element has w:rPr/w:color[@w:val=hex].
46
119
  * Creates w:rPr and w:color if they don't exist.