@univerjs/docs-toc 1.0.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.
@@ -0,0 +1,583 @@
1
+ import { BooleanNumber, BuildTextUtils, CommandType, CustomRangeType, DependentOn, ICommandService, IConfigService, IUndoRedoService, IUniverInstanceService, Inject, Injector, JSONX, NamedStyleType, Plugin, SliceBodyType, TabStopAlignment, TabStopLeader, TextX, Tools, UniverInstanceType, cloneParagraphWithId, createParagraphId, generateRandomId, getBodySlice, getPlainText, getRichTextEditPath, merge } from "@univerjs/core";
2
+ import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, RichTextEditingMutation, UniverDocsPlugin } from "@univerjs/docs";
3
+ import { IRenderManagerService, UniverRenderEnginePlugin } from "@univerjs/engine-render";
4
+ import { filter, firstValueFrom, take, timeout } from "rxjs";
5
+
6
+ //#region src/commands/table-of-contents.command.ts
7
+ const FIELD_START = "";
8
+ const FIELD_END = "";
9
+ async function replaceBodyRange(accessor, doc, unitId, selection, body, historyAction, historyGroupId = generateRandomId()) {
10
+ const textX = new TextX();
11
+ textX.push(...BuildTextUtils.selection.delete([selection], doc.getBody(), 0, body));
12
+ const caretOffset = selection.startOffset + body.dataStream.length;
13
+ accessor.get(DocStateChangeManagerService).flushPendingChanges(unitId);
14
+ const group = accessor.get(IUndoRedoService).beginUndoRedoGroup(unitId, historyGroupId, "append");
15
+ try {
16
+ return Boolean(accessor.get(ICommandService).syncExecuteCommand(RichTextEditingMutation.id, {
17
+ unitId,
18
+ historyAction,
19
+ actions: JSONX.getInstance().editOp(textX.serialize(), getRichTextEditPath(doc)),
20
+ textRanges: [{
21
+ startOffset: caretOffset,
22
+ endOffset: caretOffset,
23
+ collapsed: true
24
+ }],
25
+ debounce: false,
26
+ trigger: historyAction
27
+ }));
28
+ } finally {
29
+ group.dispose();
30
+ }
31
+ }
32
+ const UpdateTableOfContentsCommand = {
33
+ id: "doc.command.update-table-of-contents",
34
+ type: CommandType.COMMAND,
35
+ handler: (accessor, params) => updateTableOfContents(accessor, params)
36
+ };
37
+ async function updateTableOfContents(accessor, params, historyGroupId = generateRandomId()) {
38
+ var _body$customRanges, _toc$properties, _render$getInjector;
39
+ const instanceService = accessor.get(IUniverInstanceService);
40
+ const doc = (params === null || params === void 0 ? void 0 : params.unitId) ? instanceService.getUnit(params.unitId, UniverInstanceType.UNIVER_DOC) : instanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_DOC);
41
+ const body = doc === null || doc === void 0 ? void 0 : doc.getBody();
42
+ const selection = accessor.get(DocSelectionManagerService).getActiveTextRange();
43
+ const toc = (params === null || params === void 0 ? void 0 : params.rangeId) ? body === null || body === void 0 || (_body$customRanges = body.customRanges) === null || _body$customRanges === void 0 ? void 0 : _body$customRanges.find((range) => range.rangeId === params.rangeId && isTableOfContentsRange(range)) : findTableOfContentsAtOffset(body, selection === null || selection === void 0 ? void 0 : selection.startOffset);
44
+ const unitId = doc === null || doc === void 0 ? void 0 : doc.getUnitId();
45
+ if (!doc || !body || !toc || ((_toc$properties = toc.properties) === null || _toc$properties === void 0 ? void 0 : _toc$properties.locked) === BooleanNumber.TRUE || !unitId) return false;
46
+ const render = accessor.get(IRenderManagerService).getRenderUnitById(unitId);
47
+ const renderInjector = render === null || render === void 0 || (_render$getInjector = render.getInjector) === null || _render$getInjector === void 0 ? void 0 : _render$getInjector.call(render);
48
+ if (!render || !(renderInjector === null || renderInjector === void 0 ? void 0 : renderInjector.has(DocSkeletonManagerService))) return false;
49
+ const skeleton = render.with(DocSkeletonManagerService).getSkeleton();
50
+ if (!await waitForCompleteLayout(skeleton)) return false;
51
+ let updated = false;
52
+ for (let iteration = 0; iteration < 3; iteration += 1) {
53
+ var _currentBody$customRa, _currentToc$propertie, _skeleton$getLayoutPr;
54
+ const currentBody = doc.getBody();
55
+ const currentToc = currentBody === null || currentBody === void 0 || (_currentBody$customRa = currentBody.customRanges) === null || _currentBody$customRa === void 0 ? void 0 : _currentBody$customRa.find((range) => range.rangeId === toc.rangeId && isTableOfContentsRange(range));
56
+ if (!currentBody || !currentToc || ((_currentToc$propertie = currentToc.properties) === null || _currentToc$propertie === void 0 ? void 0 : _currentToc$propertie.locked) === BooleanNumber.TRUE) return false;
57
+ const resolvePageNumber = (offset) => {
58
+ var _skeleton$getSkeleton;
59
+ const pageIndex = skeleton.findBodyPageIndexByCharIndex(offset);
60
+ return (_skeleton$getSkeleton = skeleton.getSkeletonData()) === null || _skeleton$getSkeleton === void 0 || (_skeleton$getSkeleton = _skeleton$getSkeleton.pages[pageIndex]) === null || _skeleton$getSkeleton === void 0 ? void 0 : _skeleton$getSkeleton.pageNumber;
61
+ };
62
+ const replacement = (params === null || params === void 0 ? void 0 : params.mode) === "pageNumbersOnly" ? buildPageNumberOnlyTableOfContentsBody(currentBody, currentToc, resolvePageNumber) : buildTableOfContentsBody(currentBody, currentToc, resolvePageNumber);
63
+ if (!replacement) return false;
64
+ if ((params === null || params === void 0 ? void 0 : params.mode) === "pageNumbersOnly" && Tools.diffValue(getBodySlice(currentBody, currentToc.startIndex, currentToc.endIndex + 1, true, SliceBodyType.cut), replacement)) return true;
65
+ if (updated && currentBody.dataStream.slice(currentToc.startIndex, currentToc.endIndex + 1) === replacement.dataStream) return true;
66
+ const generation = ((_skeleton$getLayoutPr = skeleton.getLayoutProgress()) === null || _skeleton$getLayoutPr === void 0 ? void 0 : _skeleton$getLayoutPr.generation) ?? -1;
67
+ updated = await replaceBodyRange(accessor, doc, unitId, {
68
+ startOffset: currentToc.startIndex,
69
+ endOffset: currentToc.endIndex + 1,
70
+ collapsed: false
71
+ }, replacement, UpdateTableOfContentsCommand.id, historyGroupId);
72
+ if (!updated || iteration === 2) return updated;
73
+ if (!await waitForNextCompleteLayout(skeleton, generation)) return false;
74
+ }
75
+ return updated;
76
+ }
77
+ const DeleteTableOfContentsCommand = {
78
+ id: "doc.command.delete-table-of-contents",
79
+ type: CommandType.COMMAND,
80
+ async handler(accessor, params) {
81
+ var _body$customRanges2;
82
+ const instanceService = accessor.get(IUniverInstanceService);
83
+ const doc = (params === null || params === void 0 ? void 0 : params.unitId) ? instanceService.getUnit(params.unitId, UniverInstanceType.UNIVER_DOC) : instanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_DOC);
84
+ const body = doc === null || doc === void 0 ? void 0 : doc.getBody();
85
+ const selection = accessor.get(DocSelectionManagerService).getActiveTextRange();
86
+ const toc = (params === null || params === void 0 ? void 0 : params.rangeId) ? body === null || body === void 0 || (_body$customRanges2 = body.customRanges) === null || _body$customRanges2 === void 0 ? void 0 : _body$customRanges2.find((range) => range.rangeId === params.rangeId && isTableOfContentsRange(range)) : findTableOfContentsAtOffset(body, selection === null || selection === void 0 ? void 0 : selection.startOffset);
87
+ const unitId = doc === null || doc === void 0 ? void 0 : doc.getUnitId();
88
+ if (!toc || !unitId) return false;
89
+ return replaceBodyRange(accessor, doc, unitId, {
90
+ startOffset: toc.startIndex,
91
+ endOffset: toc.endIndex + 1,
92
+ collapsed: false
93
+ }, { dataStream: "" }, DeleteTableOfContentsCommand.id);
94
+ }
95
+ };
96
+ const InsertTableOfContentsCommand = {
97
+ id: "doc.command.insert-table-of-contents",
98
+ type: CommandType.COMMAND,
99
+ async handler(accessor, params) {
100
+ var _render$getInjector2;
101
+ const instanceService = accessor.get(IUniverInstanceService);
102
+ const doc = (params === null || params === void 0 ? void 0 : params.unitId) ? instanceService.getUnit(params.unitId, UniverInstanceType.UNIVER_DOC) : instanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_DOC);
103
+ const selection = accessor.get(DocSelectionManagerService).getActiveTextRange();
104
+ const body = doc === null || doc === void 0 ? void 0 : doc.getBody();
105
+ const unitId = doc === null || doc === void 0 ? void 0 : doc.getUnitId();
106
+ const render = unitId ? accessor.get(IRenderManagerService).getRenderUnitById(unitId) : void 0;
107
+ const renderInjector = render === null || render === void 0 || (_render$getInjector2 = render.getInjector) === null || _render$getInjector2 === void 0 ? void 0 : _render$getInjector2.call(render);
108
+ if (!doc || !body || !unitId || !selection || selection.segmentId || selection.startOffset !== selection.endOffset || findTableOfContentsAtOffset(body, selection.startOffset) || !render || !(renderInjector === null || renderInjector === void 0 ? void 0 : renderInjector.has(DocSkeletonManagerService))) return false;
109
+ const skeleton = render.with(DocSkeletonManagerService).getSkeleton();
110
+ if (!await waitForCompleteLayout(skeleton)) return false;
111
+ const levels = Math.min(9, Math.max(1, Math.trunc((params === null || params === void 0 ? void 0 : params.levels) ?? 3)));
112
+ const showPageNumbers = (params === null || params === void 0 ? void 0 : params.showPageNumbers) ?? true;
113
+ const rightAlignPageNumbers = showPageNumbers && ((params === null || params === void 0 ? void 0 : params.rightAlignPageNumbers) ?? true);
114
+ const tabLeader = (params === null || params === void 0 ? void 0 : params.tabLeader) ?? "dots";
115
+ const format = (params === null || params === void 0 ? void 0 : params.format) ?? "fromTemplate";
116
+ const rangeId = generateRandomId();
117
+ const toc = {
118
+ rangeId,
119
+ rangeType: CustomRangeType.FIELD,
120
+ startIndex: selection.startOffset,
121
+ endIndex: selection.startOffset,
122
+ wholeEntity: false,
123
+ properties: {
124
+ fieldType: "TOC",
125
+ instruction: buildTableOfContentsInstruction({
126
+ levels,
127
+ showPageNumbers,
128
+ rightAlignPageNumbers
129
+ }),
130
+ sourceKind: "complex",
131
+ showPageNumbers,
132
+ rightAlignPageNumbers,
133
+ tabLeader,
134
+ format,
135
+ title: params === null || params === void 0 ? void 0 : params.title
136
+ }
137
+ };
138
+ const { pageSize, marginLeft = 0, marginRight = 0 } = doc.getDocumentStyle();
139
+ const replacement = buildTableOfContentsBody(body, toc, (offset) => {
140
+ var _skeleton$getSkeleton2;
141
+ const pageIndex = skeleton.findBodyPageIndexByCharIndex(offset);
142
+ return (_skeleton$getSkeleton2 = skeleton.getSkeletonData()) === null || _skeleton$getSkeleton2 === void 0 || (_skeleton$getSkeleton2 = _skeleton$getSkeleton2.pages[pageIndex]) === null || _skeleton$getSkeleton2 === void 0 ? void 0 : _skeleton$getSkeleton2.pageNumber;
143
+ }, generateRandomId, { tabStopOffset: Math.max(72, ((pageSize === null || pageSize === void 0 ? void 0 : pageSize.width) ?? 594.3) - marginLeft - marginRight) });
144
+ if (!replacement) return false;
145
+ const historyGroupId = generateRandomId();
146
+ if (!await replaceBodyRange(accessor, doc, unitId, {
147
+ startOffset: selection.startOffset,
148
+ endOffset: selection.startOffset,
149
+ collapsed: true
150
+ }, replacement, InsertTableOfContentsCommand.id, historyGroupId)) return false;
151
+ await updateTableOfContents(accessor, {
152
+ unitId,
153
+ rangeId,
154
+ mode: "entireTable"
155
+ }, historyGroupId);
156
+ return true;
157
+ }
158
+ };
159
+ function buildTableOfContentsInstruction(options = {}) {
160
+ const levels = Math.min(9, Math.max(1, Math.trunc(options.levels ?? 3)));
161
+ const showPageNumbers = options.showPageNumbers ?? true;
162
+ const rightAlignPageNumbers = showPageNumbers && (options.rightAlignPageNumbers ?? true);
163
+ return `TOC ${[
164
+ `\\o "1-${levels}"`,
165
+ "\\h",
166
+ "\\z",
167
+ "\\u",
168
+ ...!showPageNumbers ? ["\\n"] : [],
169
+ ...showPageNumbers && !rightAlignPageNumbers ? ["\\p \" \""] : []
170
+ ].join(" ")}`;
171
+ }
172
+ function isTableOfContentsRange(range) {
173
+ var _range$properties;
174
+ return range.rangeType === CustomRangeType.FIELD && ((_range$properties = range.properties) === null || _range$properties === void 0 ? void 0 : _range$properties.fieldType) === "TOC";
175
+ }
176
+ function findTableOfContentsAtOffset(body, offset) {
177
+ var _body$customRanges3;
178
+ if (offset == null) return;
179
+ return body === null || body === void 0 || (_body$customRanges3 = body.customRanges) === null || _body$customRanges3 === void 0 ? void 0 : _body$customRanges3.filter((range) => isTableOfContentsRange(range) && range.startIndex <= offset && offset <= range.endIndex).sort((left, right) => left.endIndex - left.startIndex - (right.endIndex - right.startIndex))[0];
180
+ }
181
+ async function waitForCompleteLayout(skeleton) {
182
+ const progress = skeleton.getLayoutProgress();
183
+ if (progress == null || progress.complete) return true;
184
+ try {
185
+ await firstValueFrom(skeleton.layoutProgress$.pipe(filter((next) => next.complete), take(1), timeout({ first: 3e4 })));
186
+ return true;
187
+ } catch {
188
+ return false;
189
+ }
190
+ }
191
+ async function waitForNextCompleteLayout(skeleton, generation) {
192
+ try {
193
+ await firstValueFrom(skeleton.layoutProgress$.pipe(filter((next) => next.generation > generation && next.complete), take(1), timeout({ first: 3e4 })));
194
+ return true;
195
+ } catch {
196
+ return false;
197
+ }
198
+ }
199
+ function buildPageNumberOnlyTableOfContentsBody(body, toc, resolvePageNumber) {
200
+ var _toc$properties2, _result$customRanges, _result$customRanges2;
201
+ if (((_toc$properties2 = toc.properties) === null || _toc$properties2 === void 0 ? void 0 : _toc$properties2.locked) === BooleanNumber.TRUE) return null;
202
+ const result = Tools.deepClone(getBodySlice(body, toc.startIndex, toc.endIndex + 1, true, SliceBodyType.cut));
203
+ const pageFields = ((_result$customRanges = result.customRanges) === null || _result$customRanges === void 0 ? void 0 : _result$customRanges.filter((range) => {
204
+ var _range$properties2, _range$properties3;
205
+ return range.rangeType === CustomRangeType.FIELD && ((_range$properties2 = range.properties) === null || _range$properties2 === void 0 ? void 0 : _range$properties2.fieldType) === "PAGEREF" && ((_range$properties3 = range.properties) === null || _range$properties3 === void 0 ? void 0 : _range$properties3.locked) !== BooleanNumber.TRUE;
206
+ }).sort((left, right) => right.startIndex - left.startIndex)) ?? [];
207
+ for (const pageField of pageFields) {
208
+ var _body$customRanges4;
209
+ const originalField = (_body$customRanges4 = body.customRanges) === null || _body$customRanges4 === void 0 ? void 0 : _body$customRanges4.find((range) => range.rangeId === pageField.rangeId);
210
+ const targetOffset = originalField && resolvePageReferenceOffset(body, originalField);
211
+ const pageNumber = targetOffset == null ? void 0 : resolvePageNumber(targetOffset);
212
+ if (pageNumber == null) return null;
213
+ replaceFieldResult(result, pageField, String(pageNumber));
214
+ }
215
+ const outer = (_result$customRanges2 = result.customRanges) === null || _result$customRanges2 === void 0 ? void 0 : _result$customRanges2.find((range) => range.rangeId === toc.rangeId);
216
+ if (outer === null || outer === void 0 ? void 0 : outer.properties) outer.properties.cachedResult = getPlainText(result.dataStream.slice(1, -1)).replaceAll(FIELD_START, "").replaceAll(FIELD_END, "");
217
+ return result;
218
+ }
219
+ function resolvePageReferenceOffset(body, pageField) {
220
+ var _String$match, _pageField$properties, _body$customRanges6, _link$properties, _body$paragraphs;
221
+ const targetId = (_String$match = String(((_pageField$properties = pageField.properties) === null || _pageField$properties === void 0 ? void 0 : _pageField$properties.instruction) ?? "").match(/PAGEREF\s+"?([^"\s\\]+)"?/i)) === null || _String$match === void 0 ? void 0 : _String$match[1];
222
+ if (targetId) {
223
+ var _body$customRanges5;
224
+ const bookmark = (_body$customRanges5 = body.customRanges) === null || _body$customRanges5 === void 0 ? void 0 : _body$customRanges5.find((range) => {
225
+ var _range$properties4;
226
+ return range.rangeType === CustomRangeType.BOOKMARK && ((_range$properties4 = range.properties) === null || _range$properties4 === void 0 ? void 0 : _range$properties4.bookmarkId) === targetId;
227
+ });
228
+ if (bookmark) return bookmark.startIndex;
229
+ }
230
+ const link = (_body$customRanges6 = body.customRanges) === null || _body$customRanges6 === void 0 ? void 0 : _body$customRanges6.find((range) => range.rangeType === CustomRangeType.HYPERLINK && range.startIndex <= pageField.startIndex && pageField.endIndex <= range.endIndex);
231
+ const headingId = link === null || link === void 0 || (_link$properties = link.properties) === null || _link$properties === void 0 ? void 0 : _link$properties.headingId;
232
+ return (_body$paragraphs = body.paragraphs) === null || _body$paragraphs === void 0 || (_body$paragraphs = _body$paragraphs.find((paragraph) => {
233
+ var _paragraph$paragraphS;
234
+ return ((_paragraph$paragraphS = paragraph.paragraphStyle) === null || _paragraph$paragraphS === void 0 ? void 0 : _paragraph$paragraphS.headingId) === headingId;
235
+ })) === null || _body$paragraphs === void 0 ? void 0 : _body$paragraphs.startIndex;
236
+ }
237
+ function replaceFieldResult(body, field, value) {
238
+ var _body$paragraphs2, _body$customRanges7;
239
+ const start = field.startIndex + 1;
240
+ const end = field.endIndex;
241
+ if (field.properties) field.properties.cachedResult = value;
242
+ if (body.dataStream.slice(start, end) === value) return;
243
+ const delta = value.length - (end - start);
244
+ body.dataStream = `${body.dataStream.slice(0, start)}${value}${body.dataStream.slice(end)}`;
245
+ shiftRuns(body.textRuns, start, end, delta);
246
+ (_body$paragraphs2 = body.paragraphs) === null || _body$paragraphs2 === void 0 || _body$paragraphs2.forEach((paragraph) => {
247
+ if (paragraph.startIndex >= end) paragraph.startIndex += delta;
248
+ });
249
+ (_body$customRanges7 = body.customRanges) === null || _body$customRanges7 === void 0 || _body$customRanges7.forEach((range) => {
250
+ if (range.startIndex >= end) {
251
+ range.startIndex += delta;
252
+ range.endIndex += delta;
253
+ } else if (range.endIndex >= end) range.endIndex += delta;
254
+ });
255
+ }
256
+ function shiftRuns(runs, start, end, delta) {
257
+ runs === null || runs === void 0 || runs.forEach((run) => {
258
+ if (run.st >= end) {
259
+ run.st += delta;
260
+ run.ed += delta;
261
+ } else if (run.ed > start) run.ed += delta;
262
+ });
263
+ }
264
+ function buildTableOfContentsBody(body, toc, resolvePageNumber, idFactory = generateRandomId, options) {
265
+ var _toc$properties3, _toc$properties4, _toc$properties5, _toc$properties6, _toc$properties7, _toc$properties8, _body$customRanges8, _body$paragraphs3, _toc$properties9;
266
+ if (((_toc$properties3 = toc.properties) === null || _toc$properties3 === void 0 ? void 0 : _toc$properties3.locked) === BooleanNumber.TRUE) return null;
267
+ const instruction = String(((_toc$properties4 = toc.properties) === null || _toc$properties4 === void 0 ? void 0 : _toc$properties4.instruction) ?? "");
268
+ if (/\\c\s+/i.test(instruction)) return null;
269
+ const [minLevel, maxLevel] = parseOutlineLevelRange(instruction);
270
+ const headings = collectHeadings(body, toc, minLevel, maxLevel);
271
+ if (headings.length === 0) return null;
272
+ const showPageNumbers = ((_toc$properties5 = toc.properties) === null || _toc$properties5 === void 0 ? void 0 : _toc$properties5.showPageNumbers) !== false && !/\\n(?:\s|$)/i.test(instruction);
273
+ const rightAlignPageNumbers = showPageNumbers && ((_toc$properties6 = toc.properties) === null || _toc$properties6 === void 0 ? void 0 : _toc$properties6.rightAlignPageNumbers) !== false && !/\\p\s+/i.test(instruction);
274
+ const tabLeader = normalizeTabLeader((_toc$properties7 = toc.properties) === null || _toc$properties7 === void 0 ? void 0 : _toc$properties7.tabLeader);
275
+ const format = normalizeFormat((_toc$properties8 = toc.properties) === null || _toc$properties8 === void 0 ? void 0 : _toc$properties8.format);
276
+ const oldParagraphs = ((_body$customRanges8 = body.customRanges) === null || _body$customRanges8 === void 0 ? void 0 : _body$customRanges8.some((range) => range.rangeId === toc.rangeId)) ?? false ? ((_body$paragraphs3 = body.paragraphs) === null || _body$paragraphs3 === void 0 ? void 0 : _body$paragraphs3.filter((paragraph) => paragraph.startIndex >= toc.startIndex && paragraph.startIndex <= toc.endIndex)) ?? [] : [];
277
+ const title = typeof ((_toc$properties9 = toc.properties) === null || _toc$properties9 === void 0 ? void 0 : _toc$properties9.title) === "string" ? toc.properties.title.trim() : getExistingTableOfContentsTitle(body, oldParagraphs);
278
+ const result = {
279
+ dataStream: FIELD_START,
280
+ paragraphs: [],
281
+ customRanges: []
282
+ };
283
+ const ranges = result.customRanges;
284
+ const oldTitleParagraph = oldParagraphs.find((paragraph) => {
285
+ var _paragraph$styleId;
286
+ return ((_paragraph$styleId = paragraph.styleId) === null || _paragraph$styleId === void 0 ? void 0 : _paragraph$styleId.toLowerCase()) === "tocheading";
287
+ });
288
+ const oldEntryParagraphs = oldParagraphs.filter((paragraph) => paragraph !== oldTitleParagraph);
289
+ const usedParagraphIds = /* @__PURE__ */ new Set();
290
+ let cachedResult = "";
291
+ if (title) {
292
+ result.dataStream += `${title}\r`;
293
+ cachedResult += `${title}\r`;
294
+ result.paragraphs.push(cloneParagraphWithId({
295
+ startIndex: result.dataStream.length - 1,
296
+ paragraphId: (oldTitleParagraph === null || oldTitleParagraph === void 0 ? void 0 : oldTitleParagraph.paragraphId) ?? "",
297
+ styleId: "TOCHeading"
298
+ }, usedParagraphIds));
299
+ }
300
+ for (const [index, heading] of headings.entries()) {
301
+ const pageNumber = showPageNumbers ? resolvePageNumber(heading.startOffset) : void 0;
302
+ if (showPageNumbers && pageNumber == null) return null;
303
+ const entryStart = result.dataStream.length;
304
+ const separator = showPageNumbers ? rightAlignPageNumbers ? " " : " " : "";
305
+ const visibleEntry = `${heading.text}${separator}${pageNumber ?? ""}`;
306
+ result.dataStream += `${heading.text}${separator}`;
307
+ const targetId = heading.bookmarkId ?? heading.headingId;
308
+ const link = {
309
+ startIndex: entryStart,
310
+ endIndex: result.dataStream.length - 1,
311
+ rangeId: idFactory(),
312
+ rangeType: CustomRangeType.HYPERLINK,
313
+ wholeEntity: false,
314
+ properties: {
315
+ ...heading.bookmarkId ? { bookmarkId: targetId } : { headingId: targetId },
316
+ textStyleMode: "text"
317
+ }
318
+ };
319
+ ranges.push(link);
320
+ if (showPageNumbers) {
321
+ const pageFieldStart = result.dataStream.length;
322
+ result.dataStream += `${FIELD_START}${pageNumber}${FIELD_END}`;
323
+ const pageFieldEnd = result.dataStream.length - 1;
324
+ link.endIndex = pageFieldEnd;
325
+ ranges.push({
326
+ startIndex: pageFieldStart,
327
+ endIndex: pageFieldEnd,
328
+ rangeId: idFactory(),
329
+ rangeType: CustomRangeType.FIELD,
330
+ wholeEntity: false,
331
+ properties: {
332
+ fieldType: "PAGEREF",
333
+ instruction: `PAGEREF ${targetId} \\h`,
334
+ cachedResult: String(pageNumber),
335
+ sourceKind: "complex"
336
+ }
337
+ });
338
+ }
339
+ result.dataStream += "\r";
340
+ cachedResult += `${visibleEntry}\r`;
341
+ result.paragraphs.push(copyTocParagraph(oldEntryParagraphs, index, heading.level, result.dataStream.length - 1, usedParagraphIds, rightAlignPageNumbers ? options === null || options === void 0 ? void 0 : options.tabStopOffset : void 0, tabLeader, format));
342
+ }
343
+ result.dataStream += FIELD_END;
344
+ ranges.push({
345
+ startIndex: 0,
346
+ endIndex: result.dataStream.length - 1,
347
+ rangeId: toc.rangeId,
348
+ rangeType: CustomRangeType.FIELD,
349
+ wholeEntity: false,
350
+ properties: {
351
+ ...Tools.deepClone(toc.properties ?? {}),
352
+ fieldType: "TOC",
353
+ instruction,
354
+ cachedResult
355
+ }
356
+ });
357
+ ranges.sort((left, right) => left.startIndex - right.startIndex || right.endIndex - left.endIndex);
358
+ return result;
359
+ }
360
+ function getExistingTableOfContentsTitle(body, paragraphs) {
361
+ const titleParagraph = paragraphs.find((paragraph) => {
362
+ var _paragraph$styleId2;
363
+ return ((_paragraph$styleId2 = paragraph.styleId) === null || _paragraph$styleId2 === void 0 ? void 0 : _paragraph$styleId2.toLowerCase()) === "tocheading";
364
+ });
365
+ if (!titleParagraph) return "";
366
+ const allParagraphs = [...body.paragraphs ?? []].sort((left, right) => left.startIndex - right.startIndex);
367
+ const index = allParagraphs.findIndex((paragraph) => paragraph === titleParagraph || paragraph.paragraphId === titleParagraph.paragraphId);
368
+ let start = index > 0 ? allParagraphs[index - 1].startIndex + 1 : 0;
369
+ while (body.dataStream[start] === FIELD_START || body.dataStream[start] === FIELD_END) start += 1;
370
+ return getPlainText(body.dataStream.slice(start, titleParagraph.startIndex)).trim();
371
+ }
372
+ function countTableOfContentsHeadings(body, levels = 3) {
373
+ if (!body) return 0;
374
+ return collectHeadings(body, void 0, 1, Math.min(9, Math.max(1, Math.trunc(levels)))).length;
375
+ }
376
+ function parseOutlineLevelRange(instruction) {
377
+ const match = instruction.match(/\\o\s+"(\d+)\s*-\s*(\d+)"/i);
378
+ const min = Math.max(1, Number((match === null || match === void 0 ? void 0 : match[1]) ?? 1));
379
+ return [min, Math.min(9, Math.max(min, Number((match === null || match === void 0 ? void 0 : match[2]) ?? 3)))];
380
+ }
381
+ function collectHeadings(body, toc, minLevel, maxLevel) {
382
+ const paragraphs = [...body.paragraphs ?? []].sort((left, right) => left.startIndex - right.startIndex);
383
+ let paragraphStart = 0;
384
+ return paragraphs.flatMap((paragraph) => {
385
+ var _paragraph$paragraphS2, _body$customRanges9, _bookmark$properties;
386
+ let startOffset = paragraphStart;
387
+ while (body.dataStream[startOffset] === FIELD_START || body.dataStream[startOffset] === FIELD_END) startOffset += 1;
388
+ paragraphStart = paragraph.startIndex + 1;
389
+ const level = getOutlineLevel(paragraph);
390
+ const headingId = (_paragraph$paragraphS2 = paragraph.paragraphStyle) === null || _paragraph$paragraphS2 === void 0 ? void 0 : _paragraph$paragraphS2.headingId;
391
+ if (!headingId || level == null || level < minLevel || level > maxLevel || toc && startOffset >= toc.startIndex && paragraph.startIndex <= toc.endIndex) return [];
392
+ const text = getPlainText(body.dataStream.slice(startOffset, paragraph.startIndex)).trim();
393
+ if (!text) return [];
394
+ const bookmark = (_body$customRanges9 = body.customRanges) === null || _body$customRanges9 === void 0 ? void 0 : _body$customRanges9.find((range) => {
395
+ var _range$properties5;
396
+ return range.rangeType === CustomRangeType.BOOKMARK && range.startIndex <= paragraph.startIndex && range.endIndex >= startOffset && ((_range$properties5 = range.properties) === null || _range$properties5 === void 0 ? void 0 : _range$properties5.bookmarkId);
397
+ });
398
+ return [{
399
+ bookmarkId: bookmark === null || bookmark === void 0 || (_bookmark$properties = bookmark.properties) === null || _bookmark$properties === void 0 ? void 0 : _bookmark$properties.bookmarkId,
400
+ headingId,
401
+ level,
402
+ paragraph,
403
+ startOffset,
404
+ text
405
+ }];
406
+ });
407
+ }
408
+ function getOutlineLevel(paragraph) {
409
+ var _paragraph$paragraphS3, _paragraph$paragraphS4;
410
+ const outlineLevel = (_paragraph$paragraphS3 = paragraph.paragraphStyle) === null || _paragraph$paragraphS3 === void 0 ? void 0 : _paragraph$paragraphS3.outlineLevel;
411
+ if (outlineLevel != null && outlineLevel >= 0 && outlineLevel <= 8) return outlineLevel + 1;
412
+ const namedStyle = (_paragraph$paragraphS4 = paragraph.paragraphStyle) === null || _paragraph$paragraphS4 === void 0 ? void 0 : _paragraph$paragraphS4.namedStyleType;
413
+ if (namedStyle != null && namedStyle >= NamedStyleType.HEADING_1 && namedStyle <= NamedStyleType.HEADING_5) return namedStyle - NamedStyleType.HEADING_1 + 1;
414
+ }
415
+ function copyTocParagraph(oldParagraphs, index, level, startIndex, usedParagraphIds, tabStopOffset, tabLeader = "dots", format = "fromTemplate") {
416
+ const source = oldParagraphs.find((paragraph) => {
417
+ var _paragraph$styleId3;
418
+ return ((_paragraph$styleId3 = paragraph.styleId) === null || _paragraph$styleId3 === void 0 || (_paragraph$styleId3 = _paragraph$styleId3.match(/TOC(\d+)/i)) === null || _paragraph$styleId3 === void 0 ? void 0 : _paragraph$styleId3[1]) === String(level);
419
+ }) ?? oldParagraphs[Math.min(index, oldParagraphs.length - 1)];
420
+ if (source) {
421
+ var _oldParagraphs$index;
422
+ return cloneParagraphWithId({
423
+ ...source,
424
+ startIndex,
425
+ paragraphId: ((_oldParagraphs$index = oldParagraphs[index]) === null || _oldParagraphs$index === void 0 ? void 0 : _oldParagraphs$index.paragraphId) ?? ""
426
+ }, usedParagraphIds);
427
+ }
428
+ return {
429
+ startIndex,
430
+ paragraphId: createParagraphId(usedParagraphIds),
431
+ styleId: `TOC${level}`,
432
+ paragraphStyle: {
433
+ indentStart: { v: Math.max(0, level - 1) * getFormatIndent(format) },
434
+ spaceBelow: format === "modern" ? { v: 4 } : format === "classic" ? { v: 2 } : void 0,
435
+ tabStops: tabStopOffset == null ? void 0 : [{
436
+ offset: tabStopOffset,
437
+ alignment: TabStopAlignment.END,
438
+ leader: getTabStopLeader(tabLeader)
439
+ }]
440
+ }
441
+ };
442
+ }
443
+ function normalizeTabLeader(value) {
444
+ return value === "none" || value === "dashes" || value === "underline" ? value : "dots";
445
+ }
446
+ function getTabStopLeader(value) {
447
+ return {
448
+ none: TabStopLeader.NONE,
449
+ dots: TabStopLeader.DOT,
450
+ dashes: TabStopLeader.HYPHEN,
451
+ underline: TabStopLeader.UNDERSCORE
452
+ }[value];
453
+ }
454
+ function normalizeFormat(value) {
455
+ return value === "classic" || value === "modern" || value === "simple" ? value : "fromTemplate";
456
+ }
457
+ function getFormatIndent(format) {
458
+ return {
459
+ fromTemplate: 18,
460
+ classic: 14,
461
+ modern: 20,
462
+ simple: 12
463
+ }[format];
464
+ }
465
+
466
+ //#endregion
467
+ //#region package.json
468
+ var name = "@univerjs/docs-toc";
469
+ var version = "1.0.0";
470
+
471
+ //#endregion
472
+ //#region src/config/config.ts
473
+ /**
474
+ * Copyright 2023-present DreamNum Co., Ltd.
475
+ *
476
+ * Licensed under the Apache License, Version 2.0 (the "License");
477
+ * you may not use this file except in compliance with the License.
478
+ * You may obtain a copy of the License at
479
+ *
480
+ * http://www.apache.org/licenses/LICENSE-2.0
481
+ *
482
+ * Unless required by applicable law or agreed to in writing, software
483
+ * distributed under the License is distributed on an "AS IS" BASIS,
484
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
485
+ * See the License for the specific language governing permissions and
486
+ * limitations under the License.
487
+ */
488
+ const DOCS_TOC_PLUGIN_CONFIG_KEY = "docs-toc.config";
489
+ const configSymbol = Symbol(DOCS_TOC_PLUGIN_CONFIG_KEY);
490
+ const defaultPluginConfig = {};
491
+
492
+ //#endregion
493
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorateParam.js
494
+ function __decorateParam(paramIndex, decorator) {
495
+ return function(target, key) {
496
+ decorator(target, key, paramIndex);
497
+ };
498
+ }
499
+
500
+ //#endregion
501
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
502
+ function __decorate(decorators, target, key, desc) {
503
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
504
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
505
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
506
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
507
+ }
508
+
509
+ //#endregion
510
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/typeof.js
511
+ function _typeof(o) {
512
+ "@babel/helpers - typeof";
513
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
514
+ return typeof o;
515
+ } : function(o) {
516
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
517
+ }, _typeof(o);
518
+ }
519
+
520
+ //#endregion
521
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/toPrimitive.js
522
+ function toPrimitive(t, r) {
523
+ if ("object" != _typeof(t) || !t) return t;
524
+ var e = t[Symbol.toPrimitive];
525
+ if (void 0 !== e) {
526
+ var i = e.call(t, r || "default");
527
+ if ("object" != _typeof(i)) return i;
528
+ throw new TypeError("@@toPrimitive must return a primitive value.");
529
+ }
530
+ return ("string" === r ? String : Number)(t);
531
+ }
532
+
533
+ //#endregion
534
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/toPropertyKey.js
535
+ function toPropertyKey(t) {
536
+ var i = toPrimitive(t, "string");
537
+ return "symbol" == _typeof(i) ? i : i + "";
538
+ }
539
+
540
+ //#endregion
541
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/defineProperty.js
542
+ function _defineProperty(e, r, t) {
543
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
544
+ value: t,
545
+ enumerable: !0,
546
+ configurable: !0,
547
+ writable: !0
548
+ }) : e[r] = t, e;
549
+ }
550
+
551
+ //#endregion
552
+ //#region src/plugin.ts
553
+ let UniverDocsTocPlugin = class UniverDocsTocPlugin extends Plugin {
554
+ constructor(_config = defaultPluginConfig, _injector, _configService, _commandService) {
555
+ super();
556
+ this._config = _config;
557
+ this._injector = _injector;
558
+ this._configService = _configService;
559
+ this._commandService = _commandService;
560
+ const { ...rest } = merge({}, defaultPluginConfig, this._config);
561
+ this._configService.setConfig(DOCS_TOC_PLUGIN_CONFIG_KEY, rest);
562
+ }
563
+ onStarting() {
564
+ [
565
+ InsertTableOfContentsCommand,
566
+ UpdateTableOfContentsCommand,
567
+ DeleteTableOfContentsCommand
568
+ ].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command)));
569
+ }
570
+ };
571
+ _defineProperty(UniverDocsTocPlugin, "pluginName", "DOCS_TOC_PLUGIN");
572
+ _defineProperty(UniverDocsTocPlugin, "packageName", name);
573
+ _defineProperty(UniverDocsTocPlugin, "version", version);
574
+ _defineProperty(UniverDocsTocPlugin, "type", UniverInstanceType.UNIVER_DOC);
575
+ UniverDocsTocPlugin = __decorate([
576
+ DependentOn(UniverDocsPlugin, UniverRenderEnginePlugin),
577
+ __decorateParam(1, Inject(Injector)),
578
+ __decorateParam(2, IConfigService),
579
+ __decorateParam(3, ICommandService)
580
+ ], UniverDocsTocPlugin);
581
+
582
+ //#endregion
583
+ export { DeleteTableOfContentsCommand, InsertTableOfContentsCommand, UniverDocsTocPlugin, UpdateTableOfContentsCommand, buildPageNumberOnlyTableOfContentsBody, buildTableOfContentsBody, buildTableOfContentsInstruction, countTableOfContentsHeadings, findTableOfContentsAtOffset };