@univerjs/engine-render 1.1.0 → 1.2.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/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AlignTypeH, AlignTypeV, BaselineOffset, BooleanNumber, BorderStyleTypes, BulletAlignment, COLORS, CellValueType, ColorKit, ColumnResponsiveType, ColumnSeparatorType, CustomDecorationType, CustomRangeType, DEFAULT_DOCUMENT_PARAGRAPH_LINE_SPACING, DEFAULT_EMPTY_DOCUMENT_VALUE, DEFAULT_STYLES, DashStyleType, DataStreamTreeNodeType, DataStreamTreeTokenType, Disposable, DisposableCollection, DocumentBlockRangeType, DocumentDataModel, DocumentFlavor, DocxBreakType, EventSubject, FontStyleType, GridType, HorizontalAlign, IConfigService, IContextService, IUniverInstanceService, Inject, Injector, JSON1, ListGlyphType, LocaleService, LookUp, MODERN_DOCUMENT_DEFAULT_MARGIN, MODERN_DOCUMENT_WIDTH, MOVE_BUFFER_VALUE, ModernDocumentWidthMode, NAMED_STYLE_MAP, NAMED_STYLE_SPACE_MAP, NumberUnitType, ObjectMatrix, ObjectRelativeFromH, ObjectRelativeFromV, PRESET_LIST_TYPE, PageOrientType, Plugin, PositionedObjectLayoutType, Range, Rectangle, Registry, SHEET_TEXT_LINE_GAP, SectionType, SheetSkeleton, Skeleton, SpacingRule, TabStopAlignment, TableAlignmentType, TableRowHeightRule, TableSizeType, TableTextWrapType, TextDecoration, TextX, TextXActionType, ThemeService, Tools, UniverInstanceType, VerticalAlign, VerticalAlignmentType, WrapStrategy, WrapTextType, characterSpacingControlType, checkParagraphHasIndentByStyle, createIdentifier, deleteContent, getColorStyle, getDisplayValueFromCell, getRichTextEditPath, horizontalLineSegmentsSubtraction, insertTextToContent, invertColorByMatrix, isCellCoverable, isClassDependencyItem, isDefaultFormat, isNullCell, isWhiteColor, merge, noop, numberToABC, numberToListABC, numfmt, regexp, registerDependencies, remove, requestImmediateMacroTask, resolveDocumentParagraphStyle, resolveSectionHeaderFooterReferences, searchArray, sortRules, sortRulesByDesc, toDisposable } from "@univerjs/core";
1
+ import { AlignTypeH, AlignTypeV, BaselineOffset, BooleanNumber, BorderStyleTypes, BulletAlignment, COLORS, CellValueType, ColorKit, ColumnResponsiveType, ColumnSeparatorType, CustomDecorationType, CustomRangeType, DEFAULT_DOCUMENT_PARAGRAPH_LINE_SPACING, DEFAULT_EMPTY_DOCUMENT_VALUE, DEFAULT_STYLES, DashStyleType, DataStreamTreeNodeType, DataStreamTreeTokenType, Disposable, DisposableCollection, DocumentBlockRangeType, DocumentDataModel, DocumentFlavor, DocxBreakType, EventSubject, FontStyleType, GridType, HorizontalAlign, IConfigService, IContextService, IUniverInstanceService, Inject, Injector, JSON1, ListGlyphType, LocaleService, LookUp, MODERN_DOCUMENT_DEFAULT_MARGIN, MODERN_DOCUMENT_WIDTH, MOVE_BUFFER_VALUE, ModernDocumentWidthMode, NAMED_STYLE_MAP, NAMED_STYLE_SPACE_MAP, NamedStyleType, NumberUnitType, ObjectMatrix, ObjectRelativeFromH, ObjectRelativeFromV, PRESET_LIST_TYPE, PageOrientType, Plugin, PositionedObjectLayoutType, Range, Rectangle, Registry, SHEET_TEXT_LINE_GAP, SectionType, SheetSkeleton, Skeleton, SpacingRule, TabStopAlignment, TableAlignmentType, TableRowHeightRule, TableSizeType, TableTextWrapType, TextDecoration, TextX, TextXActionType, ThemeService, Tools, UniverInstanceType, VerticalAlign, VerticalAlignmentType, WrapStrategy, WrapTextType, characterSpacingControlType, checkParagraphHasIndentByStyle, createIdentifier, deleteContent, getColorStyle, getDisplayValueFromCell, getRichTextEditPath, horizontalLineSegmentsSubtraction, insertTextToContent, invertColorByMatrix, isCellCoverable, isClassDependencyItem, isDefaultFormat, isNullCell, isWhiteColor, merge, noop, numberToABC, numberToListABC, numfmt, regexp, registerDependencies, remove, requestImmediateMacroTask, resolveDocumentParagraphStyle, resolveSectionHeaderFooterReferences, searchArray, sortRules, sortRulesByDesc, toDisposable } from "@univerjs/core";
2
2
  import { BehaviorSubject, Observable, Subject, Subscription, distinctUntilChanged, merge as merge$1, shareReplay, startWith } from "rxjs";
3
3
  import { franc } from "franc-min";
4
4
  import { getOverflowAncestors } from "@floating-ui/dom";
@@ -13125,6 +13125,48 @@ var DataStreamTreeNode = class DataStreamTreeNode {
13125
13125
  }
13126
13126
  };
13127
13127
 
13128
+ //#endregion
13129
+ //#region src/components/docs/view-model/heading-sections.ts
13130
+ /** Each table cell and modern column has its own heading hierarchy. */
13131
+ function getHeadingSections(sections, getParagraph) {
13132
+ const result = [];
13133
+ function visitFlow(nodes, endOffset) {
13134
+ const open = [];
13135
+ for (const node of nodes) {
13136
+ var _paragraph$paragraphS;
13137
+ const paragraph = node.nodeType === DataStreamTreeNodeType.PARAGRAPH && node.children.length === 0 ? getParagraph(node.endIndex) : void 0;
13138
+ const level = paragraph === null || paragraph === void 0 || (_paragraph$paragraphS = paragraph.paragraphStyle) === null || _paragraph$paragraphS === void 0 ? void 0 : _paragraph$paragraphS.namedStyleType;
13139
+ if (paragraph && level != null && level >= NamedStyleType.HEADING_1 && level <= NamedStyleType.HEADING_5) {
13140
+ while (open.length && open[open.length - 1].level >= level) open.pop().section.endOffset = node.startIndex;
13141
+ const section = {
13142
+ paragraphId: paragraph.paragraphId,
13143
+ paragraphIndex: paragraph.startIndex,
13144
+ startOffset: paragraph.startIndex + 1,
13145
+ endOffset,
13146
+ collapsed: paragraph.headingCollapsed === true
13147
+ };
13148
+ result.push(section);
13149
+ open.push({
13150
+ level,
13151
+ section
13152
+ });
13153
+ }
13154
+ visitContainers(node);
13155
+ }
13156
+ }
13157
+ function visitContainers(node) {
13158
+ if (node.nodeType === DataStreamTreeNodeType.TABLE_CELL || node.nodeType === DataStreamTreeNodeType.COLUMN) {
13159
+ const nodes = node.children.flatMap((child) => child.nodeType === DataStreamTreeNodeType.SECTION_BREAK ? child.children : [child]);
13160
+ visitFlow(nodes, nodes.length ? nodes[nodes.length - 1].endIndex + 1 : node.endIndex);
13161
+ return;
13162
+ }
13163
+ for (const child of node.children) visitContainers(child);
13164
+ }
13165
+ const nodes = sections.flatMap((section) => section.children);
13166
+ visitFlow(nodes, nodes.length ? nodes[nodes.length - 1].endIndex + 1 : 0);
13167
+ return result.sort((a, b) => a.paragraphIndex - b.paragraphIndex);
13168
+ }
13169
+
13128
13170
  //#endregion
13129
13171
  //#region src/components/docs/view-model/document-view-model.ts
13130
13172
  const NON_STRUCTURAL_ROOT_FIELDS = /* @__PURE__ */ new Set([
@@ -13449,6 +13491,8 @@ var DocumentViewModel = class DocumentViewModel {
13449
13491
  _defineProperty(this, "_treeNodes", []);
13450
13492
  _defineProperty(this, "_treeNodesByStartIndex", []);
13451
13493
  _defineProperty(this, "_plainTopLevelParagraphNodes", []);
13494
+ _defineProperty(this, "_headingSections", null);
13495
+ _defineProperty(this, "_collapsedHeadingSections", null);
13452
13496
  _defineProperty(this, "_metadataCachesDirty", false);
13453
13497
  _defineProperty(this, "_textRunsOrderedAndDisjoint", true);
13454
13498
  _defineProperty(this, "_paragraphsOrdered", true);
@@ -13492,6 +13536,8 @@ var DocumentViewModel = class DocumentViewModel {
13492
13536
  this._treeNodes = [];
13493
13537
  this._treeNodesByStartIndex = [];
13494
13538
  this._plainTopLevelParagraphNodes = [];
13539
+ this._headingSections = null;
13540
+ this._collapsedHeadingSections = null;
13495
13541
  this._lastTextRun = null;
13496
13542
  this._noteTreeMap.forEach((viewModel) => viewModel.dispose());
13497
13543
  this._noteTreeMap.clear();
@@ -13519,6 +13565,41 @@ var DocumentViewModel = class DocumentViewModel {
13519
13565
  getChildren() {
13520
13566
  return this._children;
13521
13567
  }
13568
+ getHeadingSections() {
13569
+ const snapshot = this.getSnapshot();
13570
+ if (snapshot.documentStyle.documentFlavor !== DocumentFlavor.MODERN) return [];
13571
+ this._headingSections ??= getHeadingSections(this._children, (index) => {
13572
+ var _paragraph$paragraphS;
13573
+ const paragraph = this.getParagraph(index);
13574
+ if (!(paragraph === null || paragraph === void 0 ? void 0 : paragraph.styleId) || ((_paragraph$paragraphS = paragraph.paragraphStyle) === null || _paragraph$paragraphS === void 0 ? void 0 : _paragraph$paragraphS.namedStyleType) != null) return paragraph;
13575
+ return {
13576
+ ...paragraph,
13577
+ paragraphStyle: resolveDocumentParagraphStyle(snapshot.documentStyle, paragraph.paragraphStyle, {
13578
+ styles: snapshot.styles,
13579
+ paragraphStyleId: paragraph.styleId
13580
+ })
13581
+ };
13582
+ });
13583
+ return this._headingSections;
13584
+ }
13585
+ getCollapsedHeadingAtOffset(offset) {
13586
+ if (!this._collapsedHeadingSections) {
13587
+ this._collapsedHeadingSections = [];
13588
+ for (const section of this.getHeadingSections()) {
13589
+ const previous = this._collapsedHeadingSections[this._collapsedHeadingSections.length - 1];
13590
+ if (section.collapsed && section.endOffset > section.startOffset && (!previous || section.startOffset >= previous.endOffset)) this._collapsedHeadingSections.push(section);
13591
+ }
13592
+ }
13593
+ let low = 0;
13594
+ let high = this._collapsedHeadingSections.length - 1;
13595
+ while (low <= high) {
13596
+ const middle = Math.floor((low + high) / 2);
13597
+ const section = this._collapsedHeadingSections[middle];
13598
+ if (offset < section.startOffset) high = middle - 1;
13599
+ else if (offset >= section.endOffset) low = middle + 1;
13600
+ else return section;
13601
+ }
13602
+ }
13522
13603
  getBody() {
13523
13604
  const presentation = getDocumentLayoutPresentation(this._documentDataModel);
13524
13605
  return presentation ? presentation.getSnapshot(this._documentDataModel.getSnapshot()).body : this._documentDataModel.getBody();
@@ -13539,6 +13620,8 @@ var DocumentViewModel = class DocumentViewModel {
13539
13620
  return this;
13540
13621
  }
13541
13622
  reset(documentDataModel) {
13623
+ this._headingSections = null;
13624
+ this._collapsedHeadingSections = null;
13542
13625
  this._children.forEach((child) => child.dispose());
13543
13626
  this._textRunsCache.clear();
13544
13627
  this._paragraphCache.clear();
@@ -13564,6 +13647,8 @@ var DocumentViewModel = class DocumentViewModel {
13564
13647
  if (edit.insertText.length > 0 && !this._insertPlainText(edit.offset, edit.insertText)) return false;
13565
13648
  }
13566
13649
  this._documentDataModel = documentDataModel;
13650
+ this._headingSections = null;
13651
+ this._collapsedHeadingSections = null;
13567
13652
  this._metadataCachesDirty = true;
13568
13653
  this._lastTextRun = null;
13569
13654
  if (!this._textRunsOrderedAndDisjoint) this._buildTextRunsCache();
@@ -13573,6 +13658,8 @@ var DocumentViewModel = class DocumentViewModel {
13573
13658
  const rootFields = getNonStructuralMutationRootFields(actions);
13574
13659
  if (rootFields == null) return false;
13575
13660
  this._documentDataModel = documentDataModel;
13661
+ this._headingSections = null;
13662
+ this._collapsedHeadingSections = null;
13576
13663
  this._metadataCachesDirty = true;
13577
13664
  this._lastTextRun = null;
13578
13665
  if (rootFields.has("tableSource")) {
@@ -16711,7 +16798,23 @@ function hasOnlyFloatingCustomBlockGlyphs(glyphs, paragraphNonInlineSkeDrawingsB
16711
16798
 
16712
16799
  //#endregion
16713
16800
  //#region src/components/docs/layout/block/paragraph/paragraph-layout.ts
16801
+ /**
16802
+ * Copyright 2023-present DreamNum Co., Ltd.
16803
+ *
16804
+ * Licensed under the Apache License, Version 2.0 (the "License");
16805
+ * you may not use this file except in compliance with the License.
16806
+ * You may obtain a copy of the License at
16807
+ *
16808
+ * http://www.apache.org/licenses/LICENSE-2.0
16809
+ *
16810
+ * Unless required by applicable law or agreed to in writing, software
16811
+ * distributed under the License is distributed on an "AS IS" BASIS,
16812
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16813
+ * See the License for the specific language governing permissions and
16814
+ * limitations under the License.
16815
+ */
16714
16816
  function dealWidthParagraph(ctx, viewModel, paragraphNode, curPage, sectionBreakConfig, nextParagraphNode) {
16817
+ if (viewModel.getCollapsedHeadingAtOffset(paragraphNode.startIndex)) return [curPage];
16715
16818
  clearFontCreateConfigCache();
16716
16819
  const { content = "", children } = paragraphNode;
16717
16820
  let tableSkeleton = null;
@@ -16964,6 +17067,7 @@ function dealWithSection(ctx, viewModel, sectionNode, curPage, sectionBreakConfi
16964
17067
  const paragraphEndIndex = Math.min(sectionNode.children.length, paragraphStartIndex + ((options === null || options === void 0 ? void 0 : options.maxParagraphs) ?? Number.POSITIVE_INFINITY));
16965
17068
  for (let i = paragraphStartIndex; i < paragraphEndIndex; i++) {
16966
17069
  const paragraphNode = sectionNode.children[i];
17070
+ if (viewModel.getCollapsedHeadingAtOffset(paragraphNode.startIndex)) continue;
16967
17071
  let skeletonPages = [];
16968
17072
  let currentPageCache = curPage;
16969
17073
  if (allCurrentSkeletonPages.length > 0) currentPageCache = allCurrentSkeletonPages[allCurrentSkeletonPages.length - 1];
@@ -18392,7 +18496,9 @@ function _lineOperator(ctx, glyphGroup, pages, sectionBreakConfig, paragraphConf
18392
18496
  needOpenNewPageByTableLayout = _updateAndPositionTable(ctx, lineTop, lineHeight, lastPage, pages, column, section, skeTablesInParagraph, paragraphConfig.paragraphIndex, sectionBreakConfig, pDrawingAnchor === null || pDrawingAnchor === void 0 || (_pDrawingAnchor$get2 = pDrawingAnchor.get(paragraphIndex)) === null || _pDrawingAnchor$get2 === void 0 ? void 0 : _pDrawingAnchor$get2.top);
18393
18497
  }
18394
18498
  const hasSameParagraphTopBottomDrawingWithInline = hasInlineCustomBlock && paragraphNonInlineSkeDrawings != null && [...paragraphNonInlineSkeDrawings.values()].some((drawing) => drawing.drawingOrigin.layoutType === PositionedObjectLayoutType.WRAP_TOP_AND_BOTTOM);
18395
- const calculatedLineTop = positionedCustomBlockOnly ? lineTop : calculateLineTopByDrawings(lineHeight, lineTop, lastPage, headerPage, footerPage, column.left, column.width, section.top, isTraditionalDocumentCompatibility(paragraphConfig.documentCompatibilityPolicy) ? Math.max(24, Math.min(__getGlyphGroupWidth(glyphGroup) - .001, column.width - 1)) : 0);
18499
+ let tableFlowTop = lineTop;
18500
+ for (const item of skeTablesInParagraph ?? []) if (item.hasPositioned && item.table.parent === lastPage && item.table.tableSource.textWrap === TableTextWrapType.NONE) tableFlowTop = Math.max(lineTop, item.table.top + item.table.height - section.top);
18501
+ const calculatedLineTop = positionedCustomBlockOnly ? tableFlowTop : calculateLineTopByDrawings(lineHeight, tableFlowTop, lastPage, headerPage, footerPage, column.left, column.width, section.top, isTraditionalDocumentCompatibility(paragraphConfig.documentCompatibilityPolicy) ? Math.max(24, Math.min(__getGlyphGroupWidth(glyphGroup) - .001, column.width - 1)) : 0);
18396
18502
  const previousTopBottomCustomBlockFlowBottom = deferredTopBottomAnchorDrawings.length > 0 ? paragraphConfig.topBottomCustomBlockFlowBottom : void 0;
18397
18503
  const newLineTop = previousTopBottomCustomBlockFlowBottom == null ? calculatedLineTop : Math.max(calculatedLineTop, previousTopBottomCustomBlockFlowBottom);
18398
18504
  const clipsSameParagraphInlineDrawing = hasSameParagraphTopBottomDrawingWithInline && newLineTop < section.height;
@@ -23452,6 +23558,8 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23452
23558
  _defineProperty(this, "_paginationMetrics", null);
23453
23559
  _defineProperty(this, "_completedPageCount", void 0);
23454
23560
  _defineProperty(this, "_lastCompleteSkeletonData", null);
23561
+ _defineProperty(this, "_completedHeadingCollapseSignature", "[]");
23562
+ _defineProperty(this, "_publishedHeadingCollapseSignature", "[]");
23455
23563
  _defineProperty(this, "_pendingInvalidationAnchor", null);
23456
23564
  _defineProperty(this, "_layoutGeneration", 0);
23457
23565
  _defineProperty(this, "_activeLayout", null);
@@ -23509,29 +23617,48 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23509
23617
  ctx = retry;
23510
23618
  }
23511
23619
  this._lastCompleteSkeletonData = this._skeletonData;
23620
+ this._completedHeadingCollapseSignature = this._getHeadingCollapseSignature();
23621
+ this._publishedHeadingCollapseSignature = this._completedHeadingCollapseSignature;
23512
23622
  this._completedPageCount = (_this$_skeletonData = this._skeletonData) === null || _this$_skeletonData === void 0 ? void 0 : _this$_skeletonData.pages.length;
23513
23623
  this._pendingInvalidationAnchor = null;
23514
23624
  this._paginationMetrics = ctx.paginationMetrics ?? null;
23515
23625
  this._dirty$.next(true);
23516
23626
  }
23627
+ _getHeadingCollapseSignature() {
23628
+ return JSON.stringify(this.getViewModel().getHeadingSections().filter((heading) => heading.collapsed).map((heading) => [
23629
+ heading.paragraphId,
23630
+ heading.startOffset,
23631
+ heading.endOffset
23632
+ ]));
23633
+ }
23517
23634
  startIncrementalLayout(options) {
23518
- this._modernPageWidth = options === null || options === void 0 ? void 0 : options.modernPageWidth;
23519
- this._modernHorizontalMargin = options === null || options === void 0 ? void 0 : options.modernHorizontalMargin;
23635
+ const headingCollapseSignature = this._getHeadingCollapseSignature();
23636
+ const headingVisibilityChanged = headingCollapseSignature !== this._completedHeadingCollapseSignature || headingCollapseSignature !== this._publishedHeadingCollapseSignature;
23637
+ const layoutOptions = headingVisibilityChanged ? {
23638
+ ...options,
23639
+ anchor: 0,
23640
+ priorityAnchor: (options === null || options === void 0 ? void 0 : options.priorityAnchor) ?? (options === null || options === void 0 ? void 0 : options.anchor) ?? 0,
23641
+ invalidation: void 0,
23642
+ reuseUnaffectedTail: false,
23643
+ preserveInteractionWindow: false
23644
+ } : options;
23645
+ this._modernPageWidth = layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.modernPageWidth;
23646
+ this._modernHorizontalMargin = layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.modernHorizontalMargin;
23520
23647
  this.cancelIncrementalLayout();
23521
23648
  this._externalLayoutProgress = null;
23522
23649
  this._externalProtectedPages = null;
23523
23650
  this._externalProtectedContinuousLayout = null;
23524
23651
  const generation = ++this._layoutGeneration;
23525
- const reason = (options === null || options === void 0 ? void 0 : options.reason) ?? ((options === null || options === void 0 ? void 0 : options.anchor) == null ? "initial" : "edit");
23652
+ const reason = (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.reason) ?? ((layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.anchor) == null ? "initial" : "edit");
23526
23653
  const dataModel = this.getViewModel().getDataModel();
23527
23654
  const sections = this.getViewModel().getChildren();
23528
23655
  const topLevelBlocks = this._getTopLevelBlocks(sections);
23529
23656
  const mode = dataModel.documentStyle.documentFlavor === DocumentFlavor.MODERN ? "continuous" : "paginated";
23530
- const priorityAnchor = (options === null || options === void 0 ? void 0 : options.priorityAnchor) ?? (options === null || options === void 0 ? void 0 : options.anchor);
23657
+ const priorityAnchor = (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.priorityAnchor) ?? (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.anchor);
23531
23658
  const hasPublishedCheckpoint = this._skeletonData != null || this._lastCompleteSkeletonData != null;
23532
23659
  const previousSkeleton = this._lastCompleteSkeletonData ?? this._skeletonData;
23533
- const invalidation = options === null || options === void 0 ? void 0 : options.invalidation;
23534
- const layoutAnchor = options === null || options === void 0 ? void 0 : options.anchor;
23660
+ const invalidation = layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.invalidation;
23661
+ const layoutAnchor = layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.anchor;
23535
23662
  const invalidationAnchor = layoutAnchor == null ? this._pendingInvalidationAnchor ?? void 0 : Math.min(layoutAnchor, this._pendingInvalidationAnchor ?? layoutAnchor);
23536
23663
  const mutationDelta = invalidation == null ? 0 : invalidation.newEnd - invalidation.oldEnd;
23537
23664
  let priorityPageAnchor = priorityAnchor;
@@ -23544,7 +23671,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23544
23671
  const previousPriorityPage = priorityPageIndex < 0 ? null : (previousSkeleton === null || previousSkeleton === void 0 ? void 0 : previousSkeleton.pages[priorityPageIndex]) ?? null;
23545
23672
  const previousPriorityPageEnd = previousPriorityPage == null ? null : getLastBodyFlowCharIndex(previousPriorityPage);
23546
23673
  const priorityPageEnd = previousPriorityPageEnd == null ? null : previousPriorityPageEnd + (invalidation != null && invalidation.oldStart <= previousPriorityPageEnd ? mutationDelta : 0);
23547
- const incrementalStart = this._prepareIncrementalLayoutStart(invalidationAnchor, mode, invalidation, (options === null || options === void 0 ? void 0 : options.preserveInteractionWindow) === true, (options === null || options === void 0 ? void 0 : options.reuseUnaffectedTail) === false);
23674
+ const incrementalStart = this._prepareIncrementalLayoutStart(invalidationAnchor, mode, invalidation, (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.preserveInteractionWindow) === true, (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.reuseUnaffectedTail) === false);
23548
23675
  this._pendingInvalidationAnchor = hasPublishedCheckpoint ? invalidationAnchor ?? null : null;
23549
23676
  this._activeLayout = {
23550
23677
  generation,
@@ -23558,6 +23685,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23558
23685
  sectionBreakConfig: null,
23559
23686
  layoutAnchor: incrementalStart.layoutAnchor,
23560
23687
  priorityAnchor: priorityAnchor ?? null,
23688
+ minimumPublicationHeight: mode === "continuous" && headingVisibilityChanged && this._skeletonData != null ? (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.minimumPublicationHeight) ?? 0 : 0,
23561
23689
  priorityPageIndex,
23562
23690
  priorityPageEnd,
23563
23691
  interactionPageTail: incrementalStart.interactionPageTail,
@@ -23582,16 +23710,16 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23582
23710
  lastPublishedPageCount: incrementalStart.ctx.skeleton.pages.length > 0 ? Math.max(0, incrementalStart.ctx.skeleton.pages.length - 1) : 0,
23583
23711
  lastPublishedBlockCount: incrementalStart.processedBlockCount,
23584
23712
  publicationRevision: 0,
23585
- reuseUnaffectedTail: (options === null || options === void 0 ? void 0 : options.reuseUnaffectedTail) !== false,
23713
+ reuseUnaffectedTail: (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.reuseUnaffectedTail) !== false,
23586
23714
  paginationRevision: 0,
23587
- allowMetadataOnlyStructuralTailReuse: (options === null || options === void 0 ? void 0 : options.allowMetadataOnlyStructuralTailReuse) === true,
23715
+ allowMetadataOnlyStructuralTailReuse: (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.allowMetadataOnlyStructuralTailReuse) === true,
23588
23716
  reusedTail: false,
23589
23717
  tailConvergencePageCount: incrementalStart.ctx.skeleton.pages.length,
23590
23718
  dirtyRetries: /* @__PURE__ */ new Map(),
23591
23719
  preparedHyphenationNodes: /* @__PURE__ */ new WeakSet(),
23592
23720
  pendingHyphenation: null,
23593
23721
  hyphenationError: null,
23594
- waitForHyphenationPatterns: (options === null || options === void 0 ? void 0 : options.waitForHyphenationPatterns) === true
23722
+ waitForHyphenationPatterns: (layoutOptions === null || layoutOptions === void 0 ? void 0 : layoutOptions.waitForHyphenationPatterns) === true
23595
23723
  };
23596
23724
  return generation;
23597
23725
  }
@@ -23966,10 +24094,12 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23966
24094
  if (isFinalPublication) {
23967
24095
  var _state$ctx$skeleton;
23968
24096
  this._lastCompleteSkeletonData = state.ctx.skeleton;
24097
+ this._completedHeadingCollapseSignature = this._getHeadingCollapseSignature();
23969
24098
  this._completedPageCount = (_state$ctx$skeleton = state.ctx.skeleton) === null || _state$ctx$skeleton === void 0 ? void 0 : _state$ctx$skeleton.pages.length;
23970
24099
  this._pendingInvalidationAnchor = null;
23971
24100
  }
23972
24101
  this._skeletonData = isFinalPublication ? state.ctx.skeleton : !this._isolateIncrementalPublications && state.mode === "paginated" ? this._createSharedPublishablePartialSkeleton(state, publishedPageCount) : this._createPublishablePartialSkeleton(state, publishedPageCount);
24102
+ this._publishedHeadingCollapseSignature = this._getHeadingCollapseSignature();
23973
24103
  this._paginationMetrics = state.ctx.paginationMetrics ?? null;
23974
24104
  this._dirty$.next(true);
23975
24105
  didPublish = true;
@@ -23994,6 +24124,19 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
23994
24124
  const startIndex = ((_stablePages$at = stablePages.at(-1)) === null || _stablePages$at === void 0 ? void 0 : _stablePages$at.ed) ?? -1;
23995
24125
  updateBlockIndex(activePages, startIndex, state.ctx.docsConfig.documentCompatibilityPolicy);
23996
24126
  mergeContinuousDuplicatePages(activePages, state.mode === "continuous");
24127
+ if (state.mode === "continuous") {
24128
+ const paragraphConfigCache = new Map([...state.ctx.paragraphConfigCache].map(([segmentId, configs]) => [segmentId, new Map([...configs].map(([index, config]) => {
24129
+ var _config$paragraphInli;
24130
+ return [index, ((_config$paragraphInli = config.paragraphInlineSkeDrawings) === null || _config$paragraphInli === void 0 ? void 0 : _config$paragraphInli.size) ? {
24131
+ ...config,
24132
+ paragraphInlineSkeDrawings: new Map([...config.paragraphInlineSkeDrawings].map(([id, drawing]) => [id, { ...drawing }]))
24133
+ } : config];
24134
+ }))]));
24135
+ updateInlineDrawingCoordsAndBorder({
24136
+ ...state.ctx,
24137
+ paragraphConfigCache
24138
+ }, activePages);
24139
+ }
23997
24140
  const flowPages = [...stablePages, ...activePages];
23998
24141
  const currentPriorityPageIndex = state.priorityAnchor == null ? -1 : this._findBodyPageIndex(flowPages, state.priorityAnchor);
23999
24142
  const previousPriorityPage = previous === null || previous === void 0 ? void 0 : previous.pages[state.priorityPageIndex];
@@ -24267,6 +24410,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
24267
24410
  if (progress.complete) {
24268
24411
  setPageParent(skeletonData.pages, skeletonData);
24269
24412
  this._lastCompleteSkeletonData = skeletonData;
24413
+ this._completedHeadingCollapseSignature = this._getHeadingCollapseSignature();
24270
24414
  this._completedPageCount = skeletonData === null || skeletonData === void 0 ? void 0 : skeletonData.pages.length;
24271
24415
  this._pendingInvalidationAnchor = null;
24272
24416
  this._externalProtectedContinuousLayout = null;
@@ -24337,6 +24481,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
24337
24481
  if (publicationToApply.resources.drawingAnchor != null) skeletonData.drawingAnchor = hydrateDrawingAnchors(publicationToApply.resources.drawingAnchor);
24338
24482
  setPageParent(skeletonData.pages, skeletonData);
24339
24483
  this._lastCompleteSkeletonData = skeletonData;
24484
+ this._completedHeadingCollapseSignature = this._getHeadingCollapseSignature();
24340
24485
  this._completedPageCount = skeletonData === null || skeletonData === void 0 ? void 0 : skeletonData.pages.length;
24341
24486
  this._pendingInvalidationAnchor = null;
24342
24487
  this._externalProtectedPages = null;
@@ -24344,6 +24489,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
24344
24489
  }
24345
24490
  this._skeletonData = skeletonData;
24346
24491
  this._externalLayoutProgress = progress.complete ? null : progress;
24492
+ this._publishedHeadingCollapseSignature = this._getHeadingCollapseSignature();
24347
24493
  this._dirty$.next(true);
24348
24494
  this._layoutProgress$.next(progress);
24349
24495
  return { didReplaceProtectedPages };
@@ -24397,7 +24543,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
24397
24543
  const firstUnprotectedLineOffset = flow.lines.findIndex((line) => line.st > protectedEndOffset);
24398
24544
  if (firstUnprotectedLineOffset < 0) return null;
24399
24545
  const existingColumn = (_skeletonData$pages$p = skeletonData.pages[publication.block.pageIndex]) === null || _skeletonData$pages$p === void 0 || (_skeletonData$pages$p = _skeletonData$pages$p.sections[flow.sectionIndex]) === null || _skeletonData$pages$p === void 0 ? void 0 : _skeletonData$pages$p.columns[flow.columnIndex];
24400
- if (existingColumn == null) throw new Error("Continuous layout cannot merge a Worker tail without the Main interaction boundary.");
24546
+ if (existingColumn == null) return null;
24401
24547
  const firstUnprotectedLine = flow.lines[firstUnprotectedLineOffset];
24402
24548
  const matchingTailLineIndex = existingColumn.lines.findIndex((line) => line.st >= firstUnprotectedLine.st);
24403
24549
  const targetLineIndex = matchingTailLineIndex < 0 ? existingColumn.lines.length : matchingTailLineIndex;
@@ -25132,7 +25278,23 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
25132
25278
  const anchor = state.priorityAnchor;
25133
25279
  if (anchor == null || state.complete) return true;
25134
25280
  if (state.interactionPageComplete) return true;
25135
- if (state.mode === "continuous") return state.laidOutThrough >= anchor;
25281
+ if (state.mode === "continuous") {
25282
+ if (state.laidOutThrough < anchor) return false;
25283
+ if (state.minimumPublicationHeight <= 0) return true;
25284
+ let height = 0;
25285
+ for (const page of state.ctx.skeleton.pages) for (const section of page.sections) {
25286
+ let sectionHeight = 0;
25287
+ for (const column of section.columns) for (let index = column.lines.length - 1; index >= 0; index--) {
25288
+ const line = column.lines[index];
25289
+ if (line.paragraphIndex <= state.stableLaidOutThrough) {
25290
+ sectionHeight = Math.max(sectionHeight, line.top + line.lineHeight);
25291
+ break;
25292
+ }
25293
+ }
25294
+ height += sectionHeight;
25295
+ }
25296
+ return height >= state.minimumPublicationHeight;
25297
+ }
25136
25298
  if (state.priorityPageEnd != null) {
25137
25299
  var _state$invalidation;
25138
25300
  const requiredOffset = Math.max(anchor, state.priorityPageEnd, ((_state$invalidation = state.invalidation) === null || _state$invalidation === void 0 ? void 0 : _state$invalidation.newEnd) ?? anchor);
@@ -25319,7 +25481,7 @@ var DocumentSkeleton = class DocumentSkeleton extends Skeleton {
25319
25481
  const curSkeletonPage = getLastPage(ctx.skeleton.pages);
25320
25482
  if (sectionBreakConfig == null || curSkeletonPage == null) throw new Error("Incremental document layout failed to initialize a section page.");
25321
25483
  const paragraphNode = sectionNode.children[state.paragraphIndex];
25322
- const tableNode = (paragraphNode === null || paragraphNode === void 0 ? void 0 : paragraphNode.children.length) === 1 && paragraphNode.children[0].nodeType === DataStreamTreeNodeType.TABLE ? paragraphNode.children[0] : null;
25484
+ const tableNode = paragraphNode && !ctx.viewModel.getCollapsedHeadingAtOffset(paragraphNode.startIndex) && paragraphNode.children.length === 1 && paragraphNode.children[0].nodeType === DataStreamTreeNodeType.TABLE ? paragraphNode.children[0] : null;
25323
25485
  if (tableNode != null) {
25324
25486
  state.pendingTableBuild ??= startTableSkeletonBuild(ctx, curSkeletonPage, ctx.viewModel, tableNode, sectionBreakConfig);
25325
25487
  if (state.pendingTableBuild != null && !stepTableSkeletonBuild(state.pendingTableBuild)) return;
@@ -35581,7 +35743,7 @@ Engine = __decorate([__decorateParam(2, ICanvasColorService)], Engine);
35581
35743
  //#endregion
35582
35744
  //#region package.json
35583
35745
  var name = "@univerjs/engine-render";
35584
- var version = "1.1.0";
35746
+ var version = "1.2.0";
35585
35747
 
35586
35748
  //#endregion
35587
35749
  //#region src/config/config.ts
@@ -24,6 +24,7 @@ export declare function getParagraphByGlyph(glyph: Nullable<IDocumentSkeletonGly
24
24
  paragraphEnd: number;
25
25
  startIndex: number;
26
26
  paragraphId: string;
27
+ headingCollapsed?: boolean;
27
28
  styleId?: string;
28
29
  paragraphStyle?: import("@univerjs/core").IParagraphStyle;
29
30
  bullet?: import("@univerjs/core").IBullet;
@@ -14,10 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import type { ITabStop } from '@univerjs/core';
17
+ import { BooleanNumber, GridType, SpacingRule } from '@univerjs/core';
17
18
  import type { IDocumentSkeletonColumn, IDocumentSkeletonDivide, IDocumentSkeletonDrawing, IDocumentSkeletonGlyph, IDocumentSkeletonLine, IDocumentSkeletonPage, IDocumentSkeletonSection, IDocumentSkeletonTable } from '../../../../../basics/i-document-skeleton-cached';
18
19
  import type { IParagraphConfig, IParagraphTableCache, ISectionBreakConfig } from '../../../../../basics/interfaces';
19
20
  import type { IFloatObject, ILayoutContext } from '../../tools';
20
- import { BooleanNumber, GridType, SpacingRule } from '@univerjs/core';
21
21
  import { BreakPointType } from '../../line-breaker/break';
22
22
  declare function isGlyphGroupBeyondDivideWidth(glyphGroup: IDocumentSkeletonGlyph[], offsetLeft: number, divideWidth: number, hangingPunctuation?: boolean, precedingGlyphs?: IDocumentSkeletonGlyph[], preserveWordSpacing?: boolean, lineWrapTolerance?: number, includeParagraphMark?: boolean, justifyWordSpacing?: boolean): boolean;
23
23
  export declare function layoutParagraph(ctx: ILayoutContext, glyphGroup: IDocumentSkeletonGlyph[], pages: IDocumentSkeletonPage[], sectionBreakConfig: ISectionBreakConfig, paragraphConfig: IParagraphConfig, isParagraphFirstShapedText: boolean, breakPointType?: BreakPointType, renderBullet?: boolean, startNewLine?: boolean): IDocumentSkeletonPage[];
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import type { LocaleService, Nullable } from '@univerjs/core';
17
+ import { Skeleton } from '@univerjs/core';
17
18
  import type { IDocumentSkeletonCached, IDocumentSkeletonGlyph, IDocumentSkeletonPage } from '../../../basics/i-document-skeleton-cached';
18
19
  import type { INodeInfo, INodePosition, INodeSearch } from '../../../basics/interfaces';
19
20
  import type { IViewportInfo, Vector2 } from '../../../basics/vector2';
@@ -21,7 +22,6 @@ import type { DocumentViewModel } from '../view-model/document-view-model';
21
22
  import type { IDocumentLayoutGeometryPublication, IDocumentLayoutPagePublication } from './document-layout-publication';
22
23
  import type { DocumentLayoutReason, IDocumentLayoutApplyResult, IDocumentLayoutInvalidation, IDocumentLayoutPageRange, IDocumentLayoutProgress, IDocumentLayoutProtectedRange } from './document-layout-types';
23
24
  import type { IDocumentPaginationMetrics } from './tools';
24
- import { Skeleton } from '@univerjs/core';
25
25
  import { PageLayoutType } from '../../../basics/i-document-skeleton-cached';
26
26
  import { DocumentEditArea } from '../view-model/document-view-model';
27
27
  export declare enum DocumentSkeletonState {
@@ -55,6 +55,8 @@ export declare class DocumentSkeleton extends Skeleton {
55
55
  private _paginationMetrics;
56
56
  private _completedPageCount;
57
57
  private _lastCompleteSkeletonData;
58
+ private _completedHeadingCollapseSignature;
59
+ private _publishedHeadingCollapseSignature;
58
60
  private _pendingInvalidationAnchor;
59
61
  private _layoutGeneration;
60
62
  private _activeLayout;
@@ -82,6 +84,7 @@ export declare class DocumentSkeleton extends Skeleton {
82
84
  * PS: This method has significant impact on performance.
83
85
  */
84
86
  calculate(bounds?: IViewportInfo): void;
87
+ private _getHeadingCollapseSignature;
85
88
  startIncrementalLayout(options?: {
86
89
  reason?: DocumentLayoutReason;
87
90
  anchor?: number;
@@ -90,6 +93,8 @@ export declare class DocumentSkeleton extends Skeleton {
90
93
  modernPageWidth?: number;
91
94
  modernHorizontalMargin?: number;
92
95
  bounds?: IViewportInfo;
96
+ /** Keep the current Modern viewport until a visibility change fills its replacement. */
97
+ minimumPublicationHeight?: number;
93
98
  reuseUnaffectedTail?: boolean;
94
99
  /** The caller proved this edit only changes offset-preserving render metadata. */
95
100
  allowMetadataOnlyStructuralTailReuse?: boolean;
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import type { DocumentDataModel, ICustomBlock, ICustomDecorationForInterceptor, ICustomRangeForInterceptor, ICustomTable, IDisposable, ITable, JSONXActions, Nullable } from '@univerjs/core';
17
17
  import type { IDocumentLayoutBody, IDocumentLayoutColumnGroup, IDocumentLayoutCustomColumnGroup, IDocumentLayoutParagraph, IDocumentLayoutSectionBreak, IDocumentLayoutSnapshot, IDocumentLayoutTextRun } from '../document-layout-presentation';
18
+ import type { IHeadingSection } from './heading-sections';
18
19
  import { DataStreamTreeNode } from './data-stream-tree-node';
19
20
  interface ITableNodeCache {
20
21
  table: DataStreamTreeNode;
@@ -56,6 +57,8 @@ export declare class DocumentViewModel implements IDisposable {
56
57
  private _treeNodes;
57
58
  private _treeNodesByStartIndex;
58
59
  private _plainTopLevelParagraphNodes;
60
+ private _headingSections;
61
+ private _collapsedHeadingSections;
59
62
  private _metadataCachesDirty;
60
63
  private _textRunsOrderedAndDisjoint;
61
64
  private _paragraphsOrdered;
@@ -83,6 +86,8 @@ export declare class DocumentViewModel implements IDisposable {
83
86
  getEditArea(): DocumentEditArea;
84
87
  setEditArea(editArea: DocumentEditArea): void;
85
88
  getChildren(): DataStreamTreeNode[];
89
+ getHeadingSections(): readonly IHeadingSection[];
90
+ getCollapsedHeadingAtOffset(offset: number): IHeadingSection | undefined;
86
91
  getBody(): IDocumentLayoutBody | undefined;
87
92
  getSnapshot(): IDocumentLayoutSnapshot;
88
93
  getDataModel(): DocumentDataModel;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright 2023-present DreamNum Co., Ltd.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import type { IParagraph } from '@univerjs/core';
17
+ import type { DataStreamTreeNode } from './data-stream-tree-node';
18
+ export interface IHeadingSection {
19
+ paragraphId: string;
20
+ paragraphIndex: number;
21
+ startOffset: number;
22
+ endOffset: number;
23
+ collapsed: boolean;
24
+ }
25
+ /** Each table cell and modern column has its own heading hierarchy. */
26
+ export declare function getHeadingSections(sections: readonly DataStreamTreeNode[], getParagraph: (index: number) => IParagraph | undefined): IHeadingSection[];