@qvac/harness 0.7.2 → 0.8.0

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 (111) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/NOTICE.md +16 -0
  3. package/dist-lib/lib/capabilities/completion.d.ts +1 -0
  4. package/dist-lib/lib/capabilities/completion.js +46 -8
  5. package/dist-lib/lib/capabilities/tool-turns.d.ts +3 -0
  6. package/dist-lib/lib/capabilities/tool-turns.js +7 -0
  7. package/dist-lib/lib/context-window.js +2 -2
  8. package/dist-lib/lib/helpers/run-outcome.d.ts +4 -1
  9. package/dist-lib/lib/helpers/run-outcome.js +14 -8
  10. package/dist-lib/lib/helpers/tool-call-markup.js +4 -4
  11. package/dist-lib/lib/hooks/built-in/arg-repair.js +7 -0
  12. package/dist-lib/lib/office/engine/codes.d.ts +62 -0
  13. package/dist-lib/lib/office/engine/codes.js +61 -0
  14. package/dist-lib/lib/office/engine/docx/document.d.ts +216 -0
  15. package/dist-lib/lib/office/engine/docx/document.js +1152 -0
  16. package/dist-lib/lib/office/engine/docx/index.d.ts +7 -0
  17. package/dist-lib/lib/office/engine/docx/index.js +8 -0
  18. package/dist-lib/lib/office/engine/docx/styles.d.ts +31 -0
  19. package/dist-lib/lib/office/engine/docx/styles.js +173 -0
  20. package/dist-lib/lib/office/engine/docx/template.d.ts +18 -0
  21. package/dist-lib/lib/office/engine/docx/template.js +133 -0
  22. package/dist-lib/lib/office/engine/docx/text.d.ts +55 -0
  23. package/dist-lib/lib/office/engine/docx/text.js +538 -0
  24. package/dist-lib/lib/office/engine/drawingml/chart.d.ts +63 -0
  25. package/dist-lib/lib/office/engine/drawingml/chart.js +539 -0
  26. package/dist-lib/lib/office/engine/drawingml/text.d.ts +35 -0
  27. package/dist-lib/lib/office/engine/drawingml/text.js +352 -0
  28. package/dist-lib/lib/office/engine/errors.d.ts +6 -0
  29. package/dist-lib/lib/office/engine/errors.js +12 -0
  30. package/dist-lib/lib/office/engine/image.d.ts +8 -0
  31. package/dist-lib/lib/office/engine/image.js +48 -0
  32. package/dist-lib/lib/office/engine/index.d.ts +12 -0
  33. package/dist-lib/lib/office/engine/index.js +7 -0
  34. package/dist-lib/lib/office/engine/opc/content-types.d.ts +17 -0
  35. package/dist-lib/lib/office/engine/opc/content-types.js +74 -0
  36. package/dist-lib/lib/office/engine/opc/media.d.ts +13 -0
  37. package/dist-lib/lib/office/engine/opc/media.js +11 -0
  38. package/dist-lib/lib/office/engine/opc/package.d.ts +51 -0
  39. package/dist-lib/lib/office/engine/opc/package.js +324 -0
  40. package/dist-lib/lib/office/engine/opc/paths.d.ts +7 -0
  41. package/dist-lib/lib/office/engine/opc/paths.js +46 -0
  42. package/dist-lib/lib/office/engine/opc/properties.d.ts +7 -0
  43. package/dist-lib/lib/office/engine/opc/properties.js +75 -0
  44. package/dist-lib/lib/office/engine/opc/relationships.d.ts +23 -0
  45. package/dist-lib/lib/office/engine/opc/relationships.js +65 -0
  46. package/dist-lib/lib/office/engine/opc/zip.d.ts +15 -0
  47. package/dist-lib/lib/office/engine/opc/zip.js +264 -0
  48. package/dist-lib/lib/office/engine/pptx/fit.d.ts +12 -0
  49. package/dist-lib/lib/office/engine/pptx/fit.js +37 -0
  50. package/dist-lib/lib/office/engine/pptx/index.d.ts +8 -0
  51. package/dist-lib/lib/office/engine/pptx/index.js +8 -0
  52. package/dist-lib/lib/office/engine/pptx/inherit.d.ts +29 -0
  53. package/dist-lib/lib/office/engine/pptx/inherit.js +155 -0
  54. package/dist-lib/lib/office/engine/pptx/notes.d.ts +5 -0
  55. package/dist-lib/lib/office/engine/pptx/notes.js +85 -0
  56. package/dist-lib/lib/office/engine/pptx/presentation.d.ts +54 -0
  57. package/dist-lib/lib/office/engine/pptx/presentation.js +749 -0
  58. package/dist-lib/lib/office/engine/pptx/read.d.ts +9 -0
  59. package/dist-lib/lib/office/engine/pptx/read.js +66 -0
  60. package/dist-lib/lib/office/engine/pptx/shapes.d.ts +52 -0
  61. package/dist-lib/lib/office/engine/pptx/shapes.js +276 -0
  62. package/dist-lib/lib/office/engine/pptx/slides.d.ts +27 -0
  63. package/dist-lib/lib/office/engine/pptx/slides.js +123 -0
  64. package/dist-lib/lib/office/engine/pptx/template.d.ts +20 -0
  65. package/dist-lib/lib/office/engine/pptx/template.js +391 -0
  66. package/dist-lib/lib/office/engine/pptx/types.d.ts +120 -0
  67. package/dist-lib/lib/office/engine/pptx/types.js +1 -0
  68. package/dist-lib/lib/office/engine/units.d.ts +3 -0
  69. package/dist-lib/lib/office/engine/units.js +12 -0
  70. package/dist-lib/lib/office/engine/utf8.d.ts +2 -0
  71. package/dist-lib/lib/office/engine/utf8.js +103 -0
  72. package/dist-lib/lib/office/engine/xlsx/formula.d.ts +49 -0
  73. package/dist-lib/lib/office/engine/xlsx/formula.js +381 -0
  74. package/dist-lib/lib/office/engine/xlsx/index.d.ts +8 -0
  75. package/dist-lib/lib/office/engine/xlsx/index.js +8 -0
  76. package/dist-lib/lib/office/engine/xlsx/refs.d.ts +22 -0
  77. package/dist-lib/lib/office/engine/xlsx/refs.js +82 -0
  78. package/dist-lib/lib/office/engine/xlsx/reshape.d.ts +21 -0
  79. package/dist-lib/lib/office/engine/xlsx/reshape.js +424 -0
  80. package/dist-lib/lib/office/engine/xlsx/sheet.d.ts +108 -0
  81. package/dist-lib/lib/office/engine/xlsx/sheet.js +662 -0
  82. package/dist-lib/lib/office/engine/xlsx/strings.d.ts +15 -0
  83. package/dist-lib/lib/office/engine/xlsx/strings.js +93 -0
  84. package/dist-lib/lib/office/engine/xlsx/styles.d.ts +38 -0
  85. package/dist-lib/lib/office/engine/xlsx/styles.js +369 -0
  86. package/dist-lib/lib/office/engine/xlsx/template.d.ts +7 -0
  87. package/dist-lib/lib/office/engine/xlsx/template.js +54 -0
  88. package/dist-lib/lib/office/engine/xlsx/workbook.d.ts +161 -0
  89. package/dist-lib/lib/office/engine/xlsx/workbook.js +850 -0
  90. package/dist-lib/lib/office/engine/xlsx/write.d.ts +2 -0
  91. package/dist-lib/lib/office/engine/xlsx/write.js +42 -0
  92. package/dist-lib/lib/office/engine/xml.d.ts +45 -0
  93. package/dist-lib/lib/office/engine/xml.js +258 -0
  94. package/dist-lib/lib/tools/exec/scope.d.ts +2 -0
  95. package/dist-lib/lib/tools/exec/scope.js +33 -2
  96. package/dist-lib/lib/tools/pdf/build.d.ts +43 -0
  97. package/dist-lib/lib/tools/pdf/build.js +150 -0
  98. package/dist-lib/lib/tools/pdf/images.d.ts +7 -0
  99. package/dist-lib/lib/tools/pdf/images.js +82 -0
  100. package/dist-lib/lib/tools/pdf/request.d.ts +63 -0
  101. package/dist-lib/lib/tools/pdf/request.js +335 -0
  102. package/dist-lib/lib/tools/pdf.d.ts +6 -0
  103. package/dist-lib/lib/tools/pdf.js +302 -0
  104. package/dist-lib/lib/tools/skill.js +14 -0
  105. package/dist-lib/lib/tools/weather-lookup/geocode.d.ts +26 -0
  106. package/dist-lib/lib/tools/weather-lookup/geocode.js +110 -0
  107. package/dist-lib/lib/tools/weather-lookup/place.js +5 -1
  108. package/dist-lib/lib/tools/weather-lookup.d.ts +2 -1
  109. package/dist-lib/lib/tools/weather-lookup.js +115 -84
  110. package/package.json +29 -1
  111. package/worker/harness-sidecar.mjs +3 -1
@@ -0,0 +1,538 @@
1
+ import * as xml from '../xml.js';
2
+ import { DocsError } from '../errors.js';
3
+ import { isRecord } from '../../../guards.js';
4
+ export const W = xml.ns.w;
5
+ const XML_NS = 'http://www.w3.org/XML/1998/namespace';
6
+ const M = 'http://schemas.openxmlformats.org/officeDocument/2006/math';
7
+ const MC = 'http://schemas.openxmlformats.org/markup-compatibility/2006';
8
+ export const RUN_ORDER = [
9
+ 'rStyle',
10
+ 'rFonts',
11
+ 'b',
12
+ 'bCs',
13
+ 'i',
14
+ 'iCs',
15
+ 'caps',
16
+ 'smallCaps',
17
+ 'strike',
18
+ 'dstrike',
19
+ 'outline',
20
+ 'shadow',
21
+ 'emboss',
22
+ 'imprint',
23
+ 'noProof',
24
+ 'snapToGrid',
25
+ 'vanish',
26
+ 'webHidden',
27
+ 'color',
28
+ 'spacing',
29
+ 'w',
30
+ 'kern',
31
+ 'position',
32
+ 'sz',
33
+ 'szCs',
34
+ 'highlight',
35
+ 'u',
36
+ 'effect',
37
+ 'bdr',
38
+ 'shd',
39
+ 'fitText',
40
+ 'vertAlign',
41
+ 'rtl',
42
+ 'cs',
43
+ 'em',
44
+ 'lang',
45
+ 'eastAsianLayout',
46
+ 'specVanish',
47
+ 'oMath'
48
+ ];
49
+ export const PARAGRAPH_ORDER = [
50
+ 'pStyle',
51
+ 'keepNext',
52
+ 'keepLines',
53
+ 'pageBreakBefore',
54
+ 'framePr',
55
+ 'widowControl',
56
+ 'numPr',
57
+ 'suppressLineNumbers',
58
+ 'pBdr',
59
+ 'shd',
60
+ 'tabs',
61
+ 'suppressAutoHyphens',
62
+ 'kinsoku',
63
+ 'wordWrap',
64
+ 'overflowPunct',
65
+ 'topLinePunct',
66
+ 'autoSpaceDE',
67
+ 'autoSpaceDN',
68
+ 'bidi',
69
+ 'adjustRightInd',
70
+ 'snapToGrid',
71
+ 'spacing',
72
+ 'ind',
73
+ 'contextualSpacing',
74
+ 'mirrorIndents',
75
+ 'suppressOverlap',
76
+ 'jc',
77
+ 'textDirection',
78
+ 'textAlignment',
79
+ 'textboxTightWrap',
80
+ 'outlineLvl',
81
+ 'divId',
82
+ 'cnfStyle',
83
+ 'rPr',
84
+ 'sectPr',
85
+ 'pPrChange'
86
+ ];
87
+ const CONTAINERS = new Set([
88
+ 'hyperlink',
89
+ 'ins',
90
+ 'moveTo',
91
+ 'smartTag',
92
+ 'customXml',
93
+ 'fldSimple',
94
+ 'dir',
95
+ 'bdo',
96
+ 'sdtContent'
97
+ ]);
98
+ const KEPT = new Set(['bookmarkStart', 'bookmarkEnd', 'commentRangeStart', 'commentRangeEnd']);
99
+ const REFERENCES = ['commentReference', 'footnoteReference', 'endnoteReference'];
100
+ export const ALIGNMENTS = {
101
+ left: 'left',
102
+ center: 'center',
103
+ right: 'right',
104
+ justify: 'both'
105
+ };
106
+ export const FORMAT_KEYS = [
107
+ 'bold',
108
+ 'italic',
109
+ 'underline',
110
+ 'size',
111
+ 'color',
112
+ 'font',
113
+ 'align'
114
+ ];
115
+ export const RUN_KEYS = FORMAT_KEYS.filter((k) => k !== 'align');
116
+ const TOGGLES = ['bold', 'italic', 'underline'];
117
+ const LINK_FORMAT = ['rStyle', 'color', 'u'];
118
+ const OUTSIDE_TEXT = new Set(['hyperlink', 'fldSimple', 'sdt']);
119
+ const REVISIONS = ['rPrChange', 'pPrChange', 'ins', 'del', 'moveFrom', 'moveTo'];
120
+ const TRACKED = ['ins', 'del', 'moveFrom', 'moveTo', 'rPrChange', 'pPrChange'];
121
+ const BREAKS = new Set(['\n', '\v']);
122
+ export function wGet(el, name) {
123
+ return el ? el.getAttributeNS(W, name) || '' : '';
124
+ }
125
+ function runText(r) {
126
+ let out = '';
127
+ for (const n of xml.children(r, W)) {
128
+ const name = n.localName;
129
+ if (name === 't') {
130
+ out += xml.text(n);
131
+ }
132
+ else if (name === 'tab') {
133
+ out += '\t';
134
+ }
135
+ else if (name === 'br') {
136
+ out += wGet(n, 'type') === 'page' ? '' : '\n';
137
+ }
138
+ else if (name === 'cr') {
139
+ out += '\n';
140
+ }
141
+ else if (name === 'noBreakHyphen') {
142
+ out += '-';
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+ export function paragraphText(el) {
148
+ let out = '';
149
+ for (const n of xml.children(el, W)) {
150
+ const name = n.localName ?? '';
151
+ if (name === 'r') {
152
+ out += runText(n);
153
+ }
154
+ else if (name === 'sdt') {
155
+ const content = xml.child(n, W, 'sdtContent');
156
+ if (content) {
157
+ out += paragraphText(content);
158
+ }
159
+ }
160
+ else if (CONTAINERS.has(name)) {
161
+ out += paragraphText(n);
162
+ }
163
+ }
164
+ return out;
165
+ }
166
+ export function blockText(el) {
167
+ if (el.localName === 'p') {
168
+ return paragraphText(el);
169
+ }
170
+ return xml.descendants(el, W, 'p').map(paragraphText).join('\n');
171
+ }
172
+ export function hasPageBreak(p) {
173
+ return xml.descendants(p, W, 'br').some((br) => wGet(br, 'type') === 'page');
174
+ }
175
+ export function wAttr(el, name, value) {
176
+ el.setAttributeNS(W, `${el.prefix || 'w'}:${name}`, String(value));
177
+ }
178
+ export function wEl(doc, local, attributes = {}) {
179
+ const el = xml.builder(doc, W)(local);
180
+ for (const [key, value] of Object.entries(attributes)) {
181
+ if (value !== undefined && value !== null) {
182
+ wAttr(el, key, value);
183
+ }
184
+ }
185
+ return el;
186
+ }
187
+ function isAlignment(value) {
188
+ return typeof value === 'string' && value in ALIGNMENTS;
189
+ }
190
+ function checkKeys(format, known) {
191
+ const keys = Object.keys(format);
192
+ if (keys.length === 0) {
193
+ throw new DocsError('INVALID_STYLE', `format names nothing to change; use ${known.join(', ')}`);
194
+ }
195
+ const unknownKey = keys.find((key) => !known.includes(key));
196
+ if (unknownKey) {
197
+ throw new DocsError('INVALID_STYLE', `unknown format "${unknownKey}"; use ${known.join(', ')}`);
198
+ }
199
+ }
200
+ function checkValues(format) {
201
+ const { size, color, font, align } = format;
202
+ if (size !== undefined && !(typeof size === 'number' && size >= 1 && size <= 1638)) {
203
+ throw new DocsError('INVALID_STYLE', `size is in points, from 1 to 1638, got ${String(size)}`);
204
+ }
205
+ const toggle = TOGGLES.find((key) => format[key] !== undefined && typeof format[key] !== 'boolean');
206
+ if (toggle) {
207
+ throw new DocsError('INVALID_STYLE', `${toggle} must be true or false`);
208
+ }
209
+ if (color !== undefined && !(typeof color === 'string' && /^#?[0-9a-fA-F]{6}$/.test(color))) {
210
+ throw new DocsError('INVALID_STYLE', `color must be a hex RGB value like "1A73E8", got ${String(color)}`);
211
+ }
212
+ if (font !== undefined && !(typeof font === 'string' && font.trim())) {
213
+ throw new DocsError('INVALID_STYLE', 'font must be a typeface name');
214
+ }
215
+ if (align !== undefined && !isAlignment(align)) {
216
+ throw new DocsError('INVALID_STYLE', `align must be left, center, right or justify, got ${String(align)}`);
217
+ }
218
+ }
219
+ function flag(value) {
220
+ return typeof value === 'boolean' ? value : undefined;
221
+ }
222
+ export function validateFormat(format, known = FORMAT_KEYS) {
223
+ if (!isRecord(format) || Array.isArray(format)) {
224
+ throw new DocsError('INVALID_STYLE', 'format must be an object');
225
+ }
226
+ checkKeys(format, known);
227
+ checkValues(format);
228
+ const { size, color, font, align } = format;
229
+ const out = {};
230
+ if (typeof size === 'number') {
231
+ out.size = size;
232
+ }
233
+ if (flag(format.bold) !== undefined) {
234
+ out.bold = flag(format.bold);
235
+ }
236
+ if (flag(format.italic) !== undefined) {
237
+ out.italic = flag(format.italic);
238
+ }
239
+ if (flag(format.underline) !== undefined) {
240
+ out.underline = flag(format.underline);
241
+ }
242
+ if (typeof color === 'string') {
243
+ out.color = color;
244
+ }
245
+ if (typeof font === 'string') {
246
+ out.font = font;
247
+ }
248
+ if (isAlignment(align)) {
249
+ out.align = align;
250
+ }
251
+ return out;
252
+ }
253
+ function ensureRunChild(rPr, local) {
254
+ return xml.child(rPr, W, local) ?? xml.insertOrdered(rPr, wEl(xml.owner(rPr), local), RUN_ORDER);
255
+ }
256
+ function setToggle(rPr, local, value) {
257
+ const el = ensureRunChild(rPr, local);
258
+ if (value) {
259
+ el.removeAttributeNS(W, 'val');
260
+ }
261
+ else {
262
+ wAttr(el, 'val', '0');
263
+ }
264
+ }
265
+ function setValue(rPr, local, attributes) {
266
+ const el = ensureRunChild(rPr, local);
267
+ for (const [key, value] of Object.entries(attributes)) {
268
+ wAttr(el, key, value);
269
+ }
270
+ return el;
271
+ }
272
+ function applyColor(rPr, color) {
273
+ const el = setValue(rPr, 'color', { val: color.replace('#', '').toUpperCase() });
274
+ for (const attr of ['themeColor', 'themeTint', 'themeShade']) {
275
+ el.removeAttributeNS(W, attr);
276
+ }
277
+ }
278
+ function applyFont(rPr, font) {
279
+ const el = setValue(rPr, 'rFonts', { ascii: font, hAnsi: font, cs: font, eastAsia: font });
280
+ for (const attr of ['asciiTheme', 'hAnsiTheme', 'cstheme', 'eastAsiaTheme']) {
281
+ el.removeAttributeNS(W, attr);
282
+ }
283
+ }
284
+ export function applyRunFormat(rPr, format) {
285
+ if (format.bold !== undefined) {
286
+ setToggle(rPr, 'b', format.bold);
287
+ setToggle(rPr, 'bCs', format.bold);
288
+ }
289
+ if (format.italic !== undefined) {
290
+ setToggle(rPr, 'i', format.italic);
291
+ setToggle(rPr, 'iCs', format.italic);
292
+ }
293
+ if (format.underline !== undefined) {
294
+ setValue(rPr, 'u', { val: format.underline ? 'single' : 'none' });
295
+ }
296
+ if (format.size !== undefined) {
297
+ const half = Math.round(format.size * 2);
298
+ setValue(rPr, 'sz', { val: half });
299
+ setValue(rPr, 'szCs', { val: half });
300
+ }
301
+ if (format.color !== undefined) {
302
+ applyColor(rPr, format.color);
303
+ }
304
+ if (format.font !== undefined) {
305
+ applyFont(rPr, format.font);
306
+ }
307
+ }
308
+ export function ensure(parent, local, order) {
309
+ const existing = xml.child(parent, W, local);
310
+ if (existing) {
311
+ return existing;
312
+ }
313
+ const el = wEl(xml.owner(parent), local);
314
+ if (order) {
315
+ xml.insertOrdered(parent, el, order);
316
+ }
317
+ else {
318
+ parent.insertBefore(el, parent.firstChild);
319
+ }
320
+ return el;
321
+ }
322
+ export function setAlign(p, align) {
323
+ const pPr = ensure(p, 'pPr');
324
+ wAttr(ensure(pPr, 'jc', PARAGRAPH_ORDER), 'val', ALIGNMENTS[align]);
325
+ }
326
+ function normalizeRun(run, i) {
327
+ if (typeof run === 'string') {
328
+ return { text: run };
329
+ }
330
+ if (!isRecord(run) || typeof run.text !== 'string') {
331
+ throw new DocsError('INVALID_TEXT', `run ${i} must be a string or { text, bold, italic, ... }`);
332
+ }
333
+ const { text, ...rest } = run;
334
+ const format = Object.keys(rest).length ? validateFormat(rest, RUN_KEYS) : {};
335
+ return { ...format, text };
336
+ }
337
+ export function normalizeRuns(input) {
338
+ if (typeof input === 'string') {
339
+ return [{ text: input }];
340
+ }
341
+ if (isRecord(input) && Array.isArray(input.runs)) {
342
+ return input.runs.map(normalizeRun);
343
+ }
344
+ if (isRecord(input) && typeof input.text === 'string') {
345
+ return [{ text: input.text }];
346
+ }
347
+ throw new DocsError('INVALID_TEXT', 'text must be a string, { text } or { runs: [...] }');
348
+ }
349
+ function withoutRevisions(el) {
350
+ for (const local of REVISIONS) {
351
+ for (const node of xml.descendants(el, W, local)) {
352
+ xml.remove(node);
353
+ }
354
+ }
355
+ return el;
356
+ }
357
+ function insideOf(r, p) {
358
+ for (let el = r.parentNode; el && el !== p; el = el.parentNode) {
359
+ if (xml.isElement(el, W) && OUTSIDE_TEXT.has(el.localName ?? '')) {
360
+ return el.localName;
361
+ }
362
+ }
363
+ return null;
364
+ }
365
+ function withoutLinkFormat(rPr) {
366
+ for (const local of LINK_FORMAT) {
367
+ for (const el of xml.children(rPr, W, local)) {
368
+ xml.remove(el);
369
+ }
370
+ }
371
+ return rPr;
372
+ }
373
+ function templateRunProperties(p) {
374
+ if (!p)
375
+ return null;
376
+ const runs = xml.descendants(p, W, 'r').filter((r) => xml.child(r, W, 't'));
377
+ const plain = runs.find((r) => insideOf(r, p) === null);
378
+ const run = plain ?? runs[0];
379
+ if (run) {
380
+ const rPr = xml.child(run, W, 'rPr');
381
+ if (!rPr) {
382
+ return null;
383
+ }
384
+ const copy = withoutRevisions(xml.clone(rPr));
385
+ return plain || insideOf(run, p) !== 'hyperlink' ? copy : withoutLinkFormat(copy);
386
+ }
387
+ const pPr = xml.child(p, W, 'pPr');
388
+ const mark = pPr ? xml.child(pPr, W, 'rPr') : null;
389
+ return mark ? withoutRevisions(xml.clone(mark)) : null;
390
+ }
391
+ function runFormatOf(run) {
392
+ return Object.fromEntries(Object.entries(run).filter(([key]) => key !== 'text'));
393
+ }
394
+ function appendPiece(r, piece) {
395
+ const doc = xml.owner(r);
396
+ if (BREAKS.has(piece)) {
397
+ r.appendChild(wEl(doc, 'br'));
398
+ return;
399
+ }
400
+ if (piece === '\t') {
401
+ r.appendChild(wEl(doc, 'tab'));
402
+ return;
403
+ }
404
+ const t = wEl(doc, 't');
405
+ if (/^\s|\s$/.test(piece)) {
406
+ t.setAttributeNS(XML_NS, 'xml:space', 'preserve');
407
+ }
408
+ t.appendChild(doc.createTextNode(piece));
409
+ r.appendChild(t);
410
+ }
411
+ function buildRun(doc, run, base) {
412
+ const format = runFormatOf(run);
413
+ const r = wEl(doc, 'r');
414
+ let rPr = base ? xml.clone(base) : null;
415
+ if (Object.keys(format).length) {
416
+ rPr = rPr ?? wEl(doc, 'rPr');
417
+ applyRunFormat(rPr, format);
418
+ }
419
+ if (rPr?.firstChild) {
420
+ r.appendChild(rPr);
421
+ }
422
+ for (const piece of run.text.replace(/\r\n?/g, '\n').split(/(\n|\v|\t)/)) {
423
+ const value = BREAKS.has(piece) ? piece : xml.clean(piece);
424
+ if (value !== '') {
425
+ appendPiece(r, value);
426
+ }
427
+ }
428
+ return r;
429
+ }
430
+ export function assertUntracked(p, where) {
431
+ if (TRACKED.some((local) => xml.descendants(p, W, local).length)) {
432
+ throw new DocsError('TRACKED_CHANGES', `${where} has tracked changes; rewriting it would accept them silently, so accept or reject them in Word first`);
433
+ }
434
+ }
435
+ function inFallback(el) {
436
+ for (let node = el.parentNode; node; node = node.parentNode) {
437
+ if (xml.isElement(node, MC, 'Fallback')) {
438
+ return true;
439
+ }
440
+ }
441
+ return false;
442
+ }
443
+ function count(p, namespace, local) {
444
+ return xml.descendants(p, namespace, local).filter((el) => !inFallback(el)).length;
445
+ }
446
+ function equations(p) {
447
+ const inline = xml
448
+ .descendants(p, M, 'oMath')
449
+ .filter((el) => !xml.isElement(el.parentNode, M, 'oMathPara'));
450
+ return count(p, M, 'oMathPara') + inline.filter((el) => !inFallback(el)).length;
451
+ }
452
+ function losses(p) {
453
+ const begins = xml.descendants(p, W, 'fldChar').filter((f) => wGet(f, 'fldCharType') === 'begin');
454
+ const breaks = xml.descendants(p, W, 'br').filter((br) => {
455
+ const type = wGet(br, 'type');
456
+ return type === 'page' || type === 'column';
457
+ });
458
+ const found = {
459
+ hyperlinks: count(p, W, 'hyperlink'),
460
+ images: count(p, W, 'drawing') + count(p, W, 'pict'),
461
+ fields: count(p, W, 'fldSimple') + begins.length,
462
+ contentControls: count(p, W, 'sdt'),
463
+ equations: equations(p),
464
+ objects: count(p, W, 'object'),
465
+ breaks: breaks.length
466
+ };
467
+ return Object.fromEntries(Object.entries(found).filter(([, n]) => n > 0));
468
+ }
469
+ export function replaceContent(p, input) {
470
+ const doc = xml.owner(p);
471
+ const runs = normalizeRuns(input);
472
+ const base = templateRunProperties(p);
473
+ const removed = losses(p);
474
+ const kept = [];
475
+ const references = [];
476
+ for (const n of xml.children(p)) {
477
+ const name = n.localName ?? '';
478
+ if (name === 'pPr') {
479
+ continue;
480
+ }
481
+ if (KEPT.has(name)) {
482
+ kept.push(n);
483
+ }
484
+ else if (name === 'r' && REFERENCES.some((ref) => xml.child(n, W, ref))) {
485
+ references.push(n);
486
+ }
487
+ xml.remove(n);
488
+ }
489
+ const starts = kept.filter((n) => (n.localName ?? '').endsWith('Start'));
490
+ const ends = kept.filter((n) => !(n.localName ?? '').endsWith('Start'));
491
+ const built = runs.map((run) => buildRun(doc, run, base));
492
+ for (const n of [...starts, ...built, ...ends, ...references]) {
493
+ p.appendChild(n);
494
+ }
495
+ return removed;
496
+ }
497
+ function specOf(input) {
498
+ if (typeof input === 'string') {
499
+ return { text: input };
500
+ }
501
+ if (!isRecord(input) || Array.isArray(input)) {
502
+ throw new DocsError('INVALID_TEXT', 'each paragraph must be a string or an object');
503
+ }
504
+ return input;
505
+ }
506
+ function copyTemplate(p, template) {
507
+ const pPr = xml.child(template, W, 'pPr');
508
+ if (pPr) {
509
+ const copy = withoutRevisions(xml.clone(pPr));
510
+ for (const el of xml.children(copy, W, 'sectPr')) {
511
+ xml.remove(el);
512
+ }
513
+ p.appendChild(copy);
514
+ }
515
+ return templateRunProperties(template);
516
+ }
517
+ export function buildParagraph(doc, input, template) {
518
+ const p = wEl(doc, 'p');
519
+ const spec = specOf(input);
520
+ if (spec.pageBreak) {
521
+ xml.append(p, wEl(doc, 'r')).appendChild(wEl(doc, 'br', { type: 'page' }));
522
+ return p;
523
+ }
524
+ const base = template && spec.style === undefined ? copyTemplate(p, template) : null;
525
+ if (typeof spec.styleId === 'string' && spec.styleId) {
526
+ wAttr(ensure(ensure(p, 'pPr'), 'pStyle', PARAGRAPH_ORDER), 'val', spec.styleId);
527
+ }
528
+ if (spec.align) {
529
+ const { align } = validateFormat({ align: spec.align });
530
+ if (align) {
531
+ setAlign(p, align);
532
+ }
533
+ }
534
+ for (const run of normalizeRuns(spec)) {
535
+ p.appendChild(buildRun(doc, run, base));
536
+ }
537
+ return p;
538
+ }
@@ -0,0 +1,63 @@
1
+ import * as text from './text.ts';
2
+ import type { Document } from '../xml.ts';
3
+ import type { Package } from '../opc/package.ts';
4
+ export type ChartType = 'column' | 'bar' | 'line' | 'pie' | 'doughnut' | 'area';
5
+ export type Legend = false | 'right' | 'left' | 'top' | 'bottom';
6
+ export type Stacking = boolean | 'percent';
7
+ export type Category = string | number;
8
+ export interface Series {
9
+ name: string;
10
+ values: (number | null)[];
11
+ }
12
+ export interface ChartData {
13
+ categories: Category[];
14
+ series: Series[];
15
+ }
16
+ export interface ChartSpec extends ChartData {
17
+ type: ChartType;
18
+ title?: string;
19
+ stacked?: Stacking;
20
+ legend?: Legend;
21
+ }
22
+ export interface ReadSeries {
23
+ name: string | null;
24
+ values: (string | number | null)[];
25
+ }
26
+ export interface ChartInfo {
27
+ type: string | null;
28
+ types?: string[];
29
+ title: string | null;
30
+ categories: (string | number | null)[];
31
+ series: ReadSeries[];
32
+ }
33
+ export interface SeriesLink {
34
+ name: string | null;
35
+ values: string;
36
+ }
37
+ export interface ChartLinks {
38
+ categories: string;
39
+ series: SeriesLink[];
40
+ }
41
+ export interface WriteOptions {
42
+ links?: ChartLinks;
43
+ }
44
+ export interface ChartRef {
45
+ pkg: Package;
46
+ part: string;
47
+ }
48
+ export interface DataResult {
49
+ series: number;
50
+ categories: number;
51
+ }
52
+ export declare const C: "http://schemas.openxmlformats.org/drawingml/2006/chart";
53
+ export declare const CHART_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
54
+ export declare const GRAPHIC_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
55
+ export declare const CREATABLE: readonly ChartType[];
56
+ export declare function readChart(doc: Document): ChartInfo;
57
+ export declare function validateData(data: unknown): ChartData;
58
+ export declare function setChartData(ref: ChartRef, input: unknown, opts?: WriteOptions): DataResult;
59
+ export interface Changed {
60
+ changed: boolean;
61
+ }
62
+ export declare function setChartTitle(ref: ChartRef, value: text.TextInput | null): Changed;
63
+ export declare function createChart(ref: ChartRef, spec: unknown, opts?: WriteOptions): string;