@a3s-lab/office 0.30.0 → 0.32.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 (45) hide show
  1. package/README.md +25 -5
  2. package/dist/{0~7231.js → 0~4808.js} +190 -3
  3. package/dist/0~4980.js +1 -1
  4. package/dist/0~5050.js +10 -1
  5. package/dist/{0~7360.js → 0~7240.js} +245 -16
  6. package/dist/0~7614.js +1 -1
  7. package/dist/0~document-editor.js +1756 -178
  8. package/dist/0~spreadsheet-editor.js +2 -1
  9. package/dist/0~work-docx-export.js +215 -27
  10. package/dist/0~work-docx-import.js +27 -33
  11. package/dist/0~work-docx-large-document-import.js +1 -1
  12. package/dist/0~work-office-diagnostics.js +28 -7
  13. package/dist/0~work-pptx-import.js +1 -1
  14. package/dist/{1544.js → 3266.js} +17 -1862
  15. package/dist/4121.js +425 -11
  16. package/dist/4166.js +3848 -0
  17. package/dist/{6282.js → 4174.js} +812 -10
  18. package/dist/9333.js +1849 -0
  19. package/dist/core.js +5259 -4
  20. package/dist/index.js +3 -3
  21. package/dist/internal/features/work/editors/document-command-catalog.d.ts +27 -0
  22. package/dist/internal/features/work/editors/document-compare-dialog.d.ts +14 -0
  23. package/dist/internal/features/work/editors/document-dom-selection.d.ts +7 -0
  24. package/dist/internal/features/work/editors/document-editor-support.d.ts +1 -0
  25. package/dist/internal/features/work/editors/document-index-dialog.d.ts +10 -0
  26. package/dist/internal/features/work/editors/document-index-entry-dialog.d.ts +10 -0
  27. package/dist/internal/features/work/editors/document-references-ribbon.d.ts +19 -0
  28. package/dist/internal/features/work/editors/document-toolbar.d.ts +6 -1
  29. package/dist/internal/features/work/editors/use-document-comparison.d.ts +9 -0
  30. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -0
  31. package/dist/internal/features/work/work-document-changes.d.ts +5 -0
  32. package/dist/internal/features/work/work-document-compare-diff.d.ts +38 -0
  33. package/dist/internal/features/work/work-document-compare-stability.d.ts +5 -0
  34. package/dist/internal/features/work/work-document-compare.d.ts +27 -0
  35. package/dist/internal/features/work/work-document-index-fields.d.ts +19 -0
  36. package/dist/internal/features/work/work-document-index-nodes.d.ts +32 -0
  37. package/dist/internal/features/work/work-document-index.d.ts +63 -0
  38. package/dist/internal/features/work/work-docx-import.d.ts +3 -1
  39. package/dist/internal/features/work/work-docx-index-export.d.ts +14 -0
  40. package/dist/internal/features/work/work-docx-index-import.d.ts +23 -0
  41. package/dist/office-kernel.wasm +0 -0
  42. package/dist/styles.css +490 -0
  43. package/docs/latest/en/browser-editor-architecture.md +37 -0
  44. package/package.json +9 -3
  45. package/dist/4104.js +0 -9336
package/dist/4121.js CHANGED
@@ -1919,6 +1919,296 @@ function patchDocumentProofingLanguages(source, patch) {
1919
1919
  function isRecord(source) {
1920
1920
  return 'object' == typeof source && null !== source && !Array.isArray(source);
1921
1921
  }
1922
+ const DEFAULT_DOCUMENT_INDEX_OPTIONS = {
1923
+ columns: 1,
1924
+ format: 'indented',
1925
+ rightAlignPageNumbers: true,
1926
+ leader: 'dot'
1927
+ };
1928
+ const MAX_DOCUMENT_INDEX_ENTRIES = 512;
1929
+ const MAX_DOCUMENT_INDEX_MARKERS = 2048;
1930
+ const MAX_DOCUMENT_INDEX_TERM_LENGTH = 240;
1931
+ const INDEX_ENTRY_SELECTOR = '[data-document-index-entry]';
1932
+ const INDEX_SELECTOR = '[data-document-index]';
1933
+ const INDEX_ID_PATTERN = /^[a-z0-9][a-z0-9._:-]{0,127}$/i;
1934
+ function normalizeDocumentIndexEntryDraft(source) {
1935
+ const mainEntry = normalizedIndexTerm(source?.mainEntry);
1936
+ if (!mainEntry) return null;
1937
+ const subEntry = normalizedIndexTerm(source?.subEntry);
1938
+ const crossReference = normalizedIndexTerm(source?.crossReference);
1939
+ return {
1940
+ mainEntry,
1941
+ subEntry,
1942
+ crossReference,
1943
+ pageBold: !crossReference && Boolean(source?.pageBold),
1944
+ pageItalic: !crossReference && Boolean(source?.pageItalic)
1945
+ };
1946
+ }
1947
+ function normalizeDocumentIndexEntry(source, fallbackId = 'index-entry') {
1948
+ const value = normalizeDocumentIndexEntryDraft(source);
1949
+ if (!value) return null;
1950
+ return {
1951
+ id: validIndexId(source?.id) ?? fallbackId,
1952
+ ...value
1953
+ };
1954
+ }
1955
+ function normalizeDocumentIndexOptions(source) {
1956
+ return {
1957
+ columns: boundedColumns(source?.columns),
1958
+ format: source?.format === 'run-in' ? 'run-in' : 'indented',
1959
+ rightAlignPageNumbers: source?.rightAlignPageNumbers !== false,
1960
+ leader: indexLeader(source?.leader) ?? 'dot'
1961
+ };
1962
+ }
1963
+ function normalizeDocumentIndexValue(source) {
1964
+ return {
1965
+ id: validIndexId(source.id) ?? 'document-index',
1966
+ options: normalizeDocumentIndexOptions(source.options),
1967
+ entries: normalizeGeneratedEntries(source.entries),
1968
+ truncated: Boolean(source.truncated)
1969
+ };
1970
+ }
1971
+ function buildDocumentIndexEntries(document, options = {}) {
1972
+ const grouped = new Map();
1973
+ let markerCount = 0;
1974
+ document.descendants((node, position)=>{
1975
+ if ('documentIndexEntry' !== node.type.name) return;
1976
+ markerCount += 1;
1977
+ if (markerCount > MAX_DOCUMENT_INDEX_MARKERS) return;
1978
+ const marker = normalizeDocumentIndexEntry(node.attrs, `index-entry-${markerCount}`);
1979
+ if (!marker) return;
1980
+ const key = indexEntryKey(marker);
1981
+ let group = grouped.get(key);
1982
+ if (!group) {
1983
+ group = {
1984
+ entry: {
1985
+ mainEntry: marker.mainEntry,
1986
+ subEntry: marker.subEntry,
1987
+ crossReference: marker.crossReference,
1988
+ pages: []
1989
+ },
1990
+ pages: new Map()
1991
+ };
1992
+ grouped.set(key, group);
1993
+ }
1994
+ if (marker.crossReference) return;
1995
+ const pageNumber = positiveInteger(options.resolveContext?.(position)?.pageNumber) ?? fallbackDocumentPageNumber(document, position);
1996
+ const existing = group.pages.get(pageNumber);
1997
+ if (existing) {
1998
+ existing.pageBold ||= marker.pageBold;
1999
+ existing.pageItalic ||= marker.pageItalic;
2000
+ if (!existing.targetIds.includes(marker.id)) existing.targetIds.push(marker.id);
2001
+ return;
2002
+ }
2003
+ group.pages.set(pageNumber, {
2004
+ pageNumber,
2005
+ pageBold: marker.pageBold,
2006
+ pageItalic: marker.pageItalic,
2007
+ targetIds: [
2008
+ marker.id
2009
+ ]
2010
+ });
2011
+ });
2012
+ const collator = new Intl.Collator(void 0, {
2013
+ numeric: true,
2014
+ sensitivity: 'base',
2015
+ usage: 'sort'
2016
+ });
2017
+ const allEntries = Array.from(grouped.values()).map(({ entry, pages })=>({
2018
+ ...entry,
2019
+ pages: Array.from(pages.values()).sort((left, right)=>left.pageNumber - right.pageNumber)
2020
+ })).sort((left, right)=>collator.compare(left.mainEntry, right.mainEntry) || collator.compare(left.subEntry, right.subEntry) || collator.compare(left.crossReference, right.crossReference));
2021
+ return {
2022
+ entries: allEntries.slice(0, MAX_DOCUMENT_INDEX_ENTRIES),
2023
+ truncated: markerCount > MAX_DOCUMENT_INDEX_MARKERS || allEntries.length > MAX_DOCUMENT_INDEX_ENTRIES
2024
+ };
2025
+ }
2026
+ function documentIndexEntryHtml(source) {
2027
+ const value = normalizeDocumentIndexEntry(source);
2028
+ if (!value) return '';
2029
+ const detail = indexEntryDisplay(value);
2030
+ return [
2031
+ `<span data-document-index-entry="true" data-index-entry-id="${escapeHtmlAttribute(value.id)}" data-index-main-entry="${escapeHtmlAttribute(value.mainEntry)}" data-index-sub-entry="${escapeHtmlAttribute(value.subEntry)}" data-index-cross-reference="${escapeHtmlAttribute(value.crossReference)}" data-index-page-bold="${String(value.pageBold)}" data-index-page-italic="${String(value.pageItalic)}" class="work-document-index-entry" contenteditable="false" aria-label="索引项:${escapeHtmlAttribute(detail)}">`,
2032
+ '<span aria-hidden="true">索引项</span>',
2033
+ `<strong>${escapeHtml(detail)}</strong>`,
2034
+ '</span>'
2035
+ ].join('');
2036
+ }
2037
+ function documentIndexHtml(source) {
2038
+ const value = normalizeDocumentIndexValue(source);
2039
+ const rows = value.entries.length ? value.entries.map(documentIndexRowHtml) : [
2040
+ '<li class="work-document-index-empty">没有已标记的索引项</li>'
2041
+ ];
2042
+ const status = value.truncated ? `仅显示前 ${MAX_DOCUMENT_INDEX_ENTRIES} 项` : `${value.entries.length} 项`;
2043
+ return [
2044
+ `<div data-document-index="true" data-index-id="${escapeHtmlAttribute(value.id)}" data-index-columns="${value.options.columns}" data-index-format="${value.options.format}" data-index-right-align-page-numbers="${String(value.options.rightAlignPageNumbers)}" data-index-leader="${value.options.leader}" data-index-entries="${escapeHtmlAttribute(JSON.stringify(value.entries))}" data-index-truncated="${String(value.truncated)}" class="work-document-index" contenteditable="false" aria-label="索引">`,
2045
+ `<div class="work-document-index-header"><strong>索引</strong><span>${status}</span></div>`,
2046
+ '<ol class="work-document-index-list">',
2047
+ ...rows,
2048
+ '</ol></div>'
2049
+ ].join('');
2050
+ }
2051
+ function documentIndexEntryFromElement(element, index = 1) {
2052
+ return normalizeDocumentIndexEntry({
2053
+ id: element.dataset.indexEntryId,
2054
+ mainEntry: element.dataset.indexMainEntry,
2055
+ subEntry: element.dataset.indexSubEntry,
2056
+ crossReference: element.dataset.indexCrossReference,
2057
+ pageBold: 'true' === element.dataset.indexPageBold,
2058
+ pageItalic: 'true' === element.dataset.indexPageItalic
2059
+ }, `index-entry-${index}`);
2060
+ }
2061
+ function documentIndexValueFromElement(element, index = 1) {
2062
+ return normalizeDocumentIndexValue({
2063
+ id: validIndexId(element.dataset.indexId) ?? `document-index-${index}`,
2064
+ options: {
2065
+ columns: Number(element.dataset.indexColumns),
2066
+ format: 'run-in' === element.dataset.indexFormat ? 'run-in' : 'indented',
2067
+ rightAlignPageNumbers: 'false' !== element.dataset.indexRightAlignPageNumbers,
2068
+ leader: indexLeader(element.dataset.indexLeader) ?? 'dot'
2069
+ },
2070
+ entries: parseGeneratedEntries(element.dataset.indexEntries),
2071
+ truncated: 'true' === element.dataset.indexTruncated
2072
+ });
2073
+ }
2074
+ function normalizeDocumentIndexesHtml(source) {
2075
+ const document = new DOMParser().parseFromString(source, 'text/html');
2076
+ const usedEntryIds = new Set();
2077
+ for (const [index, element] of Array.from(document.body.querySelectorAll(INDEX_ENTRY_SELECTOR)).entries()){
2078
+ const value = documentIndexEntryFromElement(element, index + 1);
2079
+ if (!value) {
2080
+ element.remove();
2081
+ continue;
2082
+ }
2083
+ value.id = uniqueIndexId(value.id, 'index-entry', index + 1, usedEntryIds);
2084
+ element.replaceWith(document.createRange().createContextualFragment(documentIndexEntryHtml(value)));
2085
+ }
2086
+ const usedIndexIds = new Set();
2087
+ for (const [index, element] of Array.from(document.body.querySelectorAll(INDEX_SELECTOR)).entries()){
2088
+ const value = documentIndexValueFromElement(element, index + 1);
2089
+ value.id = uniqueIndexId(value.id, 'document-index', index + 1, usedIndexIds);
2090
+ element.replaceWith(document.createRange().createContextualFragment(documentIndexHtml(value)));
2091
+ }
2092
+ return document.body.innerHTML;
2093
+ }
2094
+ function indexLeader(value) {
2095
+ return 'dot' === value || 'dash' === value || 'underline' === value || 'none' === value ? value : null;
2096
+ }
2097
+ function documentIndexRowHtml(entry) {
2098
+ const term = entry.subEntry ? `<span class="work-document-index-main">${escapeHtml(entry.mainEntry)}</span><span class="work-document-index-sub">${escapeHtml(entry.subEntry)}</span>` : `<span class="work-document-index-main">${escapeHtml(entry.mainEntry)}</span>`;
2099
+ const pages = entry.crossReference ? `<span class="work-document-index-cross-reference">参见 ${escapeHtml(entry.crossReference)}</span>` : entry.pages.map((page)=>{
2100
+ const target = page.targetIds[0] ?? '';
2101
+ const classes = [
2102
+ page.pageBold ? 'bold' : '',
2103
+ page.pageItalic ? 'italic' : ''
2104
+ ].filter(Boolean).join(' ');
2105
+ return `<a href="#${escapeHtmlAttribute(target)}" data-index-target="${escapeHtmlAttribute(target)}" tabindex="-1"${classes ? ` class="${classes}"` : ''}>${page.pageNumber}</a>`;
2106
+ }).join('<span aria-hidden="true">, </span>');
2107
+ return `<li data-index-main-entry="${escapeHtmlAttribute(entry.mainEntry)}" data-index-sub-entry="${escapeHtmlAttribute(entry.subEntry)}">${term}<i aria-hidden="true"></i><span class="work-document-index-pages">${pages}</span></li>`;
2108
+ }
2109
+ function normalizeGeneratedEntries(source) {
2110
+ const entries = [];
2111
+ for (const item of source.slice(0, MAX_DOCUMENT_INDEX_ENTRIES)){
2112
+ if (!item || 'object' != typeof item) continue;
2113
+ const candidate = item;
2114
+ const draft = normalizeDocumentIndexEntryDraft({
2115
+ mainEntry: normalizedIndexTerm(candidate.mainEntry),
2116
+ subEntry: normalizedIndexTerm(candidate.subEntry),
2117
+ crossReference: normalizedIndexTerm(candidate.crossReference)
2118
+ });
2119
+ if (draft) entries.push({
2120
+ mainEntry: draft.mainEntry,
2121
+ subEntry: draft.subEntry,
2122
+ crossReference: draft.crossReference,
2123
+ pages: draft.crossReference ? [] : normalizeIndexPages(candidate.pages)
2124
+ });
2125
+ }
2126
+ return entries;
2127
+ }
2128
+ function normalizeIndexPages(source) {
2129
+ if (!Array.isArray(source)) return [];
2130
+ const pages = [];
2131
+ const seen = new Set();
2132
+ for (const item of source.slice(0, MAX_DOCUMENT_INDEX_MARKERS)){
2133
+ if (!item || 'object' != typeof item) continue;
2134
+ const candidate = item;
2135
+ const pageNumber = positiveInteger(candidate.pageNumber);
2136
+ if (!pageNumber || seen.has(pageNumber)) continue;
2137
+ const targetIds = Array.isArray(candidate.targetIds) ? candidate.targetIds.flatMap((value)=>validIndexId(value) ? [
2138
+ String(value)
2139
+ ] : []).slice(0, 64) : [];
2140
+ if (targetIds.length) {
2141
+ seen.add(pageNumber);
2142
+ pages.push({
2143
+ pageNumber,
2144
+ pageBold: Boolean(candidate.pageBold),
2145
+ pageItalic: Boolean(candidate.pageItalic),
2146
+ targetIds
2147
+ });
2148
+ }
2149
+ }
2150
+ return pages.sort((left, right)=>left.pageNumber - right.pageNumber);
2151
+ }
2152
+ function parseGeneratedEntries(source) {
2153
+ if (!source) return [];
2154
+ try {
2155
+ const parsed = JSON.parse(source);
2156
+ return Array.isArray(parsed) ? normalizeGeneratedEntries(parsed) : [];
2157
+ } catch {
2158
+ return [];
2159
+ }
2160
+ }
2161
+ function fallbackDocumentPageNumber(document, position) {
2162
+ let pageNumber = 1;
2163
+ document.descendants((node, offset)=>{
2164
+ if (offset >= position) return false;
2165
+ if ('pageBreak' === node.type.name) pageNumber += 1;
2166
+ return true;
2167
+ });
2168
+ return pageNumber;
2169
+ }
2170
+ function indexEntryKey(entry) {
2171
+ return [
2172
+ entry.mainEntry,
2173
+ entry.subEntry,
2174
+ entry.crossReference
2175
+ ].map((value)=>value.normalize('NFKC').toLocaleLowerCase()).join('\u0000');
2176
+ }
2177
+ function indexEntryDisplay(entry) {
2178
+ const term = entry.subEntry ? `${entry.mainEntry} › ${entry.subEntry}` : entry.mainEntry;
2179
+ return entry.crossReference ? `${term} · 参见 ${entry.crossReference}` : term;
2180
+ }
2181
+ function normalizedIndexTerm(value) {
2182
+ return 'string' == typeof value ? value.replace(/\s+/g, ' ').trim().slice(0, MAX_DOCUMENT_INDEX_TERM_LENGTH) : '';
2183
+ }
2184
+ function boundedColumns(value) {
2185
+ const number = Number(value);
2186
+ return Number.isInteger(number) && number >= 1 && number <= 4 ? number : 1;
2187
+ }
2188
+ function positiveInteger(value) {
2189
+ const number = Number(value);
2190
+ return Number.isSafeInteger(number) && number > 0 ? Math.min(999999, number) : null;
2191
+ }
2192
+ function validIndexId(value) {
2193
+ return 'string' == typeof value && INDEX_ID_PATTERN.test(value) ? value : null;
2194
+ }
2195
+ function uniqueIndexId(source, prefix, index, used) {
2196
+ if (!used.has(source)) {
2197
+ used.add(source);
2198
+ return source;
2199
+ }
2200
+ let suffix = index;
2201
+ while(used.has(`${prefix}-${suffix}`))suffix += 1;
2202
+ const id = `${prefix}-${suffix}`;
2203
+ used.add(id);
2204
+ return id;
2205
+ }
2206
+ function escapeHtml(value) {
2207
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
2208
+ }
2209
+ function escapeHtmlAttribute(value) {
2210
+ return escapeHtml(value).replaceAll('"', '&quot;').replaceAll("'", '&#39;');
2211
+ }
1922
2212
  const DOCUMENT_PARAGRAPH_ID_ATTRIBUTE = 'data-office-paragraph-id';
1923
2213
  const DOCUMENT_PARAGRAPH_TEXT_ID_ATTRIBUTE = 'data-office-paragraph-text-id';
1924
2214
  const PARAGRAPH_ID_PATTERN = /^[0-9A-F]{8}$/;
@@ -2310,7 +2600,7 @@ function documentTableOfContentsHtml(source) {
2310
2600
  ];
2311
2601
  const status = value.truncated ? `仅显示前 ${MAX_DOCUMENT_TABLE_OF_CONTENTS_ENTRIES} 项` : `${value.entries.length} 项`;
2312
2602
  return [
2313
- `<div data-document-table-of-contents="true" data-toc-id="${escapeHtmlAttribute(value.id)}" data-toc-min-level="${options.minLevel}" data-toc-max-level="${options.maxLevel}" data-toc-hyperlinks="${String(options.hyperlinks)}" data-toc-show-page-numbers="${String(options.showPageNumbers)}" data-toc-right-align-page-numbers="${String(options.rightAlignPageNumbers)}" data-toc-leader="${options.leader}" data-toc-entries="${escapeHtmlAttribute(JSON.stringify(value.entries))}" data-toc-truncated="${String(value.truncated)}" class="work-document-table-of-contents" contenteditable="false" aria-label="目录">`,
2603
+ `<div data-document-table-of-contents="true" data-toc-id="${work_document_table_of_contents_escapeHtmlAttribute(value.id)}" data-toc-min-level="${options.minLevel}" data-toc-max-level="${options.maxLevel}" data-toc-hyperlinks="${String(options.hyperlinks)}" data-toc-show-page-numbers="${String(options.showPageNumbers)}" data-toc-right-align-page-numbers="${String(options.rightAlignPageNumbers)}" data-toc-leader="${options.leader}" data-toc-entries="${work_document_table_of_contents_escapeHtmlAttribute(JSON.stringify(value.entries))}" data-toc-truncated="${String(value.truncated)}" class="work-document-table-of-contents" contenteditable="false" aria-label="目录">`,
2314
2604
  '<div class="work-document-table-of-contents-header"><strong>目录</strong>',
2315
2605
  `<span>${status}</span></div>`,
2316
2606
  '<ol class="work-document-table-of-contents-list">',
@@ -2434,10 +2724,10 @@ function tableOfContentsEntryFromOutline(document, item, resolveContext) {
2434
2724
  targetId: item.id,
2435
2725
  title: item.text.slice(0, MAX_TABLE_OF_CONTENTS_TITLE_LENGTH),
2436
2726
  level: item.level,
2437
- pageNumber: positiveInteger(resolveContext?.(item.from)?.pageNumber) ?? fallbackDocumentPageNumber(document, item.from)
2727
+ pageNumber: work_document_table_of_contents_positiveInteger(resolveContext?.(item.from)?.pageNumber) ?? work_document_table_of_contents_fallbackDocumentPageNumber(document, item.from)
2438
2728
  };
2439
2729
  }
2440
- function fallbackDocumentPageNumber(document, position) {
2730
+ function work_document_table_of_contents_fallbackDocumentPageNumber(document, position) {
2441
2731
  let pageNumber = 1;
2442
2732
  document.descendants((node, offset)=>{
2443
2733
  if (offset >= position) return false;
@@ -2447,8 +2737,8 @@ function fallbackDocumentPageNumber(document, position) {
2447
2737
  return pageNumber;
2448
2738
  }
2449
2739
  function tableOfContentsEntryHtml(entry, options) {
2450
- const title = escapeHtml(entry.title);
2451
- const targetId = escapeHtmlAttribute(entry.targetId);
2740
+ const title = work_document_table_of_contents_escapeHtml(entry.title);
2741
+ const targetId = work_document_table_of_contents_escapeHtmlAttribute(entry.targetId);
2452
2742
  const label = options.hyperlinks ? `<a href="#${targetId}" data-toc-target="${targetId}" tabindex="-1">${title}</a>` : `<span>${title}</span>`;
2453
2743
  const page = options.showPageNumbers ? `<span class="work-document-table-of-contents-page">${entry.pageNumber}</span>` : '';
2454
2744
  return `<li data-toc-target="${targetId}" data-toc-level="${entry.level}" style="--work-toc-level:${entry.level}">${label}<i aria-hidden="true"></i>${page}</li>`;
@@ -2470,7 +2760,7 @@ function normalizeTableOfContentsEntries(source) {
2470
2760
  const targetId = 'string' == typeof candidate.targetId ? candidate.targetId.trim() : '';
2471
2761
  const title = 'string' == typeof candidate.title ? candidate.title.replace(/\s+/g, ' ').trim() : '';
2472
2762
  const level = boundedLevel(candidate.level, 0);
2473
- const pageNumber = positiveInteger(candidate.pageNumber);
2763
+ const pageNumber = work_document_table_of_contents_positiveInteger(candidate.pageNumber);
2474
2764
  if (TABLE_OF_CONTENTS_TARGET_PATTERN.test(targetId) && title && level && pageNumber) entries.push({
2475
2765
  targetId,
2476
2766
  title: title.slice(0, MAX_TABLE_OF_CONTENTS_TITLE_LENGTH),
@@ -2499,7 +2789,7 @@ function boundedLevel(value, fallback) {
2499
2789
  const number = Number(value);
2500
2790
  return Number.isInteger(number) && number >= 1 && number <= 9 ? number : fallback;
2501
2791
  }
2502
- function positiveInteger(value) {
2792
+ function work_document_table_of_contents_positiveInteger(value) {
2503
2793
  const number = Number(value);
2504
2794
  return Number.isSafeInteger(number) && number > 0 ? Math.min(999999, number) : null;
2505
2795
  }
@@ -2517,11 +2807,11 @@ function uniqueTableOfContentsId(source, index, usedIds) {
2517
2807
  usedIds.add(id);
2518
2808
  return id;
2519
2809
  }
2520
- function escapeHtml(value) {
2810
+ function work_document_table_of_contents_escapeHtml(value) {
2521
2811
  return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
2522
2812
  }
2523
- function escapeHtmlAttribute(value) {
2524
- return escapeHtml(value).replaceAll('"', '&quot;').replaceAll("'", '&#39;');
2813
+ function work_document_table_of_contents_escapeHtmlAttribute(value) {
2814
+ return work_document_table_of_contents_escapeHtml(value).replaceAll('"', '&quot;').replaceAll("'", '&#39;');
2525
2815
  }
2526
2816
  const WORK_TEMPLATES = [
2527
2817
  {
@@ -2538,6 +2828,13 @@ const WORK_TEMPLATES = [
2538
2828
  description: '目标、范围、里程碑与风险',
2539
2829
  accent: '#536de2'
2540
2830
  },
2831
+ {
2832
+ id: 'document-comparison',
2833
+ kind: 'document',
2834
+ name: '文档比较',
2835
+ description: '导入版本并生成可接受或拒绝的确定性修订',
2836
+ accent: '#6a54b8'
2837
+ },
2541
2838
  {
2542
2839
  id: 'table-of-contents',
2543
2840
  kind: 'document',
@@ -2545,6 +2842,13 @@ const WORK_TEMPLATES = [
2545
2842
  description: '标题级别、超链接、页码、前导符与原生 DOCX 往返',
2546
2843
  accent: '#315f9f'
2547
2844
  },
2845
+ {
2846
+ id: 'document-index',
2847
+ kind: 'document',
2848
+ name: '原生索引',
2849
+ description: '主次索引项、交叉引用、页码样式与原生 DOCX 往返',
2850
+ accent: '#4d6398'
2851
+ },
2548
2852
  {
2549
2853
  id: 'text-effects',
2550
2854
  kind: 'document',
@@ -2638,7 +2942,9 @@ function createWorkId(prefix) {
2638
2942
  function initialTitle(templateId, kind) {
2639
2943
  const titles = {
2640
2944
  'project-brief': '新项目方案',
2945
+ 'document-comparison': '文档比较示例',
2641
2946
  'table-of-contents': '可更新目录示例',
2947
+ 'document-index': '原生索引示例',
2642
2948
  'text-effects': '文字效果示例',
2643
2949
  'run-borders': '字符边框示例',
2644
2950
  'run-shading': '字符底纹示例',
@@ -2659,6 +2965,11 @@ function contentForTemplate(templateId) {
2659
2965
  pageSize: 'a4',
2660
2966
  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>"
2661
2967
  };
2968
+ if ('document-comparison' === templateId) return {
2969
+ type: 'document',
2970
+ pageSize: 'a4',
2971
+ html: "<h1>发布审阅基线</h1><p><strong>基线版本:</strong>1.0</p><h2>比较步骤</h2><p>打开“审阅”,选择“比较文档”,再导入 DOCX、HTML 或 TXT 修订版本。</p><h2>审阅范围</h2><p>当前基线包含架构说明与发布说明。</p><h2>决策要求</h2><p>发布前必须逐项接受或拒绝所有生成的修订。</p>"
2972
+ };
2662
2973
  if ('table-of-contents' === templateId) return {
2663
2974
  type: 'document',
2664
2975
  pageSize: 'a4',
@@ -2703,6 +3014,109 @@ function contentForTemplate(templateId) {
2703
3014
  '<p>导出 DOCX 后仍保留原生 TOC 域、缓存目录项、超链接和前导符。</p>'
2704
3015
  ].join('')
2705
3016
  };
3017
+ if ('document-index' === templateId) return {
3018
+ type: 'document',
3019
+ pageSize: 'a4',
3020
+ html: [
3021
+ documentIndexHtml({
3022
+ id: 'playground-document-index',
3023
+ options: {
3024
+ columns: 2,
3025
+ format: 'indented',
3026
+ rightAlignPageNumbers: true,
3027
+ leader: 'dot'
3028
+ },
3029
+ entries: [
3030
+ {
3031
+ mainEntry: 'Architecture',
3032
+ subEntry: '',
3033
+ crossReference: '',
3034
+ pages: [
3035
+ {
3036
+ pageNumber: 1,
3037
+ pageBold: false,
3038
+ pageItalic: false,
3039
+ targetIds: [
3040
+ 'index-entry-architecture'
3041
+ ]
3042
+ }
3043
+ ]
3044
+ },
3045
+ {
3046
+ mainEntry: 'Architecture',
3047
+ subEntry: 'Runtime',
3048
+ crossReference: '',
3049
+ pages: [
3050
+ {
3051
+ pageNumber: 1,
3052
+ pageBold: true,
3053
+ pageItalic: false,
3054
+ targetIds: [
3055
+ 'index-entry-runtime'
3056
+ ]
3057
+ }
3058
+ ]
3059
+ },
3060
+ {
3061
+ mainEntry: 'Collaboration',
3062
+ subEntry: '',
3063
+ crossReference: 'Architecture',
3064
+ pages: []
3065
+ },
3066
+ {
3067
+ mainEntry: 'Performance',
3068
+ subEntry: '',
3069
+ crossReference: '',
3070
+ pages: [
3071
+ {
3072
+ pageNumber: 2,
3073
+ pageBold: false,
3074
+ pageItalic: true,
3075
+ targetIds: [
3076
+ 'index-entry-performance'
3077
+ ]
3078
+ }
3079
+ ]
3080
+ }
3081
+ ]
3082
+ }),
3083
+ '<h1>原生索引</h1>',
3084
+ `<p>选择正文后,从“引用 → 标记索引项”创建主索引项、次索引项或交叉引用。${documentIndexEntryHtml({
3085
+ id: 'index-entry-architecture',
3086
+ mainEntry: 'Architecture',
3087
+ subEntry: '',
3088
+ crossReference: '',
3089
+ pageBold: false,
3090
+ pageItalic: false
3091
+ })}</p>`,
3092
+ `<p>Runtime 项的页码使用粗体,并作为 Architecture 的次索引项。${documentIndexEntryHtml({
3093
+ id: 'index-entry-runtime',
3094
+ mainEntry: 'Architecture',
3095
+ subEntry: 'Runtime',
3096
+ crossReference: '',
3097
+ pageBold: true,
3098
+ pageItalic: false
3099
+ })}</p>`,
3100
+ `<p>Collaboration 使用“参见 Architecture”交叉引用,不生成当前页码。${documentIndexEntryHtml({
3101
+ id: 'index-entry-collaboration',
3102
+ mainEntry: 'Collaboration',
3103
+ subEntry: '',
3104
+ crossReference: 'Architecture',
3105
+ pageBold: false,
3106
+ pageItalic: false
3107
+ })}</p>`,
3108
+ '<hr class="work-page-break" data-page-break="true">',
3109
+ `<p>Performance 位于第二页,页码使用斜体。${documentIndexEntryHtml({
3110
+ id: 'index-entry-performance',
3111
+ mainEntry: 'Performance',
3112
+ subEntry: '',
3113
+ crossReference: '',
3114
+ pageBold: false,
3115
+ pageItalic: true
3116
+ })}</p>`,
3117
+ '<p>修改索引项后选择“更新索引”,再导出 DOCX 验证原生 XE 与 INDEX 域。</p>'
3118
+ ].join('')
3119
+ };
2706
3120
  if ('text-effects' === templateId) return {
2707
3121
  type: 'document',
2708
3122
  pageSize: 'a4',
@@ -3445,4 +3859,4 @@ new TextEncoder();
3445
3859
  function isOfficeKernelSpreadsheetError(value) {
3446
3860
  return 'string' == typeof value && spreadsheetErrors.has(value);
3447
3861
  }
3448
- export { DEFAULT_DOCUMENT_TABLE_OF_CONTENTS_OPTIONS, DOCUMENT_HIGHLIGHT_ATTRIBUTE, DOCUMENT_NO_PROOF_ATTRIBUTE, DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE, DOCUMENT_PARAGRAPH_BORDER_EDGES, DOCUMENT_PARAGRAPH_BORDER_STYLES, DOCUMENT_PARAGRAPH_ID_ATTRIBUTE, DOCUMENT_PARAGRAPH_TEXT_ID_ATTRIBUTE, DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE, DOCUMENT_RUN_BORDER_ATTRIBUTE, DOCUMENT_RUN_BORDER_STYLES, DOCUMENT_RUN_SHADING_ATTRIBUTE, DOCUMENT_SCRIPT_FONTS_ATTRIBUTE, DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE, DocumentParagraphIdentity, DocxThemePatchCollector, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, OoxmlPackage, WORK_TEMPLATES as officeTemplates, applyDocumentParagraphIdentityToElement, attribute, buildDocumentTableOfContentsEntries, bytesToDataUrl, childPath, collectWorkDocumentOutline, contentTypeForPart, createDocumentParagraphIdentity, createDocumentParagraphIdentityRegistry, createWorkArtifact as createArtifact, createWorkId as createOfficeId, cssDocumentFontFamily, currentWorkDocumentOutlineItem, decodeXmlBytes, descendants, directChild, directChildren, documentBorderPresentation, documentFontNameFromCssFamily, documentHasIntegrityFeature, documentHighlightCssColor, documentHighlightDomAttributes, documentHighlightForCssColor, documentHighlightFromDocxValue, documentHighlightFromElement, documentNoProofFromElement, documentParagraphBordersDomAttributes, documentParagraphIdentityFromElement, documentParagraphShadingDomAttributes, documentProofingDomAttributes, documentProofingLanguagesFromElement, documentRunBorderDomAttributes, documentRunBorderIsVisible, documentRunShadingDomAttributes, documentScriptFontDirectFamily, documentScriptFontFallbackSlots, documentScriptFontFamily, documentScriptFontFamilyForRendering, documentScriptFontSegments, documentScriptFontSlotFromElement, documentScriptFontSlotFromHint, documentScriptFontsDomAttributes, documentScriptFontsForAllText, documentScriptFontsFromElement, documentTableOfContentsHtml, documentTableOfContentsValueFromElement, firstDescendant, isDocumentParagraphArtBorderStyle, isOfficeKernelSpreadsheetError, normalizeCssColor, normalizeDocumentFontName, normalizeDocumentHighlight, normalizeDocumentLanguageTag, normalizeDocumentNoProof, normalizeDocumentParagraphBorder, normalizeDocumentParagraphBorders, normalizeDocumentParagraphId, normalizeDocumentParagraphIdentity, normalizeDocumentProofingLanguages, normalizeDocumentRunBorder, normalizeDocumentRunShading, normalizeDocumentScriptFontHint, normalizeDocumentScriptFontSlot, normalizeDocumentScriptFonts, normalizeDocumentTableOfContentsHtml, normalizeDocumentTableOfContentsOptions, normalizeDocumentTableOfContentsValue, normalizeDocumentThemeFont, parseDocumentParagraphBorders, parseDocumentParagraphBordersElement, parseDocumentParagraphShading, parseDocumentParagraphShadingElement, parseDocumentProofingLanguages, parseDocumentRunBorder, parseDocumentRunBorderElement, parseDocumentRunShading, parseDocumentRunShadingElement, parseDocumentScriptFonts, parseDocumentTableOfContentsInstruction, parseDocxThemeReference, parseXml, patchDocumentProofingLanguages, patchDocumentScriptFonts, patchDocxThemeReferences, primeDocumentIntegrityFeatures, resolvePartTarget, serializeDocumentParagraphBorders, serializeDocumentParagraphShading, serializeDocumentProofingLanguages, serializeDocumentRunBorder, serializeDocumentRunShading, serializeDocumentScriptFonts, serializeDocxThemeReference, serializeUtf8Xml, uniqueDocumentParagraphIdentity, visibleWorkDocumentOutlineItems, workDocumentOutlineLevel, work_document_paragraph_shading_DOCUMENT_PARAGRAPH_SHADING_PATTERNS, xmlContainsAnyElement, xmlNamespacePrefix };
3862
+ export { DEFAULT_DOCUMENT_INDEX_OPTIONS, DEFAULT_DOCUMENT_TABLE_OF_CONTENTS_OPTIONS, DOCUMENT_HIGHLIGHT_ATTRIBUTE, DOCUMENT_NO_PROOF_ATTRIBUTE, DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE, DOCUMENT_PARAGRAPH_BORDER_EDGES, DOCUMENT_PARAGRAPH_BORDER_STYLES, DOCUMENT_PARAGRAPH_ID_ATTRIBUTE, DOCUMENT_PARAGRAPH_TEXT_ID_ATTRIBUTE, DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE, DOCUMENT_RUN_BORDER_ATTRIBUTE, DOCUMENT_RUN_BORDER_STYLES, DOCUMENT_RUN_SHADING_ATTRIBUTE, DOCUMENT_SCRIPT_FONTS_ATTRIBUTE, DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE, DocumentParagraphIdentity, DocxThemePatchCollector, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, OoxmlPackage, WORK_TEMPLATES as officeTemplates, applyDocumentParagraphIdentityToElement, attribute, buildDocumentIndexEntries, buildDocumentTableOfContentsEntries, bytesToDataUrl, childPath, collectWorkDocumentOutline, contentTypeForPart, createDocumentParagraphIdentity, createDocumentParagraphIdentityRegistry, createWorkArtifact as createArtifact, createWorkId as createOfficeId, cssDocumentFontFamily, currentWorkDocumentOutlineItem, decodeXmlBytes, descendants, directChild, directChildren, documentBorderPresentation, documentFontNameFromCssFamily, documentHasIntegrityFeature, documentHighlightCssColor, documentHighlightDomAttributes, documentHighlightForCssColor, documentHighlightFromDocxValue, documentHighlightFromElement, documentIndexEntryFromElement, documentIndexEntryHtml, documentIndexHtml, documentIndexValueFromElement, documentNoProofFromElement, documentParagraphBordersDomAttributes, documentParagraphIdentityFromElement, documentParagraphShadingDomAttributes, documentProofingDomAttributes, documentProofingLanguagesFromElement, documentRunBorderDomAttributes, documentRunBorderIsVisible, documentRunShadingDomAttributes, documentScriptFontDirectFamily, documentScriptFontFallbackSlots, documentScriptFontFamily, documentScriptFontFamilyForRendering, documentScriptFontSegments, documentScriptFontSlotFromElement, documentScriptFontSlotFromHint, documentScriptFontsDomAttributes, documentScriptFontsForAllText, documentScriptFontsFromElement, documentTableOfContentsHtml, documentTableOfContentsValueFromElement, firstDescendant, isDocumentParagraphArtBorderStyle, isOfficeKernelSpreadsheetError, normalizeCssColor, normalizeDocumentFontName, normalizeDocumentHighlight, normalizeDocumentIndexEntry, normalizeDocumentIndexEntryDraft, normalizeDocumentIndexOptions, normalizeDocumentIndexValue, normalizeDocumentIndexesHtml, normalizeDocumentLanguageTag, normalizeDocumentNoProof, normalizeDocumentParagraphBorder, normalizeDocumentParagraphBorders, normalizeDocumentParagraphId, normalizeDocumentParagraphIdentity, normalizeDocumentProofingLanguages, normalizeDocumentRunBorder, normalizeDocumentRunShading, normalizeDocumentScriptFontHint, normalizeDocumentScriptFontSlot, normalizeDocumentScriptFonts, normalizeDocumentTableOfContentsHtml, normalizeDocumentTableOfContentsOptions, normalizeDocumentTableOfContentsValue, normalizeDocumentThemeFont, parseDocumentParagraphBorders, parseDocumentParagraphBordersElement, parseDocumentParagraphShading, parseDocumentParagraphShadingElement, parseDocumentProofingLanguages, parseDocumentRunBorder, parseDocumentRunBorderElement, parseDocumentRunShading, parseDocumentRunShadingElement, parseDocumentScriptFonts, parseDocumentTableOfContentsInstruction, parseDocxThemeReference, parseXml, patchDocumentProofingLanguages, patchDocumentScriptFonts, patchDocxThemeReferences, primeDocumentIntegrityFeatures, resolvePartTarget, serializeDocumentParagraphBorders, serializeDocumentParagraphShading, serializeDocumentProofingLanguages, serializeDocumentRunBorder, serializeDocumentRunShading, serializeDocumentScriptFonts, serializeDocxThemeReference, serializeUtf8Xml, uniqueDocumentParagraphIdentity, visibleWorkDocumentOutlineItems, workDocumentOutlineLevel, work_document_paragraph_shading_DOCUMENT_PARAGRAPH_SHADING_PATTERNS, xmlContainsAnyElement, xmlNamespacePrefix };