@univerjs/docs 1.0.0-alpha.2 → 1.0.0-alpha.3

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/lib/es/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BuildTextUtils, CommandType, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DeleteDirection, Disposable, DisposableCollection, DocumentFlavor, ICommandService, IConfigService, IUndoRedoService, IUniverInstanceService, Inject, Injector, JSONX, LocaleService, Optional, Plugin, RedoCommandId, RxDisposable, TextX, TextXActionType, UndoCommandId, UniverInstanceType, composeInterceptors, createIdentifier, createInterceptorKey, createParagraphId, generateRandomId, getRichTextEditPath, isInternalEditorID, merge, remove, toDisposable } from "@univerjs/core";
1
+ import { AlignTypeH, BuildTextUtils, CommandType, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DataStreamTreeTokenType, DeleteDirection, Disposable, DisposableCollection, DocumentFlavor, DrawingTypeEnum, ICommandService, IConfigService, IUndoRedoService, IUniverInstanceService, Inject, Injector, JSONX, LocaleService, MemoryCursor, ObjectRelativeFromH, ObjectRelativeFromV, Optional, PAGE_SIZE, PaperType, Plugin, PositionedObjectLayoutType, RedoCommandId, RxDisposable, TextX, TextXActionType, Tools, UndoCommandId, UniverInstanceType, UpdateDocsAttributeType, composeInterceptors, containsInteriorInsertionOffset, createIdentifier, createInterceptorKey, createParagraphId, createSectionId, generateRandomId, getBlockRangeInterval, getColumnGroupRangeInterval, getRichTextEditPath, getSectionHeaderFooterReferenceKey, getTableRangeInterval, isInternalEditorID, merge, remove, resolveSectionHeaderFooterReference, toDisposable, validateDocBodyStructure } from "@univerjs/core";
2
2
  import { DocumentSkeleton, DocumentViewModel, IRenderManagerService, NORMAL_TEXT_SELECTION_PLUGIN_STYLE } from "@univerjs/engine-render";
3
3
  import { BehaviorSubject, Subject, takeUntil } from "rxjs";
4
4
 
@@ -315,6 +315,27 @@ var DocStateEmitService = class extends RxDisposable {
315
315
  //#endregion
316
316
  //#region src/commands/mutations/core-editing.mutation.ts
317
317
  const RichTextEditingMutationId = "doc.mutation.rich-text-editing";
318
+ function getSegmentType(documentDataModel, segmentId) {
319
+ if (!segmentId) return "body";
320
+ const { headers, footers } = documentDataModel.getSnapshot();
321
+ if (headers === null || headers === void 0 ? void 0 : headers[segmentId]) return "header";
322
+ if (footers === null || footers === void 0 ? void 0 : footers[segmentId]) return "footer";
323
+ return "body";
324
+ }
325
+ function assertValidDocBodyStructure(documentDataModel, segmentId) {
326
+ const segmentModel = documentDataModel.getSelfOrHeaderFooterModel(segmentId);
327
+ const body = segmentModel === null || segmentModel === void 0 ? void 0 : segmentModel.getBody();
328
+ if (!body) return;
329
+ const segmentType = getSegmentType(documentDataModel, segmentId);
330
+ const issues = validateDocBodyStructure(body, {
331
+ segmentType,
332
+ segmentId: segmentId || void 0
333
+ });
334
+ if (!issues.length) return;
335
+ const detail = issues.map((issue) => `${issue.code}${issue.index == null ? "" : `@${issue.index}`}`).join(", ");
336
+ const segmentLabel = segmentId ? `${segmentType} ${segmentId}` : segmentType;
337
+ throw new Error(`[DocStructure] ${segmentLabel}: ${detail}`);
338
+ }
318
339
  /**
319
340
  * The core mutator to change rich text actions. The execution result would be undo mutation params. Could be directly
320
341
  * send to undo redo service (will be used by the triggering command).
@@ -342,6 +363,12 @@ const RichTextEditingMutation = {
342
363
  };
343
364
  const undoActions = JSONX.invertWithDoc(actions, documentDataModel.getSnapshot());
344
365
  documentDataModel.apply(actions);
366
+ try {
367
+ assertValidDocBodyStructure(documentDataModel, segmentId);
368
+ } catch (error) {
369
+ documentDataModel.apply(undoActions);
370
+ throw error;
371
+ }
345
372
  documentViewModel === null || documentViewModel === void 0 || documentViewModel.reset(documentDataModel);
346
373
  if (!noNeedSetTextRange && textRanges && trigger != null && !isSync) queueMicrotask(() => {
347
374
  docSelectionManagerService.replaceDocRanges(textRanges, {
@@ -386,14 +413,16 @@ const InsertTextCommand = {
386
413
  id: "doc.command.insert-text",
387
414
  type: CommandType.COMMAND,
388
415
  handler: (accessor, params) => {
389
- var _docDataModel$getSelf, _activeRange$segmentI;
416
+ var _ref, _ref2, _docDataModel$getSelf;
390
417
  const commandService = accessor.get(ICommandService);
391
418
  const { range, segmentId, body, unitId, cursorOffset } = params;
392
419
  const docSelectionManagerService = accessor.get(DocSelectionManagerService);
393
420
  const docDataModel = accessor.get(IUniverInstanceService).getUnit(unitId, UniverInstanceType.UNIVER_DOC);
394
421
  if (docDataModel == null) return false;
395
422
  const activeRange = docSelectionManagerService.getActiveTextRange();
396
- const originBody = (_docDataModel$getSelf = docDataModel.getSelfOrHeaderFooterModel((_activeRange$segmentI = activeRange === null || activeRange === void 0 ? void 0 : activeRange.segmentId) !== null && _activeRange$segmentI !== void 0 ? _activeRange$segmentI : "")) === null || _docDataModel$getSelf === void 0 ? void 0 : _docDataModel$getSelf.getBody();
423
+ const rangeSegmentId = "segmentId" in range ? range.segmentId : void 0;
424
+ const targetSegmentId = (_ref = (_ref2 = segmentId !== null && segmentId !== void 0 ? segmentId : rangeSegmentId) !== null && _ref2 !== void 0 ? _ref2 : activeRange === null || activeRange === void 0 ? void 0 : activeRange.segmentId) !== null && _ref !== void 0 ? _ref : "";
425
+ const originBody = (_docDataModel$getSelf = docDataModel.getSelfOrHeaderFooterModel(targetSegmentId)) === null || _docDataModel$getSelf === void 0 ? void 0 : _docDataModel$getSelf.getBody();
397
426
  if (originBody == null) return false;
398
427
  const { startOffset, collapsed } = range;
399
428
  const cursorMove = cursorOffset !== null && cursorOffset !== void 0 ? cursorOffset : body.dataStream.length;
@@ -476,14 +505,12 @@ const DeleteTextCommand = {
476
505
  };
477
506
  const textX = new TextX();
478
507
  const jsonX = JSONX.getInstance();
479
- textX.push({
480
- t: TextXActionType.RETAIN,
481
- len: start - 0
482
- });
483
- textX.push({
484
- t: TextXActionType.DELETE,
485
- len: end - start + 1
486
- });
508
+ textX.push(...BuildTextUtils.selection.delete([{
509
+ ...range,
510
+ startOffset: start,
511
+ endOffset: end + 1,
512
+ collapsed: false
513
+ }], body));
487
514
  const path = getRichTextEditPath(docDataModel, segmentId);
488
515
  doMutation.params.actions = jsonX.editOp(textX.serialize(), path);
489
516
  const result = commandService.syncExecuteCommand(doMutation.id, doMutation.params);
@@ -558,10 +585,13 @@ function getEmptyHeaderFooterBody() {
558
585
  spaceBelow: { v: 0 }
559
586
  }
560
587
  }],
561
- sectionBreaks: [{ startIndex: 1 }]
588
+ sectionBreaks: [{
589
+ sectionId: createSectionId(/* @__PURE__ */ new Set()),
590
+ startIndex: 1
591
+ }]
562
592
  };
563
593
  }
564
- function createHeaderFooterAction(segmentId, createType, documentStyle, actions, createMode = "single") {
594
+ function createHeaderFooterAction(segmentId, createType, headerFooterConfig, actions, createMode = "single", configPath = ["documentStyle"]) {
565
595
  const jsonX = JSONX.getInstance();
566
596
  const ID_LEN = 6;
567
597
  const firstSegmentId = segmentId !== null && segmentId !== void 0 ? segmentId : generateRandomId(ID_LEN);
@@ -610,11 +640,11 @@ function createHeaderFooterAction(segmentId, createType, documentStyle, actions,
610
640
  actions.push(insertPairAction);
611
641
  linkedSegmentIds.push([pairKey, secondSegmentId]);
612
642
  }
613
- for (const [k, id] of linkedSegmentIds) if (documentStyle[k] != null) {
614
- const replaceAction = jsonX.replaceOp(["documentStyle", k], documentStyle[k], id);
643
+ for (const [k, id] of linkedSegmentIds) if (headerFooterConfig[k] != null) {
644
+ const replaceAction = jsonX.replaceOp([...configPath, k], headerFooterConfig[k], id);
615
645
  actions.push(replaceAction);
616
646
  } else {
617
- const insertAction = jsonX.insertOp(["documentStyle", k], id);
647
+ const insertAction = jsonX.insertOp([...configPath, k], id);
618
648
  actions.push(insertAction);
619
649
  }
620
650
  return actions;
@@ -623,21 +653,31 @@ const CreateHeaderFooterCommand = {
623
653
  id: "doc.command.create-header-footer",
624
654
  type: CommandType.COMMAND,
625
655
  handler: (accessor, params) => {
656
+ var _body$sectionBreaks$f, _body$sectionBreaks, _body$sectionBreaks2;
626
657
  const commandService = accessor.get(ICommandService);
627
658
  const univerInstanceService = accessor.get(IUniverInstanceService);
628
- const { unitId, segmentId, createType, headerFooterProps, createMode = "single" } = params;
659
+ const { unitId, segmentId, createType, headerFooterProps, createMode = "single", sectionId } = params;
629
660
  const docDataModel = univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC);
630
661
  if (docDataModel == null) return false;
631
- const { documentStyle } = docDataModel.getSnapshot();
662
+ const { documentStyle, body } = docDataModel.getSnapshot();
632
663
  if (documentStyle.documentFlavor === DocumentFlavor.MODERN) return false;
633
664
  const rawActions = [];
634
665
  const jsonX = JSONX.getInstance();
635
- if (createType != null) createHeaderFooterAction(segmentId, createType, documentStyle, rawActions, createMode);
666
+ const sectionIndex = sectionId == null ? -1 : (_body$sectionBreaks$f = body === null || body === void 0 || (_body$sectionBreaks = body.sectionBreaks) === null || _body$sectionBreaks === void 0 ? void 0 : _body$sectionBreaks.findIndex((section) => section.sectionId === sectionId)) !== null && _body$sectionBreaks$f !== void 0 ? _body$sectionBreaks$f : -1;
667
+ const sectionBreak = sectionIndex < 0 ? void 0 : body === null || body === void 0 || (_body$sectionBreaks2 = body.sectionBreaks) === null || _body$sectionBreaks2 === void 0 ? void 0 : _body$sectionBreaks2[sectionIndex];
668
+ if (sectionId != null && !sectionBreak) return false;
669
+ const headerFooterConfig = sectionBreak !== null && sectionBreak !== void 0 ? sectionBreak : documentStyle;
670
+ const configPath = sectionId == null ? ["documentStyle"] : [
671
+ "body",
672
+ "sectionBreaks",
673
+ sectionIndex
674
+ ];
675
+ if (createType != null) createHeaderFooterAction(segmentId, createType, headerFooterConfig, rawActions, createMode, configPath);
636
676
  if (headerFooterProps != null) Object.keys(headerFooterProps).forEach((key) => {
637
677
  const value = headerFooterProps[key];
638
- const oldValue = documentStyle[key];
678
+ const oldValue = headerFooterConfig[key];
639
679
  if (value === oldValue) return;
640
- const action = oldValue === void 0 ? jsonX.insertOp(["documentStyle", key], value) : jsonX.replaceOp(["documentStyle", key], oldValue, value);
680
+ const action = oldValue === void 0 ? jsonX.insertOp([...configPath, key], value) : jsonX.replaceOp([...configPath, key], oldValue, value);
641
681
  rawActions.push(action);
642
682
  });
643
683
  if (rawActions.length === 0) return false;
@@ -660,10 +700,457 @@ const CreateHeaderFooterCommand = {
660
700
  }
661
701
  };
662
702
 
703
+ //#endregion
704
+ //#region src/commands/commands/set-document-default-paragraph-style.command.ts
705
+ const SetDocumentDefaultParagraphStyleCommand = {
706
+ id: "doc.command.set-default-paragraph-style",
707
+ type: CommandType.COMMAND,
708
+ handler: (accessor, params) => {
709
+ if (params == null) return false;
710
+ const commandService = accessor.get(ICommandService);
711
+ const documentDataModel = accessor.get(IUniverInstanceService).getUnit(params.unitId, UniverInstanceType.UNIVER_DOC);
712
+ if (documentDataModel == null) return false;
713
+ const oldStyle = documentDataModel.getSnapshot().documentStyle.defaultParagraphStyle;
714
+ const jsonX = JSONX.getInstance();
715
+ const path = ["documentStyle", "defaultParagraphStyle"];
716
+ const rawActions = [];
717
+ if (params.defaultParagraphStyle == null) {
718
+ if (oldStyle != null) rawActions.push(jsonX.removeOp(path, oldStyle));
719
+ } else if (oldStyle == null) {
720
+ const newStyle = Object.fromEntries(Object.entries(params.defaultParagraphStyle).filter(([, value]) => value != null).map(([key, value]) => [key, Tools.deepClone(value)]));
721
+ if (Object.keys(newStyle).length > 0) rawActions.push(jsonX.insertOp(path, newStyle));
722
+ } else Object.entries(params.defaultParagraphStyle).forEach(([key, value]) => {
723
+ const oldValue = oldStyle[key];
724
+ const propertyPath = [...path, key];
725
+ if (value == null) {
726
+ if (oldValue != null) rawActions.push(jsonX.removeOp(propertyPath, oldValue));
727
+ } else if (oldValue == null) rawActions.push(jsonX.insertOp(propertyPath, Tools.deepClone(value)));
728
+ else rawActions.push(jsonX.replaceOp(propertyPath, oldValue, Tools.deepClone(value)));
729
+ });
730
+ const actions = rawActions.reduce((acc, action) => JSONX.compose(acc, action), null);
731
+ if (rawActions.length === 0 || JSONX.isNoop(actions)) return false;
732
+ const mutation = {
733
+ id: RichTextEditingMutation.id,
734
+ params: {
735
+ unitId: params.unitId,
736
+ actions,
737
+ textRanges: null,
738
+ noNeedSetTextRange: true,
739
+ debounce: true,
740
+ isEditing: false
741
+ }
742
+ };
743
+ return Boolean(commandService.syncExecuteCommand(mutation.id, mutation.params));
744
+ }
745
+ };
746
+
747
+ //#endregion
748
+ //#region src/utils/sections.ts
749
+ /** Returns document-level section breaks, excluding table-cell and modern-column sentinels. */
750
+ function getTopLevelSectionBreaks(body) {
751
+ var _body$sectionBreaks;
752
+ const sectionBreakByIndex = new Map(((_body$sectionBreaks = body.sectionBreaks) !== null && _body$sectionBreaks !== void 0 ? _body$sectionBreaks : []).map((sectionBreak) => [sectionBreak.startIndex, sectionBreak]));
753
+ const result = [];
754
+ let tableCellDepth = 0;
755
+ let columnDepth = 0;
756
+ for (let index = 0; index < body.dataStream.length; index++) {
757
+ const token = body.dataStream[index];
758
+ if (token === DataStreamTreeTokenType.TABLE_CELL_START) tableCellDepth++;
759
+ else if (token === DataStreamTreeTokenType.TABLE_CELL_END) tableCellDepth = Math.max(0, tableCellDepth - 1);
760
+ else if (token === DataStreamTreeTokenType.COLUMN_START) columnDepth++;
761
+ else if (token === DataStreamTreeTokenType.COLUMN_END) columnDepth = Math.max(0, columnDepth - 1);
762
+ else if (token === DataStreamTreeTokenType.SECTION_BREAK && tableCellDepth === 0 && columnDepth === 0) {
763
+ const sectionBreak = sectionBreakByIndex.get(index);
764
+ if (sectionBreak) result.push(sectionBreak);
765
+ }
766
+ }
767
+ return result;
768
+ }
769
+
770
+ //#endregion
771
+ //#region src/commands/commands/set-section-header-footer-link.command.ts
772
+ const SetSectionHeaderFooterLinkCommand = {
773
+ id: "doc.command.set-section-header-footer-link",
774
+ type: CommandType.COMMAND,
775
+ handler: (accessor, params) => {
776
+ var _snapshot$body$sectio, _snapshot$body$sectio2;
777
+ if (!params) return false;
778
+ const instanceService = accessor.get(IUniverInstanceService);
779
+ const commandService = accessor.get(ICommandService);
780
+ const documentDataModel = instanceService.getUnit(params.unitId, UniverInstanceType.UNIVER_DOC);
781
+ const snapshot = documentDataModel === null || documentDataModel === void 0 ? void 0 : documentDataModel.getSnapshot();
782
+ if (!documentDataModel || !(snapshot === null || snapshot === void 0 ? void 0 : snapshot.body) || snapshot.documentStyle.documentFlavor !== DocumentFlavor.TRADITIONAL) return false;
783
+ const sections = getTopLevelSectionBreaks(snapshot.body);
784
+ const sectionIndex = sections.findIndex((section) => section.sectionId === params.sectionId);
785
+ if (sectionIndex <= 0) return false;
786
+ const storageIndex = (_snapshot$body$sectio = (_snapshot$body$sectio2 = snapshot.body.sectionBreaks) === null || _snapshot$body$sectio2 === void 0 ? void 0 : _snapshot$body$sectio2.findIndex((item) => item.sectionId === params.sectionId)) !== null && _snapshot$body$sectio !== void 0 ? _snapshot$body$sectio : -1;
787
+ if (storageIndex < 0) return false;
788
+ const context = {
789
+ snapshot,
790
+ sections,
791
+ sectionIndex,
792
+ storageIndex,
793
+ key: getSectionHeaderFooterReferenceKey(params.kind, params.variant)
794
+ };
795
+ const rawActions = params.linkedToPrevious ? buildLinkActions(context, params.kind) : buildUnlinkActions(context, params.kind, params.segmentId);
796
+ if (!rawActions) return false;
797
+ const mutation = {
798
+ id: RichTextEditingMutation.id,
799
+ params: {
800
+ unitId: params.unitId,
801
+ actions: rawActions.reduce((actions, action) => JSONX.compose(actions, action), null),
802
+ textRanges: null,
803
+ noNeedSetTextRange: true,
804
+ debounce: true,
805
+ isEditing: false,
806
+ trigger: SetSectionHeaderFooterLinkCommand.id
807
+ }
808
+ };
809
+ return Boolean(commandService.syncExecuteCommand(mutation.id, mutation.params));
810
+ }
811
+ };
812
+ function buildLinkActions(context, kind) {
813
+ const { snapshot, sections, sectionIndex, storageIndex, key } = context;
814
+ const section = sections[sectionIndex];
815
+ const explicitSegmentId = section[key];
816
+ if (typeof explicitSegmentId !== "string" || !explicitSegmentId) return null;
817
+ const jsonX = JSONX.getInstance();
818
+ const actions = [jsonX.removeOp([
819
+ "body",
820
+ "sectionBreaks",
821
+ storageIndex,
822
+ key
823
+ ], explicitSegmentId)];
824
+ const referenceKeys = kind === "header" ? [
825
+ "defaultHeaderId",
826
+ "firstPageHeaderId",
827
+ "evenPageHeaderId"
828
+ ] : [
829
+ "defaultFooterId",
830
+ "firstPageFooterId",
831
+ "evenPageFooterId"
832
+ ];
833
+ const referencedByDocument = referenceKeys.some((referenceKey) => snapshot.documentStyle[referenceKey] === explicitSegmentId);
834
+ const referencedBySection = sections.some((item) => referenceKeys.some((referenceKey) => !(item.sectionId === section.sectionId && referenceKey === key) && item[referenceKey] === explicitSegmentId));
835
+ const resources = kind === "header" ? snapshot.headers : snapshot.footers;
836
+ if (!referencedByDocument && !referencedBySection && (resources === null || resources === void 0 ? void 0 : resources[explicitSegmentId])) actions.push(jsonX.removeOp([kind === "header" ? "headers" : "footers", explicitSegmentId], resources[explicitSegmentId]));
837
+ return actions;
838
+ }
839
+ function buildUnlinkActions(context, kind, requestedSegmentId) {
840
+ const { snapshot, sections, sectionIndex, storageIndex, key } = context;
841
+ const explicitSegmentId = sections[sectionIndex][key];
842
+ if (typeof explicitSegmentId === "string" && explicitSegmentId) return null;
843
+ const sourceSegmentId = resolveSectionHeaderFooterReference(snapshot.documentStyle, sections, sectionIndex - 1, key).segmentId;
844
+ const segmentId = requestedSegmentId !== null && requestedSegmentId !== void 0 ? requestedSegmentId : generateRandomId(6);
845
+ const resources = kind === "header" ? snapshot.headers : snapshot.footers;
846
+ if (resources === null || resources === void 0 ? void 0 : resources[segmentId]) return null;
847
+ const source = sourceSegmentId ? resources === null || resources === void 0 ? void 0 : resources[sourceSegmentId] : void 0;
848
+ const idKey = kind === "header" ? "headerId" : "footerId";
849
+ const resource = source ? {
850
+ ...Tools.deepClone(source),
851
+ [idKey]: segmentId
852
+ } : {
853
+ [idKey]: segmentId,
854
+ body: getEmptyHeaderFooterBody()
855
+ };
856
+ const jsonX = JSONX.getInstance();
857
+ return [jsonX.insertOp([kind === "header" ? "headers" : "footers", segmentId], resource), jsonX.insertOp([
858
+ "body",
859
+ "sectionBreaks",
860
+ storageIndex,
861
+ key
862
+ ], segmentId)];
863
+ }
864
+
865
+ //#endregion
866
+ //#region src/commands/commands/update-document-section.command.ts
867
+ const UpdateDocumentSectionCommand = {
868
+ id: "doc.command.update-section",
869
+ type: CommandType.COMMAND,
870
+ handler: (accessor, params) => {
871
+ if (!(params === null || params === void 0 ? void 0 : params.updates.length) || params.updates.some(({ sectionId, config }) => !sectionId || Object.keys(config).length === 0)) return false;
872
+ const instanceService = accessor.get(IUniverInstanceService);
873
+ const commandService = accessor.get(ICommandService);
874
+ const documentDataModel = instanceService.getUnit(params.unitId, UniverInstanceType.UNIVER_DOC);
875
+ if (!documentDataModel || documentDataModel.getDocumentStyle().documentFlavor !== DocumentFlavor.TRADITIONAL) return false;
876
+ const body = documentDataModel.getBody();
877
+ if (!body) return false;
878
+ const updates = new Map(params.updates.map(({ sectionId, config }) => [sectionId, config]));
879
+ if (updates.size !== params.updates.length) return false;
880
+ const selectedIds = new Set(updates.keys());
881
+ const sections = getTopLevelSectionBreaks(body).filter((section) => selectedIds.has(section.sectionId)).sort((left, right) => left.startIndex - right.startIndex);
882
+ if (sections.length !== selectedIds.size) return false;
883
+ const cursor = new MemoryCursor();
884
+ const textX = new TextX();
885
+ for (const section of sections) {
886
+ textX.push({
887
+ t: TextXActionType.RETAIN,
888
+ len: section.startIndex - cursor.cursor
889
+ });
890
+ textX.push({
891
+ t: TextXActionType.RETAIN,
892
+ len: 1,
893
+ coverType: UpdateDocsAttributeType.REPLACE,
894
+ body: {
895
+ dataStream: "",
896
+ sectionBreaks: [{
897
+ ...Tools.deepClone(section),
898
+ ...Tools.deepClone(updates.get(section.sectionId)),
899
+ sectionId: section.sectionId,
900
+ startIndex: 0
901
+ }]
902
+ }
903
+ });
904
+ cursor.moveCursorTo(section.startIndex + 1);
905
+ }
906
+ const jsonX = JSONX.getInstance();
907
+ const mutation = {
908
+ id: RichTextEditingMutation.id,
909
+ params: {
910
+ unitId: params.unitId,
911
+ actions: jsonX.editOp(textX.serialize(), getRichTextEditPath(documentDataModel)),
912
+ textRanges: null,
913
+ noNeedSetTextRange: true,
914
+ debounce: true,
915
+ isEditing: false,
916
+ trigger: UpdateDocumentSectionCommand.id
917
+ }
918
+ };
919
+ return Boolean(commandService.syncExecuteCommand(mutation.id, mutation.params));
920
+ }
921
+ };
922
+ const InsertDocumentSectionBreakCommand = {
923
+ id: "doc.command.insert-section-break",
924
+ type: CommandType.COMMAND,
925
+ handler: (accessor, params) => {
926
+ var _body$sectionBreaks, _body$tables, _body$columnGroups, _body$blockRanges, _params$config;
927
+ if (!params) return false;
928
+ const context = getTraditionalDocumentContext(accessor, params.unitId);
929
+ if (!context || !params.sectionId || !Number.isInteger(params.offset)) return false;
930
+ const { body, documentDataModel, commandService } = context;
931
+ if (params.offset < 0 || params.offset > body.dataStream.length || ((_body$sectionBreaks = body.sectionBreaks) === null || _body$sectionBreaks === void 0 ? void 0 : _body$sectionBreaks.some((section) => section.sectionId === params.sectionId)) || ((_body$tables = body.tables) === null || _body$tables === void 0 ? void 0 : _body$tables.some((range) => containsInteriorInsertionOffset(getTableRangeInterval(range), params.offset))) || ((_body$columnGroups = body.columnGroups) === null || _body$columnGroups === void 0 ? void 0 : _body$columnGroups.some((range) => containsInteriorInsertionOffset(getColumnGroupRangeInterval(range), params.offset))) || ((_body$blockRanges = body.blockRanges) === null || _body$blockRanges === void 0 ? void 0 : _body$blockRanges.some((range) => containsInteriorInsertionOffset(getBlockRangeInterval(range), params.offset)))) return false;
932
+ const textX = new TextX();
933
+ textX.retain(params.offset);
934
+ textX.insert(1, {
935
+ dataStream: DataStreamTreeTokenType.SECTION_BREAK,
936
+ sectionBreaks: [{
937
+ ...Tools.deepClone((_params$config = params.config) !== null && _params$config !== void 0 ? _params$config : {}),
938
+ sectionId: params.sectionId,
939
+ startIndex: 0
940
+ }]
941
+ });
942
+ return executeSectionTextX(commandService, documentDataModel, textX, InsertDocumentSectionBreakCommand.id);
943
+ }
944
+ };
945
+ const DeleteDocumentSectionBreakCommand = {
946
+ id: "doc.command.delete-section-break",
947
+ type: CommandType.COMMAND,
948
+ handler: (accessor, params) => {
949
+ if (!params) return false;
950
+ const context = getTraditionalDocumentContext(accessor, params.unitId);
951
+ if (!context || !params.sectionId) return false;
952
+ const sections = getTopLevelSectionBreaks(context.body);
953
+ if (sections.length <= 1) return false;
954
+ const section = sections.find((item) => item.sectionId === params.sectionId);
955
+ if (!section) return false;
956
+ const textX = new TextX();
957
+ textX.retain(section.startIndex);
958
+ textX.delete(1);
959
+ return executeSectionTextX(context.commandService, context.documentDataModel, textX, DeleteDocumentSectionBreakCommand.id);
960
+ }
961
+ };
962
+ function getTraditionalDocumentContext(accessor, unitId) {
963
+ if (!unitId) return null;
964
+ const documentDataModel = accessor.get(IUniverInstanceService).getUnit(unitId, UniverInstanceType.UNIVER_DOC);
965
+ const body = documentDataModel === null || documentDataModel === void 0 ? void 0 : documentDataModel.getBody();
966
+ if (!documentDataModel || !body || documentDataModel.getDocumentStyle().documentFlavor !== DocumentFlavor.TRADITIONAL) return null;
967
+ return {
968
+ body,
969
+ documentDataModel,
970
+ commandService: accessor.get(ICommandService)
971
+ };
972
+ }
973
+ function executeSectionTextX(commandService, documentDataModel, textX, trigger) {
974
+ const actions = JSONX.getInstance().editOp(textX.serialize(), getRichTextEditPath(documentDataModel));
975
+ return Boolean(commandService.syncExecuteCommand(RichTextEditingMutation.id, {
976
+ unitId: documentDataModel.getUnitId(),
977
+ actions,
978
+ textRanges: null,
979
+ noNeedSetTextRange: true,
980
+ debounce: true,
981
+ isEditing: false,
982
+ trigger
983
+ }));
984
+ }
985
+
986
+ //#endregion
987
+ //#region src/embed-host-anchor.ts
988
+ const EMBED_DOCS_CUSTOM_BLOCK_DEFAULT_COMPONENT_KEY = "UniverEmbedDocsCustomBlock";
989
+ const DEFAULT_CUSTOM_BLOCK_SIZE = {
990
+ width: 720,
991
+ height: 360
992
+ };
993
+ const SHEET_LIKE_CUSTOM_BLOCK_SIZE = {
994
+ width: 960,
995
+ height: 480
996
+ };
997
+ const SLIDE_CUSTOM_BLOCK_SIZE = {
998
+ width: 720,
999
+ height: 405
1000
+ };
1001
+ function createDocsCustomBlockInsertMutation(params) {
1002
+ return createRichTextMutation(params.unitId, params.segmentId, createInsertCustomBlockActions(params));
1003
+ }
1004
+ function createDocsCustomBlockRemoveMutation(params) {
1005
+ return createRichTextMutation(params.unitId, params.segmentId, createRemoveCustomBlockActions(params));
1006
+ }
1007
+ function createInsertCustomBlockActions(params) {
1008
+ const textX = new TextX();
1009
+ if (params.startIndex > 0) textX.push({
1010
+ t: TextXActionType.RETAIN,
1011
+ len: params.startIndex
1012
+ });
1013
+ textX.push({
1014
+ t: TextXActionType.INSERT,
1015
+ body: {
1016
+ dataStream: "\b",
1017
+ customBlocks: [{
1018
+ startIndex: 0,
1019
+ blockId: params.blockId
1020
+ }]
1021
+ },
1022
+ len: 1
1023
+ });
1024
+ return composeActions([toBodyEditActions(textX, params.segmentId), createDrawingInsertActions(params)]);
1025
+ }
1026
+ function createRemoveCustomBlockActions(params) {
1027
+ const textX = new TextX();
1028
+ if (params.startIndex > 0) textX.push({
1029
+ t: TextXActionType.RETAIN,
1030
+ len: params.startIndex
1031
+ });
1032
+ textX.push({
1033
+ t: TextXActionType.DELETE,
1034
+ len: 1
1035
+ });
1036
+ return composeActions([toBodyEditActions(textX, params.segmentId), createDrawingRemoveActions(params)]);
1037
+ }
1038
+ function createDocsCustomBlockDrawing(params) {
1039
+ var _params$componentKey;
1040
+ const size = resolveDocsCustomBlockSize(params.childType);
1041
+ const isInline = params.interactionMode === "inline";
1042
+ return {
1043
+ unitId: params.unitId,
1044
+ subUnitId: params.unitId,
1045
+ drawingId: params.blockId,
1046
+ drawingType: DrawingTypeEnum.DRAWING_DOM,
1047
+ componentKey: (_params$componentKey = params.componentKey) !== null && _params$componentKey !== void 0 ? _params$componentKey : EMBED_DOCS_CUSTOM_BLOCK_DEFAULT_COMPONENT_KEY,
1048
+ data: createEmbedDocsCustomBlockData(params),
1049
+ title: params.blockId,
1050
+ description: "Univer embedded unit custom block",
1051
+ layoutType: isInline ? PositionedObjectLayoutType.INLINE : PositionedObjectLayoutType.WRAP_TOP_AND_BOTTOM,
1052
+ allowTransform: false,
1053
+ docTransform: {
1054
+ size: {
1055
+ width: size.width,
1056
+ height: size.height
1057
+ },
1058
+ positionH: {
1059
+ relativeFrom: isInline ? ObjectRelativeFromH.PAGE : ObjectRelativeFromH.COLUMN,
1060
+ ...isInline ? { posOffset: 0 } : { align: AlignTypeH.LEFT }
1061
+ },
1062
+ positionV: {
1063
+ relativeFrom: isInline ? ObjectRelativeFromV.PAGE : ObjectRelativeFromV.PARAGRAPH,
1064
+ posOffset: 0
1065
+ },
1066
+ angle: 0
1067
+ },
1068
+ transform: {
1069
+ left: 0,
1070
+ top: 0,
1071
+ width: size.width,
1072
+ height: size.height
1073
+ }
1074
+ };
1075
+ }
1076
+ function resolveDocsCustomBlockSize(childType) {
1077
+ if (childType === UniverInstanceType.UNIVER_SHEET || childType === UniverInstanceType.UNIVER_BASE) return SHEET_LIKE_CUSTOM_BLOCK_SIZE;
1078
+ if (childType === UniverInstanceType.UNIVER_SLIDE) return SLIDE_CUSTOM_BLOCK_SIZE;
1079
+ return DEFAULT_CUSTOM_BLOCK_SIZE;
1080
+ }
1081
+ function isSheetLikeDocsCustomBlockChildType(childType) {
1082
+ return childType === UniverInstanceType.UNIVER_SHEET || childType === UniverInstanceType.UNIVER_BASE;
1083
+ }
1084
+ function createEmbedDocsCustomBlockData(params) {
1085
+ var _params$embedId, _params$interactionMo;
1086
+ return {
1087
+ version: 1,
1088
+ embedId: (_params$embedId = params.embedId) !== null && _params$embedId !== void 0 ? _params$embedId : params.blockId,
1089
+ hostUnitId: params.unitId,
1090
+ hostAnchorId: params.blockId,
1091
+ childUnitId: params.childUnitId,
1092
+ childType: params.childType,
1093
+ interactionMode: (_params$interactionMo = params.interactionMode) !== null && _params$interactionMo !== void 0 ? _params$interactionMo : "block"
1094
+ };
1095
+ }
1096
+ function isEmbedDocsCustomBlockData(data) {
1097
+ if (!data || typeof data !== "object") return false;
1098
+ const candidate = data;
1099
+ return candidate.version === 1 && typeof candidate.embedId === "string" && typeof candidate.hostAnchorId === "string";
1100
+ }
1101
+ function shouldUseInlineTextSelectionForDocsCustomBlockDrawing(drawing) {
1102
+ const data = drawing && typeof drawing === "object" ? drawing.data : void 0;
1103
+ if (!isEmbedDocsCustomBlockData(data)) return true;
1104
+ return data.interactionMode === "inline";
1105
+ }
1106
+ function createRichTextMutation(unitId, segmentId, actions) {
1107
+ return {
1108
+ id: RichTextEditingMutation.id,
1109
+ params: {
1110
+ unitId,
1111
+ segmentId,
1112
+ actions,
1113
+ textRanges: [],
1114
+ isEditing: false,
1115
+ noNeedSetTextRange: true
1116
+ }
1117
+ };
1118
+ }
1119
+ function toBodyEditActions(textX, segmentId) {
1120
+ const action = JSONX.getInstance().editOp(textX.serialize(), segmentId ? [
1121
+ "headers",
1122
+ segmentId,
1123
+ "body"
1124
+ ] : ["body"]);
1125
+ return action !== null && action !== void 0 ? action : [];
1126
+ }
1127
+ function createDrawingInsertActions(params) {
1128
+ var _jsonX$insertOp, _jsonX$insertOp2, _params$drawingOrderI;
1129
+ if (params.segmentId) return [];
1130
+ const jsonX = JSONX.getInstance();
1131
+ const drawing = createDocsCustomBlockDrawing(params);
1132
+ return composeActions([(_jsonX$insertOp = jsonX.insertOp(["drawings", params.blockId], drawing)) !== null && _jsonX$insertOp !== void 0 ? _jsonX$insertOp : [], (_jsonX$insertOp2 = jsonX.insertOp(["drawingsOrder", (_params$drawingOrderI = params.drawingOrderIndex) !== null && _params$drawingOrderI !== void 0 ? _params$drawingOrderI : 0], params.blockId)) !== null && _jsonX$insertOp2 !== void 0 ? _jsonX$insertOp2 : []]);
1133
+ }
1134
+ function createDrawingRemoveActions(params) {
1135
+ var _jsonX$removeOp, _jsonX$removeOp2, _params$drawingOrderI2;
1136
+ if (params.segmentId) return [];
1137
+ const jsonX = JSONX.getInstance();
1138
+ const drawing = createDocsCustomBlockDrawing(params);
1139
+ return composeActions([(_jsonX$removeOp = jsonX.removeOp(["drawings", params.blockId], drawing)) !== null && _jsonX$removeOp !== void 0 ? _jsonX$removeOp : [], (_jsonX$removeOp2 = jsonX.removeOp(["drawingsOrder", (_params$drawingOrderI2 = params.drawingOrderIndex) !== null && _params$drawingOrderI2 !== void 0 ? _params$drawingOrderI2 : 0], params.blockId)) !== null && _jsonX$removeOp2 !== void 0 ? _jsonX$removeOp2 : []]);
1140
+ }
1141
+ function composeActions(actions) {
1142
+ return actions.reduce((composed, action) => {
1143
+ var _JSONX$compose;
1144
+ if (!action || JSONX.isNoop(action) || action.length === 0) return composed;
1145
+ if (!composed || JSONX.isNoop(composed) || composed.length === 0) return action;
1146
+ return (_JSONX$compose = JSONX.compose(composed, action)) !== null && _JSONX$compose !== void 0 ? _JSONX$compose : [];
1147
+ }, []);
1148
+ }
1149
+
663
1150
  //#endregion
664
1151
  //#region package.json
665
1152
  var name = "@univerjs/docs";
666
- var version = "1.0.0-alpha.2";
1153
+ var version = "1.0.0-alpha.3";
667
1154
 
668
1155
  //#endregion
669
1156
  //#region src/commands/mutations/docs-rename.mutation.ts
@@ -986,6 +1473,11 @@ let UniverDocsPlugin = class UniverDocsPlugin extends Plugin {
986
1473
  DeleteTextCommand,
987
1474
  UpdateTextCommand,
988
1475
  CreateHeaderFooterCommand,
1476
+ SetDocumentDefaultParagraphStyleCommand,
1477
+ SetSectionHeaderFooterLinkCommand,
1478
+ UpdateDocumentSectionCommand,
1479
+ InsertDocumentSectionBreakCommand,
1480
+ DeleteDocumentSectionBreakCommand,
989
1481
  RichTextEditingMutation,
990
1482
  DocsRenameMutation,
991
1483
  SetTextSelectionsOperation
@@ -1181,6 +1673,35 @@ function deleteCustomRangeFactory(accessor, params) {
1181
1673
  return doMutation;
1182
1674
  }
1183
1675
 
1676
+ //#endregion
1677
+ //#region src/utils/paragraphs.ts
1678
+ /** Builds paragraph metadata for inserted paragraph tokens, including horizontal-rule borders. */
1679
+ function generateParagraphs(dataStream, prevParagraph, borderBottom, existingParagraphIds = []) {
1680
+ const paragraphs = [];
1681
+ const existingIds = new Set(existingParagraphIds);
1682
+ for (let i = 0, len = dataStream.length; i < len; i++) {
1683
+ if (dataStream[i] !== DataStreamTreeTokenType.PARAGRAPH) continue;
1684
+ paragraphs.push({
1685
+ startIndex: i,
1686
+ paragraphId: createParagraphId(existingIds)
1687
+ });
1688
+ }
1689
+ for (const paragraph of paragraphs) {
1690
+ if (prevParagraph === null || prevParagraph === void 0 ? void 0 : prevParagraph.bullet) paragraph.bullet = Tools.deepClone(prevParagraph.bullet);
1691
+ if (prevParagraph === null || prevParagraph === void 0 ? void 0 : prevParagraph.paragraphStyle) {
1692
+ paragraph.paragraphStyle = Tools.deepClone(prevParagraph.paragraphStyle);
1693
+ delete paragraph.paragraphStyle.borderBottom;
1694
+ if (prevParagraph.paragraphStyle.headingId) paragraph.paragraphStyle.headingId = generateRandomId(6);
1695
+ }
1696
+ if (borderBottom) {
1697
+ var _paragraph$paragraphS;
1698
+ (_paragraph$paragraphS = paragraph.paragraphStyle) !== null && _paragraph$paragraphS !== void 0 || (paragraph.paragraphStyle = {});
1699
+ paragraph.paragraphStyle.borderBottom = Tools.deepClone(borderBottom);
1700
+ }
1701
+ }
1702
+ return paragraphs;
1703
+ }
1704
+
1184
1705
  //#endregion
1185
1706
  //#region src/utils/replace-selection-factory.ts
1186
1707
  function replaceSelectionFactory(accessor, params) {
@@ -1223,6 +1744,22 @@ function replaceSelectionFactory(accessor, params) {
1223
1744
  return doMutation;
1224
1745
  }
1225
1746
 
1747
+ //#endregion
1748
+ //#region src/utils/section-columns.ts
1749
+ /** Creates explicit OOXML section columns from a count, gap, and optional widths. */
1750
+ function createSectionColumnProperties(documentStyle, section, columnCount, gap, widths) {
1751
+ var _ref, _section$pageSize$wid, _section$pageSize, _documentStyle$pageSi, _ref2, _section$marginLeft, _ref3, _section$marginRight;
1752
+ if (columnCount <= 1) return [];
1753
+ const safeGap = Math.max(0, gap);
1754
+ const pageWidth = (_ref = (_section$pageSize$wid = section === null || section === void 0 || (_section$pageSize = section.pageSize) === null || _section$pageSize === void 0 ? void 0 : _section$pageSize.width) !== null && _section$pageSize$wid !== void 0 ? _section$pageSize$wid : documentStyle === null || documentStyle === void 0 || (_documentStyle$pageSi = documentStyle.pageSize) === null || _documentStyle$pageSi === void 0 ? void 0 : _documentStyle$pageSi.width) !== null && _ref !== void 0 ? _ref : PAGE_SIZE[PaperType.A4].width;
1755
+ const contentWidth = Math.max(0, pageWidth - ((_ref2 = (_section$marginLeft = section === null || section === void 0 ? void 0 : section.marginLeft) !== null && _section$marginLeft !== void 0 ? _section$marginLeft : documentStyle === null || documentStyle === void 0 ? void 0 : documentStyle.marginLeft) !== null && _ref2 !== void 0 ? _ref2 : 72) - ((_ref3 = (_section$marginRight = section === null || section === void 0 ? void 0 : section.marginRight) !== null && _section$marginRight !== void 0 ? _section$marginRight : documentStyle === null || documentStyle === void 0 ? void 0 : documentStyle.marginRight) !== null && _ref3 !== void 0 ? _ref3 : 72));
1756
+ const availableWidth = Math.max(0, contentWidth - safeGap * (columnCount - 1));
1757
+ return (widths !== null && widths !== void 0 ? widths : Array.from({ length: columnCount }, () => availableWidth / columnCount)).map((width, index) => ({
1758
+ width: Math.max(0, width),
1759
+ paddingEnd: index === columnCount - 1 ? 0 : safeGap
1760
+ }));
1761
+ }
1762
+
1226
1763
  //#endregion
1227
1764
  //#region src/utils/util.ts
1228
1765
  function consumeContentInsertRange(accessor, unitId) {
@@ -1237,4 +1774,4 @@ function isHeaderFooterSelection(range) {
1237
1774
  }
1238
1775
 
1239
1776
  //#endregion
1240
- export { CreateHeaderFooterCommand, DOC_INTERCEPTOR_POINT, DeleteTextCommand, DocBlockMoveValidatorService, DocContentInsertService, DocInterceptorService, DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService, HeaderFooterType, IDocStateChangeInterceptorService, InsertTextCommand, RichTextEditingMutation, SetTextSelectionsOperation, UniverDocsPlugin, UpdateTextCommand, addCustomRangeBySelectionFactory, addCustomRangeFactory, consumeContentInsertRange, deleteCustomRangeFactory, isHeaderFooterSelection, replaceSelectionFactory };
1777
+ export { CreateHeaderFooterCommand, DOC_INTERCEPTOR_POINT, DeleteDocumentSectionBreakCommand, DeleteTextCommand, DocBlockMoveValidatorService, DocContentInsertService, DocInterceptorService, DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService, EMBED_DOCS_CUSTOM_BLOCK_DEFAULT_COMPONENT_KEY, HeaderFooterType, IDocStateChangeInterceptorService, InsertDocumentSectionBreakCommand, InsertTextCommand, RichTextEditingMutation, SetDocumentDefaultParagraphStyleCommand, SetSectionHeaderFooterLinkCommand, SetTextSelectionsOperation, UniverDocsPlugin, UpdateDocumentSectionCommand, UpdateTextCommand, addCustomRangeBySelectionFactory, addCustomRangeFactory, consumeContentInsertRange, createDocsCustomBlockDrawing, createDocsCustomBlockInsertMutation, createDocsCustomBlockRemoveMutation, createEmbedDocsCustomBlockData, createInsertCustomBlockActions, createRemoveCustomBlockActions, createSectionColumnProperties, deleteCustomRangeFactory, generateParagraphs, getTopLevelSectionBreaks, isEmbedDocsCustomBlockData, isHeaderFooterSelection, isSheetLikeDocsCustomBlockChildType, replaceSelectionFactory, resolveDocsCustomBlockSize, shouldUseInlineTextSelectionForDocsCustomBlockDrawing };