@univerjs/docs 1.0.0-insiders.20260809-70eefdd → 1.0.0-rc.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 (53) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +0 -1
  3. package/lib/cjs/facade.js +331 -60
  4. package/lib/cjs/index.js +2472 -102
  5. package/lib/es/facade.js +326 -62
  6. package/lib/es/index.js +2419 -85
  7. package/lib/facade.js +326 -62
  8. package/lib/index.js +2419 -85
  9. package/lib/types/commands/commands/core-editing.command.d.ts +5 -1
  10. package/lib/types/commands/commands/set-document-permission.command.d.ts +27 -0
  11. package/lib/types/commands/commands/set-document-permissions.command.d.ts +17 -0
  12. package/lib/types/commands/mutations/core-editing.mutation-id.d.ts +16 -0
  13. package/lib/types/commands/mutations/core-editing.mutation.d.ts +20 -1
  14. package/lib/types/commands/mutations/doc-structure-mutation-validation.d.ts +17 -0
  15. package/lib/types/commands/mutations/set-document-permission-rule.mutation.d.ts +17 -0
  16. package/lib/types/commands/mutations/set-document-permission-rules.mutation.d.ts +17 -0
  17. package/lib/types/controllers/doc-permission.controller.d.ts +26 -0
  18. package/lib/types/facade/f-document-paragraph.d.ts +15 -2
  19. package/lib/types/facade/f-document-permission.d.ts +177 -0
  20. package/lib/types/facade/f-document-section.d.ts +15 -2
  21. package/lib/types/facade/f-document-text-range.d.ts +3 -1
  22. package/lib/types/facade/f-document.d.ts +34 -2
  23. package/lib/types/facade/f-univer.d.ts +4 -3
  24. package/lib/types/facade/index.d.ts +1 -0
  25. package/lib/types/facade/utils.d.ts +2 -1
  26. package/lib/types/index.d.ts +13 -1
  27. package/lib/types/layout-worker/config/config.d.ts +24 -0
  28. package/lib/types/layout-worker/document-font-metrics.d.ts +18 -0
  29. package/lib/types/layout-worker/index.d.ts +70 -0
  30. package/lib/types/layout-worker/performance-tracker.d.ts +27 -0
  31. package/lib/types/layout-worker/protocol.d.ts +35 -0
  32. package/lib/types/layout-worker/worker.d.ts +49 -0
  33. package/lib/types/services/doc-interceptor/interceptor-const.d.ts +2 -1
  34. package/lib/types/services/doc-layout-executor.service.d.ts +218 -0
  35. package/lib/types/services/doc-selection-manager.service.d.ts +7 -1
  36. package/lib/types/services/doc-skeleton-manager.service.d.ts +6 -1
  37. package/lib/types/services/doc-state-change-manager.service.d.ts +1 -0
  38. package/lib/types/services/doc-state-emit.service.d.ts +4 -0
  39. package/lib/types/services/document-layout-snapshot.d.ts +22 -0
  40. package/lib/types/services/permission/document-permission-resolver.d.ts +27 -0
  41. package/lib/types/services/permission/document-permission-rule.model.d.ts +19 -0
  42. package/lib/types/services/permission/document-permission.d.ts +35 -0
  43. package/lib/types/services/permission/permission-point/document/comment.d.ts +27 -0
  44. package/lib/types/services/permission/permission-point/document/copy.d.ts +27 -0
  45. package/lib/types/services/permission/permission-point/document/editable.d.ts +27 -0
  46. package/lib/types/services/permission/permission-point/document/export.d.ts +27 -0
  47. package/lib/types/services/permission/permission-point/document/print.d.ts +27 -0
  48. package/lib/types/services/permission/permission-point/entity/edit.d.ts +28 -0
  49. package/lib/types/services/permission/permission-point/paragraph/edit.d.ts +28 -0
  50. package/lib/types/services/permission/permission-point/section/edit.d.ts +28 -0
  51. package/lib/umd/facade.js +3 -3
  52. package/lib/umd/index.js +2 -2
  53. package/package.json +7 -5
package/lib/index.js CHANGED
@@ -1,6 +1,8 @@
1
- import { AlignTypeH, BuildTextUtils, ColumnSeparatorType, CommandType, CustomRangeType, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DataStreamTreeTokenType, DeleteDirection, Disposable, DisposableCollection, DocumentFlavor, DocxBreakType, DrawingTypeEnum, ICommandService, IConfigService, IUndoRedoService, IUniverInstanceService, Inject, Injector, JSONX, LocaleService, MemoryCursor, ObjectRelativeFromH, ObjectRelativeFromV, Optional, PAGE_SIZE, PageOrientType, PaperType, Plugin, PositionedObjectLayoutType, RESTORE_INSERTED_PARAGRAPH_IDS, RedoCommandId, RxDisposable, SectionType, TRADITIONAL_DOCUMENT_DEFAULT_MARGIN, TextX, TextXActionType, Tools, UndoCommandId, UniverInstanceType, UpdateDocsAttributeType, composeInterceptors, containsInteriorInsertionOffset, createIdentifier, createInterceptorKey, createParagraphId, createSectionId, generateRandomId, getBlockRangeInterval, getColumnGroupRangeInterval, getParagraphContentStartOffset, getRichTextEditPath, getSectionHeaderFooterReferenceKey, getTableRangeInterval, isInternalEditorID, merge, remove, resolveSectionHeaderFooterReference, toDisposable, validateDocBodyStructure } from "@univerjs/core";
2
- import { DocumentSkeleton, DocumentViewModel, IRenderManagerService, NORMAL_TEXT_SELECTION_PLUGIN_STYLE } from "@univerjs/engine-render";
3
- import { BehaviorSubject, Subject, takeUntil } from "rxjs";
1
+ import { AlignTypeH, BooleanNumber, BuildTextUtils, ColumnSeparatorType, CommandType, CustomCommandExecutionError, CustomRangeType, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DataStreamTreeTokenType, DeleteDirection, DependentOn, Disposable, DisposableCollection, DocumentDataModel, DocumentFlavor, DocxBreakType, DrawingTypeEnum, ErrorService, ICommandService, IConfigService, ILogService, IPermissionService, IResourceManagerService, IUndoRedoService, IUniverInstanceService, Inject, Injector, JSON1, JSONX, LocaleService, MemoryCursor, ObjectPermissionRuleModel, ObjectPermissionService, ObjectRelativeFromH, ObjectRelativeFromV, Optional, PAGE_SIZE, PageOrientType, PaperType, PermissionStatus, Plugin, PositionedObjectLayoutType, RESTORE_INSERTED_PARAGRAPH_IDS, RedoCommandId, RxDisposable, SectionType, TRADITIONAL_DOCUMENT_DEFAULT_MARGIN, TextX, TextXActionType, Tools, UndoCommandId, UniverInstanceType, UpdateDocsAttributeType, composeInterceptors, containsInteriorInsertionOffset, createIdentifier, createInterceptorKey, createParagraphId, createSectionId, generateRandomId, getBlockRangeInterval, getColumnGroupRangeInterval, getParagraphContentStartOffset, getParagraphContentStartOffsets, getRichTextEditPath, getSectionHeaderFooterReferenceKey, getTableRangeInterval, isInternalEditorID, merge, remove, requestImmediateMacroTask, resolveSectionHeaderFooterReference, toDisposable, validateDocBodyStructure, validateDocumentStructure } from "@univerjs/core";
2
+ import { DocumentLayoutSession, DocumentSkeleton, DocumentViewModel, FontCache, IRenderManagerService, NORMAL_TEXT_SELECTION_PLUGIN_STYLE, getDocsCustomBlockRenderViewport, getFontStyleString, setDocsCustomBlockRenderViewportProvider } from "@univerjs/engine-render";
3
+ import { BehaviorSubject, Observable, Subject, shareReplay, takeUntil } from "rxjs";
4
+ import { UnitAction, UnitObject } from "@univerjs/protocol";
5
+ import { ChannelService, fromModule, toModule } from "@univerjs/rpc";
4
6
 
5
7
  //#region src/commands/operations/text-selection.operation.ts
6
8
  const SetTextSelectionsOperation = {
@@ -106,9 +108,9 @@ let DocSelectionManagerService = class DocSelectionManagerService extends RxDisp
106
108
  getSelectionInfo(params = this._currentSelection) {
107
109
  return this._getTextRanges(params);
108
110
  }
109
- refreshSelection(params = this._currentSelection) {
111
+ refreshSelection(params = this._currentSelection, isEditing = false) {
110
112
  if (params == null) return;
111
- this._refresh(params);
113
+ this._refresh(params, isEditing);
112
114
  }
113
115
  __TEST_ONLY_setCurrentSelection(param) {
114
116
  this._currentSelection = param;
@@ -160,8 +162,19 @@ let DocSelectionManagerService = class DocSelectionManagerService extends RxDisp
160
162
  options
161
163
  });
162
164
  }
165
+ /**
166
+ * Replaces logical selection state without rebuilding render ranges. Atomic
167
+ * external mutations use this before applying document actions so layout can
168
+ * follow transformed offsets, then refresh after new geometry is available.
169
+ */
170
+ replaceSelectionInfoWithoutRefresh(selectionInfo, params = this._currentSelection) {
171
+ if (params == null) return;
172
+ this._replaceByParam({
173
+ ...selectionInfo,
174
+ ...params
175
+ });
176
+ }
163
177
  __replaceTextRangesWithNoRefresh(textSelectionInfo, search) {
164
- if (this._currentSelection == null) return;
165
178
  const params = {
166
179
  ...textSelectionInfo,
167
180
  ...search
@@ -196,7 +209,7 @@ let DocSelectionManagerService = class DocSelectionManagerService extends RxDisp
196
209
  const { unitId, subUnitId = "" } = param;
197
210
  return (_this$_textSelectionI = this._textSelectionInfo.get(unitId)) === null || _this$_textSelectionI === void 0 ? void 0 : _this$_textSelectionI.get(subUnitId);
198
211
  }
199
- _refresh(param) {
212
+ _refresh(param, isEditing = false) {
200
213
  const allTextSelectionInfo = this._getTextRanges(param);
201
214
  if (allTextSelectionInfo == null) return;
202
215
  const { textRanges, rectRanges, options } = allTextSelectionInfo;
@@ -206,8 +219,11 @@ let DocSelectionManagerService = class DocSelectionManagerService extends RxDisp
206
219
  unitId,
207
220
  subUnitId,
208
221
  docRanges,
209
- isEditing: false,
210
- options
222
+ isEditing,
223
+ options: (options === null || options === void 0 ? void 0 : options.forceFocus) ? {
224
+ ...options,
225
+ forceFocus: false
226
+ } : options
211
227
  });
212
228
  }
213
229
  _replaceByParam(insertParam) {
@@ -225,14 +241,645 @@ let DocSelectionManagerService = class DocSelectionManagerService extends RxDisp
225
241
  };
226
242
  DocSelectionManagerService = __decorate([__decorateParam(0, ICommandService), __decorateParam(1, IUniverInstanceService)], DocSelectionManagerService);
227
243
 
244
+ //#endregion
245
+ //#region src/commands/mutations/core-editing.mutation-id.ts
246
+ /**
247
+ * Copyright 2023-present DreamNum Co., Ltd.
248
+ *
249
+ * Licensed under the Apache License, Version 2.0 (the "License");
250
+ * you may not use this file except in compliance with the License.
251
+ * You may obtain a copy of the License at
252
+ *
253
+ * http://www.apache.org/licenses/LICENSE-2.0
254
+ *
255
+ * Unless required by applicable law or agreed to in writing, software
256
+ * distributed under the License is distributed on an "AS IS" BASIS,
257
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
258
+ * See the License for the specific language governing permissions and
259
+ * limitations under the License.
260
+ */
261
+ const RICH_TEXT_EDITING_MUTATION_ID = "doc.mutation.rich-text-editing";
262
+
263
+ //#endregion
264
+ //#region src/services/document-layout-snapshot.ts
265
+ function projectCustomBlock(block) {
266
+ const { docxRawXml: _docxRawXml, docxExportTs: _docxExportTs, ...layoutBlock } = block;
267
+ return layoutBlock;
268
+ }
269
+ function projectDocumentBody(body) {
270
+ const { docxRawCustomBlocks: _docxRawCustomBlocks, docxRawBlocks: _docxRawBlocks, docxExportExcludedRanges: _docxExportExcludedRanges, payloads: _payloads, customBlocks, ...layoutBody } = body;
271
+ return {
272
+ ...layoutBody,
273
+ ...customBlocks == null ? {} : { customBlocks: customBlocks.map(projectCustomBlock) }
274
+ };
275
+ }
276
+ function projectHeader(header) {
277
+ return {
278
+ ...header,
279
+ body: projectDocumentBody(header.body)
280
+ };
281
+ }
282
+ function projectFooter(footer) {
283
+ return {
284
+ ...footer,
285
+ body: projectDocumentBody(footer.body)
286
+ };
287
+ }
288
+ /**
289
+ * Builds the model snapshot owned by the layout Worker. Exchange-only payloads
290
+ * remain in the authoritative Main model and are deliberately excluded from
291
+ * the structured-clone boundary because they do not affect document geometry.
292
+ */
293
+ function createDocumentLayoutSnapshot(snapshot) {
294
+ const { resources: _resources, body, headers, footers, ...layoutSnapshot } = snapshot;
295
+ return {
296
+ ...layoutSnapshot,
297
+ ...body == null ? {} : { body: projectDocumentBody(body) },
298
+ ...headers == null ? {} : { headers: Object.fromEntries(Object.entries(headers).map(([headerId, header]) => [headerId, projectHeader(header)])) },
299
+ ...footers == null ? {} : { footers: Object.fromEntries(Object.entries(footers).map(([footerId, footer]) => [footerId, projectFooter(footer)])) }
300
+ };
301
+ }
302
+
303
+ //#endregion
304
+ //#region src/services/doc-layout-executor.service.ts
305
+ var _DocLayoutExecutorService;
306
+ let DocLayoutExecutorType = /* @__PURE__ */ function(DocLayoutExecutorType) {
307
+ DocLayoutExecutorType["MAIN_THREAD"] = "main-thread";
308
+ DocLayoutExecutorType["WORKER"] = "worker";
309
+ return DocLayoutExecutorType;
310
+ }({});
311
+ let DocLayoutSessionStatus = /* @__PURE__ */ function(DocLayoutSessionStatus) {
312
+ DocLayoutSessionStatus["ACCEPTED"] = "accepted";
313
+ DocLayoutSessionStatus["NOT_FOUND"] = "not-found";
314
+ DocLayoutSessionStatus["RESNAPSHOT_REQUIRED"] = "resnapshot-required";
315
+ DocLayoutSessionStatus["SUPERSEDED"] = "superseded";
316
+ return DocLayoutSessionStatus;
317
+ }({});
318
+ let DocLayoutExecutorState = /* @__PURE__ */ function(DocLayoutExecutorState) {
319
+ DocLayoutExecutorState["UNREGISTERED"] = "unregistered";
320
+ DocLayoutExecutorState["INITIALIZING"] = "initializing";
321
+ DocLayoutExecutorState["ACTIVE"] = "active";
322
+ DocLayoutExecutorState["RECOVERING"] = "recovering";
323
+ DocLayoutExecutorState["FAILED"] = "failed";
324
+ return DocLayoutExecutorState;
325
+ }({});
326
+ function areShallowRecordsEqual(left, right) {
327
+ const leftRecord = left;
328
+ const rightRecord = right;
329
+ const leftKeys = Object.keys(leftRecord);
330
+ const rightKeys = Object.keys(rightRecord);
331
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => leftRecord[key] === rightRecord[key]);
332
+ }
333
+ function createRecordPatch(previous, current) {
334
+ const upserts = {};
335
+ const removals = [];
336
+ for (const [key, value] of Object.entries(current)) if (previous[key] == null || !areShallowRecordsEqual(previous[key], value)) upserts[key] = value;
337
+ for (const key of Object.keys(previous)) if (current[key] == null) removals.push(key);
338
+ return {
339
+ removals,
340
+ upserts
341
+ };
342
+ }
343
+ function getCustomRangePresentationKey$1(entry) {
344
+ return JSON.stringify([entry.segmentId, entry.rangeId]);
345
+ }
346
+ function toCustomRangePresentationMap(entries) {
347
+ return new Map(entries.map((entry) => [getCustomRangePresentationKey$1(entry), entry]));
348
+ }
349
+ function createCustomRangePresentationPatch(previous, current) {
350
+ const upserts = [];
351
+ const removals = [];
352
+ for (const [key, entry] of current) {
353
+ const previousEntry = previous.get(key);
354
+ if (previousEntry == null || !areShallowRecordsEqual(previousEntry.presentation, entry.presentation)) upserts.push(entry);
355
+ }
356
+ for (const [key, entry] of previous) if (!current.has(key)) removals.push({
357
+ segmentId: entry.segmentId,
358
+ rangeId: entry.rangeId
359
+ });
360
+ return {
361
+ removals,
362
+ upserts
363
+ };
364
+ }
365
+ function isRichTextEditingMutationParams(value) {
366
+ return typeof value === "object" && value != null && "unitId" in value && typeof value.unitId === "string" && "actions" in value;
367
+ }
368
+ function collectCustomBlockViewports(dataModel) {
369
+ var _snapshot$body;
370
+ const snapshot = dataModel.getSnapshot();
371
+ const documentStyle = snapshot.documentStyle;
372
+ const viewports = {};
373
+ for (const customBlock of ((_snapshot$body = snapshot.body) === null || _snapshot$body === void 0 ? void 0 : _snapshot$body.customBlocks) ?? []) {
374
+ var _snapshot$drawings, _documentStyle$pageSi;
375
+ const drawing = (_snapshot$drawings = snapshot.drawings) === null || _snapshot$drawings === void 0 ? void 0 : _snapshot$drawings[customBlock.blockId];
376
+ if (drawing == null) continue;
377
+ const viewport = getDocsCustomBlockRenderViewport(dataModel.getUnitId(), customBlock.blockId, {
378
+ fallbackHeight: drawing.docTransform.size.height ?? 0,
379
+ fallbackWidth: drawing.docTransform.size.width ?? 0,
380
+ pageMarginLeft: documentStyle.marginLeft,
381
+ pageMarginRight: documentStyle.marginRight,
382
+ pageWidth: (_documentStyle$pageSi = documentStyle.pageSize) === null || _documentStyle$pageSi === void 0 ? void 0 : _documentStyle$pageSi.width
383
+ });
384
+ if (viewport != null) viewports[customBlock.blockId] = { ...viewport };
385
+ }
386
+ return viewports;
387
+ }
388
+ let DocLayoutExecutorService = _DocLayoutExecutorService = class DocLayoutExecutorService extends Disposable {
389
+ constructor(_univerInstanceService, _commandService, _localeService, _logService) {
390
+ super();
391
+ this._univerInstanceService = _univerInstanceService;
392
+ this._commandService = _commandService;
393
+ this._localeService = _localeService;
394
+ this._logService = _logService;
395
+ _defineProperty(this, "_executor", null);
396
+ _defineProperty(this, "_executorReady", null);
397
+ _defineProperty(this, "_recoveryFailure", null);
398
+ _defineProperty(this, "_sessionEpoch", 0);
399
+ _defineProperty(this, "_sessions", /* @__PURE__ */ new Map());
400
+ _defineProperty(this, "_hydrationSamples", /* @__PURE__ */ new Map());
401
+ _defineProperty(this, "_customRangePresentationProviders", /* @__PURE__ */ new Set());
402
+ _defineProperty(this, "_executorStatus$", new BehaviorSubject({
403
+ state: "unregistered",
404
+ executor: null,
405
+ diagnostic: null,
406
+ recoveryUnitId: null
407
+ }));
408
+ _defineProperty(this, "executorStatus$", this._executorStatus$.asObservable());
409
+ this.disposeWithMe(this._univerInstanceService.getTypeOfUnitAdded$(UniverInstanceType.UNIVER_DOC).subscribe(({ unit }) => this._createSessionIfEligible(unit)));
410
+ this.disposeWithMe(this._univerInstanceService.getTypeOfUnitDisposed$(UniverInstanceType.UNIVER_DOC).subscribe((unit) => this._disposeSession(unit.getUnitId())));
411
+ this.disposeWithMe(this._commandService.onCommandExecuted((command) => {
412
+ if (command.id !== "doc.mutation.rich-text-editing") return;
413
+ const params = command.params;
414
+ if (!isRichTextEditingMutationParams(params)) {
415
+ this._logService.warn("[DocLayoutExecutorService]: ignored malformed document mutation parameters.");
416
+ return;
417
+ }
418
+ this._enqueueCommittedMutation(params);
419
+ }));
420
+ this.disposeWithMe(this._localeService.localeChanged$.subscribe(() => {
421
+ for (const unitId of this._sessions.keys()) {
422
+ const dataModel = this._getEligibleModel(unitId);
423
+ if (dataModel != null) this._replaceSession(dataModel).catch((error) => {
424
+ this._logService.error("[DocLayoutExecutorService]: failed to refresh Worker locale.", error);
425
+ });
426
+ }
427
+ }));
428
+ }
429
+ register(executor) {
430
+ if (this._executor != null) throw new Error("A document layout executor is already registered.");
431
+ this._executor = executor;
432
+ this._recoveryFailure = null;
433
+ this._executorStatus$.next({
434
+ state: "initializing",
435
+ executor: executor.type,
436
+ diagnostic: null,
437
+ recoveryUnitId: null
438
+ });
439
+ const executorReady = executor.initialize();
440
+ this._executorReady = executorReady;
441
+ executorReady.then(() => {
442
+ if (this._executor === executor) this._executorStatus$.next({
443
+ state: "active",
444
+ executor: executor.type,
445
+ diagnostic: null,
446
+ recoveryUnitId: null
447
+ });
448
+ }).catch((error) => {
449
+ if (this._executor !== executor) return;
450
+ this._executor = null;
451
+ this._executorReady = null;
452
+ this._disposeManagedSessions();
453
+ this._sessions.clear();
454
+ const diagnostic = error instanceof Error ? error.message : String(error);
455
+ this._executorStatus$.next({
456
+ state: "failed",
457
+ executor: executor.type,
458
+ diagnostic,
459
+ recoveryUnitId: null
460
+ });
461
+ this._logService.error("[DocLayoutExecutorService]: document layout executor initialization failed.", error);
462
+ });
463
+ for (const unit of this._univerInstanceService.getAllUnitsForType(UniverInstanceType.UNIVER_DOC)) this._createSessionIfEligible(unit);
464
+ return toDisposable(() => {
465
+ if (this._executor === executor) {
466
+ this._executor = null;
467
+ this._executorReady = null;
468
+ this._disposeManagedSessions();
469
+ this._sessions.clear();
470
+ this._executorStatus$.next({
471
+ state: "unregistered",
472
+ executor: null,
473
+ diagnostic: null,
474
+ recoveryUnitId: null
475
+ });
476
+ }
477
+ });
478
+ }
479
+ getExecutor() {
480
+ return this._executor;
481
+ }
482
+ getExecutorStatus() {
483
+ return this._executorStatus$.value;
484
+ }
485
+ registerCustomRangePresentationProvider(provider) {
486
+ this._customRangePresentationProviders.add(provider);
487
+ return toDisposable(() => this._customRangePresentationProviders.delete(provider));
488
+ }
489
+ async getPerformanceMetrics(unitId) {
490
+ var _this$_executor;
491
+ const executorMetrics = await ((_this$_executor = this._executor) === null || _this$_executor === void 0 ? void 0 : _this$_executor.getPerformanceMetrics(unitId));
492
+ return {
493
+ hydrationMs: [...this._hydrationSamples.get(unitId) ?? []],
494
+ mutationTransferMs: [...(executorMetrics === null || executorMetrics === void 0 ? void 0 : executorMetrics.mutationTransferMs) ?? []],
495
+ patchTransferMs: [...(executorMetrics === null || executorMetrics === void 0 ? void 0 : executorMetrics.patchTransferMs) ?? []],
496
+ snapshotTransferMs: [...(executorMetrics === null || executorMetrics === void 0 ? void 0 : executorMetrics.snapshotTransferMs) ?? []]
497
+ };
498
+ }
499
+ async resetPerformanceMetrics(unitId) {
500
+ var _this$_executor2;
501
+ this._hydrationSamples.delete(unitId);
502
+ await ((_this$_executor2 = this._executor) === null || _this$_executor2 === void 0 ? void 0 : _this$_executor2.resetPerformanceMetrics(unitId));
503
+ }
504
+ recordHydrationDuration(unitId, durationMs) {
505
+ this._appendPerformanceSample(this._hydrationSamples, unitId, durationMs);
506
+ }
507
+ async recoverExecutor(unitId, diagnostic) {
508
+ const executor = this._requireExecutor();
509
+ this._executorStatus$.next({
510
+ state: "recovering",
511
+ executor: executor.type,
512
+ diagnostic,
513
+ recoveryUnitId: unitId
514
+ });
515
+ for (const session of this._sessions.values()) this._markSessionDisposed(session);
516
+ this._sessions.clear();
517
+ this._hydrationSamples.clear();
518
+ try {
519
+ const executorReady = executor.recover();
520
+ this._executorReady = executorReady;
521
+ await executorReady;
522
+ } catch (error) {
523
+ const recoveryDiagnostic = error instanceof Error ? error.message : String(error);
524
+ this._executor = null;
525
+ this._executorReady = null;
526
+ this._recoveryFailure = {
527
+ diagnostic: recoveryDiagnostic,
528
+ unitId
529
+ };
530
+ this._executorStatus$.next({
531
+ state: "recovering",
532
+ executor: executor.type,
533
+ diagnostic: recoveryDiagnostic,
534
+ recoveryUnitId: unitId
535
+ });
536
+ throw error;
537
+ }
538
+ }
539
+ completeRecovery(unitId) {
540
+ var _this$_recoveryFailur;
541
+ const status = this._executorStatus$.value;
542
+ if (status.state !== "recovering" || status.recoveryUnitId !== unitId) return;
543
+ const recoveryFailure = ((_this$_recoveryFailur = this._recoveryFailure) === null || _this$_recoveryFailur === void 0 ? void 0 : _this$_recoveryFailur.unitId) === unitId ? this._recoveryFailure : null;
544
+ this._recoveryFailure = null;
545
+ this._executorStatus$.next({
546
+ state: recoveryFailure == null ? "active" : "failed",
547
+ executor: status.executor,
548
+ diagnostic: (recoveryFailure === null || recoveryFailure === void 0 ? void 0 : recoveryFailure.diagnostic) ?? null,
549
+ recoveryUnitId: null
550
+ });
551
+ }
552
+ async startLayout(identity, options, budgetMs) {
553
+ const { unitId } = identity;
554
+ const executor = this._executor;
555
+ const dataModel = this._getEligibleModel(unitId);
556
+ if (executor == null || dataModel == null) return null;
557
+ let session = this._sessions.get(unitId);
558
+ if (session != null && session.sourceMutationRevision !== dataModel.getMutationRevision()) session.needsResnapshot = true;
559
+ if (session == null || session.needsResnapshot) session = await this._replaceSession(dataModel);
560
+ let result = await this._startSynchronizedLayout(executor, session, identity, options, budgetMs);
561
+ if (result.status === "not-found" || result.status === "resnapshot-required") {
562
+ session = await this._replaceSession(dataModel);
563
+ result = await this._startSynchronizedLayout(executor, session, identity, options, budgetMs);
564
+ }
565
+ return result;
566
+ }
567
+ stepLayout(request) {
568
+ return this._requireExecutor().stepLayout(request);
569
+ }
570
+ publishBacklog(request) {
571
+ return this._requireExecutor().publishBacklog(request);
572
+ }
573
+ getLayoutPage(request) {
574
+ return this._requireExecutor().getLayoutPage(request);
575
+ }
576
+ cancelLayout(request) {
577
+ const executor = this._executor;
578
+ return executor == null ? Promise.resolve() : executor.cancelLayout(request);
579
+ }
580
+ disposeLayoutMount(request) {
581
+ const executor = this._executor;
582
+ const session = this._sessions.get(request.unitId);
583
+ const latestStart = session === null || session === void 0 ? void 0 : session.latestStarts.get(request.mountId);
584
+ if ((latestStart === null || latestStart === void 0 ? void 0 : latestStart.mountEpoch) === request.mountEpoch && latestStart.viewportEpoch === request.viewportEpoch) {
585
+ session === null || session === void 0 || session.latestStarts.delete(request.mountId);
586
+ session === null || session === void 0 || session.customBlockViewportsByMount.delete(request.mountId);
587
+ }
588
+ return executor == null ? Promise.resolve() : executor.disposeLayoutMount(request);
589
+ }
590
+ dispose() {
591
+ const executor = this._executor;
592
+ if (executor != null) for (const [unitId, session] of this._sessions) {
593
+ this._markSessionDisposed(session);
594
+ executor.disposeSession({
595
+ unitId,
596
+ sessionEpoch: session.sessionEpoch
597
+ }).catch((error) => {
598
+ this._logService.error("[DocLayoutExecutorService]: failed to dispose a Worker session.", error);
599
+ });
600
+ }
601
+ this._sessions.clear();
602
+ this._executor = null;
603
+ this._executorReady = null;
604
+ this._recoveryFailure = null;
605
+ this._executorStatus$.complete();
606
+ this._customRangePresentationProviders.clear();
607
+ super.dispose();
608
+ }
609
+ _createSessionIfEligible(dataModel) {
610
+ if (this._executor == null || !this._isEligible(dataModel) || this._sessions.has(dataModel.getUnitId())) return;
611
+ this._replaceSession(dataModel).catch((error) => {
612
+ this._logService.error("[DocLayoutExecutorService]: failed to create a Worker session.", error);
613
+ });
614
+ }
615
+ _enqueueCommittedMutation(params) {
616
+ const executor = this._executor;
617
+ const dataModel = this._getEligibleModel(params.unitId);
618
+ if (executor == null || dataModel == null) return;
619
+ const session = this._sessions.get(params.unitId);
620
+ if (session == null) {
621
+ this._replaceSession(dataModel).catch((error) => {
622
+ this._logService.error("[DocLayoutExecutorService]: failed to snapshot a document mutation.", error);
623
+ });
624
+ return;
625
+ }
626
+ if (JSONX.isNoop(params.actions)) return;
627
+ const sourceMutationRevision = dataModel.getMutationRevision();
628
+ if (sourceMutationRevision !== session.sourceMutationRevision + 1) {
629
+ session.needsResnapshot = true;
630
+ return;
631
+ }
632
+ const baseRevision = session.modelRevision;
633
+ const modelRevision = baseRevision + 1;
634
+ session.modelRevision = modelRevision;
635
+ session.sourceMutationRevision = sourceMutationRevision;
636
+ session.pendingMutations.push({
637
+ baseRevision,
638
+ modelRevision,
639
+ actions: params.actions
640
+ });
641
+ }
642
+ async _startSynchronizedLayout(executor, session, identity, options, budgetMs) {
643
+ var _session$latestStarts;
644
+ const { unitId, mountId } = identity;
645
+ const startId = (((_session$latestStarts = session.latestStarts.get(mountId)) === null || _session$latestStarts === void 0 ? void 0 : _session$latestStarts.startId) ?? 0) + 1;
646
+ const startIdentity = {
647
+ startId,
648
+ mountEpoch: identity.mountEpoch,
649
+ viewportEpoch: identity.viewportEpoch
650
+ };
651
+ session.latestStarts.set(mountId, startIdentity);
652
+ const startTask = session.queue.then(async () => {
653
+ if (session.disposed || this._executor !== executor) return { status: "not-found" };
654
+ const currentModel = this._getEligibleModel(unitId);
655
+ if (currentModel == null || currentModel.getMutationRevision() !== session.sourceMutationRevision) {
656
+ session.needsResnapshot = true;
657
+ return {
658
+ status: "resnapshot-required",
659
+ modelRevision: session.workerRevision
660
+ };
661
+ }
662
+ if (session.latestStarts.get(mountId) !== startIdentity) return { status: "superseded" };
663
+ const mutations = session.pendingMutations.slice();
664
+ const projections = this._createLayoutProjectionPayload(session, identity, this._getRequiredModel(unitId));
665
+ const request = {
666
+ ...identity,
667
+ metricsRevision: startId,
668
+ baseRevision: session.workerRevision,
669
+ modelRevision: session.modelRevision,
670
+ mutations,
671
+ ...projections.request,
672
+ budgetMs,
673
+ ...options
674
+ };
675
+ const result = await executor.startLayout(request);
676
+ if (result.status === "accepted") {
677
+ session.workerRevision = request.modelRevision;
678
+ session.pendingMutations.splice(0, mutations.length);
679
+ session.customBlockViewportsByMount.set(mountId, {
680
+ mountEpoch: identity.mountEpoch,
681
+ value: projections.customBlockViewports
682
+ });
683
+ session.customRangePresentations = projections.customRangePresentations;
684
+ } else session.needsResnapshot = true;
685
+ return result;
686
+ });
687
+ session.queue = startTask.then(() => void 0, (error) => {
688
+ session.needsResnapshot = true;
689
+ this._logService.error("[DocLayoutExecutorService]: synchronized layout start failed.", error);
690
+ });
691
+ return startTask;
692
+ }
693
+ _createLayoutProjectionPayload(session, identity, dataModel) {
694
+ const customBlockViewports = collectCustomBlockViewports(dataModel);
695
+ const previousCustomBlockViewports = session.customBlockViewportsByMount.get(identity.mountId);
696
+ const customRangePresentations = this._collectCustomRangePresentations(dataModel);
697
+ const customRangePresentationMap = toCustomRangePresentationMap(customRangePresentations);
698
+ const request = {};
699
+ if (previousCustomBlockViewports == null || previousCustomBlockViewports.mountEpoch !== identity.mountEpoch) request.customBlockViewports = customBlockViewports;
700
+ else {
701
+ const patch = createRecordPatch(previousCustomBlockViewports.value, customBlockViewports);
702
+ if (patch.removals.length > 0 || Object.keys(patch.upserts).length > 0) request.customBlockViewportPatch = patch;
703
+ }
704
+ if (session.customRangePresentations == null) request.customRangePresentations = customRangePresentations;
705
+ else {
706
+ const patch = createCustomRangePresentationPatch(session.customRangePresentations, customRangePresentationMap);
707
+ if (patch.removals.length > 0 || patch.upserts.length > 0) request.customRangePresentationPatch = patch;
708
+ }
709
+ return {
710
+ request,
711
+ customBlockViewports,
712
+ customRangePresentations: customRangePresentationMap
713
+ };
714
+ }
715
+ async _replaceSession(dataModel) {
716
+ const executor = this._requireExecutor();
717
+ const unitId = dataModel.getUnitId();
718
+ const previous = this._sessions.get(unitId);
719
+ if (previous != null) this._markSessionDisposed(previous);
720
+ const modelRevision = (previous === null || previous === void 0 ? void 0 : previous.modelRevision) ?? 0;
721
+ const session = {
722
+ sessionEpoch: ++this._sessionEpoch,
723
+ modelRevision,
724
+ sourceMutationRevision: dataModel.getMutationRevision(),
725
+ workerRevision: modelRevision,
726
+ pendingMutations: [],
727
+ latestStarts: /* @__PURE__ */ new Map(),
728
+ customBlockViewportsByMount: /* @__PURE__ */ new Map(),
729
+ customRangePresentations: null,
730
+ queue: Promise.resolve(),
731
+ needsResnapshot: false,
732
+ disposed: false,
733
+ cancelPendingCreateTask: null
734
+ };
735
+ const createTask = this._createSessionOnNextMacroTask(executor, session, dataModel);
736
+ session.queue = createTask;
737
+ this._sessions.set(unitId, session);
738
+ try {
739
+ await createTask;
740
+ } catch (error) {
741
+ session.needsResnapshot = true;
742
+ throw error;
743
+ }
744
+ return session;
745
+ }
746
+ _disposeSession(unitId) {
747
+ var _this$_executor3;
748
+ const session = this._sessions.get(unitId);
749
+ if (session == null) return;
750
+ this._markSessionDisposed(session);
751
+ this._sessions.delete(unitId);
752
+ this._hydrationSamples.delete(unitId);
753
+ (_this$_executor3 = this._executor) === null || _this$_executor3 === void 0 || _this$_executor3.disposeSession({
754
+ unitId,
755
+ sessionEpoch: session.sessionEpoch
756
+ }).catch((error) => {
757
+ this._logService.error("[DocLayoutExecutorService]: failed to dispose a Worker session.", error);
758
+ });
759
+ }
760
+ _getEligibleModel(unitId) {
761
+ const dataModel = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC);
762
+ return dataModel != null && this._isEligible(dataModel) ? dataModel : null;
763
+ }
764
+ _getRequiredModel(unitId) {
765
+ const dataModel = this._getEligibleModel(unitId);
766
+ if (dataModel == null) throw new Error(`Eligible document model not found for Worker layout: "${unitId}".`);
767
+ return dataModel;
768
+ }
769
+ _collectCustomRangePresentations(dataModel) {
770
+ const presentations = [];
771
+ const providers = Array.from(this._customRangePresentationProviders).reverse();
772
+ if (providers.length === 0) return presentations;
773
+ const collectSegment = (segmentId, body) => {
774
+ if (body == null) return;
775
+ const seenRangeIds = /* @__PURE__ */ new Set();
776
+ for (const range of body.customRanges ?? []) {
777
+ if (seenRangeIds.has(range.rangeId)) continue;
778
+ seenRangeIds.add(range.rangeId);
779
+ for (const provider of providers) {
780
+ const presentation = provider(dataModel.getUnitId(), range, {
781
+ segmentId,
782
+ body
783
+ });
784
+ if (presentation != null) {
785
+ presentations.push({
786
+ segmentId,
787
+ rangeId: range.rangeId,
788
+ presentation: { ...presentation }
789
+ });
790
+ break;
791
+ }
792
+ }
793
+ }
794
+ };
795
+ collectSegment("", dataModel.getBody());
796
+ for (const [segmentId, headerModel] of dataModel.headerModelMap) collectSegment(segmentId, headerModel.getBody());
797
+ for (const [segmentId, footerModel] of dataModel.footerModelMap) collectSegment(segmentId, footerModel.getBody());
798
+ return presentations;
799
+ }
800
+ _isEligible(dataModel) {
801
+ if (isInternalEditorID(dataModel.getUnitId())) return false;
802
+ const flavor = dataModel.getSnapshot().documentStyle.documentFlavor;
803
+ return flavor === DocumentFlavor.TRADITIONAL || flavor === DocumentFlavor.MODERN;
804
+ }
805
+ _requireExecutor() {
806
+ if (this._executor == null) throw new Error("No document layout executor is registered.");
807
+ return this._executor;
808
+ }
809
+ _appendPerformanceSample(target, unitId, durationMs) {
810
+ const samples = target.get(unitId) ?? [];
811
+ samples.push(durationMs);
812
+ if (samples.length > _DocLayoutExecutorService._performanceSampleLimit) samples.splice(0, samples.length - _DocLayoutExecutorService._performanceSampleLimit);
813
+ target.set(unitId, samples);
814
+ }
815
+ _createSessionOnNextMacroTask(executor, session, dataModel) {
816
+ return new Promise((resolve, reject) => {
817
+ const cancelTask = requestImmediateMacroTask(() => {
818
+ session.cancelPendingCreateTask = null;
819
+ const unitId = dataModel.getUnitId();
820
+ if (session.disposed || this._executor !== executor || this._sessions.get(unitId) !== session) {
821
+ resolve();
822
+ return;
823
+ }
824
+ const executorReady = this._executorReady;
825
+ if (executorReady == null) {
826
+ resolve();
827
+ return;
828
+ }
829
+ executorReady.then(() => {
830
+ if (session.disposed || this._executor !== executor || this._sessions.get(unitId) !== session || this._getEligibleModel(unitId) !== dataModel) {
831
+ resolve();
832
+ return;
833
+ }
834
+ session.sourceMutationRevision = dataModel.getMutationRevision();
835
+ session.workerRevision = session.modelRevision;
836
+ session.pendingMutations.length = 0;
837
+ const request = {
838
+ unitId,
839
+ sessionEpoch: session.sessionEpoch,
840
+ snapshot: createDocumentLayoutSnapshot(dataModel.getSnapshot()),
841
+ modelRevision: session.modelRevision,
842
+ locale: this._localeService.getCurrentLocale(),
843
+ localeData: this._localeService.getLocales(),
844
+ direction: this._localeService.getDirection()
845
+ };
846
+ executor.createSession(request).then(resolve, reject);
847
+ }, reject);
848
+ });
849
+ session.cancelPendingCreateTask = () => {
850
+ cancelTask();
851
+ session.cancelPendingCreateTask = null;
852
+ resolve();
853
+ };
854
+ });
855
+ }
856
+ _markSessionDisposed(session) {
857
+ var _session$cancelPendin;
858
+ session.disposed = true;
859
+ (_session$cancelPendin = session.cancelPendingCreateTask) === null || _session$cancelPendin === void 0 || _session$cancelPendin.call(session);
860
+ session.cancelPendingCreateTask = null;
861
+ }
862
+ _disposeManagedSessions() {
863
+ for (const session of this._sessions.values()) this._markSessionDisposed(session);
864
+ }
865
+ };
866
+ _defineProperty(DocLayoutExecutorService, "_performanceSampleLimit", 2048);
867
+ DocLayoutExecutorService = _DocLayoutExecutorService = __decorate([
868
+ __decorateParam(0, IUniverInstanceService),
869
+ __decorateParam(1, ICommandService),
870
+ __decorateParam(2, Inject(LocaleService)),
871
+ __decorateParam(3, ILogService)
872
+ ], DocLayoutExecutorService);
873
+
228
874
  //#endregion
229
875
  //#region src/services/doc-skeleton-manager.service.ts
230
876
  let DocSkeletonManagerService = class DocSkeletonManagerService extends RxDisposable {
231
- constructor(_context, _localeService, _univerInstanceService) {
877
+ constructor(_context, _localeService, _univerInstanceService, _docLayoutExecutorService) {
232
878
  super();
233
879
  this._context = _context;
234
880
  this._localeService = _localeService;
235
881
  this._univerInstanceService = _univerInstanceService;
882
+ this._docLayoutExecutorService = _docLayoutExecutorService;
236
883
  _defineProperty(this, "_skeleton", void 0);
237
884
  _defineProperty(this, "_docViewModel", void 0);
238
885
  _defineProperty(this, "_currentSkeleton$", new BehaviorSubject(null));
@@ -257,12 +904,23 @@ let DocSkeletonManagerService = class DocSkeletonManagerService extends RxDispos
257
904
  getViewModel() {
258
905
  return this._docViewModel;
259
906
  }
907
+ getLayoutPerformanceMetrics() {
908
+ return this._docLayoutExecutorService.getPerformanceMetrics(this._context.unitId);
909
+ }
910
+ supportsIncrementalLayout() {
911
+ var _this$_context$unit$g;
912
+ const documentFlavor = (_this$_context$unit$g = this._context.unit.getSnapshot().documentStyle) === null || _this$_context$unit$g === void 0 ? void 0 : _this$_context$unit$g.documentFlavor;
913
+ return this._docLayoutExecutorService.getExecutor() != null && !isInternalEditorID(this._context.unitId) && (documentFlavor === DocumentFlavor.TRADITIONAL || documentFlavor === DocumentFlavor.MODERN);
914
+ }
260
915
  recalculate() {
261
916
  const skeleton = this._skeleton;
262
917
  skeleton.calculate();
918
+ this._publishSkeleton(skeleton);
919
+ return skeleton;
920
+ }
921
+ _publishSkeleton(skeleton) {
263
922
  this._currentSkeletonBefore$.next(skeleton);
264
923
  this._currentSkeleton$.next(skeleton);
265
- return skeleton;
266
924
  }
267
925
  _init() {
268
926
  const documentDataModel = this._context.unit;
@@ -276,7 +934,8 @@ let DocSkeletonManagerService = class DocSkeletonManagerService extends RxDispos
276
934
  this._context.unit = documentDataModel;
277
935
  } else if (!this._docViewModel) this._docViewModel = this._buildDocViewModel(documentDataModel);
278
936
  if (!this._skeleton) this._skeleton = this._buildSkeleton(this._docViewModel);
279
- this.recalculate();
937
+ if (this.supportsIncrementalLayout()) this._publishSkeleton(this._skeleton);
938
+ else this.recalculate();
280
939
  this._currentViewModel$.next(this._docViewModel);
281
940
  }
282
941
  _buildSkeleton(documentViewModel) {
@@ -286,7 +945,11 @@ let DocSkeletonManagerService = class DocSkeletonManagerService extends RxDispos
286
945
  return new DocumentViewModel(documentDataModel);
287
946
  }
288
947
  };
289
- DocSkeletonManagerService = __decorate([__decorateParam(1, Inject(LocaleService)), __decorateParam(2, IUniverInstanceService)], DocSkeletonManagerService);
948
+ DocSkeletonManagerService = __decorate([
949
+ __decorateParam(1, Inject(LocaleService)),
950
+ __decorateParam(2, IUniverInstanceService),
951
+ __decorateParam(3, Inject(DocLayoutExecutorService))
952
+ ], DocSkeletonManagerService);
290
953
 
291
954
  //#endregion
292
955
  //#region src/services/doc-state-emit.service.ts
@@ -306,21 +969,113 @@ var DocStateEmitService = class extends RxDisposable {
306
969
  };
307
970
 
308
971
  //#endregion
309
- //#region src/commands/mutations/core-editing.mutation.ts
310
- const RichTextEditingMutationId = "doc.mutation.rich-text-editing";
972
+ //#region src/commands/mutations/doc-structure-mutation-validation.ts
973
+ const STRUCTURAL_BODY_FIELDS = [
974
+ "paragraphs",
975
+ "sectionBreaks",
976
+ "customBlocks",
977
+ "docxRawCustomBlocks",
978
+ "docxRawBlocks",
979
+ "docxExportExcludedRanges",
980
+ "tables",
981
+ "columnGroups",
982
+ "blockRanges"
983
+ ];
984
+ const STRUCTURAL_DATA_STREAM_TOKENS = /* @__PURE__ */ new Set([
985
+ DataStreamTreeTokenType.PARAGRAPH,
986
+ DataStreamTreeTokenType.SECTION_BREAK,
987
+ DataStreamTreeTokenType.TABLE_START,
988
+ DataStreamTreeTokenType.TABLE_ROW_START,
989
+ DataStreamTreeTokenType.TABLE_CELL_START,
990
+ DataStreamTreeTokenType.TABLE_CELL_END,
991
+ DataStreamTreeTokenType.TABLE_ROW_END,
992
+ DataStreamTreeTokenType.TABLE_END,
993
+ DataStreamTreeTokenType.COLUMN_GROUP_START,
994
+ DataStreamTreeTokenType.COLUMN_START,
995
+ DataStreamTreeTokenType.COLUMN_END,
996
+ DataStreamTreeTokenType.COLUMN_GROUP_END,
997
+ DataStreamTreeTokenType.BLOCK_START,
998
+ DataStreamTreeTokenType.BLOCK_END,
999
+ DataStreamTreeTokenType.CUSTOM_BLOCK
1000
+ ]);
1001
+ const NON_STRUCTURAL_ROOT_FIELDS = /* @__PURE__ */ new Set([
1002
+ "disabled",
1003
+ "documentStyle",
1004
+ "drawings",
1005
+ "drawingsOrder",
1006
+ "lists",
1007
+ "locale",
1008
+ "resources",
1009
+ "rev",
1010
+ "settings",
1011
+ "styles",
1012
+ "tableSource",
1013
+ "title"
1014
+ ]);
1015
+ function isRecord$3(value) {
1016
+ return typeof value === "object" && value != null && !Array.isArray(value);
1017
+ }
1018
+ function containsStructuralToken(dataStream) {
1019
+ for (let index = 0; index < dataStream.length; index++) if (STRUCTURAL_DATA_STREAM_TOKENS.has(dataStream[index])) return true;
1020
+ return false;
1021
+ }
1022
+ function isStructurePreservingBody(value) {
1023
+ if (!isRecord$3(value)) return false;
1024
+ const dataStream = value.dataStream;
1025
+ if (dataStream != null && (typeof dataStream !== "string" || containsStructuralToken(dataStream))) return false;
1026
+ return STRUCTURAL_BODY_FIELDS.every((field) => {
1027
+ const metadata = value[field];
1028
+ return metadata == null || Array.isArray(metadata) && metadata.length === 0;
1029
+ });
1030
+ }
1031
+ function isStructurePreservingTextXEdit(value) {
1032
+ if (!Array.isArray(value)) return false;
1033
+ return value.every((action) => {
1034
+ if (!isRecord$3(action) || typeof action.len !== "number" || !Number.isFinite(action.len)) return false;
1035
+ if (action.t === TextXActionType.DELETE) return true;
1036
+ if (action.t === TextXActionType.INSERT) return isStructurePreservingBody(action.body);
1037
+ return action.t === TextXActionType.RETAIN && (action.body == null || isStructurePreservingBody(action.body));
1038
+ });
1039
+ }
1040
+ function pathsEqual(left, right) {
1041
+ return left.length === right.length && left.every((item, index) => item === right[index]);
1042
+ }
1043
+ function isStructurePreservingJSONXEdit(actions, expectedPath) {
1044
+ const cursor = JSON1.type.readCursor(actions);
1045
+ let hasComponent = false;
1046
+ let isStructurePreserving = true;
1047
+ cursor.traverse(null, (component) => {
1048
+ if (!isStructurePreserving) return;
1049
+ const path = cursor.getPath();
1050
+ hasComponent = true;
1051
+ if (NON_STRUCTURAL_ROOT_FIELDS.has(String(path[0]))) return;
1052
+ const componentKeys = Object.keys(component);
1053
+ if (component.et !== TextX.id || componentKeys.some((key) => key !== "et" && key !== "e") || !pathsEqual(path, expectedPath)) {
1054
+ isStructurePreserving = false;
1055
+ return;
1056
+ }
1057
+ const edit = component.e;
1058
+ isStructurePreserving = isStructurePreservingTextXEdit(edit);
1059
+ });
1060
+ return hasComponent && isStructurePreserving;
1061
+ }
311
1062
  function getSegmentType(documentDataModel, segmentId) {
1063
+ var _documentDataModel$ge;
312
1064
  if (!segmentId) return "body";
313
1065
  const { headers, footers } = documentDataModel.getSnapshot();
314
1066
  if (headers === null || headers === void 0 ? void 0 : headers[segmentId]) return "header";
315
1067
  if (footers === null || footers === void 0 ? void 0 : footers[segmentId]) return "footer";
1068
+ if ((_documentDataModel$ge = documentDataModel.getSnapshot().notes) === null || _documentDataModel$ge === void 0 ? void 0 : _documentDataModel$ge[segmentId]) return "note";
316
1069
  return "body";
317
1070
  }
318
1071
  function assertValidDocBodyStructure(documentDataModel, segmentId) {
1072
+ var _documentDataModel$ge2;
319
1073
  const segmentModel = documentDataModel.getSelfOrHeaderFooterModel(segmentId);
320
1074
  const body = segmentModel === null || segmentModel === void 0 ? void 0 : segmentModel.getBody();
321
1075
  if (!body) return;
322
1076
  const segmentType = getSegmentType(documentDataModel, segmentId);
323
- const issues = validateDocBodyStructure(body, {
1077
+ const note = (_documentDataModel$ge2 = documentDataModel.getSnapshot().notes) === null || _documentDataModel$ge2 === void 0 ? void 0 : _documentDataModel$ge2[segmentId];
1078
+ const issues = note ? validateDocumentStructure({ notes: { [segmentId]: note } }) : validateDocBodyStructure(body, {
324
1079
  segmentType,
325
1080
  segmentId: segmentId || void 0
326
1081
  });
@@ -329,17 +1084,175 @@ function assertValidDocBodyStructure(documentDataModel, segmentId) {
329
1084
  const segmentLabel = segmentId ? `${segmentType} ${segmentId}` : segmentType;
330
1085
  throw new Error(`[DocStructure] ${segmentLabel}: ${detail}`);
331
1086
  }
1087
+ function validateDocStructureMutation(documentDataModel, segmentId, actions, undoActions) {
1088
+ const editPath = getRichTextEditPath(documentDataModel, segmentId);
1089
+ const preservesStructure = isStructurePreservingJSONXEdit(actions, editPath) && isStructurePreservingJSONXEdit(undoActions, editPath);
1090
+ let changesFootnoteStructure = false;
1091
+ const cursor = JSON1.type.readCursor(actions);
1092
+ cursor.traverse(null, (component) => {
1093
+ const path = cursor.getPath();
1094
+ if (path[0] === "notes" && (path.length <= 2 || path[2] !== "body") || path.includes("customRanges")) changesFootnoteStructure = true;
1095
+ if (component.et === TextX.id && Array.isArray(component.e)) for (const action of component.e) {
1096
+ const ranges = isRecord$3(action) && isRecord$3(action.body) ? action.body.customRanges : void 0;
1097
+ if (Array.isArray(ranges) && ranges.some((range) => isRecord$3(range) && (range.rangeType === CustomRangeType.FOOTNOTE || range.rangeType === CustomRangeType.ENDNOTE))) changesFootnoteStructure = true;
1098
+ }
1099
+ });
1100
+ if (changesFootnoteStructure) {
1101
+ const issues = validateDocumentStructure(documentDataModel.getSnapshot());
1102
+ if (issues.length > 0) throw new Error(`[DocStructure] ${issues.map((issue) => `${issue.code}@${issue.index ?? issue.segmentId ?? ""}`).join(", ")}`);
1103
+ } else if (!preservesStructure) assertValidDocBodyStructure(documentDataModel, segmentId);
1104
+ return preservesStructure && !changesFootnoteStructure;
1105
+ }
1106
+
1107
+ //#endregion
1108
+ //#region src/commands/mutations/core-editing.mutation.ts
1109
+ let DocHistoryAction = /* @__PURE__ */ function(DocHistoryAction) {
1110
+ DocHistoryAction["DeleteChart"] = "delete-chart";
1111
+ DocHistoryAction["DeleteDivider"] = "delete-divider";
1112
+ DocHistoryAction["DeleteImage"] = "delete-image";
1113
+ DocHistoryAction["DeleteShape"] = "delete-shape";
1114
+ DocHistoryAction["EditTableCell"] = "edit-table-cell";
1115
+ DocHistoryAction["FormatParagraph"] = "format-paragraph";
1116
+ DocHistoryAction["InsertCustomRange"] = "insert-custom-range";
1117
+ DocHistoryAction["UpdateImage"] = "update-image";
1118
+ DocHistoryAction["UpdatePageLayout"] = "update-page-layout";
1119
+ return DocHistoryAction;
1120
+ }({});
1121
+ function extractDocumentBodyActions(actions, segmentId) {
1122
+ if (!Array.isArray(actions)) return;
1123
+ const bodyActions = actions.indexOf("body") > -1 ? actions : actions.find((action) => Array.isArray(action) && action.indexOf("body") > -1);
1124
+ if (!Array.isArray(bodyActions)) return;
1125
+ const bodyIndex = bodyActions.indexOf("body");
1126
+ if (bodyIndex === -1) return;
1127
+ return (bodyIndex === 0 ? "" : bodyActions[bodyIndex - 1]) === segmentId ? bodyActions.slice(bodyIndex) : void 0;
1128
+ }
1129
+ /**
1130
+ * Transforms document selections through the same JSONX actions applied by a rich-text mutation.
1131
+ * Collaboration and rendering use this shared offset rule so the Main interaction window follows
1132
+ * the transformed local caret before an authoritative background layout is published.
1133
+ */
1134
+ function transformDocumentTextRanges(actions, textRanges, priority = "right") {
1135
+ if (textRanges.length === 0) return [];
1136
+ const bodyActions = extractDocumentBodyActions(actions, textRanges[0].segmentId ?? "");
1137
+ if (bodyActions == null) return textRanges;
1138
+ return textRanges.map((textRange) => {
1139
+ const startOffset = JSONX.transformPosition(bodyActions, textRange.startOffset, priority);
1140
+ const endOffset = JSONX.transformPosition(bodyActions, textRange.endOffset, priority);
1141
+ return {
1142
+ ...textRange,
1143
+ startOffset,
1144
+ endOffset,
1145
+ collapsed: startOffset === endOffset
1146
+ };
1147
+ });
1148
+ }
1149
+ function canRemoveFootnoteReference(actions, references) {
1150
+ let canRemove = false;
1151
+ const cursor = JSON1.type.readCursor(actions);
1152
+ cursor.traverse(null, (component) => {
1153
+ const path = cursor.getPath();
1154
+ if (path.length === 0) canRemove = true;
1155
+ if (canRemove || path[0] !== "body") return;
1156
+ if (path.length === 1 && component.et === TextX.id && Array.isArray(component.e)) {
1157
+ let offset = 0;
1158
+ for (const action of component.e) {
1159
+ var _action$body;
1160
+ if (action.t === TextXActionType.DELETE && references.some((reference) => offset <= reference.endIndex && offset + action.len > reference.startIndex)) {
1161
+ canRemove = true;
1162
+ return;
1163
+ }
1164
+ if (action.t === TextXActionType.RETAIN && ((_action$body = action.body) === null || _action$body === void 0 ? void 0 : _action$body.customRanges) != null) {
1165
+ canRemove = true;
1166
+ return;
1167
+ }
1168
+ if (action.t !== TextXActionType.INSERT) offset += action.len;
1169
+ }
1170
+ } else if (path.length === 1 || path[1] === "customRanges" || path[1] === "dataStream") canRemove = true;
1171
+ });
1172
+ return canRemove;
1173
+ }
1174
+ function includeFootnoteCleanup(before, actions) {
1175
+ var _before$body, _after$body;
1176
+ if (!before.notes) return actions;
1177
+ const references = ((_before$body = before.body) === null || _before$body === void 0 || (_before$body = _before$body.customRanges) === null || _before$body === void 0 ? void 0 : _before$body.filter((range) => range.rangeType === CustomRangeType.FOOTNOTE || range.rangeType === CustomRangeType.ENDNOTE)) ?? [];
1178
+ if (references.length === 0 || !canRemoveFootnoteReference(actions, references)) return actions;
1179
+ const after = JSONX.apply(Tools.deepClone(before), actions);
1180
+ const remaining = new Set((_after$body = after.body) === null || _after$body === void 0 || (_after$body = _after$body.customRanges) === null || _after$body === void 0 ? void 0 : _after$body.filter((range) => range.rangeType === CustomRangeType.FOOTNOTE || range.rangeType === CustomRangeType.ENDNOTE).map((range) => {
1181
+ var _range$properties;
1182
+ return (_range$properties = range.properties) === null || _range$properties === void 0 ? void 0 : _range$properties.noteId;
1183
+ }));
1184
+ let cleanup = null;
1185
+ const previousIds = new Set(references.map((reference) => {
1186
+ var _reference$properties;
1187
+ return (_reference$properties = reference.properties) === null || _reference$properties === void 0 ? void 0 : _reference$properties.noteId;
1188
+ }));
1189
+ for (const id of previousIds) {
1190
+ var _after$notes;
1191
+ if (typeof id === "string" && !remaining.has(id) && ((_after$notes = after.notes) === null || _after$notes === void 0 ? void 0 : _after$notes[id])) cleanup = JSONX.compose(cleanup, JSONX.getInstance().removeOp(["notes", id], after.notes[id]));
1192
+ }
1193
+ return JSONX.isNoop(cleanup) ? actions : JSONX.compose(actions, cleanup);
1194
+ }
1195
+ function applyValidatedDocumentActions(documentDataModel, segmentId, actions) {
1196
+ const before = documentDataModel.getSnapshot();
1197
+ const appliedActions = includeFootnoteCleanup(before, actions);
1198
+ const undoActions = JSONX.invertWithDoc(appliedActions, before);
1199
+ documentDataModel.apply(appliedActions);
1200
+ try {
1201
+ return {
1202
+ actions: appliedActions,
1203
+ undoActions,
1204
+ preservesStructure: validateDocStructureMutation(documentDataModel, segmentId, appliedActions, undoActions)
1205
+ };
1206
+ } catch (error) {
1207
+ documentDataModel.apply(undoActions);
1208
+ throw error;
1209
+ }
1210
+ }
1211
+ function resetDocumentViewModel(documentViewModel, documentDataModel, segmentId, actions, preservesStructure) {
1212
+ var _documentViewModel$re, _documentViewModel$re2;
1213
+ if (documentViewModel == null) return;
1214
+ if (!(segmentId === "" && preservesStructure && (((_documentViewModel$re = documentViewModel.resetByValidatedTextMutation) === null || _documentViewModel$re === void 0 ? void 0 : _documentViewModel$re.call(documentViewModel, documentDataModel, actions)) || ((_documentViewModel$re2 = documentViewModel.resetByValidatedMetadataMutation) === null || _documentViewModel$re2 === void 0 ? void 0 : _documentViewModel$re2.call(documentViewModel, documentDataModel, actions))))) documentViewModel.reset(documentDataModel);
1215
+ }
1216
+ function scheduleDocumentSelectionUpdate(selectionManager, params, isSync) {
1217
+ const { unitId, textRanges, trigger, noNeedSetTextRange, isEditing = true } = params;
1218
+ if (noNeedSetTextRange || textRanges == null || textRanges.length === 0 || trigger == null || isSync) return;
1219
+ const selectionTarget = {
1220
+ unitId,
1221
+ subUnitId: unitId
1222
+ };
1223
+ const currentSelection = selectionManager.getSelectionInfo(selectionTarget);
1224
+ const logicalTextRanges = textRanges.map((textRange, index) => ({
1225
+ ...textRange,
1226
+ segmentId: textRange.segmentId ?? params.segmentId ?? "",
1227
+ collapsed: textRange.startOffset === textRange.endOffset,
1228
+ isActive: index === textRanges.length - 1
1229
+ }));
1230
+ if (currentSelection != null) selectionManager.replaceSelectionInfoWithoutRefresh({
1231
+ ...currentSelection,
1232
+ segmentId: logicalTextRanges[logicalTextRanges.length - 1].segmentId,
1233
+ textRanges: logicalTextRanges,
1234
+ rectRanges: [],
1235
+ isEditing,
1236
+ options: params.options
1237
+ }, selectionTarget);
1238
+ const updatedSelection = selectionManager.getSelectionInfo(selectionTarget);
1239
+ queueMicrotask(() => {
1240
+ if (selectionManager.getSelectionInfo(selectionTarget) !== updatedSelection) return;
1241
+ if (updatedSelection == null) selectionManager.replaceDocRanges(logicalTextRanges, selectionTarget, isEditing, params.options);
1242
+ else selectionManager.refreshSelection(selectionTarget, isEditing);
1243
+ });
1244
+ }
332
1245
  /**
333
1246
  * The core mutator to change rich text actions. The execution result would be undo mutation params. Could be directly
334
1247
  * send to undo redo service (will be used by the triggering command).
335
1248
  */
336
1249
  const RichTextEditingMutation = {
337
- id: RichTextEditingMutationId,
1250
+ id: RICH_TEXT_EDITING_MUTATION_ID,
338
1251
  type: CommandType.MUTATION,
339
1252
  handler: (accessor, params, options) => {
340
1253
  var _renderManagerService;
341
- const { unitId, segmentId = "", actions, textRanges, prevTextRanges, trigger, noHistory, isCompositionEnd, noNeedSetTextRange, debounce, isEditing = true, isSync: paramsIsSync, syncer } = params;
342
- const isSync = paramsIsSync || (options === null || options === void 0 ? void 0 : options.fromCollab) || (options === null || options === void 0 ? void 0 : options.fromChangeset);
1254
+ const { unitId, segmentId = "", actions, textRanges, prevTextRanges, trigger, noHistory, isCompositionEnd, debounce, isEditing = true, isSync: paramsIsSync, syncer } = params;
1255
+ const isSync = Boolean(paramsIsSync || (options === null || options === void 0 ? void 0 : options.fromCollab) || (options === null || options === void 0 ? void 0 : options.fromChangeset));
343
1256
  const univerInstanceService = accessor.get(IUniverInstanceService);
344
1257
  const renderManagerService = accessor.get(IRenderManagerService);
345
1258
  const docStateEmitService = accessor.get(DocStateEmitService);
@@ -348,41 +1261,35 @@ const RichTextEditingMutation = {
348
1261
  if (documentDataModel == null) throw new Error(`DocumentDataModel not found for unitId: ${unitId}`);
349
1262
  const docSelectionManagerService = accessor.get(DocSelectionManagerService);
350
1263
  const docRanges = docSelectionManagerService.getDocRanges() ?? [];
1264
+ const selectionInfo = docSelectionManagerService.getSelectionInfo();
351
1265
  const disabled = !!documentDataModel.getSnapshot().disabled;
352
1266
  if (JSONX.isNoop(actions) || actions && actions.length === 0 || disabled) return {
353
1267
  unitId,
354
1268
  actions: [],
355
1269
  textRanges: docRanges
356
1270
  };
357
- const undoActions = JSONX.invertWithDoc(actions, documentDataModel.getSnapshot());
358
- documentDataModel.apply(actions);
359
- try {
360
- assertValidDocBodyStructure(documentDataModel, segmentId);
361
- } catch (error) {
362
- documentDataModel.apply(undoActions);
363
- throw error;
364
- }
365
- documentViewModel === null || documentViewModel === void 0 || documentViewModel.reset(documentDataModel);
366
- if (!noNeedSetTextRange && textRanges && trigger != null && !isSync) queueMicrotask(() => {
367
- docSelectionManagerService.replaceDocRanges(textRanges, {
368
- unitId,
369
- subUnitId: unitId
370
- }, isEditing, params.options);
371
- });
1271
+ const { actions: appliedActions, undoActions, preservesStructure } = applyValidatedDocumentActions(documentDataModel, segmentId, actions);
1272
+ params.actions = appliedActions;
1273
+ resetDocumentViewModel(documentViewModel, documentDataModel, segmentId, appliedActions, preservesStructure);
1274
+ scheduleDocumentSelectionUpdate(docSelectionManagerService, params, isSync);
372
1275
  const changeState = {
373
- commandId: RichTextEditingMutationId,
1276
+ commandId: RICH_TEXT_EDITING_MUTATION_ID,
374
1277
  unitId,
375
1278
  segmentId,
376
1279
  trigger,
377
1280
  noHistory,
378
1281
  debounce,
379
1282
  redoState: {
380
- actions,
381
- textRanges
1283
+ actions: appliedActions,
1284
+ textRanges,
1285
+ options: params.options,
1286
+ isEditing
382
1287
  },
383
1288
  undoState: {
384
1289
  actions: undoActions,
385
- textRanges: prevTextRanges ?? docRanges
1290
+ textRanges: prevTextRanges ?? docRanges,
1291
+ options: selectionInfo === null || selectionInfo === void 0 ? void 0 : selectionInfo.options,
1292
+ isEditing: selectionInfo === null || selectionInfo === void 0 ? void 0 : selectionInfo.isEditing
386
1293
  },
387
1294
  isCompositionEnd,
388
1295
  isSync,
@@ -408,59 +1315,63 @@ const InsertTextCommand = {
408
1315
  handler: (accessor, params) => {
409
1316
  var _docDataModel$getSelf;
410
1317
  const commandService = accessor.get(ICommandService);
411
- const { range, segmentId, body, unitId, cursorOffset } = params;
1318
+ const { range, segmentId, body, unitId, cursorOffset, debounce = true, textRanges, noNeedSetTextRange, isEditing } = params;
412
1319
  const docSelectionManagerService = accessor.get(DocSelectionManagerService);
413
1320
  const docDataModel = accessor.get(IUniverInstanceService).getUnit(unitId, UniverInstanceType.UNIVER_DOC);
414
1321
  if (docDataModel == null) return false;
415
1322
  const activeRange = docSelectionManagerService.getActiveTextRange();
416
- const rangeSegmentId = "segmentId" in range ? range.segmentId : void 0;
1323
+ const rangeSegmentId = "segmentId" in range && typeof range.segmentId === "string" ? range.segmentId : void 0;
417
1324
  const targetSegmentId = segmentId ?? rangeSegmentId ?? (activeRange === null || activeRange === void 0 ? void 0 : activeRange.segmentId) ?? "";
418
1325
  const originBody = (_docDataModel$getSelf = docDataModel.getSelfOrHeaderFooterModel(targetSegmentId)) === null || _docDataModel$getSelf === void 0 ? void 0 : _docDataModel$getSelf.getBody();
419
1326
  if (originBody == null) return false;
420
1327
  const { startOffset, collapsed } = range;
421
1328
  const cursorMove = cursorOffset ?? body.dataStream.length;
422
- const textRanges = [{
1329
+ const mutationTextRanges = textRanges ?? [{
423
1330
  startOffset: startOffset + cursorMove,
424
1331
  endOffset: startOffset + cursorMove,
425
- style: activeRange === null || activeRange === void 0 ? void 0 : activeRange.style,
426
1332
  collapsed
427
1333
  }];
428
1334
  const doMutation = {
429
1335
  id: RichTextEditingMutation.id,
430
1336
  params: {
431
1337
  unitId,
1338
+ segmentId: targetSegmentId,
432
1339
  actions: [],
433
- textRanges,
434
- debounce: true
1340
+ textRanges: mutationTextRanges,
1341
+ debounce,
1342
+ noNeedSetTextRange,
1343
+ isEditing
435
1344
  }
436
1345
  };
437
- const textX = new TextX();
438
- const jsonX = JSONX.getInstance();
439
- if (collapsed) {
440
- if (startOffset > 0) textX.push({
441
- t: TextXActionType.RETAIN,
442
- len: startOffset
443
- });
444
- textX.push({
445
- t: TextXActionType.INSERT,
446
- body,
447
- len: body.dataStream.length
448
- });
449
- } else {
450
- const dos = BuildTextUtils.selection.delete([range], originBody, 0, body);
451
- textX.push(...dos);
452
- }
453
- doMutation.params.textRanges = [{
454
- startOffset: startOffset + cursorMove,
455
- endOffset: startOffset + cursorMove,
456
- collapsed
457
- }];
458
- const path = getRichTextEditPath(docDataModel, segmentId);
459
- doMutation.params.actions = jsonX.editOp(textX.serialize(), path);
1346
+ doMutation.params.actions = buildInsertTextActions(docDataModel, originBody, range, body, targetSegmentId);
460
1347
  const result = commandService.syncExecuteCommand(doMutation.id, doMutation.params);
461
1348
  return Boolean(result);
462
1349
  }
463
1350
  };
1351
+ function buildInsertTextActions(documentDataModel, originBody, range, insertBody, segmentId) {
1352
+ const { startOffset, endOffset, collapsed } = range;
1353
+ const textX = new TextX();
1354
+ if (collapsed) {
1355
+ if (startOffset > 0) textX.push({
1356
+ t: TextXActionType.RETAIN,
1357
+ len: startOffset
1358
+ });
1359
+ textX.push({
1360
+ t: TextXActionType.INSERT,
1361
+ body: insertBody,
1362
+ len: insertBody.dataStream.length
1363
+ });
1364
+ } else textX.push(...BuildTextUtils.selection.delete([range], originBody, 0, insertBody));
1365
+ const textActions = JSONX.getInstance().editOp(textX.serialize(), getRichTextEditPath(documentDataModel, segmentId));
1366
+ return collapsed ? textActions : appendRemovedDrawingActions(textActions, documentDataModel, originBody, startOffset, endOffset, segmentId);
1367
+ }
1368
+ function appendRemovedDrawingActions(textActions, documentDataModel, body, startOffset, endOffset, segmentId) {
1369
+ return [textActions, ...BuildTextUtils.drawing.remove(documentDataModel.getSnapshot(), [{
1370
+ startOffset,
1371
+ endOffset,
1372
+ collapsed: false
1373
+ }], body, segmentId)].reduce((accumulator, action) => JSONX.compose(accumulator, action));
1374
+ }
464
1375
  /**
465
1376
  * The command to delete text, mainly used in BACKSPACE and DELETE when collapsed is true. ONLY handle collapsed range!!!
466
1377
  */
@@ -487,6 +1398,7 @@ const DeleteTextCommand = {
487
1398
  id: RichTextEditingMutation.id,
488
1399
  params: {
489
1400
  unitId,
1401
+ segmentId,
490
1402
  actions: [],
491
1403
  textRanges: [{
492
1404
  startOffset: start,
@@ -505,7 +1417,7 @@ const DeleteTextCommand = {
505
1417
  collapsed: false
506
1418
  }], body));
507
1419
  const path = getRichTextEditPath(docDataModel, segmentId);
508
- doMutation.params.actions = jsonX.editOp(textX.serialize(), path);
1420
+ doMutation.params.actions = appendRemovedDrawingActions(jsonX.editOp(textX.serialize(), path), docDataModel, body, start, end + 1, segmentId);
509
1421
  const result = commandService.syncExecuteCommand(doMutation.id, doMutation.params);
510
1422
  return Boolean(result);
511
1423
  }
@@ -785,14 +1697,243 @@ const SetDocumentNameCommand = {
785
1697
  };
786
1698
 
787
1699
  //#endregion
788
- //#region src/utils/sections.ts
789
- /** Returns document-level section breaks, excluding table-cell and modern-column sentinels. */
790
- function getTopLevelSectionBreaks(body) {
791
- const sectionBreakByIndex = new Map((body.sectionBreaks ?? []).map((sectionBreak) => [sectionBreak.startIndex, sectionBreak]));
792
- const result = [];
793
- let tableCellDepth = 0;
794
- let columnDepth = 0;
795
- for (let index = 0; index < body.dataStream.length; index++) {
1700
+ //#region src/services/permission/permission-point/document/comment.ts
1701
+ var DocumentCommentPermission = class {
1702
+ constructor(unitId) {
1703
+ this.unitId = unitId;
1704
+ _defineProperty(this, "type", UnitObject.Document);
1705
+ _defineProperty(this, "subType", UnitAction.Comment);
1706
+ _defineProperty(this, "status", PermissionStatus.INIT);
1707
+ _defineProperty(this, "id", void 0);
1708
+ _defineProperty(this, "value", true);
1709
+ this.id = `${this.type}.${this.subType}_${unitId}`;
1710
+ }
1711
+ };
1712
+
1713
+ //#endregion
1714
+ //#region src/services/permission/permission-point/document/copy.ts
1715
+ var DocumentCopyPermission = class {
1716
+ constructor(unitId) {
1717
+ this.unitId = unitId;
1718
+ _defineProperty(this, "type", UnitObject.Document);
1719
+ _defineProperty(this, "subType", UnitAction.Copy);
1720
+ _defineProperty(this, "status", PermissionStatus.INIT);
1721
+ _defineProperty(this, "id", void 0);
1722
+ _defineProperty(this, "value", true);
1723
+ this.id = `${this.type}.${this.subType}_${unitId}`;
1724
+ }
1725
+ };
1726
+
1727
+ //#endregion
1728
+ //#region src/services/permission/permission-point/document/editable.ts
1729
+ var DocumentEditablePermission = class {
1730
+ constructor(unitId) {
1731
+ this.unitId = unitId;
1732
+ _defineProperty(this, "type", UnitObject.Document);
1733
+ _defineProperty(this, "subType", UnitAction.Edit);
1734
+ _defineProperty(this, "status", PermissionStatus.INIT);
1735
+ _defineProperty(this, "id", void 0);
1736
+ _defineProperty(this, "value", true);
1737
+ this.id = `${this.type}.${this.subType}_${unitId}`;
1738
+ }
1739
+ };
1740
+
1741
+ //#endregion
1742
+ //#region src/services/permission/permission-point/document/export.ts
1743
+ var DocumentExportPermission = class {
1744
+ constructor(unitId) {
1745
+ this.unitId = unitId;
1746
+ _defineProperty(this, "type", UnitObject.Document);
1747
+ _defineProperty(this, "subType", UnitAction.Export);
1748
+ _defineProperty(this, "status", PermissionStatus.INIT);
1749
+ _defineProperty(this, "id", void 0);
1750
+ _defineProperty(this, "value", true);
1751
+ this.id = `${this.type}.${this.subType}_${unitId}`;
1752
+ }
1753
+ };
1754
+
1755
+ //#endregion
1756
+ //#region src/services/permission/permission-point/document/print.ts
1757
+ var DocumentPrintPermission = class {
1758
+ constructor(unitId) {
1759
+ this.unitId = unitId;
1760
+ _defineProperty(this, "type", UnitObject.Document);
1761
+ _defineProperty(this, "subType", UnitAction.Print);
1762
+ _defineProperty(this, "status", PermissionStatus.INIT);
1763
+ _defineProperty(this, "id", void 0);
1764
+ _defineProperty(this, "value", true);
1765
+ this.id = `${this.type}.${this.subType}_${unitId}`;
1766
+ }
1767
+ };
1768
+
1769
+ //#endregion
1770
+ //#region src/services/permission/permission-point/entity/edit.ts
1771
+ var DocumentEntityEditPermission = class {
1772
+ constructor(unitId, objectId) {
1773
+ this.unitId = unitId;
1774
+ this.objectId = objectId;
1775
+ _defineProperty(this, "type", UnitObject.DocumentEntity);
1776
+ _defineProperty(this, "subType", UnitAction.Edit);
1777
+ _defineProperty(this, "status", PermissionStatus.INIT);
1778
+ _defineProperty(this, "id", void 0);
1779
+ _defineProperty(this, "value", true);
1780
+ this.id = `${this.type}.${this.subType}_${unitId}_${objectId}`;
1781
+ }
1782
+ };
1783
+
1784
+ //#endregion
1785
+ //#region src/services/permission/permission-point/paragraph/edit.ts
1786
+ var DocumentParagraphEditPermission = class {
1787
+ constructor(unitId, objectId) {
1788
+ this.unitId = unitId;
1789
+ this.objectId = objectId;
1790
+ _defineProperty(this, "type", UnitObject.DocumentParagraph);
1791
+ _defineProperty(this, "subType", UnitAction.Edit);
1792
+ _defineProperty(this, "status", PermissionStatus.INIT);
1793
+ _defineProperty(this, "id", void 0);
1794
+ _defineProperty(this, "value", true);
1795
+ this.id = `${this.type}.${this.subType}_${unitId}_${objectId}`;
1796
+ }
1797
+ };
1798
+
1799
+ //#endregion
1800
+ //#region src/services/permission/permission-point/section/edit.ts
1801
+ var DocumentSectionEditPermission = class {
1802
+ constructor(unitId, objectId) {
1803
+ this.unitId = unitId;
1804
+ this.objectId = objectId;
1805
+ _defineProperty(this, "type", UnitObject.DocumentSection);
1806
+ _defineProperty(this, "subType", UnitAction.Edit);
1807
+ _defineProperty(this, "status", PermissionStatus.INIT);
1808
+ _defineProperty(this, "id", void 0);
1809
+ _defineProperty(this, "value", true);
1810
+ this.id = `${this.type}.${this.subType}_${unitId}_${objectId}`;
1811
+ }
1812
+ };
1813
+
1814
+ //#endregion
1815
+ //#region src/services/permission/document-permission.ts
1816
+ const DOCUMENT_UNIT_PERMISSION_ACTIONS = [
1817
+ UnitAction.Edit,
1818
+ UnitAction.Copy,
1819
+ UnitAction.Print,
1820
+ UnitAction.Export,
1821
+ UnitAction.Comment
1822
+ ];
1823
+ const DOCUMENT_PERMISSION_OBJECT_TYPES = /* @__PURE__ */ new Set([
1824
+ UnitObject.Document,
1825
+ UnitObject.DocumentSection,
1826
+ UnitObject.DocumentParagraph,
1827
+ UnitObject.DocumentEntity
1828
+ ]);
1829
+ const DOCUMENT_SECTION_PERMISSION_OBJECT_PREFIX = "section/";
1830
+ const DOCUMENT_PARAGRAPH_PERMISSION_OBJECT_PREFIX = "paragraph/";
1831
+ const DOCUMENT_ENTITY_PERMISSION_OBJECT_PREFIX = "entity/";
1832
+ function getDocumentSectionPermissionObjectId(segmentId, sectionId) {
1833
+ return `${DOCUMENT_SECTION_PERMISSION_OBJECT_PREFIX}${encodeURIComponent(segmentId)}/${encodeURIComponent(sectionId)}`;
1834
+ }
1835
+ function getDocumentParagraphPermissionObjectId(segmentId, paragraphId) {
1836
+ return `${DOCUMENT_PARAGRAPH_PERMISSION_OBJECT_PREFIX}${encodeURIComponent(segmentId)}/${encodeURIComponent(paragraphId)}`;
1837
+ }
1838
+ function getDocumentEntityPermissionObjectId(segmentId, entityType, entityId) {
1839
+ return `${DOCUMENT_ENTITY_PERMISSION_OBJECT_PREFIX}${encodeURIComponent(segmentId)}/${encodeURIComponent(entityType)}/${encodeURIComponent(entityId)}`;
1840
+ }
1841
+ function createDocumentPermissionPoint(unitId, objectId, action) {
1842
+ if (objectId === unitId) switch (action) {
1843
+ case UnitAction.Edit: return new DocumentEditablePermission(unitId);
1844
+ case UnitAction.Copy: return new DocumentCopyPermission(unitId);
1845
+ case UnitAction.Print: return new DocumentPrintPermission(unitId);
1846
+ case UnitAction.Export: return new DocumentExportPermission(unitId);
1847
+ case UnitAction.Comment: return new DocumentCommentPermission(unitId);
1848
+ default: throw new Error(`Unsupported Document permission action: ${action}`);
1849
+ }
1850
+ if (action !== UnitAction.Edit) throw new Error(`Document object permissions only support Edit: ${objectId}`);
1851
+ if (objectId.startsWith(DOCUMENT_SECTION_PERMISSION_OBJECT_PREFIX)) return new DocumentSectionEditPermission(unitId, objectId);
1852
+ if (objectId.startsWith(DOCUMENT_PARAGRAPH_PERMISSION_OBJECT_PREFIX)) return new DocumentParagraphEditPermission(unitId, objectId);
1853
+ if (objectId.startsWith(DOCUMENT_ENTITY_PERMISSION_OBJECT_PREFIX)) return new DocumentEntityEditPermission(unitId, objectId);
1854
+ throw new Error(`Unsupported Document permission object: ${objectId}`);
1855
+ }
1856
+ function getDocumentPermissionValue(permissionService, unitId, objectId, action) {
1857
+ var _permissionService$ge;
1858
+ return ((_permissionService$ge = permissionService.getPermissionPoint(createDocumentPermissionPoint(unitId, objectId, action).id)) === null || _permissionService$ge === void 0 ? void 0 : _permissionService$ge.value) ?? true;
1859
+ }
1860
+ function setDocumentPermissionValue(permissionService, unitId, objectId, action, value) {
1861
+ const point = createDocumentPermissionPoint(unitId, objectId, action);
1862
+ if (!permissionService.getPermissionPoint(point.id)) permissionService.addPermissionPoint(point);
1863
+ permissionService.updatePermissionPoint(point.id, value);
1864
+ }
1865
+ function clearDocumentPermissionValuesForUnit(permissionService, unitId) {
1866
+ permissionService.getAllPermissionPoint().forEach((point$, id) => {
1867
+ point$.subscribe((point) => {
1868
+ if (DOCUMENT_PERMISSION_OBJECT_TYPES.has(point.type) && "unitId" in point && point.unitId === unitId) permissionService.deletePermissionPoint(id);
1869
+ }).unsubscribe();
1870
+ });
1871
+ }
1872
+ function canEditDocumentTargets(permissionService, unitId, objectIds) {
1873
+ if (!getDocumentPermissionValue(permissionService, unitId, unitId, UnitAction.Edit)) return false;
1874
+ for (const objectId of objectIds) if (!getDocumentPermissionValue(permissionService, unitId, objectId, UnitAction.Edit)) return false;
1875
+ return true;
1876
+ }
1877
+
1878
+ //#endregion
1879
+ //#region src/commands/commands/set-document-permission.command.ts
1880
+ const SetDocumentPermissionCommand = {
1881
+ type: CommandType.COMMAND,
1882
+ id: "doc.command.set-permission",
1883
+ async handler(accessor, params) {
1884
+ if (!params || !DOCUMENT_UNIT_PERMISSION_ACTIONS.includes(params.action)) return false;
1885
+ if (params.objectId !== params.unitId && params.action !== UnitAction.Edit) return false;
1886
+ if (params.policy && (![
1887
+ "all",
1888
+ "owner",
1889
+ "members"
1890
+ ].includes(params.policy.edit) || params.policy.strategies.some((strategy) => !DOCUMENT_UNIT_PERMISSION_ACTIONS.includes(strategy.action) || params.objectId !== params.unitId && strategy.action !== UnitAction.Edit))) return false;
1891
+ const point = createDocumentPermissionPoint(params.unitId, params.objectId, params.action);
1892
+ const target = {
1893
+ unitId: params.unitId,
1894
+ objectId: params.objectId,
1895
+ objectType: point.type
1896
+ };
1897
+ const service = accessor.get(ObjectPermissionService);
1898
+ if (params.remove) await service.remove(target);
1899
+ else if (params.policy) await service.save(target, params.policy);
1900
+ else await service.setPoint(target, point, params.value);
1901
+ return true;
1902
+ }
1903
+ };
1904
+
1905
+ //#endregion
1906
+ //#region src/commands/commands/set-document-permissions.command.ts
1907
+ const SetDocumentPermissionsCommand = {
1908
+ type: CommandType.COMMAND,
1909
+ id: "doc.command.set-permissions",
1910
+ async handler(accessor, params) {
1911
+ if (!(params === null || params === void 0 ? void 0 : params.unitId) || !Array.isArray(params.changes) || !params.changes.length || params.changes.some((change) => !(change === null || change === void 0 ? void 0 : change.objectId) || change.objectId === params.unitId || change.policy !== null && (!change.policy || ![
1912
+ "all",
1913
+ "owner",
1914
+ "members"
1915
+ ].includes(change.policy.edit) || !Array.isArray(change.policy.collaborators) || !Array.isArray(change.policy.strategies) || change.policy.strategies.some((strategy) => strategy.action !== UnitAction.Edit)))) throw new Error("Invalid object permission batch.");
1916
+ return accessor.get(ObjectPermissionService).saveMany(params.changes.map((change) => {
1917
+ const point = createDocumentPermissionPoint(params.unitId, change.objectId, UnitAction.Edit);
1918
+ return {
1919
+ unitId: params.unitId,
1920
+ objectId: change.objectId,
1921
+ objectType: point.type,
1922
+ policy: change.policy
1923
+ };
1924
+ }));
1925
+ }
1926
+ };
1927
+
1928
+ //#endregion
1929
+ //#region src/utils/sections.ts
1930
+ /** Returns document-level section breaks, excluding table-cell and modern-column sentinels. */
1931
+ function getTopLevelSectionBreaks(body) {
1932
+ const sectionBreakByIndex = new Map((body.sectionBreaks ?? []).map((sectionBreak) => [sectionBreak.startIndex, sectionBreak]));
1933
+ const result = [];
1934
+ let tableCellDepth = 0;
1935
+ let columnDepth = 0;
1936
+ for (let index = 0; index < body.dataStream.length; index++) {
796
1937
  const token = body.dataStream[index];
797
1938
  if (token === DataStreamTreeTokenType.TABLE_CELL_START) tableCellDepth++;
798
1939
  else if (token === DataStreamTreeTokenType.TABLE_CELL_END) tableCellDepth = Math.max(0, tableCellDepth - 1);
@@ -1193,7 +2334,7 @@ const InsertDocumentColumnBreakCommand = {
1193
2334
  rangeId: `docx-break-${generateRandomId()}`,
1194
2335
  rangeType: CustomRangeType.CUSTOM,
1195
2336
  wholeEntity: true,
1196
- properties: { docxBreakType: DocxBreakType.COLUMN }
2337
+ properties: { breakType: DocxBreakType.COLUMN }
1197
2338
  }]
1198
2339
  });
1199
2340
  return executeSectionTextX(context.commandService, context.documentDataModel, textX, InsertDocumentColumnBreakCommand.id);
@@ -1272,6 +2413,56 @@ function executeSectionTextX(commandService, documentDataModel, textX, trigger)
1272
2413
  }));
1273
2414
  }
1274
2415
 
2416
+ //#endregion
2417
+ //#region src/services/permission/document-permission-rule.model.ts
2418
+ /**
2419
+ * Copyright 2023-present DreamNum Co., Ltd.
2420
+ *
2421
+ * Licensed under the Apache License, Version 2.0 (the "License");
2422
+ * you may not use this file except in compliance with the License.
2423
+ * You may obtain a copy of the License at
2424
+ *
2425
+ * http://www.apache.org/licenses/LICENSE-2.0
2426
+ *
2427
+ * Unless required by applicable law or agreed to in writing, software
2428
+ * distributed under the License is distributed on an "AS IS" BASIS,
2429
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2430
+ * See the License for the specific language governing permissions and
2431
+ * limitations under the License.
2432
+ */
2433
+ let DocumentPermissionRuleModel = class DocumentPermissionRuleModel extends ObjectPermissionRuleModel {
2434
+ constructor(resources) {
2435
+ super(resources, "DOC_OBJECT_PERMISSION_PLUGIN", UniverInstanceType.UNIVER_DOC, [
2436
+ UnitObject.DocumentSection,
2437
+ UnitObject.DocumentParagraph,
2438
+ UnitObject.DocumentEntity
2439
+ ]);
2440
+ }
2441
+ };
2442
+ DocumentPermissionRuleModel = __decorate([__decorateParam(0, IResourceManagerService)], DocumentPermissionRuleModel);
2443
+
2444
+ //#endregion
2445
+ //#region src/commands/mutations/set-document-permission-rule.mutation.ts
2446
+ const SetDocumentPermissionRuleMutation = {
2447
+ id: "doc.mutation.set-permission-rule",
2448
+ type: CommandType.MUTATION,
2449
+ handler: (accessor, params) => {
2450
+ if (!params || !accessor.get(IUniverInstanceService).getUnit(params.unitId, UniverInstanceType.UNIVER_DOC)) return false;
2451
+ return accessor.get(DocumentPermissionRuleModel).setRule(params.unitId, params.objectType, params.objectId, params.rule);
2452
+ }
2453
+ };
2454
+
2455
+ //#endregion
2456
+ //#region src/commands/mutations/set-document-permission-rules.mutation.ts
2457
+ const SetDocumentPermissionRulesMutation = {
2458
+ id: "doc.mutation.set-permission-rules",
2459
+ type: CommandType.MUTATION,
2460
+ handler: (accessor, params) => {
2461
+ if (!params || !accessor.get(IUniverInstanceService).getUnit(params.unitId, UniverInstanceType.UNIVER_DOC)) return false;
2462
+ return accessor.get(DocumentPermissionRuleModel).setRules(params.unitId, params.rules);
2463
+ }
2464
+ };
2465
+
1275
2466
  //#endregion
1276
2467
  //#region src/embed-host-anchor.ts
1277
2468
  const EMBED_DOCS_CUSTOM_BLOCK_DEFAULT_COMPONENT_KEY = "UniverEmbedDocsCustomBlock";
@@ -1433,7 +2624,7 @@ function composeActions(actions) {
1433
2624
  //#endregion
1434
2625
  //#region package.json
1435
2626
  var name = "@univerjs/docs";
1436
- var version = "1.0.0-insiders.20260809-70eefdd";
2627
+ var version = "1.0.0-rc.0";
1437
2628
 
1438
2629
  //#endregion
1439
2630
  //#region src/config/config.ts
@@ -1453,7 +2644,7 @@ var version = "1.0.0-insiders.20260809-70eefdd";
1453
2644
  * limitations under the License.
1454
2645
  */
1455
2646
  const DOCS_PLUGIN_CONFIG_KEY = "docs.config";
1456
- const configSymbol = Symbol(DOCS_PLUGIN_CONFIG_KEY);
2647
+ const configSymbol$1 = Symbol(DOCS_PLUGIN_CONFIG_KEY);
1457
2648
  const defaultPluginConfig = {};
1458
2649
 
1459
2650
  //#endregion
@@ -1509,6 +2700,473 @@ DocCustomRangeController = __decorate([
1509
2700
  __decorateParam(2, IUniverInstanceService)
1510
2701
  ], DocCustomRangeController);
1511
2702
 
2703
+ //#endregion
2704
+ //#region src/services/permission/document-permission-resolver.ts
2705
+ const HEADER_FOOTER_REFERENCE_KEYS = [
2706
+ "defaultHeaderId",
2707
+ "defaultFooterId",
2708
+ "firstPageHeaderId",
2709
+ "firstPageFooterId",
2710
+ "evenPageHeaderId",
2711
+ "evenPageFooterId"
2712
+ ];
2713
+ const documentPermissionResolverIndexCache = /* @__PURE__ */ new WeakMap();
2714
+ function getDocumentPermissionResolverIndex(documentDataModel) {
2715
+ var _getMutationRevision, _snapshot$body;
2716
+ const mutationRevision = ((_getMutationRevision = documentDataModel.getMutationRevision) === null || _getMutationRevision === void 0 ? void 0 : _getMutationRevision.call(documentDataModel)) ?? NaN;
2717
+ const cached = documentPermissionResolverIndexCache.get(documentDataModel);
2718
+ if ((cached === null || cached === void 0 ? void 0 : cached.mutationRevision) === mutationRevision) return cached;
2719
+ const snapshot = documentDataModel.getSnapshot();
2720
+ const topLevelSections = snapshot.body ? getTopLevelSectionBreaks(snapshot.body) : [];
2721
+ const drawingSegmentIds = /* @__PURE__ */ new Map();
2722
+ const addDrawingSegments = (body, segmentId) => {
2723
+ for (const block of (body === null || body === void 0 ? void 0 : body.customBlocks) ?? []) if (!drawingSegmentIds.has(block.blockId)) drawingSegmentIds.set(block.blockId, segmentId);
2724
+ };
2725
+ addDrawingSegments(snapshot.body, "");
2726
+ Object.entries(snapshot.headers ?? {}).forEach(([segmentId, header]) => addDrawingSegments(header.body, segmentId));
2727
+ Object.entries(snapshot.footers ?? {}).forEach(([segmentId, footer]) => addDrawingSegments(footer.body, segmentId));
2728
+ Object.entries(snapshot.notes ?? {}).forEach(([segmentId, footnote]) => addDrawingSegments(footnote.body, segmentId));
2729
+ const footnoteReferenceRanges = /* @__PURE__ */ new Map();
2730
+ for (const reference of ((_snapshot$body = snapshot.body) === null || _snapshot$body === void 0 ? void 0 : _snapshot$body.customRanges) ?? []) {
2731
+ var _reference$properties;
2732
+ if ((reference.rangeType === CustomRangeType.FOOTNOTE || reference.rangeType === CustomRangeType.ENDNOTE) && typeof ((_reference$properties = reference.properties) === null || _reference$properties === void 0 ? void 0 : _reference$properties.noteId) === "string") footnoteReferenceRanges.set(reference.properties.noteId, {
2733
+ startOffset: reference.startIndex,
2734
+ endOffset: reference.endIndex + 1
2735
+ });
2736
+ }
2737
+ const index = {
2738
+ drawingSegmentIds,
2739
+ footnoteReferenceRanges,
2740
+ mutationRevision,
2741
+ segments: /* @__PURE__ */ new Map(),
2742
+ topLevelSections
2743
+ };
2744
+ documentPermissionResolverIndexCache.set(documentDataModel, index);
2745
+ return index;
2746
+ }
2747
+ function getDocumentPermissionSegmentIndex(documentDataModel, segmentId, resolverIndex = getDocumentPermissionResolverIndex(documentDataModel)) {
2748
+ var _documentDataModel$ge, _snapshot$notes;
2749
+ const cached = resolverIndex.segments.get(segmentId);
2750
+ if (cached) return cached;
2751
+ const body = ((_documentDataModel$ge = documentDataModel.getSelfOrHeaderFooterModel(segmentId)) === null || _documentDataModel$ge === void 0 ? void 0 : _documentDataModel$ge.getBody()) ?? null;
2752
+ const snapshot = documentDataModel.getSnapshot();
2753
+ const note = (_snapshot$notes = snapshot.notes) === null || _snapshot$notes === void 0 ? void 0 : _snapshot$notes[segmentId];
2754
+ const drawings = (note ? note.drawings : snapshot.drawings) ?? {};
2755
+ const entities = body == null ? [] : [
2756
+ ...(body.tables ?? []).map((item) => ({
2757
+ type: "table",
2758
+ id: item.tableId,
2759
+ startOffset: item.startIndex,
2760
+ endOffset: item.endIndex
2761
+ })),
2762
+ ...(body.customBlocks ?? []).map((item) => ({
2763
+ type: drawings[item.blockId] ? "drawing" : "custom-block",
2764
+ id: item.blockId,
2765
+ startOffset: item.startIndex,
2766
+ endOffset: item.startIndex + 1
2767
+ })),
2768
+ ...(body.blockRanges ?? []).map((item) => ({
2769
+ type: "block-range",
2770
+ id: item.blockId,
2771
+ startOffset: item.startIndex,
2772
+ endOffset: item.endIndex + 1
2773
+ })),
2774
+ ...(body.customRanges ?? []).map((item) => ({
2775
+ type: "custom-range",
2776
+ id: item.rangeId,
2777
+ startOffset: item.startIndex,
2778
+ endOffset: item.endIndex + 1
2779
+ })),
2780
+ ...(body.columnGroups ?? []).map((item) => ({
2781
+ type: "column-group",
2782
+ id: item.columnGroupId,
2783
+ startOffset: item.startIndex,
2784
+ endOffset: item.endIndex + 1
2785
+ }))
2786
+ ];
2787
+ const entityRanges = new Map(entities.map((entity) => [`${entity.type}:${entity.id}`, {
2788
+ startOffset: entity.startOffset,
2789
+ endOffset: entity.endOffset
2790
+ }]));
2791
+ for (const block of (body === null || body === void 0 ? void 0 : body.customBlocks) ?? []) {
2792
+ const range = {
2793
+ startOffset: block.startIndex,
2794
+ endOffset: block.startIndex + 1
2795
+ };
2796
+ entityRanges.set(`custom-block:${block.blockId}`, range);
2797
+ entityRanges.set(`drawing:${block.blockId}`, range);
2798
+ }
2799
+ const segmentIndex = {
2800
+ body,
2801
+ entities,
2802
+ entityRanges,
2803
+ paragraphContentStartOffsets: body == null ? /* @__PURE__ */ new Map() : getParagraphContentStartOffsets(body),
2804
+ paragraphsById: new Map(((body === null || body === void 0 ? void 0 : body.paragraphs) ?? []).filter((paragraph) => paragraph.paragraphId != null).map((paragraph) => [paragraph.paragraphId, paragraph]))
2805
+ };
2806
+ resolverIndex.segments.set(segmentId, segmentIndex);
2807
+ return segmentIndex;
2808
+ }
2809
+ function getDocumentDrawingSegmentId(documentDataModel, drawingId) {
2810
+ return getDocumentPermissionResolverIndex(documentDataModel).drawingSegmentIds.get(drawingId) ?? "";
2811
+ }
2812
+ function getDocumentEditTargetObjectIds(documentDataModel, segmentId, range) {
2813
+ const resolverIndex = getDocumentPermissionResolverIndex(documentDataModel);
2814
+ const segmentIndex = getDocumentPermissionSegmentIndex(documentDataModel, segmentId, resolverIndex);
2815
+ const { body } = segmentIndex;
2816
+ if (!body) return [];
2817
+ return [
2818
+ ...getSectionPermissionObjectIds(documentDataModel, segmentId, range, resolverIndex),
2819
+ ...getParagraphPermissionObjectIds(body, segmentId, range, segmentIndex),
2820
+ ...getEntityPermissionObjectIds(segmentId, range, segmentIndex)
2821
+ ];
2822
+ }
2823
+ function getDocumentEditTargetObjectIdsFromActions(documentDataModel, segmentId, actions) {
2824
+ const result = /* @__PURE__ */ new Set();
2825
+ if (actions == null) return [];
2826
+ const cursor = JSON1.type.readCursor(actions);
2827
+ cursor.traverse(null, () => {
2828
+ const path = cursor.getPath();
2829
+ const edit = cursor.getComponent();
2830
+ const scopedSegment = [
2831
+ "headers",
2832
+ "footers",
2833
+ "notes"
2834
+ ].includes(String(path[0])) && typeof path[1] === "string" ? path[1] : segmentId;
2835
+ if (path[0] === "notes" && typeof path[1] === "string") getSectionPermissionObjectIds(documentDataModel, path[1], {
2836
+ startOffset: 0,
2837
+ endOffset: 0
2838
+ }).forEach((objectId) => result.add(objectId));
2839
+ if (path[0] === "noteSettings") getDocumentPermissionResolverIndex(documentDataModel).topLevelSections.forEach((section) => {
2840
+ result.add(getDocumentSectionPermissionObjectId("", section.sectionId));
2841
+ });
2842
+ if (path[0] === "body" && path[1] === "sectionBreaks" && typeof path[2] === "number" && (path[3] === "noteProperties" || path[3] === "suppressEndnotes")) {
2843
+ var _documentDataModel$ge2;
2844
+ const sectionId = (_documentDataModel$ge2 = documentDataModel.getBody()) === null || _documentDataModel$ge2 === void 0 || (_documentDataModel$ge2 = _documentDataModel$ge2.sectionBreaks) === null || _documentDataModel$ge2 === void 0 || (_documentDataModel$ge2 = _documentDataModel$ge2[path[2]]) === null || _documentDataModel$ge2 === void 0 ? void 0 : _documentDataModel$ge2.sectionId;
2845
+ if (sectionId) result.add(getDocumentSectionPermissionObjectId("", sectionId));
2846
+ }
2847
+ if (path[path.length - 1] !== "body" || (edit === null || edit === void 0 ? void 0 : edit.et) !== TextX.id || !Array.isArray(edit.e) || !edit.e.every(isTextXAction)) return;
2848
+ const textActions = edit.e;
2849
+ let offset = 0;
2850
+ const addRange = (startOffset, endOffset) => {
2851
+ getDocumentEditTargetObjectIds(documentDataModel, scopedSegment, {
2852
+ startOffset,
2853
+ endOffset
2854
+ }).forEach((objectId) => result.add(objectId));
2855
+ };
2856
+ textActions.forEach((action) => {
2857
+ if (action.t === TextXActionType.INSERT) {
2858
+ addRange(offset, offset);
2859
+ return;
2860
+ }
2861
+ if (action.t === TextXActionType.DELETE) {
2862
+ addRange(offset, offset + action.len);
2863
+ offset += action.len;
2864
+ return;
2865
+ }
2866
+ if (action.body !== void 0 || action.oldBody !== void 0 || action.coverType !== void 0) addRange(offset, offset + action.len);
2867
+ offset += action.len;
2868
+ });
2869
+ });
2870
+ getDrawingIdsFromActions(actions).forEach((drawingId) => {
2871
+ var _documentDataModel$ge3, _body$customBlocks;
2872
+ result.add(getDocumentEntityPermissionObjectId(segmentId, "drawing", drawingId));
2873
+ const body = (_documentDataModel$ge3 = documentDataModel.getSelfOrHeaderFooterModel(segmentId)) === null || _documentDataModel$ge3 === void 0 ? void 0 : _documentDataModel$ge3.getBody();
2874
+ const block = body === null || body === void 0 || (_body$customBlocks = body.customBlocks) === null || _body$customBlocks === void 0 ? void 0 : _body$customBlocks.find((item) => item.blockId === drawingId);
2875
+ if (block) getSectionPermissionObjectIds(documentDataModel, segmentId, {
2876
+ startOffset: block.startIndex,
2877
+ endOffset: block.startIndex + 1
2878
+ }).forEach((objectId) => result.add(objectId));
2879
+ });
2880
+ return [...result];
2881
+ }
2882
+ function getDrawingIdsFromActions(actions) {
2883
+ const result = /* @__PURE__ */ new Set();
2884
+ const visit = (value) => {
2885
+ if (Array.isArray(value)) {
2886
+ value.forEach((item, index) => {
2887
+ if (item === "drawings" && typeof value[index + 1] === "string") result.add(value[index + 1]);
2888
+ visit(item);
2889
+ });
2890
+ return;
2891
+ }
2892
+ if (!isRecord$2(value)) return;
2893
+ Object.values(value).forEach(visit);
2894
+ };
2895
+ visit(actions);
2896
+ return [...result];
2897
+ }
2898
+ function isTextXAction(value) {
2899
+ if (!isRecord$2(value) || typeof value.len !== "number") return false;
2900
+ if (value.t === TextXActionType.INSERT) return isRecord$2(value.body);
2901
+ return value.t === TextXActionType.RETAIN || value.t === TextXActionType.DELETE;
2902
+ }
2903
+ function isRecord$2(value) {
2904
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2905
+ }
2906
+ function getDocumentEntityParentPermissionObjectIds(documentDataModel, segmentId, entityType, entityId) {
2907
+ const resolverIndex = getDocumentPermissionResolverIndex(documentDataModel);
2908
+ const range = getDocumentPermissionSegmentIndex(documentDataModel, segmentId, resolverIndex).entityRanges.get(`${entityType}:${entityId}`) ?? null;
2909
+ return range ? getSectionPermissionObjectIds(documentDataModel, segmentId, range, resolverIndex) : [];
2910
+ }
2911
+ function getDocumentParagraphParentPermissionObjectIds(documentDataModel, segmentId, paragraphId) {
2912
+ const resolverIndex = getDocumentPermissionResolverIndex(documentDataModel);
2913
+ const segmentIndex = getDocumentPermissionSegmentIndex(documentDataModel, segmentId, resolverIndex);
2914
+ const { body } = segmentIndex;
2915
+ const paragraph = segmentIndex.paragraphsById.get(paragraphId);
2916
+ if (!body || !paragraph) return [];
2917
+ return getSectionPermissionObjectIds(documentDataModel, segmentId, {
2918
+ startOffset: segmentIndex.paragraphContentStartOffsets.get(paragraph.startIndex) ?? getParagraphContentStartOffset(body, paragraph),
2919
+ endOffset: paragraph.startIndex + 1
2920
+ }, resolverIndex);
2921
+ }
2922
+ function getDocumentSectionPermissionObjectIdsByIds(sectionIds) {
2923
+ return Array.from(sectionIds, (sectionId) => getDocumentSectionPermissionObjectId("", sectionId));
2924
+ }
2925
+ function getDocumentSectionIdsAtOffset(body, offset) {
2926
+ return getSectionsIntersectingRange(getTopLevelSectionBreaks(body), {
2927
+ startOffset: offset,
2928
+ endOffset: offset
2929
+ }).map((section) => section.sectionId);
2930
+ }
2931
+ function getSectionPermissionObjectIds(documentDataModel, segmentId, range, resolverIndex = getDocumentPermissionResolverIndex(documentDataModel)) {
2932
+ if (segmentId) {
2933
+ const referenceRange = resolverIndex.footnoteReferenceRanges.get(segmentId);
2934
+ if (referenceRange) return getDocumentEditTargetObjectIds(documentDataModel, "", referenceRange);
2935
+ return getHeaderFooterOwnerSectionIds(documentDataModel, segmentId, resolverIndex.topLevelSections).map((sectionId) => getDocumentSectionPermissionObjectId("", sectionId));
2936
+ }
2937
+ return getSectionsIntersectingRange(resolverIndex.topLevelSections, range).map((section) => getDocumentSectionPermissionObjectId("", section.sectionId));
2938
+ }
2939
+ function getParagraphPermissionObjectIds(body, segmentId, range, segmentIndex) {
2940
+ const startOffset = Math.min(range.startOffset, range.endOffset);
2941
+ const endOffset = Math.max(range.startOffset, range.endOffset);
2942
+ return (body.paragraphs ?? []).filter((paragraph) => intersectsRange(segmentIndex.paragraphContentStartOffsets.get(paragraph.startIndex) ?? getParagraphContentStartOffset(body, paragraph), paragraph.startIndex + 1, startOffset, endOffset)).flatMap((paragraph) => paragraph.paragraphId ? [getDocumentParagraphPermissionObjectId(segmentId, paragraph.paragraphId)] : []);
2943
+ }
2944
+ function getEntityPermissionObjectIds(segmentId, range, segmentIndex) {
2945
+ const startOffset = Math.min(range.startOffset, range.endOffset);
2946
+ const endOffset = Math.max(range.startOffset, range.endOffset);
2947
+ return segmentIndex.entities.filter((item) => intersectsRange(item.startOffset, item.endOffset, startOffset, endOffset)).map((item) => getDocumentEntityPermissionObjectId(segmentId, item.type, item.id));
2948
+ }
2949
+ function getSectionsIntersectingRange(sections, range) {
2950
+ const startOffset = Math.min(range.startOffset, range.endOffset);
2951
+ const endOffset = Math.max(range.startOffset, range.endOffset);
2952
+ return sections.filter((section, index) => intersectsRange(index === 0 ? 0 : sections[index - 1].startIndex + 1, section.startIndex + 1, startOffset, endOffset));
2953
+ }
2954
+ function intersectsRange(targetStart, targetEnd, rangeStart, rangeEnd) {
2955
+ if (rangeStart === rangeEnd) return rangeStart >= targetStart && rangeStart < targetEnd;
2956
+ return rangeStart < targetEnd && rangeEnd > targetStart;
2957
+ }
2958
+ function getHeaderFooterOwnerSectionIds(documentDataModel, segmentId, sections = getDocumentPermissionResolverIndex(documentDataModel).topLevelSections) {
2959
+ const snapshot = documentDataModel.getSnapshot();
2960
+ const result = /* @__PURE__ */ new Set();
2961
+ sections.forEach((section, sectionIndex) => {
2962
+ if (HEADER_FOOTER_REFERENCE_KEYS.some((key) => resolveSectionHeaderFooterReference(snapshot.documentStyle, sections, sectionIndex, key).segmentId === segmentId)) result.add(section.sectionId);
2963
+ });
2964
+ return [...result];
2965
+ }
2966
+
2967
+ //#endregion
2968
+ //#region src/controllers/doc-permission.controller.ts
2969
+ const NON_EDIT_DOCUMENT_COMMAND_IDS = /* @__PURE__ */ new Set([
2970
+ SetDocumentPermissionCommand.id,
2971
+ SetDocumentPermissionsCommand.id,
2972
+ "doc.command.open-header-footer-panel",
2973
+ "doc.command.close-header-footer",
2974
+ "doc.command.select-all",
2975
+ "doc.command.set-zoom-ratio"
2976
+ ]);
2977
+ const DERIVED_DOCUMENT_MUTATION_IDS = /* @__PURE__ */ new Set(["docs-formula.mutation.set-last-values"]);
2978
+ let DocPermissionController = class DocPermissionController extends Disposable {
2979
+ constructor(_injector, _commandService, _permissionService, _univerInstanceService) {
2980
+ super();
2981
+ this._injector = _injector;
2982
+ this._commandService = _commandService;
2983
+ this._permissionService = _permissionService;
2984
+ this._univerInstanceService = _univerInstanceService;
2985
+ this._univerInstanceService.getAllUnitsForType(UniverInstanceType.UNIVER_DOC).forEach((unit) => this._registerUnitPermissionPoints(unit.getUnitId()));
2986
+ this.disposeWithMe(this._univerInstanceService.getTypeOfUnitAdded$(UniverInstanceType.UNIVER_DOC).subscribe(({ unit }) => this._registerUnitPermissionPoints(unit.getUnitId())));
2987
+ this.disposeWithMe(this._commandService.beforeCommandExecuted((commandInfo, options) => {
2988
+ this._check(commandInfo, options);
2989
+ }));
2990
+ this.disposeWithMe(this._univerInstanceService.getTypeOfUnitDisposed$(UniverInstanceType.UNIVER_DOC).subscribe((unit) => {
2991
+ this._injector.get(ObjectPermissionService).clearUnit(unit.getUnitId());
2992
+ clearDocumentPermissionValuesForUnit(this._permissionService, unit.getUnitId());
2993
+ }));
2994
+ }
2995
+ _registerUnitPermissionPoints(unitId) {
2996
+ DOCUMENT_UNIT_PERMISSION_ACTIONS.forEach((action) => {
2997
+ const point = createDocumentPermissionPoint(unitId, unitId, action);
2998
+ if (!this._permissionService.getPermissionPoint(point.id)) this._permissionService.addPermissionPoint(point);
2999
+ });
3000
+ this._injector.get(ObjectPermissionService).initializeUnit({
3001
+ unitId,
3002
+ objectId: unitId,
3003
+ objectType: UnitObject.Document
3004
+ });
3005
+ }
3006
+ _check(commandInfo, options) {
3007
+ var _this$_univerInstance;
3008
+ if ((options === null || options === void 0 ? void 0 : options.fromCollab) || (options === null || options === void 0 ? void 0 : options.fromChangeset)) return;
3009
+ const unitAction = getDocumentUnitAction(commandInfo.id);
3010
+ const unitId = getUnitId(commandInfo, options) ?? (unitAction ? (_this$_univerInstance = this._univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_DOC)) === null || _this$_univerInstance === void 0 ? void 0 : _this$_univerInstance.getUnitId() : void 0);
3011
+ if (!unitId || this._univerInstanceService.getUnitType(unitId) !== UniverInstanceType.UNIVER_DOC) return;
3012
+ if (unitAction) {
3013
+ if (!getDocumentPermissionValue(this._permissionService, unitId, unitId, unitAction)) {
3014
+ this._injector.get(ErrorService).emitPermissionDenied(unitId);
3015
+ throw new CustomCommandExecutionError(`Document ${UnitAction[unitAction]} permission denied.`);
3016
+ }
3017
+ return;
3018
+ }
3019
+ const documentDataModel = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC);
3020
+ if (!documentDataModel) return;
3021
+ const targetObjectIds = this._resolveTargetObjectIds(documentDataModel, commandInfo);
3022
+ if (targetObjectIds === null) return;
3023
+ if (!canEditDocumentTargets(this._permissionService, unitId, targetObjectIds)) {
3024
+ this._injector.get(ErrorService).emitPermissionDenied(unitId, targetObjectIds);
3025
+ throw new CustomCommandExecutionError("Document edit permission denied.");
3026
+ }
3027
+ }
3028
+ _resolveTargetObjectIds(documentDataModel, commandInfo) {
3029
+ if (commandInfo.id === RichTextEditingMutation.id) {
3030
+ const params = commandInfo.params;
3031
+ return getDocumentEditTargetObjectIdsFromActions(documentDataModel, params.segmentId ?? "", params.actions);
3032
+ }
3033
+ if (commandInfo.id === DocsRenameMutation.id) return [];
3034
+ if (isDerivedDocumentMutation(commandInfo)) return null;
3035
+ const knownTargets = resolveTextTargets(documentDataModel, commandInfo) ?? resolveSectionTargets(documentDataModel, commandInfo);
3036
+ if (knownTargets) return knownTargets;
3037
+ const entityTargets = resolveEntityTargets(documentDataModel, commandInfo);
3038
+ if (entityTargets.length) return entityTargets;
3039
+ return isDocumentEditCommand(commandInfo.id) || isDocumentDataMutation(commandInfo) ? [] : null;
3040
+ }
3041
+ };
3042
+ DocPermissionController = __decorate([
3043
+ __decorateParam(0, Inject(Injector)),
3044
+ __decorateParam(1, ICommandService),
3045
+ __decorateParam(2, IPermissionService),
3046
+ __decorateParam(3, IUniverInstanceService)
3047
+ ], DocPermissionController);
3048
+ function isDocumentEditCommand(id) {
3049
+ if (NON_EDIT_DOCUMENT_COMMAND_IDS.has(id)) return false;
3050
+ return id.startsWith("doc.command.") || id.startsWith("docs.command.") || id.startsWith("doc.command-") || id.startsWith("doc.table.");
3051
+ }
3052
+ function isDocumentDataMutation(commandInfo) {
3053
+ const { id } = commandInfo;
3054
+ if (isDerivedDocumentMutation(commandInfo)) return false;
3055
+ return id.startsWith("doc.mutation.") || id.startsWith("docs.mutation.") || /^docs-[^.]+\.mutation\./.test(id);
3056
+ }
3057
+ function isDerivedDocumentMutation(commandInfo) {
3058
+ const params = isRecord$1(commandInfo.params) ? commandInfo.params : void 0;
3059
+ return DERIVED_DOCUMENT_MUTATION_IDS.has(commandInfo.id) || commandInfo.id === "doc.mutation.update-shape-data" && !!(params === null || params === void 0 ? void 0 : params.formulaLastValueGuard);
3060
+ }
3061
+ function resolveEntityTargets(documentDataModel, commandInfo) {
3062
+ const params = commandInfo.params;
3063
+ if (!isRecord$1(params)) return [];
3064
+ const result = /* @__PURE__ */ new Set();
3065
+ const add = (segmentId, entityType, entityId) => {
3066
+ result.add(getDocumentEntityPermissionObjectId(segmentId, entityType, entityId));
3067
+ getDocumentEntityParentPermissionObjectIds(documentDataModel, segmentId, entityType, entityId).forEach((objectId) => result.add(objectId));
3068
+ };
3069
+ collectEntityReferences(params).forEach(({ entityType, entityId }) => {
3070
+ const explicitSegmentId = params.segmentId;
3071
+ const segmentId = typeof explicitSegmentId === "string" ? explicitSegmentId : entityType === "drawing" || entityType === "custom-block" ? getDocumentDrawingSegmentId(documentDataModel, entityId) : "";
3072
+ add(segmentId, entityType, entityId);
3073
+ });
3074
+ return [...result];
3075
+ }
3076
+ function collectEntityReferences(value) {
3077
+ const result = /* @__PURE__ */ new Map();
3078
+ const typeByKey = {
3079
+ blockId: ["custom-block"],
3080
+ drawingId: ["drawing"],
3081
+ shapeId: ["drawing", "custom-block"],
3082
+ tableId: ["table"],
3083
+ rangeId: ["custom-range"],
3084
+ columnGroupId: ["column-group"]
3085
+ };
3086
+ const visit = (candidate, key = "") => {
3087
+ if (typeof candidate === "string") {
3088
+ var _typeByKey$key;
3089
+ (_typeByKey$key = typeByKey[key]) === null || _typeByKey$key === void 0 || _typeByKey$key.forEach((entityType) => {
3090
+ result.set(`${entityType}\u001F${candidate}`, {
3091
+ entityType,
3092
+ entityId: candidate
3093
+ });
3094
+ });
3095
+ return;
3096
+ }
3097
+ if (Array.isArray(candidate)) {
3098
+ const singularKey = key.endsWith("Ids") ? `${key.slice(0, -3)}Id` : key;
3099
+ candidate.forEach((item) => visit(item, singularKey));
3100
+ return;
3101
+ }
3102
+ if (!isRecord$1(candidate)) return;
3103
+ Object.entries(candidate).forEach(([childKey, child]) => visit(child, childKey));
3104
+ };
3105
+ visit(value);
3106
+ return [...result.values()];
3107
+ }
3108
+ function getUnitId(commandInfo, options) {
3109
+ const params = isRecord$1(commandInfo.params) ? commandInfo.params : void 0;
3110
+ return typeof (params === null || params === void 0 ? void 0 : params.unitId) === "string" ? params.unitId : typeof (options === null || options === void 0 ? void 0 : options.unitId) === "string" ? options.unitId : void 0;
3111
+ }
3112
+ function getDocumentUnitAction(commandId) {
3113
+ if (commandId === "doc.command.copy-current-paragraph" || commandId === "docs-table.command.copy-selection") return UnitAction.Copy;
3114
+ if (commandId === "docs.operation.print") return UnitAction.Print;
3115
+ if (commandId === "docs-exchange-client.operation.export-doc") return UnitAction.Export;
3116
+ if (commandId === "docs.operation.start-add-comment" || commandId === "docs.operation.add-drawing-comment" || commandId.startsWith("docs.command.") && commandId.includes("comment") || commandId.startsWith("thread-comment.command.") || commandId.startsWith("thread-comment.mutation.")) return UnitAction.Comment;
3117
+ }
3118
+ function resolveTextTargets(documentDataModel, commandInfo) {
3119
+ const { id } = commandInfo;
3120
+ if (id === InsertTextCommand.id || id === UpdateTextCommand.id) {
3121
+ const params = commandInfo.params;
3122
+ return getDocumentEditTargetObjectIds(documentDataModel, params.segmentId ?? "", params.range);
3123
+ }
3124
+ if (id === DeleteTextCommand.id) {
3125
+ const params = commandInfo.params;
3126
+ const isDeleteLeft = params.direction === DeleteDirection.LEFT;
3127
+ return getDocumentEditTargetObjectIds(documentDataModel, params.segmentId ?? "", {
3128
+ startOffset: isDeleteLeft ? params.range.startOffset - (params.len ?? 1) : params.range.startOffset,
3129
+ endOffset: isDeleteLeft ? params.range.startOffset : params.range.startOffset + (params.len ?? 1)
3130
+ });
3131
+ }
3132
+ if (id === UpdateDocumentParagraphStyleCommand.id) {
3133
+ const params = commandInfo.params;
3134
+ return getDocumentEditTargetObjectIds(documentDataModel, params.segmentId ?? "", params);
3135
+ }
3136
+ return null;
3137
+ }
3138
+ function resolveSectionTargets(documentDataModel, commandInfo) {
3139
+ const { id } = commandInfo;
3140
+ if (id === UpdateDocumentSectionCommand.id) {
3141
+ const params = commandInfo.params;
3142
+ return getDocumentSectionPermissionObjectIdsByIds(params.updates.map((update) => update.sectionId));
3143
+ }
3144
+ if (id === InsertDocumentSectionBreakCommand.id || id === InsertDocumentColumnBreakCommand.id) {
3145
+ const params = commandInfo.params;
3146
+ const body = documentDataModel.getBody();
3147
+ return body ? getDocumentSectionPermissionObjectIdsByIds(getDocumentSectionIdsAtOffset(body, params.offset)) : [];
3148
+ }
3149
+ if (id === DeleteDocumentSectionBreakCommand.id) {
3150
+ const params = commandInfo.params;
3151
+ const body = documentDataModel.getBody();
3152
+ const sections = body ? getTopLevelSectionBreaks(body) : [];
3153
+ const index = sections.findIndex((section) => section.sectionId === params.sectionId);
3154
+ return getDocumentSectionPermissionObjectIdsByIds([params.sectionId, ...index >= 0 && index + 1 < sections.length ? [sections[index + 1].sectionId] : []]);
3155
+ }
3156
+ if (id === SetSectionHeaderFooterLinkCommand.id) {
3157
+ const params = commandInfo.params;
3158
+ return [getDocumentSectionPermissionObjectId("", params.sectionId)];
3159
+ }
3160
+ if (id === CreateHeaderFooterCommand.id) {
3161
+ const params = commandInfo.params;
3162
+ return params.sectionId ? [getDocumentSectionPermissionObjectId("", params.sectionId)] : [];
3163
+ }
3164
+ return null;
3165
+ }
3166
+ function isRecord$1(value) {
3167
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3168
+ }
3169
+
1512
3170
  //#endregion
1513
3171
  //#region src/services/doc-block-move-validator.service.ts
1514
3172
  var DocBlockMoveValidatorService = class extends Disposable {
@@ -1604,6 +3262,9 @@ let DocStateChangeManagerService = class DocStateChangeManagerService extends Rx
1604
3262
  this._historyStateCache.set(unitId, cache.history);
1605
3263
  this._changeStateCache.set(unitId, cache.collaboration);
1606
3264
  }
3265
+ clearHistory(unitId) {
3266
+ this._historyStateCache.delete(unitId);
3267
+ }
1607
3268
  _setChangeState(changeState) {
1608
3269
  this._cacheChangeState(changeState, "history");
1609
3270
  this._cacheChangeState(changeState, "collaboration");
@@ -1636,6 +3297,8 @@ let DocStateChangeManagerService = class DocStateChangeManagerService extends Rx
1636
3297
  if (type === "history" && (trigger === RedoCommandId || trigger === UndoCommandId)) return;
1637
3298
  const stateCache = type === "history" ? this._historyStateCache : this._changeStateCache;
1638
3299
  const cb = type === "history" ? this._pushHistory.bind(this) : this._emitChangeState.bind(this);
3300
+ const pendingStates = stateCache.get(unitId);
3301
+ if ((pendingStates === null || pendingStates === void 0 ? void 0 : pendingStates.length) && pendingStates[pendingStates.length - 1].segmentId !== changeState.segmentId) cb(unitId);
1639
3302
  if (stateCache.has(unitId)) {
1640
3303
  const cacheStates = stateCache.get(unitId);
1641
3304
  cacheStates === null || cacheStates === void 0 || cacheStates.push(changeState);
@@ -1664,12 +3327,18 @@ let DocStateChangeManagerService = class DocStateChangeManagerService extends Rx
1664
3327
  const redoParams = {
1665
3328
  unitId,
1666
3329
  actions: cacheStates.reduce((acc, cur) => JSONX.compose(acc, cur.redoState.actions), null),
1667
- textRanges: lastState.redoState.textRanges
3330
+ textRanges: lastState.redoState.textRanges,
3331
+ segmentId: lastState.segmentId,
3332
+ options: lastState.redoState.options,
3333
+ isEditing: lastState.redoState.isEditing
1668
3334
  };
1669
3335
  const undoParams = {
1670
3336
  unitId,
1671
3337
  actions: cacheStates.reverse().reduce((acc, cur) => JSONX.compose(acc, cur.undoState.actions), null),
1672
- textRanges: firstState.undoState.textRanges
3338
+ textRanges: firstState.undoState.textRanges,
3339
+ segmentId: firstState.segmentId,
3340
+ options: firstState.undoState.options,
3341
+ isEditing: firstState.undoState.isEditing
1673
3342
  };
1674
3343
  undoRedoService.pushUndoRedo({
1675
3344
  unitID: unitId,
@@ -1698,12 +3367,18 @@ let DocStateChangeManagerService = class DocStateChangeManagerService extends Rx
1698
3367
  redoState: {
1699
3368
  unitId,
1700
3369
  actions: cacheStates.reduce((acc, cur) => JSONX.compose(acc, cur.redoState.actions), null),
1701
- textRanges: lastState.redoState.textRanges
3370
+ textRanges: lastState.redoState.textRanges,
3371
+ segmentId: lastState.segmentId,
3372
+ options: lastState.redoState.options,
3373
+ isEditing: lastState.redoState.isEditing
1702
3374
  },
1703
3375
  undoState: {
1704
3376
  unitId,
1705
3377
  actions: cacheStates.reverse().reduce((acc, cur) => JSONX.compose(acc, cur.undoState.actions), null),
1706
- textRanges: firstState.undoState.textRanges
3378
+ textRanges: firstState.undoState.textRanges,
3379
+ segmentId: firstState.segmentId,
3380
+ options: firstState.undoState.options,
3381
+ isEditing: firstState.undoState.isEditing
1707
3382
  },
1708
3383
  segmentId,
1709
3384
  noHistory,
@@ -1806,6 +3481,10 @@ let UniverDocsPlugin = class UniverDocsPlugin extends Plugin {
1806
3481
  this._configService.setConfig(DOCS_PLUGIN_CONFIG_KEY, rest);
1807
3482
  }
1808
3483
  onStarting() {
3484
+ this._injector.add([DocumentPermissionRuleModel]);
3485
+ this.disposeWithMe(this._injector.get(ICommandService).registerCommand(SetDocumentPermissionRuleMutation));
3486
+ this.disposeWithMe(this._injector.get(ICommandService).registerCommand(SetDocumentPermissionRulesMutation));
3487
+ this.disposeWithMe(this._injector.get(ObjectPermissionService).registerRuleModel(UnitObject.Document, this._injector.get(DocumentPermissionRuleModel), SetDocumentPermissionRuleMutation.id, SetDocumentPermissionRulesMutation.id));
1809
3488
  this._initializeDependencies();
1810
3489
  this._initializeCommands();
1811
3490
  }
@@ -1815,6 +3494,8 @@ let UniverDocsPlugin = class UniverDocsPlugin extends Plugin {
1815
3494
  DeleteTextCommand,
1816
3495
  UpdateTextCommand,
1817
3496
  CreateHeaderFooterCommand,
3497
+ SetDocumentPermissionCommand,
3498
+ SetDocumentPermissionsCommand,
1818
3499
  SetDocumentDefaultParagraphStyleCommand,
1819
3500
  SetDocumentNameCommand,
1820
3501
  SetSectionHeaderFooterLinkCommand,
@@ -1837,13 +3518,16 @@ let UniverDocsPlugin = class UniverDocsPlugin extends Plugin {
1837
3518
  [DocStateChangeManagerService],
1838
3519
  [DocBlockMoveValidatorService],
1839
3520
  [DocContentInsertService],
3521
+ [DocLayoutExecutorService],
1840
3522
  [DocTextResolverService],
1841
- [DocCustomRangeController]
3523
+ [DocCustomRangeController],
3524
+ [DocPermissionController]
1842
3525
  ].forEach((d) => this._injector.add(d));
1843
3526
  }
1844
3527
  onReady() {
1845
3528
  this._injector.get(DocStateChangeManagerService);
1846
3529
  this._injector.get(DocCustomRangeController);
3530
+ this._injector.get(DocPermissionController);
1847
3531
  }
1848
3532
  };
1849
3533
  _defineProperty(UniverDocsPlugin, "pluginName", "DOCS_PLUGIN");
@@ -1851,6 +3535,655 @@ _defineProperty(UniverDocsPlugin, "packageName", name);
1851
3535
  _defineProperty(UniverDocsPlugin, "version", version);
1852
3536
  UniverDocsPlugin = __decorate([__decorateParam(1, Inject(Injector)), __decorateParam(2, IConfigService)], UniverDocsPlugin);
1853
3537
 
3538
+ //#endregion
3539
+ //#region src/layout-worker/config/config.ts
3540
+ /**
3541
+ * Copyright 2023-present DreamNum Co., Ltd.
3542
+ *
3543
+ * Licensed under the Apache License, Version 2.0 (the "License");
3544
+ * you may not use this file except in compliance with the License.
3545
+ * You may obtain a copy of the License at
3546
+ *
3547
+ * http://www.apache.org/licenses/LICENSE-2.0
3548
+ *
3549
+ * Unless required by applicable law or agreed to in writing, software
3550
+ * distributed under the License is distributed on an "AS IS" BASIS,
3551
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3552
+ * See the License for the specific language governing permissions and
3553
+ * limitations under the License.
3554
+ */
3555
+ const DOCS_LAYOUT_WORKER_PLUGIN_CONFIG_KEY = "docs-layout-worker.config";
3556
+ const configSymbol = Symbol(DOCS_LAYOUT_WORKER_PLUGIN_CONFIG_KEY);
3557
+ const DEFAULT_DOCS_LAYOUT_WORKER_REQUEST_TIMEOUT_MS = 15e3;
3558
+ const defaultPluginDocsLayoutWorkerConfig = { requestTimeoutMs: DEFAULT_DOCS_LAYOUT_WORKER_REQUEST_TIMEOUT_MS };
3559
+
3560
+ //#endregion
3561
+ //#region src/layout-worker/document-font-metrics.ts
3562
+ /**
3563
+ * Copyright 2023-present DreamNum Co., Ltd.
3564
+ *
3565
+ * Licensed under the Apache License, Version 2.0 (the "License");
3566
+ * you may not use this file except in compliance with the License.
3567
+ * You may obtain a copy of the License at
3568
+ *
3569
+ * http://www.apache.org/licenses/LICENSE-2.0
3570
+ *
3571
+ * Unless required by applicable law or agreed to in writing, software
3572
+ * distributed under the License is distributed on an "AS IS" BASIS,
3573
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3574
+ * See the License for the specific language governing permissions and
3575
+ * limitations under the License.
3576
+ */
3577
+ /** Collect once from the snapshot, then only from mutation payloads (including JSON1 font replacements). */
3578
+ function collectDocumentFontFamilies(value, families) {
3579
+ if (value == null || typeof value !== "object") return;
3580
+ if (Array.isArray(value)) {
3581
+ for (let index = 0; index < value.length; index++) {
3582
+ const item = value[index];
3583
+ if (value[index - 1] === "ff" && item != null && typeof item === "object" && typeof item.i === "string") families.add(item.i);
3584
+ collectDocumentFontFamilies(item, families);
3585
+ }
3586
+ return;
3587
+ }
3588
+ for (const [key, item] of Object.entries(value)) if (key === "ff" && typeof item === "string") families.add(item);
3589
+ else collectDocumentFontFamilies(item, families);
3590
+ }
3591
+ function measureDocumentFontFamilies(families) {
3592
+ FontCache.getNormalLineHeight(getFontStyleString());
3593
+ for (const ff of ["", ...families]) for (const bl of [BooleanNumber.FALSE, BooleanNumber.TRUE]) for (const it of [BooleanNumber.FALSE, BooleanNumber.TRUE]) FontCache.getNormalLineHeight(getFontStyleString({
3594
+ ff,
3595
+ bl,
3596
+ it
3597
+ }));
3598
+ return FontCache.getNormalLineHeightCache();
3599
+ }
3600
+
3601
+ //#endregion
3602
+ //#region src/layout-worker/performance-tracker.ts
3603
+ const PERFORMANCE_SAMPLE_LIMIT = 2048;
3604
+ function isRecord(value) {
3605
+ return typeof value === "object" && value != null;
3606
+ }
3607
+ function getRequest(message) {
3608
+ if (!isRecord(message) || typeof message.method !== "string" || !Array.isArray(message.args)) return null;
3609
+ const request = message.args[0];
3610
+ return isRecord(request) ? {
3611
+ method: message.method,
3612
+ request
3613
+ } : null;
3614
+ }
3615
+ function getPublicationResult(message) {
3616
+ if (!isRecord(message) || !isRecord(message.data)) return null;
3617
+ const result = message.data;
3618
+ if (isRecord(result.step)) return result.step;
3619
+ return result;
3620
+ }
3621
+ function createTransferMetrics() {
3622
+ return {
3623
+ mutationTransferMs: [],
3624
+ patchTransferMs: [],
3625
+ snapshotTransferMs: []
3626
+ };
3627
+ }
3628
+ var DocsLayoutWorkerPerformanceTracker = class {
3629
+ constructor() {
3630
+ _defineProperty(this, "_metrics", /* @__PURE__ */ new Map());
3631
+ }
3632
+ recordRequest(message, durationMs) {
3633
+ const requestInfo = getRequest(message);
3634
+ const unitId = requestInfo === null || requestInfo === void 0 ? void 0 : requestInfo.request.unitId;
3635
+ if (requestInfo == null || typeof unitId !== "string") return;
3636
+ if (requestInfo.method === "createSession") {
3637
+ this._append(unitId, "snapshotTransferMs", durationMs);
3638
+ return;
3639
+ }
3640
+ if (requestInfo.method === "startLayout" && Array.isArray(requestInfo.request.mutations) && requestInfo.request.mutations.length > 0) this._append(unitId, "mutationTransferMs", durationMs);
3641
+ }
3642
+ recordResponse(message, durationMs) {
3643
+ const result = getPublicationResult(message);
3644
+ if (result == null || typeof result.unitId !== "string" || result.publication == null) return;
3645
+ this._append(result.unitId, "patchTransferMs", durationMs);
3646
+ }
3647
+ getMetrics(unitId) {
3648
+ const metrics = this._metrics.get(unitId) ?? createTransferMetrics();
3649
+ return {
3650
+ mutationTransferMs: [...metrics.mutationTransferMs],
3651
+ patchTransferMs: [...metrics.patchTransferMs],
3652
+ snapshotTransferMs: [...metrics.snapshotTransferMs]
3653
+ };
3654
+ }
3655
+ reset(unitId) {
3656
+ this._metrics.delete(unitId);
3657
+ }
3658
+ resetAll() {
3659
+ this._metrics.clear();
3660
+ }
3661
+ _append(unitId, metric, durationMs) {
3662
+ const metrics = this._metrics.get(unitId) ?? createTransferMetrics();
3663
+ const samples = metrics[metric];
3664
+ samples.push(durationMs);
3665
+ if (samples.length > PERFORMANCE_SAMPLE_LIMIT) samples.splice(0, samples.length - PERFORMANCE_SAMPLE_LIMIT);
3666
+ this._metrics.set(unitId, metrics);
3667
+ }
3668
+ };
3669
+
3670
+ //#endregion
3671
+ //#region src/layout-worker/protocol.ts
3672
+ const DOCS_LAYOUT_WORKER_CHANNEL = "univer.docs-layout-worker";
3673
+
3674
+ //#endregion
3675
+ //#region src/layout-worker/worker.ts
3676
+ const FONT_PROBE_CONTENT = "Univer 文档 Worker 0123456789";
3677
+ const FONT_PROBE_STYLE = "16px Arial";
3678
+ const WORKER_LAYOUT_INTERRUPT_INTERVAL = 8;
3679
+ function yieldForWorkerMessage() {
3680
+ return new Promise((resolve) => {
3681
+ requestImmediateMacroTask(() => resolve());
3682
+ });
3683
+ }
3684
+ function createDocsLayoutWorkerMessageProtocol$1(performanceTracker) {
3685
+ return {
3686
+ send(message) {
3687
+ const startedAt = Tools.now();
3688
+ postMessage(message);
3689
+ performanceTracker.recordResponse(message, Tools.now() - startedAt);
3690
+ },
3691
+ onMessage: new Observable((subscriber) => {
3692
+ const handler = (event) => subscriber.next(event.data);
3693
+ addEventListener("message", handler);
3694
+ return () => removeEventListener("message", handler);
3695
+ }).pipe(shareReplay({
3696
+ bufferSize: 1,
3697
+ refCount: true
3698
+ }))
3699
+ };
3700
+ }
3701
+ function getCustomRangePresentationKey(entry) {
3702
+ return JSON.stringify([entry.segmentId, entry.rangeId]);
3703
+ }
3704
+ function applyRecordPatch(target, patch) {
3705
+ if (patch == null) return;
3706
+ for (const key of patch.removals) delete target[key];
3707
+ Object.assign(target, patch.upserts);
3708
+ }
3709
+ var DocsLayoutWorkerRuntime = class {
3710
+ constructor(_performanceTracker = new DocsLayoutWorkerPerformanceTracker()) {
3711
+ this._performanceTracker = _performanceTracker;
3712
+ _defineProperty(this, "_sessions", /* @__PURE__ */ new Map());
3713
+ _defineProperty(this, "type", "worker");
3714
+ }
3715
+ initialize() {
3716
+ return Promise.resolve();
3717
+ }
3718
+ async getCapabilities() {
3719
+ const metrics = FontCache.getMeasureText(FONT_PROBE_CONTENT, FONT_PROBE_STYLE);
3720
+ return {
3721
+ protocolVersion: 5,
3722
+ executor: "worker",
3723
+ offscreenCanvas: typeof OffscreenCanvas !== "undefined",
3724
+ structuredClone: typeof structuredClone === "function",
3725
+ fontProbe: {
3726
+ content: FONT_PROBE_CONTENT,
3727
+ font: FONT_PROBE_STYLE,
3728
+ width: metrics.width,
3729
+ actualBoundingBoxAscent: metrics.actualBoundingBoxAscent,
3730
+ actualBoundingBoxDescent: metrics.actualBoundingBoxDescent
3731
+ }
3732
+ };
3733
+ }
3734
+ async recover() {
3735
+ for (const session of this._sessions.values()) for (const mount of session.layoutSessions.values()) mount.layoutSession.dispose();
3736
+ this._sessions.clear();
3737
+ }
3738
+ async createSession(request) {
3739
+ if (typeof OffscreenCanvas === "undefined") throw new Error("Document layout Worker requires OffscreenCanvas text measurement support.");
3740
+ const currentSession = this._sessions.get(request.unitId);
3741
+ if (currentSession != null && request.sessionEpoch < currentSession.sessionEpoch) return;
3742
+ this._disposeSession(request.unitId);
3743
+ FontCache.setNormalLineHeightCache(request.normalFontLineHeights ?? {});
3744
+ const dataModel = new DocumentDataModel(request.snapshot);
3745
+ const localeService = new LocaleService();
3746
+ if (request.localeData != null) localeService.load({ [request.locale]: request.localeData });
3747
+ localeService.setLocale(request.locale);
3748
+ localeService.setDirection(request.direction);
3749
+ this._sessions.set(request.unitId, {
3750
+ sessionEpoch: request.sessionEpoch,
3751
+ dataModel,
3752
+ layoutSessions: /* @__PURE__ */ new Map(),
3753
+ localeService,
3754
+ modelRevision: request.modelRevision,
3755
+ customRangePresentations: /* @__PURE__ */ new Map()
3756
+ });
3757
+ }
3758
+ async startLayout(request) {
3759
+ const session = this._sessions.get(request.unitId);
3760
+ if (session == null) return { status: "not-found" };
3761
+ if (session.modelRevision !== request.baseRevision) return {
3762
+ status: "resnapshot-required",
3763
+ modelRevision: session.modelRevision
3764
+ };
3765
+ if (!this._applyMutations(session, request)) return {
3766
+ status: "resnapshot-required",
3767
+ modelRevision: session.modelRevision
3768
+ };
3769
+ FontCache.setNormalLineHeightCache(request.normalFontLineHeights ?? {});
3770
+ this._updateCustomRangePresentations(session, request);
3771
+ this._applyCustomRangePresentations(session.dataModel, Array.from(session.customRangePresentations.values()));
3772
+ let mount = session.layoutSessions.get(request.mountId);
3773
+ if (mount != null && (request.mountEpoch < mount.mountEpoch || request.mountEpoch === mount.mountEpoch && request.viewportEpoch < mount.viewportEpoch)) return { status: "superseded" };
3774
+ if (mount != null && request.mountEpoch > mount.mountEpoch) {
3775
+ mount.layoutSession.dispose();
3776
+ session.layoutSessions.delete(request.mountId);
3777
+ mount = void 0;
3778
+ }
3779
+ if (mount == null) {
3780
+ const customBlockViewports = { ...request.customBlockViewports };
3781
+ applyRecordPatch(customBlockViewports, request.customBlockViewportPatch);
3782
+ mount = {
3783
+ mountId: request.mountId,
3784
+ layoutSession: new DocumentLayoutSession(session.dataModel, session.localeService),
3785
+ customBlockViewports,
3786
+ metricsRevision: request.metricsRevision,
3787
+ mountEpoch: request.mountEpoch,
3788
+ viewportEpoch: request.viewportEpoch,
3789
+ activeGeneration: null,
3790
+ cancelledGeneration: null
3791
+ };
3792
+ session.layoutSessions.set(request.mountId, mount);
3793
+ } else {
3794
+ if (request.customBlockViewports != null) mount.customBlockViewports = { ...request.customBlockViewports };
3795
+ applyRecordPatch(mount.customBlockViewports, request.customBlockViewportPatch);
3796
+ mount.metricsRevision = request.metricsRevision;
3797
+ mount.viewportEpoch = request.viewportEpoch;
3798
+ }
3799
+ const generation = mount.layoutSession.start({
3800
+ waitForHyphenationPatterns: true,
3801
+ reason: request.reason,
3802
+ anchor: request.anchor,
3803
+ priorityAnchor: request.priorityAnchor,
3804
+ invalidation: request.invalidation
3805
+ });
3806
+ mount.activeGeneration = generation;
3807
+ mount.cancelledGeneration = null;
3808
+ return {
3809
+ status: "accepted",
3810
+ step: await this._runLayoutStep(session, mount, generation, request.budgetMs)
3811
+ };
3812
+ }
3813
+ _applyMutations(session, request) {
3814
+ for (const mutation of request.mutations) {
3815
+ if (mutation.baseRevision !== session.modelRevision || mutation.modelRevision !== mutation.baseRevision + 1) return false;
3816
+ session.dataModel.apply(mutation.actions);
3817
+ session.modelRevision = mutation.modelRevision;
3818
+ }
3819
+ if (session.modelRevision !== request.modelRevision) return false;
3820
+ if (request.mutations.length > 0) for (const mount of session.layoutSessions.values()) {
3821
+ mount.activeGeneration = null;
3822
+ mount.cancelledGeneration = null;
3823
+ mount.layoutSession.resetDataModel(session.dataModel);
3824
+ }
3825
+ return true;
3826
+ }
3827
+ _applyCustomRangePresentations(dataModel, presentations) {
3828
+ const rangeMaps = /* @__PURE__ */ new Map();
3829
+ const segmentModels = [
3830
+ ["", dataModel],
3831
+ ...dataModel.headerModelMap,
3832
+ ...dataModel.footerModelMap
3833
+ ];
3834
+ for (const [segmentId, segmentModel] of segmentModels) {
3835
+ var _segmentModel$getBody;
3836
+ const rangeMap = /* @__PURE__ */ new Map();
3837
+ for (const range of ((_segmentModel$getBody = segmentModel.getBody()) === null || _segmentModel$getBody === void 0 ? void 0 : _segmentModel$getBody.customRanges) ?? []) {
3838
+ const workerRange = range;
3839
+ delete workerRange.active;
3840
+ delete workerRange.glyphAscentEm;
3841
+ delete workerRange.glyphDescentEm;
3842
+ delete workerRange.glyphWidthEm;
3843
+ delete workerRange.show;
3844
+ if (!rangeMap.has(range.rangeId)) rangeMap.set(range.rangeId, workerRange);
3845
+ }
3846
+ rangeMaps.set(segmentId, rangeMap);
3847
+ }
3848
+ for (const { segmentId, rangeId, presentation } of presentations ?? []) {
3849
+ const rangeMap = rangeMaps.get(segmentId);
3850
+ const range = rangeMap === null || rangeMap === void 0 ? void 0 : rangeMap.get(rangeId);
3851
+ if (range != null) {
3852
+ Object.assign(range, presentation);
3853
+ rangeMap === null || rangeMap === void 0 || rangeMap.delete(rangeId);
3854
+ }
3855
+ }
3856
+ }
3857
+ _updateCustomRangePresentations(session, request) {
3858
+ var _request$customRangeP, _request$customRangeP2;
3859
+ if (request.customRangePresentations != null) {
3860
+ session.customRangePresentations.clear();
3861
+ for (const entry of request.customRangePresentations) {
3862
+ const key = getCustomRangePresentationKey(entry);
3863
+ if (!session.customRangePresentations.has(key)) session.customRangePresentations.set(key, entry);
3864
+ }
3865
+ }
3866
+ for (const removal of ((_request$customRangeP = request.customRangePresentationPatch) === null || _request$customRangeP === void 0 ? void 0 : _request$customRangeP.removals) ?? []) session.customRangePresentations.delete(getCustomRangePresentationKey(removal));
3867
+ for (const entry of ((_request$customRangeP2 = request.customRangePresentationPatch) === null || _request$customRangeP2 === void 0 ? void 0 : _request$customRangeP2.upserts) ?? []) session.customRangePresentations.set(getCustomRangePresentationKey(entry), entry);
3868
+ }
3869
+ async stepLayout(request) {
3870
+ const session = this._getSession(request.unitId);
3871
+ const mount = this._getLayoutMount(session, request);
3872
+ return await this._runLayoutStep(session, mount, request.generation, request.budgetMs);
3873
+ }
3874
+ async publishBacklog(request) {
3875
+ const session = this._getSession(request.unitId);
3876
+ const mount = this._getLayoutMount(session, request);
3877
+ return this._runWithCustomBlockViewports(session, mount, () => ({
3878
+ ...mount.layoutSession.publishBacklog(request.generation),
3879
+ unitId: request.unitId,
3880
+ mountId: request.mountId,
3881
+ mountEpoch: mount.mountEpoch,
3882
+ viewportEpoch: mount.viewportEpoch,
3883
+ modelRevision: session.modelRevision,
3884
+ metricsRevision: mount.metricsRevision
3885
+ }));
3886
+ }
3887
+ async getLayoutPage(request) {
3888
+ const session = this._getSession(request.unitId);
3889
+ const mount = this._getLayoutMount(session, request);
3890
+ return {
3891
+ ...request,
3892
+ modelRevision: session.modelRevision,
3893
+ metricsRevision: mount.metricsRevision,
3894
+ page: mount.layoutSession.getPage(request.pageIndex)
3895
+ };
3896
+ }
3897
+ async cancelLayout(request) {
3898
+ var _this$_sessions$get;
3899
+ const mount = (_this$_sessions$get = this._sessions.get(request.unitId)) === null || _this$_sessions$get === void 0 ? void 0 : _this$_sessions$get.layoutSessions.get(request.mountId);
3900
+ if ((mount === null || mount === void 0 ? void 0 : mount.mountEpoch) === request.mountEpoch && mount.viewportEpoch === request.viewportEpoch) {
3901
+ if (request.generation == null || request.generation === mount.activeGeneration) mount.cancelledGeneration = request.generation ?? mount.activeGeneration;
3902
+ mount.layoutSession.cancel(request.generation);
3903
+ }
3904
+ }
3905
+ async getPerformanceMetrics(unitId) {
3906
+ return this._performanceTracker.getMetrics(unitId);
3907
+ }
3908
+ async resetPerformanceMetrics(unitId) {
3909
+ this._performanceTracker.reset(unitId);
3910
+ }
3911
+ async disposeLayoutMount(request) {
3912
+ const session = this._sessions.get(request.unitId);
3913
+ const mount = session === null || session === void 0 ? void 0 : session.layoutSessions.get(request.mountId);
3914
+ if (session == null || mount == null || mount.mountEpoch !== request.mountEpoch || mount.viewportEpoch !== request.viewportEpoch) return;
3915
+ session.layoutSessions.delete(request.mountId);
3916
+ mount.activeGeneration = null;
3917
+ mount.layoutSession.dispose();
3918
+ }
3919
+ async disposeSession(request) {
3920
+ const session = this._sessions.get(request.unitId);
3921
+ if ((session === null || session === void 0 ? void 0 : session.sessionEpoch) !== request.sessionEpoch) return;
3922
+ this._disposeSession(request.unitId);
3923
+ this._performanceTracker.reset(request.unitId);
3924
+ }
3925
+ dispose() {
3926
+ for (const unitId of this._sessions.keys()) this._disposeSession(unitId);
3927
+ this._performanceTracker.resetAll();
3928
+ }
3929
+ _getSession(unitId) {
3930
+ const session = this._sessions.get(unitId);
3931
+ if (session == null) throw new Error(`Document layout Worker session not found for unit "${unitId}".`);
3932
+ return session;
3933
+ }
3934
+ _getLayoutMount(session, request) {
3935
+ const mount = session.layoutSessions.get(request.mountId);
3936
+ if (mount == null) throw new Error(`Document layout Worker mount not found: "${request.mountId}".`);
3937
+ if (mount.mountEpoch !== request.mountEpoch || mount.viewportEpoch !== request.viewportEpoch) throw new Error(`Document layout Worker rejected a stale mount publication: "${request.mountId}".`);
3938
+ return mount;
3939
+ }
3940
+ async _runLayoutStep(session, mount, generation, budgetMs) {
3941
+ const result = this._runWithCustomBlockViewports(session, mount, () => ({
3942
+ ...mount.layoutSession.step(generation, budgetMs, WORKER_LAYOUT_INTERRUPT_INTERVAL),
3943
+ unitId: session.dataModel.getUnitId(),
3944
+ mountId: mount.mountId,
3945
+ mountEpoch: mount.mountEpoch,
3946
+ viewportEpoch: mount.viewportEpoch,
3947
+ modelRevision: session.modelRevision,
3948
+ metricsRevision: mount.metricsRevision
3949
+ }));
3950
+ await yieldForWorkerMessage();
3951
+ if (mount.activeGeneration === generation && mount.cancelledGeneration !== generation) return result;
3952
+ return this._runWithCustomBlockViewports(session, mount, () => ({
3953
+ ...mount.layoutSession.step(generation, 0, 1),
3954
+ unitId: session.dataModel.getUnitId(),
3955
+ mountId: mount.mountId,
3956
+ mountEpoch: mount.mountEpoch,
3957
+ viewportEpoch: mount.viewportEpoch,
3958
+ modelRevision: session.modelRevision,
3959
+ metricsRevision: mount.metricsRevision
3960
+ }));
3961
+ }
3962
+ _runWithCustomBlockViewports(session, mount, run) {
3963
+ const unregister = setDocsCustomBlockRenderViewportProvider((unitId, blockId) => unitId === session.dataModel.getUnitId() ? mount.customBlockViewports[blockId] : null);
3964
+ try {
3965
+ return run();
3966
+ } finally {
3967
+ unregister();
3968
+ }
3969
+ }
3970
+ _disposeSession(unitId) {
3971
+ const session = this._sessions.get(unitId);
3972
+ if (session == null) return;
3973
+ this._sessions.delete(unitId);
3974
+ for (const mount of session.layoutSessions.values()) {
3975
+ mount.activeGeneration = null;
3976
+ mount.layoutSession.dispose();
3977
+ }
3978
+ session.layoutSessions.clear();
3979
+ session.dataModel.dispose();
3980
+ session.localeService.dispose();
3981
+ }
3982
+ };
3983
+ function startDocsLayoutWorker() {
3984
+ const performanceTracker = new DocsLayoutWorkerPerformanceTracker();
3985
+ const channelService = new ChannelService(createDocsLayoutWorkerMessageProtocol$1(performanceTracker));
3986
+ channelService.registerChannel(DOCS_LAYOUT_WORKER_CHANNEL, fromModule(new DocsLayoutWorkerRuntime(performanceTracker)));
3987
+ return channelService;
3988
+ }
3989
+
3990
+ //#endregion
3991
+ //#region src/layout-worker/index.ts
3992
+ const FONT_METRICS_TOLERANCE = 1;
3993
+ var DocsLayoutWorkerCapabilityError = class extends Error {
3994
+ constructor(message) {
3995
+ super(message);
3996
+ this.name = "DocsLayoutWorkerCapabilityError";
3997
+ }
3998
+ };
3999
+ function createDocsLayoutWorkerMessageProtocol(worker, performanceTracker) {
4000
+ return {
4001
+ send(message) {
4002
+ const startedAt = Tools.now();
4003
+ worker.postMessage(message);
4004
+ performanceTracker.recordRequest(message, Tools.now() - startedAt);
4005
+ },
4006
+ onMessage: new Observable((subscriber) => {
4007
+ const handler = (event) => subscriber.next(event.data);
4008
+ worker.addEventListener("message", handler);
4009
+ return () => worker.removeEventListener("message", handler);
4010
+ }).pipe(shareReplay({
4011
+ bufferSize: 1,
4012
+ refCount: true
4013
+ }))
4014
+ };
4015
+ }
4016
+ var DocsLayoutWorkerClientService = class extends Disposable {
4017
+ constructor(_workerFactory, _requestTimeoutMs = DEFAULT_DOCS_LAYOUT_WORKER_REQUEST_TIMEOUT_MS) {
4018
+ super();
4019
+ this._workerFactory = _workerFactory;
4020
+ this._requestTimeoutMs = _requestTimeoutMs;
4021
+ _defineProperty(this, "type", "worker");
4022
+ _defineProperty(this, "_worker", null);
4023
+ _defineProperty(this, "_channelService", null);
4024
+ _defineProperty(this, "_runtime", null);
4025
+ _defineProperty(this, "_initialization", void 0);
4026
+ _defineProperty(this, "_initialized", false);
4027
+ _defineProperty(this, "_performanceTracker", new DocsLayoutWorkerPerformanceTracker());
4028
+ _defineProperty(this, "_fontFamilies", /* @__PURE__ */ new Map());
4029
+ this._initialization = this._replaceRuntime();
4030
+ }
4031
+ initialize() {
4032
+ return this._initialization;
4033
+ }
4034
+ getCapabilities() {
4035
+ return this._getRuntime().getCapabilities();
4036
+ }
4037
+ async recover() {
4038
+ this._initialization = this._replaceRuntime();
4039
+ await this._initialization;
4040
+ }
4041
+ createSession(request) {
4042
+ const families = /* @__PURE__ */ new Set();
4043
+ collectDocumentFontFamilies(request.snapshot, families);
4044
+ this._fontFamilies.set(request.unitId, families);
4045
+ const measuredRequest = {
4046
+ ...request,
4047
+ normalFontLineHeights: measureDocumentFontFamilies(families)
4048
+ };
4049
+ if (this._initialized) return this._withTimeout(this._getRuntime().createSession(measuredRequest), "create session");
4050
+ const capturedRequest = Tools.deepClone(measuredRequest);
4051
+ return this._initialization.then(() => this._withTimeout(this._getRuntime().createSession(capturedRequest), "create session"));
4052
+ }
4053
+ async startLayout(request) {
4054
+ await this._initialization;
4055
+ const families = this._fontFamilies.get(request.unitId);
4056
+ if (families != null) collectDocumentFontFamilies(request.mutations, families);
4057
+ const measuredRequest = families == null ? request : {
4058
+ ...request,
4059
+ normalFontLineHeights: measureDocumentFontFamilies(families)
4060
+ };
4061
+ return this._withTimeout(this._getRuntime().startLayout(measuredRequest), "start layout");
4062
+ }
4063
+ async stepLayout(request) {
4064
+ await this._initialization;
4065
+ return this._withTimeout(this._getRuntime().stepLayout(request), "step layout");
4066
+ }
4067
+ async publishBacklog(request) {
4068
+ await this._initialization;
4069
+ return this._withTimeout(this._getRuntime().publishBacklog(request), "publish layout backlog");
4070
+ }
4071
+ async getLayoutPage(request) {
4072
+ await this._initialization;
4073
+ return this._withTimeout(this._getRuntime().getLayoutPage(request), "get layout page");
4074
+ }
4075
+ async cancelLayout(request) {
4076
+ await this._initialization;
4077
+ return this._withTimeout(this._getRuntime().cancelLayout(request), "cancel layout");
4078
+ }
4079
+ async getPerformanceMetrics(unitId) {
4080
+ await this._initialization;
4081
+ const workerMetrics = await this._withTimeout(this._getRuntime().getPerformanceMetrics(unitId), "read performance metrics");
4082
+ const clientMetrics = this._performanceTracker.getMetrics(unitId);
4083
+ return {
4084
+ mutationTransferMs: clientMetrics.mutationTransferMs,
4085
+ patchTransferMs: workerMetrics.patchTransferMs,
4086
+ snapshotTransferMs: clientMetrics.snapshotTransferMs
4087
+ };
4088
+ }
4089
+ async resetPerformanceMetrics(unitId) {
4090
+ await this._initialization;
4091
+ await this._withTimeout(this._getRuntime().resetPerformanceMetrics(unitId), "reset performance metrics");
4092
+ this._performanceTracker.reset(unitId);
4093
+ }
4094
+ async disposeLayoutMount(request) {
4095
+ await this._initialization;
4096
+ return this._withTimeout(this._getRuntime().disposeLayoutMount(request), "dispose layout mount");
4097
+ }
4098
+ async disposeSession(request) {
4099
+ await this._initialization;
4100
+ await this._withTimeout(this._getRuntime().disposeSession(request), "dispose session");
4101
+ this._performanceTracker.reset(request.unitId);
4102
+ this._fontFamilies.delete(request.unitId);
4103
+ }
4104
+ dispose() {
4105
+ this._disposeRuntime();
4106
+ super.dispose();
4107
+ }
4108
+ async _replaceRuntime() {
4109
+ this._initialized = false;
4110
+ this._disposeRuntime();
4111
+ const worker = this._workerFactory();
4112
+ const channelService = new ChannelService(createDocsLayoutWorkerMessageProtocol(worker, this._performanceTracker));
4113
+ this._worker = worker;
4114
+ this._channelService = channelService;
4115
+ this._runtime = toModule(channelService.requestChannel(DOCS_LAYOUT_WORKER_CHANNEL));
4116
+ await this._withTimeout(this._verifyCapabilities(), "verify capabilities");
4117
+ this._initialized = true;
4118
+ }
4119
+ _disposeRuntime() {
4120
+ var _this$_channelService, _this$_worker;
4121
+ this._fontFamilies.clear();
4122
+ this._initialized = false;
4123
+ (_this$_channelService = this._channelService) === null || _this$_channelService === void 0 || _this$_channelService.dispose();
4124
+ this._channelService = null;
4125
+ this._runtime = null;
4126
+ (_this$_worker = this._worker) === null || _this$_worker === void 0 || _this$_worker.terminate();
4127
+ this._worker = null;
4128
+ this._performanceTracker.resetAll();
4129
+ }
4130
+ _getRuntime() {
4131
+ if (this._runtime == null) throw new Error("Document layout Worker runtime is unavailable.");
4132
+ return this._runtime;
4133
+ }
4134
+ _withTimeout(task, operation) {
4135
+ let timeoutId = null;
4136
+ const timeout = new Promise((_resolve, reject) => {
4137
+ timeoutId = setTimeout(() => {
4138
+ reject(/* @__PURE__ */ new Error(`Document layout Worker timed out while attempting to ${operation}.`));
4139
+ }, this._requestTimeoutMs);
4140
+ });
4141
+ return Promise.race([task, timeout]).finally(() => {
4142
+ if (timeoutId != null) clearTimeout(timeoutId);
4143
+ });
4144
+ }
4145
+ async _verifyCapabilities() {
4146
+ const capabilities = await this.getCapabilities();
4147
+ if (capabilities.protocolVersion !== 5) throw new DocsLayoutWorkerCapabilityError("Document layout Worker protocol version mismatch.");
4148
+ if (!capabilities.offscreenCanvas || !capabilities.structuredClone) throw new DocsLayoutWorkerCapabilityError("Document layout Worker requires OffscreenCanvas and structured clone support.");
4149
+ const mainMetrics = FontCache.getMeasureText(capabilities.fontProbe.content, capabilities.fontProbe.font);
4150
+ if ([
4151
+ [mainMetrics.width, capabilities.fontProbe.width],
4152
+ [mainMetrics.actualBoundingBoxAscent, capabilities.fontProbe.actualBoundingBoxAscent],
4153
+ [mainMetrics.actualBoundingBoxDescent, capabilities.fontProbe.actualBoundingBoxDescent]
4154
+ ].some(([mainValue, workerValue]) => !Number.isFinite(mainValue) || !Number.isFinite(workerValue) || Math.abs(mainValue - workerValue) > FONT_METRICS_TOLERANCE)) throw new DocsLayoutWorkerCapabilityError("Document layout Worker font metrics differ from the main rendering context.");
4155
+ }
4156
+ };
4157
+ let UniverDocsLayoutWorkerPlugin = class UniverDocsLayoutWorkerPlugin extends Plugin {
4158
+ constructor(_config = defaultPluginDocsLayoutWorkerConfig, _injector, _layoutExecutorService, _configService) {
4159
+ super();
4160
+ this._config = _config;
4161
+ this._injector = _injector;
4162
+ this._layoutExecutorService = _layoutExecutorService;
4163
+ this._configService = _configService;
4164
+ const { ...rest } = merge({}, defaultPluginDocsLayoutWorkerConfig, this._config);
4165
+ this._configService.setConfig(DOCS_LAYOUT_WORKER_PLUGIN_CONFIG_KEY, rest);
4166
+ }
4167
+ onStarting() {
4168
+ if (typeof this._config.workerFactory !== "function") throw new TypeError("[UniverDocsLayoutWorkerPlugin]: workerFactory is required.");
4169
+ const requestTimeoutMs = this._config.requestTimeoutMs ?? 15e3;
4170
+ if (!Number.isFinite(requestTimeoutMs) || requestTimeoutMs <= 0) throw new RangeError("[UniverDocsLayoutWorkerPlugin]: requestTimeoutMs must be a positive finite number.");
4171
+ const client = new DocsLayoutWorkerClientService(this._config.workerFactory, requestTimeoutMs);
4172
+ this._injector.add([DocsLayoutWorkerClientService, { useValue: client }]);
4173
+ this.disposeWithMe(client);
4174
+ this.disposeWithMe(this._layoutExecutorService.register(client));
4175
+ }
4176
+ };
4177
+ _defineProperty(UniverDocsLayoutWorkerPlugin, "pluginName", "DOCS_LAYOUT_WORKER_PLUGIN");
4178
+ _defineProperty(UniverDocsLayoutWorkerPlugin, "packageName", name);
4179
+ _defineProperty(UniverDocsLayoutWorkerPlugin, "version", version);
4180
+ UniverDocsLayoutWorkerPlugin = __decorate([
4181
+ DependentOn(UniverDocsPlugin),
4182
+ __decorateParam(1, Inject(Injector)),
4183
+ __decorateParam(2, Inject(DocLayoutExecutorService)),
4184
+ __decorateParam(3, IConfigService)
4185
+ ], UniverDocsLayoutWorkerPlugin);
4186
+
1854
4187
  //#endregion
1855
4188
  //#region src/services/doc-interceptor/interceptor-const.ts
1856
4189
  const CUSTOM_RANGE = createInterceptorKey("CUSTOM_RANGE");
@@ -1907,6 +4240,7 @@ let DocInterceptorService = class DocInterceptorService extends Disposable {
1907
4240
  return this.fetchThroughInterceptors(DOC_INTERCEPTOR_POINT.CUSTOM_RANGE)(viewModel.getCustomRangeRaw(index), {
1908
4241
  index,
1909
4242
  unitId: viewModel.getDataModel().getUnitId(),
4243
+ body: viewModel.getDataModel().getBody(),
1910
4244
  customRanges: viewModel.getDataModel().getCustomRanges() ?? []
1911
4245
  });
1912
4246
  },
@@ -2168,4 +4502,4 @@ function normalizeTextRange(textRange) {
2168
4502
  }
2169
4503
 
2170
4504
  //#endregion
2171
- export { CreateHeaderFooterCommand, DOC_INTERCEPTOR_POINT, DOC_SELECTION_OPTION_PRESERVE_CARET, DeleteDocumentSectionBreakCommand, DeleteTextCommand, DocBlockMoveValidatorService, DocContentInsertService, DocInterceptorService, DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService, DocTextResolverService, EMBED_DOCS_CUSTOM_BLOCK_DEFAULT_COMPONENT_KEY, HeaderFooterType, IDocStateChangeInterceptorService, InsertDocumentColumnBreakCommand, InsertDocumentSectionBreakCommand, InsertTextCommand, RichTextEditingMutation, SetDocumentDefaultParagraphStyleCommand, SetDocumentNameCommand, SetSectionHeaderFooterLinkCommand, SetTextSelectionsOperation, UniverDocsPlugin, UpdateDocumentParagraphStyleCommand, UpdateDocumentSectionCommand, UpdateTextCommand, addCustomRangeBySelectionFactory, addCustomRangeFactory, buildDocTransform, consumeContentInsertRange, createDocsCustomBlockDrawing, createDocsCustomBlockInsertMutation, createDocsCustomBlockRemoveMutation, createEmbedDocsCustomBlockData, createInsertCustomBlockActions, createRemoveCustomBlockActions, createSectionColumnProperties, deleteCustomRangeFactory, docDrawingPositionToTransform, generateParagraphs, getContentInsertRange, getEffectiveSectionPageSetup, getSectionContentWidth, getTopLevelSectionBreaks, isEmbedDocsCustomBlockData, isHeaderFooterSelection, isSheetLikeDocsCustomBlockChildType, normalizeTextRange, replaceSelectionFactory, resolveDocsCustomBlockSize, shouldUseInlineTextSelectionForDocsCustomBlockDrawing, transformToDocDrawingPosition };
4505
+ export { CreateHeaderFooterCommand, DOCUMENT_UNIT_PERMISSION_ACTIONS, DOC_INTERCEPTOR_POINT, DOC_SELECTION_OPTION_PRESERVE_CARET, DeleteDocumentSectionBreakCommand, DeleteTextCommand, DocBlockMoveValidatorService, DocContentInsertService, DocHistoryAction, DocInterceptorService, DocLayoutExecutorService, DocLayoutExecutorState, DocLayoutExecutorType, DocLayoutSessionStatus, DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService, DocTextResolverService, DocsLayoutWorkerCapabilityError, DocsLayoutWorkerClientService, EMBED_DOCS_CUSTOM_BLOCK_DEFAULT_COMPONENT_KEY, HeaderFooterType, IDocStateChangeInterceptorService, InsertDocumentColumnBreakCommand, InsertDocumentSectionBreakCommand, InsertTextCommand, RichTextEditingMutation, SetDocumentDefaultParagraphStyleCommand, SetDocumentNameCommand, SetDocumentPermissionCommand, SetDocumentPermissionRuleMutation, SetDocumentPermissionRulesMutation, SetDocumentPermissionsCommand, SetSectionHeaderFooterLinkCommand, SetTextSelectionsOperation, UniverDocsLayoutWorkerPlugin, UniverDocsPlugin, UpdateDocumentParagraphStyleCommand, UpdateDocumentSectionCommand, UpdateTextCommand, addCustomRangeBySelectionFactory, addCustomRangeFactory, buildDocTransform, canEditDocumentTargets, consumeContentInsertRange, createDocsCustomBlockDrawing, createDocsCustomBlockInsertMutation, createDocsCustomBlockRemoveMutation, createDocumentPermissionPoint, createEmbedDocsCustomBlockData, createInsertCustomBlockActions, createRemoveCustomBlockActions, createSectionColumnProperties, deleteCustomRangeFactory, docDrawingPositionToTransform, generateParagraphs, getContentInsertRange, getDocumentDrawingSegmentId, getDocumentEditTargetObjectIds, getDocumentEntityParentPermissionObjectIds, getDocumentEntityPermissionObjectId, getDocumentParagraphParentPermissionObjectIds, getDocumentParagraphPermissionObjectId, getDocumentPermissionValue, getDocumentSectionPermissionObjectId, getEffectiveSectionPageSetup, getSectionContentWidth, getTopLevelSectionBreaks, isEmbedDocsCustomBlockData, isHeaderFooterSelection, isSheetLikeDocsCustomBlockChildType, normalizeTextRange, replaceSelectionFactory, resolveDocsCustomBlockSize, setDocumentPermissionValue, shouldUseInlineTextSelectionForDocsCustomBlockDrawing, startDocsLayoutWorker, transformDocumentTextRanges, transformToDocDrawingPosition };