@a3s-lab/office 0.30.0 → 0.31.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 (29) hide show
  1. package/README.md +22 -5
  2. package/dist/{0~7231.js → 0~4808.js} +189 -2
  3. package/dist/0~5050.js +10 -1
  4. package/dist/{0~7360.js → 0~7240.js} +244 -15
  5. package/dist/0~document-editor.js +622 -125
  6. package/dist/0~work-docx-export.js +214 -26
  7. package/dist/0~work-docx-import.js +17 -6
  8. package/dist/0~work-office-diagnostics.js +25 -4
  9. package/dist/4104.js +1 -1
  10. package/dist/4121.js +412 -11
  11. package/dist/6282.js +431 -2
  12. package/dist/internal/features/work/editors/document-command-catalog.d.ts +27 -0
  13. package/dist/internal/features/work/editors/document-dom-selection.d.ts +7 -0
  14. package/dist/internal/features/work/editors/document-editor-support.d.ts +1 -0
  15. package/dist/internal/features/work/editors/document-index-dialog.d.ts +10 -0
  16. package/dist/internal/features/work/editors/document-index-entry-dialog.d.ts +10 -0
  17. package/dist/internal/features/work/editors/document-references-ribbon.d.ts +19 -0
  18. package/dist/internal/features/work/editors/document-toolbar.d.ts +4 -1
  19. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -0
  20. package/dist/internal/features/work/work-document-index-fields.d.ts +19 -0
  21. package/dist/internal/features/work/work-document-index-nodes.d.ts +32 -0
  22. package/dist/internal/features/work/work-document-index.d.ts +63 -0
  23. package/dist/internal/features/work/work-docx-import.d.ts +3 -1
  24. package/dist/internal/features/work/work-docx-index-export.d.ts +14 -0
  25. package/dist/internal/features/work/work-docx-index-import.d.ts +23 -0
  26. package/dist/office-kernel.wasm +0 -0
  27. package/dist/styles.css +296 -0
  28. package/docs/latest/en/browser-editor-architecture.md +14 -0
  29. package/package.json +4 -1
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
  {
@@ -2545,6 +2835,13 @@ const WORK_TEMPLATES = [
2545
2835
  description: '标题级别、超链接、页码、前导符与原生 DOCX 往返',
2546
2836
  accent: '#315f9f'
2547
2837
  },
2838
+ {
2839
+ id: 'document-index',
2840
+ kind: 'document',
2841
+ name: '原生索引',
2842
+ description: '主次索引项、交叉引用、页码样式与原生 DOCX 往返',
2843
+ accent: '#4d6398'
2844
+ },
2548
2845
  {
2549
2846
  id: 'text-effects',
2550
2847
  kind: 'document',
@@ -2639,6 +2936,7 @@ function initialTitle(templateId, kind) {
2639
2936
  const titles = {
2640
2937
  'project-brief': '新项目方案',
2641
2938
  'table-of-contents': '可更新目录示例',
2939
+ 'document-index': '原生索引示例',
2642
2940
  'text-effects': '文字效果示例',
2643
2941
  'run-borders': '字符边框示例',
2644
2942
  'run-shading': '字符底纹示例',
@@ -2703,6 +3001,109 @@ function contentForTemplate(templateId) {
2703
3001
  '<p>导出 DOCX 后仍保留原生 TOC 域、缓存目录项、超链接和前导符。</p>'
2704
3002
  ].join('')
2705
3003
  };
3004
+ if ('document-index' === templateId) return {
3005
+ type: 'document',
3006
+ pageSize: 'a4',
3007
+ html: [
3008
+ documentIndexHtml({
3009
+ id: 'playground-document-index',
3010
+ options: {
3011
+ columns: 2,
3012
+ format: 'indented',
3013
+ rightAlignPageNumbers: true,
3014
+ leader: 'dot'
3015
+ },
3016
+ entries: [
3017
+ {
3018
+ mainEntry: 'Architecture',
3019
+ subEntry: '',
3020
+ crossReference: '',
3021
+ pages: [
3022
+ {
3023
+ pageNumber: 1,
3024
+ pageBold: false,
3025
+ pageItalic: false,
3026
+ targetIds: [
3027
+ 'index-entry-architecture'
3028
+ ]
3029
+ }
3030
+ ]
3031
+ },
3032
+ {
3033
+ mainEntry: 'Architecture',
3034
+ subEntry: 'Runtime',
3035
+ crossReference: '',
3036
+ pages: [
3037
+ {
3038
+ pageNumber: 1,
3039
+ pageBold: true,
3040
+ pageItalic: false,
3041
+ targetIds: [
3042
+ 'index-entry-runtime'
3043
+ ]
3044
+ }
3045
+ ]
3046
+ },
3047
+ {
3048
+ mainEntry: 'Collaboration',
3049
+ subEntry: '',
3050
+ crossReference: 'Architecture',
3051
+ pages: []
3052
+ },
3053
+ {
3054
+ mainEntry: 'Performance',
3055
+ subEntry: '',
3056
+ crossReference: '',
3057
+ pages: [
3058
+ {
3059
+ pageNumber: 2,
3060
+ pageBold: false,
3061
+ pageItalic: true,
3062
+ targetIds: [
3063
+ 'index-entry-performance'
3064
+ ]
3065
+ }
3066
+ ]
3067
+ }
3068
+ ]
3069
+ }),
3070
+ '<h1>原生索引</h1>',
3071
+ `<p>选择正文后,从“引用 → 标记索引项”创建主索引项、次索引项或交叉引用。${documentIndexEntryHtml({
3072
+ id: 'index-entry-architecture',
3073
+ mainEntry: 'Architecture',
3074
+ subEntry: '',
3075
+ crossReference: '',
3076
+ pageBold: false,
3077
+ pageItalic: false
3078
+ })}</p>`,
3079
+ `<p>Runtime 项的页码使用粗体,并作为 Architecture 的次索引项。${documentIndexEntryHtml({
3080
+ id: 'index-entry-runtime',
3081
+ mainEntry: 'Architecture',
3082
+ subEntry: 'Runtime',
3083
+ crossReference: '',
3084
+ pageBold: true,
3085
+ pageItalic: false
3086
+ })}</p>`,
3087
+ `<p>Collaboration 使用“参见 Architecture”交叉引用,不生成当前页码。${documentIndexEntryHtml({
3088
+ id: 'index-entry-collaboration',
3089
+ mainEntry: 'Collaboration',
3090
+ subEntry: '',
3091
+ crossReference: 'Architecture',
3092
+ pageBold: false,
3093
+ pageItalic: false
3094
+ })}</p>`,
3095
+ '<hr class="work-page-break" data-page-break="true">',
3096
+ `<p>Performance 位于第二页,页码使用斜体。${documentIndexEntryHtml({
3097
+ id: 'index-entry-performance',
3098
+ mainEntry: 'Performance',
3099
+ subEntry: '',
3100
+ crossReference: '',
3101
+ pageBold: false,
3102
+ pageItalic: true
3103
+ })}</p>`,
3104
+ '<p>修改索引项后选择“更新索引”,再导出 DOCX 验证原生 XE 与 INDEX 域。</p>'
3105
+ ].join('')
3106
+ };
2706
3107
  if ('text-effects' === templateId) return {
2707
3108
  type: 'document',
2708
3109
  pageSize: 'a4',
@@ -3445,4 +3846,4 @@ new TextEncoder();
3445
3846
  function isOfficeKernelSpreadsheetError(value) {
3446
3847
  return 'string' == typeof value && spreadsheetErrors.has(value);
3447
3848
  }
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 };
3849
+ 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 };