@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.
- package/LICENSE +176 -0
- package/README.md +0 -1
- package/lib/cjs/facade.js +331 -60
- package/lib/cjs/index.js +2472 -102
- package/lib/es/facade.js +326 -62
- package/lib/es/index.js +2419 -85
- package/lib/facade.js +326 -62
- package/lib/index.js +2419 -85
- package/lib/types/commands/commands/core-editing.command.d.ts +5 -1
- package/lib/types/commands/commands/set-document-permission.command.d.ts +27 -0
- package/lib/types/commands/commands/set-document-permissions.command.d.ts +17 -0
- package/lib/types/commands/mutations/core-editing.mutation-id.d.ts +16 -0
- package/lib/types/commands/mutations/core-editing.mutation.d.ts +20 -1
- package/lib/types/commands/mutations/doc-structure-mutation-validation.d.ts +17 -0
- package/lib/types/commands/mutations/set-document-permission-rule.mutation.d.ts +17 -0
- package/lib/types/commands/mutations/set-document-permission-rules.mutation.d.ts +17 -0
- package/lib/types/controllers/doc-permission.controller.d.ts +26 -0
- package/lib/types/facade/f-document-paragraph.d.ts +15 -2
- package/lib/types/facade/f-document-permission.d.ts +177 -0
- package/lib/types/facade/f-document-section.d.ts +15 -2
- package/lib/types/facade/f-document-text-range.d.ts +3 -1
- package/lib/types/facade/f-document.d.ts +34 -2
- package/lib/types/facade/f-univer.d.ts +4 -3
- package/lib/types/facade/index.d.ts +1 -0
- package/lib/types/facade/utils.d.ts +2 -1
- package/lib/types/index.d.ts +13 -1
- package/lib/types/layout-worker/config/config.d.ts +24 -0
- package/lib/types/layout-worker/document-font-metrics.d.ts +18 -0
- package/lib/types/layout-worker/index.d.ts +70 -0
- package/lib/types/layout-worker/performance-tracker.d.ts +27 -0
- package/lib/types/layout-worker/protocol.d.ts +35 -0
- package/lib/types/layout-worker/worker.d.ts +49 -0
- package/lib/types/services/doc-interceptor/interceptor-const.d.ts +2 -1
- package/lib/types/services/doc-layout-executor.service.d.ts +218 -0
- package/lib/types/services/doc-selection-manager.service.d.ts +7 -1
- package/lib/types/services/doc-skeleton-manager.service.d.ts +6 -1
- package/lib/types/services/doc-state-change-manager.service.d.ts +1 -0
- package/lib/types/services/doc-state-emit.service.d.ts +4 -0
- package/lib/types/services/document-layout-snapshot.d.ts +22 -0
- package/lib/types/services/permission/document-permission-resolver.d.ts +27 -0
- package/lib/types/services/permission/document-permission-rule.model.d.ts +19 -0
- package/lib/types/services/permission/document-permission.d.ts +35 -0
- package/lib/types/services/permission/permission-point/document/comment.d.ts +27 -0
- package/lib/types/services/permission/permission-point/document/copy.d.ts +27 -0
- package/lib/types/services/permission/permission-point/document/editable.d.ts +27 -0
- package/lib/types/services/permission/permission-point/document/export.d.ts +27 -0
- package/lib/types/services/permission/permission-point/document/print.d.ts +27 -0
- package/lib/types/services/permission/permission-point/entity/edit.d.ts +28 -0
- package/lib/types/services/permission/permission-point/paragraph/edit.d.ts +28 -0
- package/lib/types/services/permission/permission-point/section/edit.d.ts +28 -0
- package/lib/umd/facade.js +3 -3
- package/lib/umd/index.js +2 -2
- 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
|
|
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.
|
|
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([
|
|
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/
|
|
310
|
-
const
|
|
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
|
|
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:
|
|
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,
|
|
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 =
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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:
|
|
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
|
|
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
|
|
1340
|
+
textRanges: mutationTextRanges,
|
|
1341
|
+
debounce,
|
|
1342
|
+
noNeedSetTextRange,
|
|
1343
|
+
isEditing
|
|
435
1344
|
}
|
|
436
1345
|
};
|
|
437
|
-
|
|
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/
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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: {
|
|
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-
|
|
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 };
|