@a3s-lab/office 0.27.0 → 0.29.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 (47) hide show
  1. package/README.md +41 -1
  2. package/dist/0~1403.js +1 -1
  3. package/dist/0~4980.js +1 -1
  4. package/dist/0~5093.js +164 -5
  5. package/dist/0~7043.js +241 -241
  6. package/dist/0~7048.js +1 -1
  7. package/dist/0~7614.js +1 -1
  8. package/dist/0~document-editor.js +626 -12
  9. package/dist/0~presentation-editor.js +1 -1
  10. package/dist/0~spreadsheet-editor.js +1 -2
  11. package/dist/0~work-docx-export.js +396 -20
  12. package/dist/0~work-docx-import.js +7 -3
  13. package/dist/0~work-office-diagnostics.js +98 -4
  14. package/dist/0~work-pptx-export.js +1 -1
  15. package/dist/0~work-pptx-import.js +1 -1
  16. package/dist/0~work-presentation-charts.js +1 -1
  17. package/dist/1544.js +1 -1
  18. package/dist/4104.js +2 -2
  19. package/dist/4121.js +1510 -0
  20. package/dist/6282.js +220 -682
  21. package/dist/8715.js +1 -1
  22. package/dist/core.js +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/internal/features/work/editors/document-command-catalog.d.ts +9 -0
  25. package/dist/internal/features/work/editors/document-font-dialog-model.d.ts +33 -2
  26. package/dist/internal/features/work/editors/document-font-dialog-run-border-model.d.ts +24 -0
  27. package/dist/internal/features/work/editors/document-font-dialog-run-border-section.d.ts +10 -0
  28. package/dist/internal/features/work/work-document-character-formatting.d.ts +4 -0
  29. package/dist/internal/features/work/work-document-format-changes.d.ts +5 -0
  30. package/dist/internal/features/work/work-document-legacy-text-effects.d.ts +27 -0
  31. package/dist/internal/features/work/work-document-run-border.d.ts +11 -0
  32. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +5 -0
  33. package/dist/internal/features/work/work-docx-hidden-text-export.d.ts +1 -0
  34. package/dist/internal/features/work/work-docx-legacy-text-effects-diagnostics.d.ts +3 -0
  35. package/dist/internal/features/work/work-docx-legacy-text-effects-export.d.ts +18 -0
  36. package/dist/internal/features/work/work-docx-legacy-text-effects.d.ts +12 -0
  37. package/dist/internal/features/work/work-docx-run-border-diagnostics.d.ts +3 -0
  38. package/dist/internal/features/work/work-docx-run-border-export.d.ts +17 -0
  39. package/dist/internal/features/work/work-docx-run-border.d.ts +20 -0
  40. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +6 -0
  41. package/dist/internal/features/work/work-docx-run-property-order.d.ts +2 -0
  42. package/dist/office-kernel.wasm +0 -0
  43. package/dist/styles.css +72 -0
  44. package/docs/latest/en/browser-editor-architecture.md +36 -0
  45. package/package.json +7 -1
  46. package/dist/5184.js +0 -649
  47. package/dist/9424.js +0 -16
package/dist/4121.js ADDED
@@ -0,0 +1,1510 @@
1
+ import jszip from "jszip";
2
+ function normalizeCssColor(source) {
3
+ const value = source?.trim().toLowerCase();
4
+ if (!value) return null;
5
+ if ('transparent' === value) return 'transparent';
6
+ const shortHex = /^#([0-9a-f]{3})$/i.exec(value);
7
+ if (shortHex?.[1]) return `#${Array.from(shortHex[1]).map((channel)=>`${channel}${channel}`).join('')}`;
8
+ if (/^#[0-9a-f]{6}$/i.test(value)) return value;
9
+ const rgb = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([\d.]+))?\s*\)$/i.exec(value);
10
+ if (!rgb) return null;
11
+ const channels = rgb.slice(1, 4).map(Number);
12
+ if (channels.some((channel)=>channel < 0 || channel > 255)) return null;
13
+ if (void 0 !== rgb[4] && 0 === Number(rgb[4])) return 'transparent';
14
+ if (void 0 !== rgb[4] && 1 !== Number(rgb[4])) return null;
15
+ return `#${channels.map((channel)=>channel.toString(16).padStart(2, '0')).join('')}`;
16
+ }
17
+ function decodeXmlBytes(bytes, label) {
18
+ let encoding = 'utf-8';
19
+ let offset = 0;
20
+ if (0xef === bytes[0] && 0xbb === bytes[1] && 0xbf === bytes[2]) offset = 3;
21
+ else if (0xff === bytes[0] && 0xfe === bytes[1]) {
22
+ encoding = 'utf-16le';
23
+ offset = 2;
24
+ } else if (0xfe === bytes[0] && 0xff === bytes[1]) {
25
+ encoding = 'utf-16be';
26
+ offset = 2;
27
+ } else if (0x3c === bytes[0] && 0 === bytes[1] && 0x3f === bytes[2] && 0 === bytes[3]) encoding = 'utf-16le';
28
+ else if (0 === bytes[0] && 0x3c === bytes[1] && 0 === bytes[2] && 0x3f === bytes[3]) encoding = 'utf-16be';
29
+ try {
30
+ return new TextDecoder(encoding, {
31
+ fatal: true
32
+ }).decode(bytes.subarray(offset));
33
+ } catch {
34
+ throw new Error(`${label} uses an invalid ${encoding} XML encoding.`);
35
+ }
36
+ }
37
+ function serializeUtf8Xml(document) {
38
+ const serialized = new XMLSerializer().serializeToString(document);
39
+ const body = serialized.replace(/^\s*<\?xml[^?]*\?>\s*/i, '');
40
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>${body}`;
41
+ }
42
+ class OoxmlPackage {
43
+ zip;
44
+ textCache = new Map();
45
+ constructor(zip){
46
+ this.zip = zip;
47
+ }
48
+ static async load(buffer) {
49
+ return new OoxmlPackage(await jszip.loadAsync(buffer));
50
+ }
51
+ has(partPath) {
52
+ return Boolean(this.zip.file(partPath));
53
+ }
54
+ paths(prefix) {
55
+ return Object.keys(this.zip.files).filter((path)=>path.startsWith(prefix) && !this.zip.files[path]?.dir);
56
+ }
57
+ async text(partPath) {
58
+ const cached = this.textCache.get(partPath);
59
+ if (cached) return cached;
60
+ const entry = this.zip.file(partPath);
61
+ if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
62
+ const pending = entry.async('uint8array').then((bytes)=>decodeXmlBytes(bytes, partPath));
63
+ this.textCache.set(partPath, pending);
64
+ try {
65
+ return await pending;
66
+ } catch (error) {
67
+ this.textCache.delete(partPath);
68
+ throw error;
69
+ }
70
+ }
71
+ async xml(partPath) {
72
+ return parseXml(await this.text(partPath), partPath);
73
+ }
74
+ async bytes(partPath) {
75
+ const entry = this.zip.file(partPath);
76
+ if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
77
+ return entry.async('uint8array');
78
+ }
79
+ async relationships(sourcePart) {
80
+ const partPath = relationshipsPartPath(sourcePart);
81
+ if (!this.has(partPath)) return new Map();
82
+ const document = await this.xml(partPath);
83
+ return new Map(descendants(document, 'Relationship').map((element)=>{
84
+ const relationship = {
85
+ id: attribute(element, 'Id') ?? '',
86
+ target: resolvePartTarget(sourcePart, attribute(element, 'Target') ?? ''),
87
+ type: attribute(element, 'Type') ?? '',
88
+ targetMode: attribute(element, 'TargetMode') ?? void 0
89
+ };
90
+ return [
91
+ relationship.id,
92
+ relationship
93
+ ];
94
+ }));
95
+ }
96
+ }
97
+ function parseXml(source, label = 'Office XML') {
98
+ const document = new DOMParser().parseFromString(source, 'application/xml');
99
+ const error = descendants(document, 'parsererror')[0];
100
+ if (error) throw new Error(`${label} is not valid XML: ${error.textContent?.trim() || 'parse error'}`);
101
+ return document;
102
+ }
103
+ const xmlElementPatterns = new Map();
104
+ function xmlContainsAnyElement(source, localNames) {
105
+ if (!source || !localNames.length) return false;
106
+ const key = localNames.join('\u0000');
107
+ let pattern = xmlElementPatterns.get(key);
108
+ if (!pattern) {
109
+ const alternatives = localNames.map((name)=>name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
110
+ pattern = new RegExp(`<(?:[A-Za-z_][\\w.-]*:)?(?:${alternatives})(?=[\\s/>])`);
111
+ xmlElementPatterns.set(key, pattern);
112
+ }
113
+ return pattern.test(source);
114
+ }
115
+ function attribute(element, name) {
116
+ const direct = element.getAttribute(name);
117
+ if (null !== direct) return direct;
118
+ const localName = name.includes(':') ? name.slice(name.indexOf(':') + 1) : name;
119
+ return Array.from(element.attributes).find((item)=>{
120
+ const itemLocalName = item.localName.includes(':') ? item.localName.slice(item.localName.indexOf(':') + 1) : item.localName;
121
+ return itemLocalName === localName && (!name.includes(':') || item.name === name);
122
+ })?.value ?? null;
123
+ }
124
+ function xmlNamespacePrefix(element, namespace) {
125
+ if (!namespace) return element.prefix;
126
+ if ('function' == typeof element.lookupPrefix) {
127
+ const prefix = element.lookupPrefix(namespace);
128
+ if (prefix) return prefix;
129
+ }
130
+ let current = element;
131
+ while(current){
132
+ if (current.namespaceURI === namespace && current.prefix) return current.prefix;
133
+ const declaration = Array.from(current.attributes).find((item)=>item.value === namespace && ('xmlns' === item.name || item.name.startsWith('xmlns:')));
134
+ if (declaration?.name.startsWith('xmlns:')) return declaration.name.slice(6);
135
+ current = current.parentElement;
136
+ }
137
+ return null;
138
+ }
139
+ function directChildren(parent, localName) {
140
+ return Array.from(parent.children).filter((element)=>!localName || element.localName === localName);
141
+ }
142
+ function directChild(parent, localName) {
143
+ return directChildren(parent, localName)[0];
144
+ }
145
+ function descendants(parent, localName) {
146
+ return Array.from(parent.querySelectorAll('*')).filter((element)=>element.localName === localName);
147
+ }
148
+ function firstDescendant(parent, localName) {
149
+ if (!parent) return;
150
+ return descendants(parent, localName)[0];
151
+ }
152
+ function childPath(parent, ...localNames) {
153
+ let current = parent;
154
+ for (const name of localNames){
155
+ if (!current) return;
156
+ current = directChild(current, name);
157
+ }
158
+ return current instanceof Element ? current : void 0;
159
+ }
160
+ function resolvePartTarget(sourcePart, target) {
161
+ if (/^[a-z][a-z0-9+.-]*:/i.test(target)) return target;
162
+ const segments = target.startsWith('/') ? [] : sourcePart.split('/').slice(0, -1);
163
+ for (const segment of target.replace(/^\/+/, '').split('/'))if (segment && '.' !== segment) if ('..' === segment) segments.pop();
164
+ else segments.push(segment);
165
+ return segments.join('/');
166
+ }
167
+ function contentTypeForPart(partPath) {
168
+ const extension = partPath.split('.').pop()?.toLowerCase();
169
+ const types = {
170
+ apng: 'image/apng',
171
+ bmp: 'image/bmp',
172
+ emf: 'image/emf',
173
+ gif: 'image/gif',
174
+ jpeg: 'image/jpeg',
175
+ jpg: 'image/jpeg',
176
+ png: 'image/png',
177
+ svg: 'image/svg+xml',
178
+ tif: 'image/tiff',
179
+ tiff: 'image/tiff',
180
+ webp: 'image/webp',
181
+ wmf: 'image/wmf'
182
+ };
183
+ return types[extension ?? ''] ?? 'application/octet-stream';
184
+ }
185
+ function bytesToDataUrl(bytes, contentType) {
186
+ let binary = '';
187
+ const chunkSize = 32768;
188
+ for(let offset = 0; offset < bytes.length; offset += chunkSize)binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
189
+ return `data:${contentType};base64,${btoa(binary)}`;
190
+ }
191
+ function relationshipsPartPath(sourcePart) {
192
+ const separator = sourcePart.lastIndexOf('/');
193
+ const directory = separator >= 0 ? sourcePart.slice(0, separator + 1) : '';
194
+ const fileName = separator >= 0 ? sourcePart.slice(separator + 1) : sourcePart;
195
+ return `${directory}_rels/${fileName}.rels`;
196
+ }
197
+ const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
198
+ class DocxThemePatchCollector {
199
+ patches = [];
200
+ nextMarker = 1;
201
+ usedColors;
202
+ constructor(sourceHtml){
203
+ this.usedColors = sourceColors(sourceHtml);
204
+ }
205
+ marker(kind, reference, currentColor) {
206
+ if (!reference || normalizeColor(currentColor) !== reference.resolved) return null;
207
+ let marker = '';
208
+ do {
209
+ marker = (0xf00000 + this.nextMarker).toString(16).padStart(6, '0').toUpperCase();
210
+ this.nextMarker += 1;
211
+ }while (this.usedColors.has(marker))
212
+ this.usedColors.add(marker);
213
+ this.patches.push({
214
+ kind,
215
+ marker,
216
+ reference
217
+ });
218
+ return marker;
219
+ }
220
+ }
221
+ function serializeDocxThemeReference(reference) {
222
+ return reference ? JSON.stringify(reference) : void 0;
223
+ }
224
+ function parseDocxThemeReference(value) {
225
+ if (!value) return null;
226
+ try {
227
+ const parsed = JSON.parse(value);
228
+ const theme = 'string' == typeof parsed.theme ? parsed.theme.trim() : '';
229
+ const resolved = normalizeColor('string' == typeof parsed.resolved ? parsed.resolved : null);
230
+ const tint = byteHex(parsed.tint);
231
+ const shade = byteHex(parsed.shade);
232
+ if (!theme || !resolved) return null;
233
+ return {
234
+ theme,
235
+ resolved,
236
+ ...tint ? {
237
+ tint
238
+ } : {},
239
+ ...shade ? {
240
+ shade
241
+ } : {}
242
+ };
243
+ } catch {
244
+ return null;
245
+ }
246
+ }
247
+ async function patchDocxThemeReferences(buffer, patches) {
248
+ if (!patches.length) return buffer;
249
+ const archive = await jszip.loadAsync(buffer);
250
+ const byMarker = new Map(patches.map((patch)=>[
251
+ patch.marker,
252
+ patch
253
+ ]));
254
+ const entries = Object.values(archive.files).filter((entry)=>!entry.dir && /^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/.test(entry.name));
255
+ for (const entry of entries){
256
+ const document = parseXml(await entry.async('text'), entry.name);
257
+ let changed = false;
258
+ for (const element of Array.from(document.getElementsByTagName('*')))for (const target of themePatchTargets(element.localName)){
259
+ const marker = wordAttribute(element, target.directAttribute)?.toUpperCase();
260
+ const patch = marker ? byMarker.get(marker) : void 0;
261
+ if (patch && patch.kind === target.kind) {
262
+ setWordAttribute(document, element, target.directAttribute, patch.reference.resolved.slice(1).toUpperCase());
263
+ setWordAttribute(document, element, target.themeAttribute, patch.reference.theme);
264
+ setOptionalWordAttribute(document, element, target.tintAttribute, patch.reference.tint);
265
+ setOptionalWordAttribute(document, element, target.shadeAttribute, patch.reference.shade);
266
+ changed = true;
267
+ }
268
+ }
269
+ if (changed) archive.file(entry.name, new XMLSerializer().serializeToString(document));
270
+ }
271
+ return archive.generateAsync({
272
+ type: 'arraybuffer'
273
+ });
274
+ }
275
+ function themePatchTargets(localName) {
276
+ if ('color' === localName) return [
277
+ {
278
+ kind: 'color',
279
+ directAttribute: 'val',
280
+ themeAttribute: 'themeColor',
281
+ tintAttribute: 'themeTint',
282
+ shadeAttribute: 'themeShade'
283
+ }
284
+ ];
285
+ if ('u' === localName) return [
286
+ {
287
+ kind: 'underline',
288
+ directAttribute: 'color',
289
+ themeAttribute: 'themeColor',
290
+ tintAttribute: 'themeTint',
291
+ shadeAttribute: 'themeShade'
292
+ }
293
+ ];
294
+ if ('shd' === localName) return [
295
+ {
296
+ kind: 'fill',
297
+ directAttribute: 'fill',
298
+ themeAttribute: 'themeFill',
299
+ tintAttribute: 'themeFillTint',
300
+ shadeAttribute: 'themeFillShade'
301
+ },
302
+ {
303
+ kind: 'shadingColor',
304
+ directAttribute: 'color',
305
+ themeAttribute: 'themeColor',
306
+ tintAttribute: 'themeTint',
307
+ shadeAttribute: 'themeShade'
308
+ }
309
+ ];
310
+ return [
311
+ 'top',
312
+ 'right',
313
+ 'bottom',
314
+ 'left',
315
+ 'start',
316
+ 'end'
317
+ ].includes(localName) ? [
318
+ {
319
+ kind: 'border',
320
+ directAttribute: 'color',
321
+ themeAttribute: 'themeColor',
322
+ tintAttribute: 'themeTint',
323
+ shadeAttribute: 'themeShade'
324
+ }
325
+ ] : [];
326
+ }
327
+ function normalizeColor(value) {
328
+ const normalized = value?.trim().toLowerCase();
329
+ if (!normalized || !/^#[0-9a-f]{6}$/.test(normalized)) return null;
330
+ return normalized;
331
+ }
332
+ function sourceColors(source) {
333
+ const colors = new Set();
334
+ for (const match of source.matchAll(/#([0-9a-f]{6})\b/gi))if (match[1]) colors.add(match[1].toUpperCase());
335
+ for (const match of source.matchAll(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/gi)){
336
+ const channels = match.slice(1, 4).map(Number);
337
+ if (!channels.some((channel)=>channel < 0 || channel > 255)) colors.add(channels.map((channel)=>channel.toString(16).padStart(2, '0')).join('').toUpperCase());
338
+ }
339
+ return colors;
340
+ }
341
+ function byteHex(value) {
342
+ if ('string' != typeof value) return;
343
+ const normalized = value.trim().toUpperCase();
344
+ return /^[0-9A-F]{2}$/.test(normalized) ? normalized : void 0;
345
+ }
346
+ function wordAttribute(element, name) {
347
+ return element.getAttributeNS(WORD_NAMESPACE, name) ?? element.getAttribute(`w:${name}`);
348
+ }
349
+ function setWordAttribute(document, element, name, value) {
350
+ const prefix = xmlNamespacePrefix(document.documentElement, WORD_NAMESPACE) ?? 'w';
351
+ element.setAttributeNS(WORD_NAMESPACE, `${prefix}:${name}`, value);
352
+ }
353
+ function setOptionalWordAttribute(document, element, name, value) {
354
+ if (value) setWordAttribute(document, element, name, value);
355
+ else element.removeAttributeNS(WORD_NAMESPACE, name);
356
+ }
357
+ const DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE = 'data-office-paragraph-borders';
358
+ const DOCUMENT_PARAGRAPH_BORDER_EDGES = [
359
+ 'top',
360
+ 'left',
361
+ 'bottom',
362
+ 'right',
363
+ 'between',
364
+ 'bar'
365
+ ];
366
+ const DOCUMENT_PARAGRAPH_BORDER_STYLES = [
367
+ 'nil',
368
+ 'none',
369
+ 'single',
370
+ 'thick',
371
+ 'double',
372
+ 'dotted',
373
+ 'dashed',
374
+ 'dotDash',
375
+ 'dotDotDash',
376
+ 'triple',
377
+ 'thinThickSmallGap',
378
+ 'thickThinSmallGap',
379
+ 'thinThickThinSmallGap',
380
+ 'thinThickMediumGap',
381
+ 'thickThinMediumGap',
382
+ 'thinThickThinMediumGap',
383
+ 'thinThickLargeGap',
384
+ 'thickThinLargeGap',
385
+ 'thinThickThinLargeGap',
386
+ 'wave',
387
+ 'doubleWave',
388
+ 'dashSmallGap',
389
+ 'dashDotStroked',
390
+ 'threeDEmboss',
391
+ 'threeDEngrave',
392
+ 'outset',
393
+ 'inset',
394
+ 'apples',
395
+ 'archedScallops',
396
+ 'babyPacifier',
397
+ 'babyRattle',
398
+ 'balloons3Colors',
399
+ 'balloonsHotAir',
400
+ 'basicBlackDashes',
401
+ 'basicBlackDots',
402
+ 'basicBlackSquares',
403
+ 'basicThinLines',
404
+ 'basicWhiteDashes',
405
+ 'basicWhiteDots',
406
+ 'basicWhiteSquares',
407
+ 'basicWideInline',
408
+ 'basicWideMidline',
409
+ 'basicWideOutline',
410
+ 'bats',
411
+ 'birds',
412
+ 'birdsFlight',
413
+ 'cabins',
414
+ 'cakeSlice',
415
+ 'candyCorn',
416
+ 'celticKnotwork',
417
+ 'certificateBanner',
418
+ 'chainLink',
419
+ 'champagneBottle',
420
+ 'checkedBarBlack',
421
+ 'checkedBarColor',
422
+ 'checkered',
423
+ 'christmasTree',
424
+ 'circlesLines',
425
+ 'circlesRectangles',
426
+ 'classicalWave',
427
+ 'clocks',
428
+ 'compass',
429
+ 'confetti',
430
+ 'confettiGrays',
431
+ 'confettiOutline',
432
+ 'confettiStreamers',
433
+ 'confettiWhite',
434
+ 'cornerTriangles',
435
+ 'couponCutoutDashes',
436
+ 'couponCutoutDots',
437
+ 'crazyMaze',
438
+ 'creaturesButterfly',
439
+ 'creaturesFish',
440
+ 'creaturesInsects',
441
+ 'creaturesLadyBug',
442
+ 'crossStitch',
443
+ 'cup',
444
+ 'decoArch',
445
+ 'decoArchColor',
446
+ 'decoBlocks',
447
+ 'diamondsGray',
448
+ 'doubleD',
449
+ 'doubleDiamonds',
450
+ 'earth1',
451
+ 'earth2',
452
+ 'eclipsingSquares1',
453
+ 'eclipsingSquares2',
454
+ 'eggsBlack',
455
+ 'fans',
456
+ 'film',
457
+ 'firecrackers',
458
+ 'flowersBlockPrint',
459
+ 'flowersDaisies',
460
+ 'flowersModern1',
461
+ 'flowersModern2',
462
+ 'flowersPansy',
463
+ 'flowersRedRose',
464
+ 'flowersRoses',
465
+ 'flowersTeacup',
466
+ 'flowersTiny',
467
+ 'gems',
468
+ 'gingerbreadMan',
469
+ 'gradient',
470
+ 'handmade1',
471
+ 'handmade2',
472
+ 'heartBalloon',
473
+ 'heartGray',
474
+ 'hearts',
475
+ 'heebieJeebies',
476
+ 'holly',
477
+ 'houseFunky',
478
+ 'hypnotic',
479
+ 'iceCreamCones',
480
+ 'lightBulb',
481
+ 'lightning1',
482
+ 'lightning2',
483
+ 'mapPins',
484
+ 'mapleLeaf',
485
+ 'mapleMuffins',
486
+ 'marquee',
487
+ 'marqueeToothed',
488
+ 'moons',
489
+ 'mosaic',
490
+ 'musicNotes',
491
+ 'northwest',
492
+ 'ovals',
493
+ 'packages',
494
+ 'palmsBlack',
495
+ 'palmsColor',
496
+ 'paperClips',
497
+ 'papyrus',
498
+ 'partyFavor',
499
+ 'partyGlass',
500
+ 'pencils',
501
+ 'people',
502
+ 'peopleWaving',
503
+ 'peopleHats',
504
+ 'poinsettias',
505
+ 'postageStamp',
506
+ 'pumpkin1',
507
+ 'pushPinNote2',
508
+ 'pushPinNote1',
509
+ 'pyramids',
510
+ 'pyramidsAbove',
511
+ 'quadrants',
512
+ 'rings',
513
+ 'safari',
514
+ 'sawtooth',
515
+ 'sawtoothGray',
516
+ 'scaredCat',
517
+ 'seattle',
518
+ 'shadowedSquares',
519
+ 'sharksTeeth',
520
+ 'shorebirdTracks',
521
+ 'skyrocket',
522
+ 'snowflakeFancy',
523
+ 'snowflakes',
524
+ 'sombrero',
525
+ 'southwest',
526
+ 'stars',
527
+ 'starsTop',
528
+ 'stars3d',
529
+ 'starsBlack',
530
+ 'starsShadowed',
531
+ 'sun',
532
+ 'swirligig',
533
+ 'tornPaper',
534
+ 'tornPaperBlack',
535
+ 'trees',
536
+ 'triangleParty',
537
+ 'triangles',
538
+ 'tribal1',
539
+ 'tribal2',
540
+ 'tribal3',
541
+ 'tribal4',
542
+ 'tribal5',
543
+ 'tribal6',
544
+ 'triangle1',
545
+ 'triangle2',
546
+ 'triangleCircle1',
547
+ 'triangleCircle2',
548
+ 'shapes1',
549
+ 'shapes2',
550
+ 'twistedLines1',
551
+ 'twistedLines2',
552
+ 'vine',
553
+ 'waveline',
554
+ 'weavingAngles',
555
+ 'weavingBraid',
556
+ 'weavingRibbon',
557
+ 'weavingStrips',
558
+ 'whiteFlowers',
559
+ 'woodwork',
560
+ 'xIllusions',
561
+ 'zanyTriangles',
562
+ 'zigZag',
563
+ 'zigZagStitch'
564
+ ];
565
+ const BORDER_STYLE_SET = new Set(DOCUMENT_PARAGRAPH_BORDER_STYLES);
566
+ const BORDER_EDGE_SET = new Set(DOCUMENT_PARAGRAPH_BORDER_EDGES);
567
+ const BORDER_PROPERTY_SET = new Set([
568
+ 'style',
569
+ 'color',
570
+ 'size',
571
+ 'space',
572
+ 'shadow',
573
+ 'frame'
574
+ ]);
575
+ const LINE_BORDER_STYLES = new Set(DOCUMENT_PARAGRAPH_BORDER_STYLES.slice(0, 27));
576
+ const DASHED_BORDER_STYLES = new Set([
577
+ 'dashed',
578
+ 'dashSmallGap',
579
+ 'dashDotStroked',
580
+ 'dotDash',
581
+ 'dotDotDash'
582
+ ]);
583
+ const DOUBLE_BORDER_STYLES = new Set([
584
+ 'double',
585
+ 'triple',
586
+ 'thinThickSmallGap',
587
+ 'thickThinSmallGap',
588
+ 'thinThickThinSmallGap',
589
+ 'thinThickMediumGap',
590
+ 'thickThinMediumGap',
591
+ 'thinThickThinMediumGap',
592
+ 'thinThickLargeGap',
593
+ 'thickThinLargeGap',
594
+ 'thinThickThinLargeGap',
595
+ 'doubleWave'
596
+ ]);
597
+ const MAX_SERIALIZED_PARAGRAPH_BORDERS = 32768;
598
+ const POINTS_TO_PIXELS = 96 / 72;
599
+ function normalizeDocumentParagraphBorders(source) {
600
+ if (!source || 'object' != typeof source || Array.isArray(source)) return null;
601
+ const record = source;
602
+ if (Object.keys(record).some((key)=>!BORDER_EDGE_SET.has(key))) return null;
603
+ const borders = {};
604
+ for (const edge of DOCUMENT_PARAGRAPH_BORDER_EDGES){
605
+ if (void 0 === record[edge]) continue;
606
+ const border = normalizeDocumentParagraphBorder(record[edge]);
607
+ if (!border) return null;
608
+ borders[edge] = border;
609
+ }
610
+ return Object.keys(borders).length ? borders : null;
611
+ }
612
+ function normalizeDocumentParagraphBorder(source) {
613
+ if (!source || 'object' != typeof source || Array.isArray(source)) return null;
614
+ const record = source;
615
+ if (Object.keys(record).some((key)=>!BORDER_PROPERTY_SET.has(key))) return null;
616
+ const style = record.style;
617
+ if ('string' != typeof style || !BORDER_STYLE_SET.has(style)) return null;
618
+ const normalizedStyle = style;
619
+ const color = normalizeBorderColor(record.color);
620
+ if (void 0 !== record.color && !color) return null;
621
+ const size = optionalInteger(record.size);
622
+ if (null === size || void 0 !== size && !validBorderSize(normalizedStyle, size)) return null;
623
+ const space = optionalInteger(record.space);
624
+ if (null === space || void 0 !== space && (space < 0 || space > 31)) return null;
625
+ const shadow = optionalBoolean(record.shadow);
626
+ const frame = optionalBoolean(record.frame);
627
+ if (null === shadow || null === frame) return null;
628
+ return {
629
+ style: normalizedStyle,
630
+ ...color ? {
631
+ color
632
+ } : {},
633
+ ...void 0 !== size ? {
634
+ size
635
+ } : {},
636
+ ...void 0 !== space ? {
637
+ space
638
+ } : {},
639
+ ...void 0 !== shadow ? {
640
+ shadow
641
+ } : {},
642
+ ...void 0 !== frame ? {
643
+ frame
644
+ } : {}
645
+ };
646
+ }
647
+ function parseDocumentParagraphBorders(source) {
648
+ if ('string' != typeof source) return normalizeDocumentParagraphBorders(source);
649
+ if (!source.trim() || source.length > MAX_SERIALIZED_PARAGRAPH_BORDERS) return null;
650
+ try {
651
+ return normalizeDocumentParagraphBorders(JSON.parse(source));
652
+ } catch {
653
+ return null;
654
+ }
655
+ }
656
+ function serializeDocumentParagraphBorders(source) {
657
+ const borders = normalizeDocumentParagraphBorders(source);
658
+ if (!borders) return;
659
+ return JSON.stringify(Object.fromEntries(DOCUMENT_PARAGRAPH_BORDER_EDGES.flatMap((edge)=>{
660
+ const border = borders[edge];
661
+ return border ? [
662
+ [
663
+ edge,
664
+ serializedBorder(border)
665
+ ]
666
+ ] : [];
667
+ })));
668
+ }
669
+ function parseDocumentParagraphBordersElement(element) {
670
+ const semantic = parseDocumentParagraphBorders(element.getAttribute(DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE));
671
+ if (!semantic) return paragraphBordersFromCss(element);
672
+ const edited = {
673
+ ...semantic
674
+ };
675
+ for (const edge of [
676
+ 'top',
677
+ 'left',
678
+ 'bottom',
679
+ 'right'
680
+ ]){
681
+ const border = semantic[edge];
682
+ const css = cssBorder(element, edge);
683
+ if (!(!css || border && sameBorderPresentation(border, css))) {
684
+ if ('none' === css.style) {
685
+ edited[edge] = {
686
+ style: 'nil'
687
+ };
688
+ continue;
689
+ }
690
+ edited[edge] = {
691
+ ...border ?? {
692
+ style: 'single'
693
+ },
694
+ style: cssStyleToBorderStyle(css.style),
695
+ color: {
696
+ value: css.color
697
+ },
698
+ size: cssWidthToEighthPoints(css.width)
699
+ };
700
+ }
701
+ }
702
+ return normalizeDocumentParagraphBorders(edited);
703
+ }
704
+ function documentParagraphBordersDomAttributes(source) {
705
+ const borders = normalizeDocumentParagraphBorders(source);
706
+ const serialized = serializeDocumentParagraphBorders(borders);
707
+ if (!borders || !serialized) return {};
708
+ const styles = [];
709
+ const shadows = [];
710
+ for (const edge of [
711
+ 'top',
712
+ 'left',
713
+ 'bottom',
714
+ 'right'
715
+ ]){
716
+ const border = borders[edge];
717
+ if (!border) continue;
718
+ const presentation = documentBorderPresentation(border);
719
+ styles.push(`border-${edge}: ${formatPixels(presentation.width)}px ${presentation.style} ${presentation.color}`);
720
+ if (border.space) styles.push(`padding-${edge}: ${formatPixels(border.space * POINTS_TO_PIXELS)}px`);
721
+ if (border.shadow && presentation.width > 0) shadows.push(`2px 2px 0 ${presentation.color}`);
722
+ }
723
+ const between = borders.between ? documentBorderPresentation(borders.between) : null;
724
+ if (between && between.width > 0) shadows.push(`inset 0 -${formatPixels(between.width)}px 0 ${between.color}`);
725
+ const bar = borders.bar ? documentBorderPresentation(borders.bar) : null;
726
+ if (bar && bar.width > 0) shadows.push(`inset ${formatPixels(bar.width)}px 0 0 ${bar.color}`);
727
+ if (shadows.length) styles.push(`box-shadow: ${shadows.join(', ')}`);
728
+ return {
729
+ [DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE]: serialized,
730
+ ...styles.length ? {
731
+ style: styles.join('; ')
732
+ } : {}
733
+ };
734
+ }
735
+ function isDocumentParagraphArtBorderStyle(style) {
736
+ return !LINE_BORDER_STYLES.has(style);
737
+ }
738
+ function normalizeBorderColor(source) {
739
+ if (!source || 'object' != typeof source || Array.isArray(source)) return null;
740
+ const record = source;
741
+ if (Object.keys(record).some((key)=>'value' !== key && 'theme' !== key)) return null;
742
+ const theme = parseDocxThemeReference('string' == typeof record.theme ? record.theme : record.theme ? JSON.stringify(record.theme) : void 0);
743
+ const direct = 'auto' === record.value ? 'auto' : 'string' == typeof record.value ? normalizeCssColor(record.value) : null;
744
+ const resolved = direct ?? theme?.resolved ?? null;
745
+ if (!resolved || 'transparent' === resolved) return null;
746
+ if (theme && resolved !== theme.resolved && !('auto' === resolved && 'none' === theme.theme && '#000000' === theme.resolved)) return null;
747
+ return {
748
+ value: resolved,
749
+ ...theme ? {
750
+ theme
751
+ } : {}
752
+ };
753
+ }
754
+ function serializedBorder(border) {
755
+ const color = border.color ? serializedBorderColor(border.color) : void 0;
756
+ return {
757
+ style: border.style,
758
+ ...color ? {
759
+ color
760
+ } : {},
761
+ ...void 0 !== border.size ? {
762
+ size: border.size
763
+ } : {},
764
+ ...void 0 !== border.space ? {
765
+ space: border.space
766
+ } : {},
767
+ ...void 0 !== border.shadow ? {
768
+ shadow: border.shadow
769
+ } : {},
770
+ ...void 0 !== border.frame ? {
771
+ frame: border.frame
772
+ } : {}
773
+ };
774
+ }
775
+ function serializedBorderColor(color) {
776
+ const theme = serializeDocxThemeReference(color.theme ?? null);
777
+ return {
778
+ value: color.value,
779
+ ...theme ? {
780
+ theme: JSON.parse(theme)
781
+ } : {}
782
+ };
783
+ }
784
+ function optionalInteger(value) {
785
+ if (void 0 === value) return;
786
+ return 'number' == typeof value && Number.isSafeInteger(value) ? value : null;
787
+ }
788
+ function optionalBoolean(value) {
789
+ if (void 0 === value) return;
790
+ return 'boolean' == typeof value ? value : null;
791
+ }
792
+ function validBorderSize(style, size) {
793
+ if ('nil' === style || 'none' === style) return size >= 0 && size <= 96;
794
+ return isDocumentParagraphArtBorderStyle(style) ? size >= 1 && size <= 31 : size >= 2 && size <= 96;
795
+ }
796
+ function documentBorderPresentation(border) {
797
+ if ('nil' === border.style || 'none' === border.style || !border.size) return {
798
+ color: 'transparent',
799
+ style: 'none',
800
+ width: 0
801
+ };
802
+ const width = Math.min(16, isDocumentParagraphArtBorderStyle(border.style) ? border.size * POINTS_TO_PIXELS : border.size / 6);
803
+ return {
804
+ color: border.color && 'auto' !== border.color.value ? border.color.value : '#000000',
805
+ style: borderStyleToCssStyle(border.style),
806
+ width
807
+ };
808
+ }
809
+ function borderStyleToCssStyle(style) {
810
+ if ('nil' === style || 'none' === style) return 'none';
811
+ if ('dotted' === style) return 'dotted';
812
+ if (DASHED_BORDER_STYLES.has(style)) return 'dashed';
813
+ if (DOUBLE_BORDER_STYLES.has(style)) return 'double';
814
+ if ('inset' === style || 'threeDEngrave' === style) return 'inset';
815
+ if ('outset' === style || 'threeDEmboss' === style) return 'outset';
816
+ return 'solid';
817
+ }
818
+ function paragraphBordersFromCss(element) {
819
+ const borders = {};
820
+ for (const edge of [
821
+ 'top',
822
+ 'left',
823
+ 'bottom',
824
+ 'right'
825
+ ]){
826
+ const css = cssBorder(element, edge);
827
+ if (css && 'none' !== css.style) borders[edge] = {
828
+ style: cssStyleToBorderStyle(css.style),
829
+ color: {
830
+ value: css.color
831
+ },
832
+ size: cssWidthToEighthPoints(css.width)
833
+ };
834
+ }
835
+ return normalizeDocumentParagraphBorders(borders);
836
+ }
837
+ function cssBorder(element, edge) {
838
+ const style = element.style.getPropertyValue(`border-${edge}-style`).trim();
839
+ const width = Number.parseFloat(element.style.getPropertyValue(`border-${edge}-width`));
840
+ const color = normalizeCssColor(element.style.getPropertyValue(`border-${edge}-color`));
841
+ if (!style) return null;
842
+ if ('none' === style || 'hidden' === style) return {
843
+ color: '#000000',
844
+ style: 'none',
845
+ width: 0
846
+ };
847
+ return color && 'transparent' !== color && Number.isFinite(width) && width > 0 ? {
848
+ color,
849
+ style,
850
+ width
851
+ } : null;
852
+ }
853
+ function sameBorderPresentation(border, css) {
854
+ const expected = documentBorderPresentation(border);
855
+ if ('none' === expected.style) return 'none' === css.style && 0 === css.width;
856
+ return expected.color === css.color && expected.style === css.style && Math.abs(expected.width - css.width) < 0.01;
857
+ }
858
+ function cssStyleToBorderStyle(style) {
859
+ if ('double' === style) return 'double';
860
+ if ('dashed' === style) return 'dashed';
861
+ if ('dotted' === style) return 'dotted';
862
+ if ('inset' === style || 'groove' === style) return 'inset';
863
+ if ('outset' === style || 'ridge' === style) return 'outset';
864
+ return 'single';
865
+ }
866
+ function cssWidthToEighthPoints(width) {
867
+ return Math.max(2, Math.min(96, Math.round(6 * width)));
868
+ }
869
+ function formatPixels(value) {
870
+ return Number(value.toFixed(3)).toString();
871
+ }
872
+ const DOCUMENT_RUN_BORDER_ATTRIBUTE = 'data-office-run-border';
873
+ const DOCUMENT_RUN_BORDER_STYLES = DOCUMENT_PARAGRAPH_BORDER_STYLES.slice(0, 27);
874
+ const MAX_SERIALIZED_RUN_BORDER_BYTES = 4096;
875
+ const work_document_run_border_POINTS_TO_PIXELS = 96 / 72;
876
+ function normalizeDocumentRunBorder(source) {
877
+ const border = normalizeDocumentParagraphBorder(source);
878
+ return border && !isDocumentParagraphArtBorderStyle(border.style) ? border : null;
879
+ }
880
+ function parseDocumentRunBorder(source) {
881
+ if ('string' != typeof source) return normalizeDocumentRunBorder(source);
882
+ if (!source.trim() || source.length > MAX_SERIALIZED_RUN_BORDER_BYTES) return null;
883
+ try {
884
+ return normalizeDocumentRunBorder(JSON.parse(source));
885
+ } catch {
886
+ return null;
887
+ }
888
+ }
889
+ function serializeDocumentRunBorder(source) {
890
+ const border = normalizeDocumentRunBorder(source);
891
+ if (!border) return;
892
+ const theme = serializeDocxThemeReference(border.color?.theme ?? null);
893
+ return JSON.stringify({
894
+ style: border.style,
895
+ ...border.color ? {
896
+ color: {
897
+ value: border.color.value,
898
+ ...theme ? {
899
+ theme: JSON.parse(theme)
900
+ } : {}
901
+ }
902
+ } : {},
903
+ ...void 0 !== border.size ? {
904
+ size: border.size
905
+ } : {},
906
+ ...void 0 !== border.space ? {
907
+ space: border.space
908
+ } : {},
909
+ ...void 0 !== border.shadow ? {
910
+ shadow: border.shadow
911
+ } : {},
912
+ ...void 0 !== border.frame ? {
913
+ frame: border.frame
914
+ } : {}
915
+ });
916
+ }
917
+ function parseDocumentRunBorderElement(element) {
918
+ const semantic = parseDocumentRunBorder(element.getAttribute(DOCUMENT_RUN_BORDER_ATTRIBUTE));
919
+ return semantic ?? documentRunBorderFromCss(element);
920
+ }
921
+ function documentRunBorderDomAttributes(source) {
922
+ const border = normalizeDocumentRunBorder(source);
923
+ const serialized = serializeDocumentRunBorder(border);
924
+ if (!border || !serialized) return {};
925
+ const presentation = documentBorderPresentation(border);
926
+ const declarations = [
927
+ `border: ${work_document_run_border_formatPixels(presentation.width)}px ${presentation.style} ${presentation.color}`,
928
+ `padding: ${work_document_run_border_formatPixels((border.space ?? 0) * work_document_run_border_POINTS_TO_PIXELS)}px`,
929
+ 'box-decoration-break: clone',
930
+ '-webkit-box-decoration-break: clone'
931
+ ];
932
+ if (border.shadow && presentation.width > 0) declarations.push(`box-shadow: 2px 2px 0 ${presentation.color}`);
933
+ return {
934
+ [DOCUMENT_RUN_BORDER_ATTRIBUTE]: serialized,
935
+ style: declarations.join('; ')
936
+ };
937
+ }
938
+ function documentRunBorderIsVisible(source) {
939
+ const border = normalizeDocumentRunBorder(source);
940
+ return Boolean(border && documentBorderPresentation(border).width > 0);
941
+ }
942
+ function documentRunBorderFromCss(element) {
943
+ const style = element.style.borderStyle.trim();
944
+ if (!style) return null;
945
+ if ('none' === style || 'hidden' === style) return {
946
+ style: 'none'
947
+ };
948
+ const width = Number.parseFloat(element.style.borderWidth);
949
+ const color = normalizeCssColor(element.style.borderColor);
950
+ if (!Number.isFinite(width) || width <= 0 || !color || 'transparent' === color) return null;
951
+ const padding = Number.parseFloat(element.style.padding);
952
+ return normalizeDocumentRunBorder({
953
+ style: cssBorderStyle(style),
954
+ color: {
955
+ value: color
956
+ },
957
+ size: Math.max(2, Math.min(96, Math.round(6 * width))),
958
+ ...Number.isFinite(padding) && padding >= 0 ? {
959
+ space: Math.max(0, Math.min(31, Math.round(padding / work_document_run_border_POINTS_TO_PIXELS)))
960
+ } : {}
961
+ });
962
+ }
963
+ function cssBorderStyle(style) {
964
+ if ('double' === style) return 'double';
965
+ if ('dashed' === style) return 'dashed';
966
+ if ('dotted' === style) return 'dotted';
967
+ if ('inset' === style || 'groove' === style) return 'inset';
968
+ if ('outset' === style || 'ridge' === style) return 'outset';
969
+ return 'single';
970
+ }
971
+ function work_document_run_border_formatPixels(value) {
972
+ return Number(value.toFixed(3)).toString();
973
+ }
974
+ const WORK_TEMPLATES = [
975
+ {
976
+ id: 'blank-document',
977
+ kind: 'document',
978
+ name: '空白文字',
979
+ description: '从一张干净的 A4 页面开始',
980
+ accent: '#2f6fed'
981
+ },
982
+ {
983
+ id: 'project-brief',
984
+ kind: 'document',
985
+ name: '项目方案',
986
+ description: '目标、范围、里程碑与风险',
987
+ accent: '#536de2'
988
+ },
989
+ {
990
+ id: 'text-effects',
991
+ kind: 'document',
992
+ name: '文字效果',
993
+ description: '空心、阴影、阳文与阴文',
994
+ accent: '#6b5bd2'
995
+ },
996
+ {
997
+ id: 'run-borders',
998
+ kind: 'document',
999
+ name: '字符边框',
1000
+ description: '原生线型、颜色、宽度、间距与阴影',
1001
+ accent: '#4472c4'
1002
+ },
1003
+ {
1004
+ id: 'blank-markdown',
1005
+ kind: 'markdown',
1006
+ name: '空白 Markdown',
1007
+ description: '用轻量标记编写结构化内容',
1008
+ accent: '#586574'
1009
+ },
1010
+ {
1011
+ id: 'blank-spreadsheet',
1012
+ kind: 'spreadsheet',
1013
+ name: '空白表格',
1014
+ description: '公式、表格、筛选与多工作表',
1015
+ accent: '#16a36a'
1016
+ },
1017
+ {
1018
+ id: 'quarterly-plan',
1019
+ kind: 'spreadsheet',
1020
+ name: '季度计划',
1021
+ description: '目标进度与预算跟踪',
1022
+ accent: '#168f72'
1023
+ },
1024
+ {
1025
+ id: 'blank-presentation',
1026
+ kind: 'presentation',
1027
+ name: '空白演示',
1028
+ description: '16:9 宽屏演示文稿',
1029
+ accent: '#e16b3d'
1030
+ },
1031
+ {
1032
+ id: 'strategy-deck',
1033
+ kind: 'presentation',
1034
+ name: '策略汇报',
1035
+ description: '结论先行的三页汇报',
1036
+ accent: '#c85637'
1037
+ }
1038
+ ];
1039
+ function createWorkArtifact(templateId) {
1040
+ const template = WORK_TEMPLATES.find((item)=>item.id === templateId) ?? WORK_TEMPLATES[0];
1041
+ const now = Date.now();
1042
+ return {
1043
+ id: createWorkId('artifact'),
1044
+ kind: template.kind,
1045
+ title: initialTitle(template.id, template.kind),
1046
+ favorite: false,
1047
+ createdAt: now,
1048
+ updatedAt: now,
1049
+ lastOpenedAt: now,
1050
+ revision: 1,
1051
+ content: contentForTemplate(template.id)
1052
+ };
1053
+ }
1054
+ function createWorkId(prefix) {
1055
+ const random = "u" > typeof crypto && 'function' == typeof crypto.randomUUID ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
1056
+ return `${prefix}-${random}`;
1057
+ }
1058
+ function initialTitle(templateId, kind) {
1059
+ const titles = {
1060
+ 'project-brief': '新项目方案',
1061
+ 'text-effects': '文字效果示例',
1062
+ 'run-borders': '字符边框示例',
1063
+ 'quarterly-plan': '季度执行计划',
1064
+ 'strategy-deck': '业务策略汇报'
1065
+ };
1066
+ if (titles[templateId]) return titles[templateId];
1067
+ if ('document' === kind) return '无标题文字';
1068
+ if ('markdown' === kind) return '无标题 Markdown';
1069
+ if ('spreadsheet' === kind) return '无标题表格';
1070
+ return '无标题演示';
1071
+ }
1072
+ function contentForTemplate(templateId) {
1073
+ if ('project-brief' === templateId) return {
1074
+ type: 'document',
1075
+ pageSize: 'a4',
1076
+ html: "<h1>新项目方案</h1><p><strong>负责人:</strong>项目团队  <strong>更新日期:</strong>今天</p><blockquote><p>用一句话说明这项工作的目标,以及完成后会带来什么变化。</p></blockquote><h2>背景与目标</h2><p>描述当前情况、核心问题和可衡量的成功标准。</p><h2>工作范围</h2><ul><li><p>需要完成的关键交付物</p></li><li><p>明确不在本期范围内的事项</p></li></ul><h2>里程碑</h2><ol><li><p>方案确认</p></li><li><p>执行与评审</p></li><li><p>交付与复盘</p></li></ol><h2>风险与决策</h2><p>记录尚未解决的问题、依赖和决策负责人。</p>"
1077
+ };
1078
+ if ('text-effects' === templateId) return {
1079
+ type: 'document',
1080
+ pageSize: 'a4',
1081
+ html: '<h1>原生文字效果</h1><p>选择下面任一示例并打开字体高级设置,可以组合空心与阴影,或在互斥的阳文和阴文之间切换。</p><h2>可组合效果</h2><p><span data-office-legacy-text-outline="true" data-office-legacy-text-shadow="true">空心 + 阴影</span></p><p><span data-office-legacy-text-outline="true">空心</span></p><p><span data-office-legacy-text-shadow="true">阴影</span></p><h2>互斥效果</h2><p><span data-office-legacy-text-emboss="true">阳文</span></p><p><span data-office-legacy-text-imprint="true">阴文</span></p>'
1082
+ };
1083
+ if ('run-borders' === templateId) return {
1084
+ type: 'document',
1085
+ pageSize: 'a4',
1086
+ html: [
1087
+ '<h1>原生字符边框</h1>',
1088
+ '<p>选择任一示例,可从开始选项卡直接开关边框,或在字体高级设置中编辑完整的原生线型、颜色、宽度、文字间距、阴影和框架属性。</p>',
1089
+ '<h2>常用线型</h2>',
1090
+ `<p>${runBorderTemplateSpan({
1091
+ style: 'single',
1092
+ color: {
1093
+ value: '#4472c4'
1094
+ },
1095
+ size: 4,
1096
+ space: 1
1097
+ }, '单实线字符边框')}</p>`,
1098
+ `<p>${runBorderTemplateSpan({
1099
+ style: 'double',
1100
+ color: {
1101
+ value: '#c00000'
1102
+ },
1103
+ size: 8,
1104
+ space: 2
1105
+ }, '双线字符边框')}</p>`,
1106
+ `<p>${runBorderTemplateSpan({
1107
+ style: 'wave',
1108
+ color: {
1109
+ value: '#7030a0'
1110
+ },
1111
+ size: 8,
1112
+ space: 2
1113
+ }, '波浪字符边框')}</p>`,
1114
+ '<h2>高级属性</h2>',
1115
+ `<p>${runBorderTemplateSpan({
1116
+ style: 'thinThickMediumGap',
1117
+ color: {
1118
+ value: '#0070c0'
1119
+ },
1120
+ size: 16,
1121
+ space: 3,
1122
+ shadow: true,
1123
+ frame: true
1124
+ }, '阴影与框架字符边框')}</p>`
1125
+ ].join('')
1126
+ };
1127
+ if ('quarterly-plan' === templateId) return {
1128
+ type: 'spreadsheet',
1129
+ sheets: quarterlyPlanSheets()
1130
+ };
1131
+ if ('strategy-deck' === templateId) return strategyPresentation();
1132
+ if ('blank-spreadsheet' === templateId) return {
1133
+ type: 'spreadsheet',
1134
+ sheets: [
1135
+ blankSheet()
1136
+ ]
1137
+ };
1138
+ if ('blank-markdown' === templateId) return {
1139
+ type: 'markdown',
1140
+ markdown: ''
1141
+ };
1142
+ if ('blank-presentation' === templateId) return {
1143
+ type: 'presentation',
1144
+ slides: [
1145
+ blankSlide()
1146
+ ]
1147
+ };
1148
+ return {
1149
+ type: 'document',
1150
+ pageSize: 'a4',
1151
+ html: '<p></p>'
1152
+ };
1153
+ }
1154
+ function runBorderTemplateSpan(border, text) {
1155
+ const attributes = documentRunBorderDomAttributes(border);
1156
+ return `<span ${DOCUMENT_RUN_BORDER_ATTRIBUTE}='${attributes[DOCUMENT_RUN_BORDER_ATTRIBUTE]}' style="${attributes.style}">${text}</span>`;
1157
+ }
1158
+ function blankSheet() {
1159
+ return {
1160
+ id: createWorkId('sheet'),
1161
+ name: '工作表1',
1162
+ status: 1,
1163
+ order: 0,
1164
+ row: 60,
1165
+ column: 26,
1166
+ data: emptyMatrix(60, 26)
1167
+ };
1168
+ }
1169
+ function quarterlyPlanSheets() {
1170
+ const data = emptyMatrix(40, 12);
1171
+ data[0][0] = styledCell('季度执行计划', {
1172
+ bl: 1,
1173
+ fs: 16,
1174
+ fc: '#ffffff',
1175
+ bg: '#168f72'
1176
+ });
1177
+ data[2][0] = headerCell('目标');
1178
+ data[2][1] = headerCell('负责人');
1179
+ data[2][2] = headerCell('一月');
1180
+ data[2][3] = headerCell('二月');
1181
+ data[2][4] = headerCell('三月');
1182
+ data[2][5] = headerCell('完成率');
1183
+ data[2][6] = headerCell('状态');
1184
+ const rows = [
1185
+ [
1186
+ '客户洞察报告',
1187
+ '林岚',
1188
+ 1,
1189
+ 1,
1190
+ 0,
1191
+ '=SUM(C4:E4)/3',
1192
+ '进行中'
1193
+ ],
1194
+ [
1195
+ '新版发布',
1196
+ '周启',
1197
+ 0.8,
1198
+ 0.6,
1199
+ 0,
1200
+ '=AVERAGE(C5:E5)',
1201
+ '有风险'
1202
+ ],
1203
+ [
1204
+ '渠道增长',
1205
+ '陈一',
1206
+ 1,
1207
+ 0.9,
1208
+ 0.7,
1209
+ '=AVERAGE(C6:E6)',
1210
+ '正常'
1211
+ ],
1212
+ [
1213
+ '团队能力建设',
1214
+ '项目组',
1215
+ 1,
1216
+ 1,
1217
+ 1,
1218
+ '=AVERAGE(C7:E7)',
1219
+ '已完成'
1220
+ ]
1221
+ ];
1222
+ rows.forEach((row, rowIndex)=>{
1223
+ row.forEach((value, columnIndex)=>{
1224
+ data[rowIndex + 3][columnIndex] = styledCell(value, {
1225
+ bg: rowIndex % 2 ? '#f7faf9' : '#ffffff',
1226
+ ...columnIndex >= 2 && columnIndex <= 5 ? {
1227
+ ct: {
1228
+ fa: '0%',
1229
+ t: 'n'
1230
+ },
1231
+ ...'number' == typeof value ? {
1232
+ m: `${Math.round(100 * value)}%`
1233
+ } : {}
1234
+ } : {}
1235
+ });
1236
+ });
1237
+ });
1238
+ return [
1239
+ {
1240
+ id: createWorkId('sheet'),
1241
+ name: '执行看板',
1242
+ status: 1,
1243
+ order: 0,
1244
+ row: 40,
1245
+ column: 12,
1246
+ data,
1247
+ config: {
1248
+ columnlen: {
1249
+ 0: 180,
1250
+ 1: 96,
1251
+ 2: 76,
1252
+ 3: 76,
1253
+ 4: 76,
1254
+ 5: 96,
1255
+ 6: 96
1256
+ },
1257
+ rowlen: {
1258
+ 0: 34,
1259
+ 2: 28
1260
+ },
1261
+ merge: {
1262
+ '0_0': {
1263
+ r: 0,
1264
+ c: 0,
1265
+ rs: 1,
1266
+ cs: 7
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+ ];
1272
+ }
1273
+ function emptyMatrix(rows, columns) {
1274
+ return Array.from({
1275
+ length: rows
1276
+ }, ()=>Array(columns).fill(null));
1277
+ }
1278
+ function styledCell(value, style = {}) {
1279
+ const formula = 'string' == typeof value && value.startsWith('=') ? value : void 0;
1280
+ return {
1281
+ v: formula ? void 0 : value,
1282
+ m: formula ? '' : String(value),
1283
+ f: formula,
1284
+ ...style
1285
+ };
1286
+ }
1287
+ function headerCell(value) {
1288
+ return styledCell(value, {
1289
+ bl: 1,
1290
+ fc: '#215446',
1291
+ bg: '#dff3ec',
1292
+ ht: 0,
1293
+ vt: 0
1294
+ });
1295
+ }
1296
+ function blankSlide() {
1297
+ return {
1298
+ id: createWorkId('slide'),
1299
+ name: '标题幻灯片',
1300
+ background: '#ffffff',
1301
+ elements: [
1302
+ {
1303
+ id: createWorkId('element'),
1304
+ type: 'text',
1305
+ x: 12,
1306
+ y: 25,
1307
+ width: 76,
1308
+ height: 18,
1309
+ text: '',
1310
+ fontSize: 34,
1311
+ color: '#172033',
1312
+ fill: 'transparent',
1313
+ bold: true,
1314
+ align: 'center',
1315
+ placeholder: {
1316
+ key: 'title',
1317
+ type: 'title',
1318
+ prompt: '单击添加标题'
1319
+ }
1320
+ },
1321
+ {
1322
+ id: createWorkId('element'),
1323
+ type: 'text',
1324
+ x: 18,
1325
+ y: 49,
1326
+ width: 64,
1327
+ height: 10,
1328
+ text: '',
1329
+ fontSize: 17,
1330
+ color: '#727b8f',
1331
+ fill: 'transparent',
1332
+ bold: false,
1333
+ align: 'center',
1334
+ placeholder: {
1335
+ key: 'subtitle',
1336
+ type: 'subtitle',
1337
+ prompt: '添加副标题'
1338
+ }
1339
+ }
1340
+ ]
1341
+ };
1342
+ }
1343
+ function strategyPresentation() {
1344
+ const slides = [
1345
+ {
1346
+ id: createWorkId('slide'),
1347
+ name: '封面',
1348
+ background: '#16213d',
1349
+ elements: [
1350
+ {
1351
+ id: createWorkId('element'),
1352
+ type: 'shape',
1353
+ x: 8,
1354
+ y: 12,
1355
+ width: 9,
1356
+ height: 3,
1357
+ text: '',
1358
+ fontSize: 12,
1359
+ color: '#ffffff',
1360
+ fill: '#ffb15a',
1361
+ bold: false,
1362
+ align: 'left',
1363
+ radius: 2
1364
+ },
1365
+ {
1366
+ id: createWorkId('element'),
1367
+ type: 'text',
1368
+ x: 8,
1369
+ y: 30,
1370
+ width: 72,
1371
+ height: 24,
1372
+ text: '业务策略汇报',
1373
+ fontSize: 38,
1374
+ color: '#ffffff',
1375
+ fill: 'transparent',
1376
+ bold: true,
1377
+ align: 'left'
1378
+ },
1379
+ {
1380
+ id: createWorkId('element'),
1381
+ type: 'text',
1382
+ x: 8,
1383
+ y: 58,
1384
+ width: 62,
1385
+ height: 10,
1386
+ text: '把最重要的结论放在标题中',
1387
+ fontSize: 17,
1388
+ color: '#b8c4df',
1389
+ fill: 'transparent',
1390
+ bold: false,
1391
+ align: 'left'
1392
+ }
1393
+ ]
1394
+ },
1395
+ {
1396
+ id: createWorkId('slide'),
1397
+ name: '核心判断',
1398
+ background: '#f7f4ee',
1399
+ elements: [
1400
+ {
1401
+ id: createWorkId('element'),
1402
+ type: 'text',
1403
+ x: 8,
1404
+ y: 10,
1405
+ width: 84,
1406
+ height: 11,
1407
+ text: '01 核心判断',
1408
+ fontSize: 15,
1409
+ color: '#b44e34',
1410
+ fill: 'transparent',
1411
+ bold: true,
1412
+ align: 'left'
1413
+ },
1414
+ {
1415
+ id: createWorkId('element'),
1416
+ type: 'text',
1417
+ x: 8,
1418
+ y: 27,
1419
+ width: 76,
1420
+ height: 22,
1421
+ text: '用一句可以独立成立的话,说明我们看到了什么。',
1422
+ fontSize: 31,
1423
+ color: '#20273a',
1424
+ fill: 'transparent',
1425
+ bold: true,
1426
+ align: 'left'
1427
+ },
1428
+ {
1429
+ id: createWorkId('element'),
1430
+ type: 'shape',
1431
+ x: 8,
1432
+ y: 60,
1433
+ width: 84,
1434
+ height: 22,
1435
+ text: '关键证据或数据',
1436
+ fontSize: 18,
1437
+ color: '#ffffff',
1438
+ fill: '#b44e34',
1439
+ bold: true,
1440
+ align: 'center',
1441
+ radius: 3
1442
+ }
1443
+ ]
1444
+ },
1445
+ {
1446
+ id: createWorkId('slide'),
1447
+ name: '下一步',
1448
+ background: '#ffffff',
1449
+ elements: [
1450
+ {
1451
+ id: createWorkId('element'),
1452
+ type: 'text',
1453
+ x: 8,
1454
+ y: 10,
1455
+ width: 84,
1456
+ height: 12,
1457
+ text: '02 下一步',
1458
+ fontSize: 15,
1459
+ color: '#b44e34',
1460
+ fill: 'transparent',
1461
+ bold: true,
1462
+ align: 'left'
1463
+ },
1464
+ {
1465
+ id: createWorkId('element'),
1466
+ type: 'text',
1467
+ x: 8,
1468
+ y: 28,
1469
+ width: 84,
1470
+ height: 46,
1471
+ text: '1 确认优先级\n2 指定负责人\n3 设定可验证的里程碑',
1472
+ fontSize: 26,
1473
+ color: '#20273a',
1474
+ fill: '#f3f0ea',
1475
+ bold: false,
1476
+ align: 'left',
1477
+ radius: 3
1478
+ }
1479
+ ]
1480
+ }
1481
+ ];
1482
+ return {
1483
+ type: 'presentation',
1484
+ slides
1485
+ };
1486
+ }
1487
+ const OFFICE_KERNEL_SPREADSHEET_MAX_ROWS = 1048576;
1488
+ const spreadsheetErrors = new Set([
1489
+ '#BLOCKED!',
1490
+ '#BUSY!',
1491
+ '#CALC!',
1492
+ '#CONNECT!',
1493
+ '#DIV/0!',
1494
+ '#FIELD!',
1495
+ '#GETTING_DATA',
1496
+ '#N/A',
1497
+ '#NAME?',
1498
+ '#NULL!',
1499
+ '#NUM!',
1500
+ '#PYTHON!',
1501
+ '#REF!',
1502
+ '#SPILL!',
1503
+ '#UNKNOWN!',
1504
+ '#VALUE!'
1505
+ ]);
1506
+ new TextEncoder();
1507
+ function isOfficeKernelSpreadsheetError(value) {
1508
+ return 'string' == typeof value && spreadsheetErrors.has(value);
1509
+ }
1510
+ export { DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE, DOCUMENT_PARAGRAPH_BORDER_EDGES, DOCUMENT_PARAGRAPH_BORDER_STYLES, DOCUMENT_RUN_BORDER_ATTRIBUTE, DOCUMENT_RUN_BORDER_STYLES, DocxThemePatchCollector, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, OoxmlPackage, WORK_TEMPLATES as officeTemplates, attribute, bytesToDataUrl, childPath, contentTypeForPart, createWorkArtifact as createArtifact, createWorkId as createOfficeId, decodeXmlBytes, descendants, directChild, directChildren, documentBorderPresentation, documentParagraphBordersDomAttributes, documentRunBorderDomAttributes, documentRunBorderIsVisible, firstDescendant, isDocumentParagraphArtBorderStyle, isOfficeKernelSpreadsheetError, normalizeCssColor, normalizeDocumentParagraphBorder, normalizeDocumentParagraphBorders, normalizeDocumentRunBorder, parseDocumentParagraphBorders, parseDocumentParagraphBordersElement, parseDocumentRunBorder, parseDocumentRunBorderElement, parseDocxThemeReference, parseXml, patchDocxThemeReferences, resolvePartTarget, serializeDocumentParagraphBorders, serializeDocumentRunBorder, serializeDocxThemeReference, serializeUtf8Xml, xmlContainsAnyElement, xmlNamespacePrefix };