@univerjs/docs 1.0.0-alpha.7 → 1.0.0-beta.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/es/facade.js CHANGED
@@ -1,6 +1,6 @@
1
- import { BooleanNumber, ColumnSeparatorType, DashStyleType, DataStreamTreeTokenType, DocumentFlavor, ICommandService, IResourceLoaderService, IUniverInstanceService, Inject, Injector, JSONX, PresetListType, RESTORE_INSERTED_PARAGRAPH_IDS, RedoCommand, SectionType, TextX, TextXActionType, Tools, UndoCommand, UniverInstanceType, UpdateDocsAttributeType, createParagraphId, createSectionId, generateRandomId, getParagraphContentStartOffset, getRichTextEditPath, getSectionHeaderFooterReferenceKey, resolveSectionHeaderFooterReference } from "@univerjs/core";
1
+ import { BooleanNumber, ColumnSeparatorType, DashStyleType, DataStreamTreeTokenType, DocumentFlavor, ICommandService, IResourceLoaderService, IUniverInstanceService, Inject, Injector, JSONX, PageOrientType, PresetListType, RESTORE_INSERTED_PARAGRAPH_IDS, RedoCommand, SectionType, TextX, TextXActionType, Tools, UndoCommand, UniverInstanceType, UpdateDocsAttributeType, createParagraphId, createSectionId, generateRandomId, getParagraphContentStartOffset, getRichTextEditPath, getSectionHeaderFooterReferenceKey, regexp, resolveSectionHeaderFooterReference } from "@univerjs/core";
2
2
  import { FBaseInitialable, FEnum, FUniver } from "@univerjs/core/facade";
3
- import { CreateHeaderFooterCommand, DeleteDocumentSectionBreakCommand, HeaderFooterType, InsertDocumentSectionBreakCommand, RichTextEditingMutation, SetSectionHeaderFooterLinkCommand, UpdateDocumentSectionCommand, createSectionColumnProperties, generateParagraphs, getTopLevelSectionBreaks } from "@univerjs/docs";
3
+ import { CreateHeaderFooterCommand, DeleteDocumentSectionBreakCommand, HeaderFooterType, InsertDocumentColumnBreakCommand, InsertDocumentSectionBreakCommand, RichTextEditingMutation, SetSectionHeaderFooterLinkCommand, UpdateDocumentParagraphStyleCommand, UpdateDocumentSectionCommand, createSectionColumnProperties, generateParagraphs, getEffectiveSectionPageSetup, getSectionContentWidth, getTopLevelSectionBreaks } from "@univerjs/docs";
4
4
 
5
5
  //#region src/facade/utils.ts
6
6
  function cloneParagraphStyle(paragraphStyle) {
@@ -106,8 +106,9 @@ function stripBlockTokens(text) {
106
106
  * that insert or remove content before it.
107
107
  * @hideconstructor
108
108
  */
109
- var FDocumentTextRange = class {
109
+ var FDocumentTextRange = class extends FBaseInitialable {
110
110
  constructor(_document, _startOffset, _endOffset, _segmentId, _injector) {
111
+ super(_injector);
111
112
  this._document = _document;
112
113
  this._startOffset = _startOffset;
113
114
  this._endOffset = _endOffset;
@@ -155,18 +156,11 @@ var FDocumentTextRange = class {
155
156
  */
156
157
  getExplicitTextStyleRuns() {
157
158
  const { textRuns = [] } = this._document.getBody(this._segmentId);
158
- return textRuns.filter((run) => run.st < this._endOffset && run.ed > this._startOffset).map((run) => {
159
- var _run$ts;
160
- return {
161
- startOffset: Math.max(run.st, this._startOffset),
162
- endOffset: Math.min(run.ed, this._endOffset),
163
- textStyle: Tools.deepClone((_run$ts = run.ts) !== null && _run$ts !== void 0 ? _run$ts : {})
164
- };
165
- });
166
- }
167
- /** @deprecated Use `getExplicitTextStyleRuns()` to distinguish stored styles from effective styles. */
168
- getTextStyleRuns() {
169
- return this.getExplicitTextStyleRuns();
159
+ return textRuns.filter((run) => run.st < this._endOffset && run.ed > this._startOffset).map((run) => ({
160
+ startOffset: Math.max(run.st, this._startOffset),
161
+ endOffset: Math.min(run.ed, this._endOffset),
162
+ textStyle: Tools.deepClone(run.ts ?? {})
163
+ }));
170
164
  }
171
165
  /**
172
166
  * Returns top-level style properties that have the same explicit value
@@ -185,10 +179,6 @@ var FDocumentTextRange = class {
185
179
  for (const key of Object.keys(common)) if (rest.some((run) => !isDeepEqual(run.textStyle[key], common[key]))) delete common[key];
186
180
  return common;
187
181
  }
188
- /** @deprecated Use `getCommonExplicitTextStyle()` to distinguish stored styles from effective styles. */
189
- getCommonTextStyle() {
190
- return this.getCommonExplicitTextStyle();
191
- }
192
182
  /**
193
183
  * Returns a serializable summary suitable for an agent/tool response.
194
184
  * @example
@@ -206,9 +196,7 @@ var FDocumentTextRange = class {
206
196
  text: this.getText(),
207
197
  length: this._endOffset - this._startOffset,
208
198
  explicitTextStyleRuns,
209
- commonExplicitTextStyle,
210
- textStyleRuns: explicitTextStyleRuns,
211
- commonTextStyle: commonExplicitTextStyle
199
+ commonExplicitTextStyle
212
200
  };
213
201
  }
214
202
  /**
@@ -275,23 +263,33 @@ function isDeepEqual(left, right) {
275
263
  return JSON.stringify(left) === JSON.stringify(right);
276
264
  }
277
265
 
266
+ //#endregion
267
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/decorateParam.js
268
+ function __decorateParam(paramIndex, decorator) {
269
+ return function(target, key) {
270
+ decorator(target, key, paramIndex);
271
+ };
272
+ }
273
+
274
+ //#endregion
275
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/decorate.js
276
+ function __decorate(decorators, target, key, desc) {
277
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
278
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
279
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
280
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
281
+ }
282
+
278
283
  //#endregion
279
284
  //#region src/facade/f-document-paragraph.ts
280
- /**
281
- * A paragraph facade wrapper.
282
- *
283
- * Paragraph identity is backed by the persisted `paragraphId`. The id is
284
- * re-resolved before each method call, so insertions before this paragraph do
285
- * not break the wrapper.
286
- *
287
- * @hideconstructor
288
- */
289
- var FDocumentParagraph = class {
290
- constructor(_document, _paragraphId, _segmentId = "", _injector) {
285
+ let FDocumentParagraph = class FDocumentParagraph extends FBaseInitialable {
286
+ constructor(_document, _paragraphId, _segmentId = "", _injector, _commandService) {
287
+ super(_injector);
291
288
  this._document = _document;
292
289
  this._paragraphId = _paragraphId;
293
290
  this._segmentId = _segmentId;
294
291
  this._injector = _injector;
292
+ this._commandService = _commandService;
295
293
  }
296
294
  /**
297
295
  * Get the persisted paragraph id.
@@ -382,6 +380,71 @@ var FDocumentParagraph = class {
382
380
  return this._injector.createInstance(FDocumentTextRange, this._document, startOffset, endOffset, this._segmentId, this._injector);
383
381
  }
384
382
  /**
383
+ * Finds one literal text occurrence inside this paragraph.
384
+ *
385
+ * The returned text range is fixed when it is created. Resolve a new range
386
+ * after edits that insert or remove content before the match.
387
+ *
388
+ * @param {string} text Literal text to find. It must not be empty.
389
+ * @param {IFDocumentFindTextOptions} [options] Case sensitivity and zero-based occurrence.
390
+ * @returns {FDocumentTextRange | null} The matching fixed text range, or `null` when no such occurrence exists.
391
+ * @example
392
+ * ```ts
393
+ * const univerAPI = FUniver.newAPI(univer);
394
+ * const document = univerAPI.getActiveDocument();
395
+ * if (!document) throw new Error('No active document');
396
+ *
397
+ * const paragraph = document.findParagraphByText('Launch formula');
398
+ * if (!paragraph) throw new Error('Target paragraph not found');
399
+ *
400
+ * const range = paragraph.findText('formula');
401
+ * if (!range) throw new Error('Target text not found');
402
+ *
403
+ * console.log(range.describe());
404
+ * ```
405
+ */
406
+ findText(text, options = {}) {
407
+ const occurrence = options.occurrence ?? 0;
408
+ if (!Number.isInteger(occurrence) || occurrence < 0) throw new RangeError("Text occurrence must be a non-negative integer.");
409
+ return this.findAllText(text, options)[occurrence] ?? null;
410
+ }
411
+ /**
412
+ * Finds every non-overlapping literal text occurrence inside this paragraph.
413
+ *
414
+ * Results are ordered from the start of the paragraph. The returned ranges
415
+ * are fixed when created; use them immediately and resolve new ranges after
416
+ * edits that change earlier document content.
417
+ *
418
+ * @param {string} text Literal text to find. It must not be empty.
419
+ * @param {Omit<IFDocumentFindTextOptions, 'occurrence'>} [options] Case-sensitivity option.
420
+ * @returns {FDocumentTextRange[]} All matching fixed text ranges, or an empty array when no matches exist.
421
+ * @example
422
+ * ```ts
423
+ * const univerAPI = FUniver.newAPI(univer);
424
+ * const document = univerAPI.getActiveDocument();
425
+ * if (!document) throw new Error('No active document');
426
+ *
427
+ * const paragraph = document.findParagraphByText('x plus x');
428
+ * if (!paragraph) throw new Error('Target paragraph not found');
429
+ *
430
+ * const matches = paragraph.findAllText('x');
431
+ * console.log(matches.map((range) => range.describe()));
432
+ * ```
433
+ */
434
+ findAllText(text, options = {}) {
435
+ if (text.length === 0) throw new TypeError("Text to find must not be empty.");
436
+ const matchCase = options.matchCase ?? true;
437
+ const paragraphText = this.getText();
438
+ const matcher = regexp.createLiteralRegExp(text, matchCase ? "gu" : "giu");
439
+ const { startOffset } = this.getInfo();
440
+ const matches = [];
441
+ for (const match of paragraphText.matchAll(matcher)) {
442
+ const matchStartOffset = startOffset + match.index;
443
+ matches.push(this._injector.createInstance(FDocumentTextRange, this._document, matchStartOffset, matchStartOffset + match[0].length, this._segmentId, this._injector));
444
+ }
445
+ return matches;
446
+ }
447
+ /**
385
448
  * Get this paragraph's plain text.
386
449
  * @returns {string} The paragraph text without the trailing paragraph break.
387
450
  * @example
@@ -433,59 +496,65 @@ var FDocumentParagraph = class {
433
496
  return this._document.insertText(endOffset, text, this._segmentId);
434
497
  }
435
498
  /**
436
- * Apply paragraph style to a paragraph handle or text range.
499
+ * Applies a paragraph and optional text-style patch through one document command.
500
+ *
501
+ * Pagination values use `BooleanNumber.TRUE` or `BooleanNumber.FALSE`; explicit
502
+ * false is preserved and overrides inherited true. The paragraph and text-style
503
+ * changes share one undo/redo item. A stale paragraph handle returns `false`
504
+ * without applying a partial update.
505
+ *
506
+ * The Traditional renderer applies these Word-compatible pagination rules:
507
+ * use `pageBreakBefore` for a hard chapter-page boundary, `keepLines` for a
508
+ * short paragraph that should stay intact, `keepNext` for a heading or caption
509
+ * that should accompany the next paragraph, and `widowControl` for natural
510
+ * multi-line body text. Do not enable every rule on every paragraph. Modern
511
+ * and Unspecified Docs preserve the values in the model but do not apply them
512
+ * to physical pages.
513
+ *
437
514
  * `style.textStyle.fs` is a font size in points (pt), not CSS pixels.
438
515
  * @param {IParagraphStyle} style The Univer paragraph style patch.
439
- * @returns {boolean} `true` if the style was applied.
516
+ * @returns {boolean} `true` when the complete patch was applied; otherwise `false`.
440
517
  * @example
441
518
  * ```ts
442
- * const fDocument = univerAPI.getActiveDocument();
443
- * const paragraph = fDocument.getParagraphs()[0];
444
- * paragraph?.setText('Styled text');
445
- * paragraph?.setStyle({
446
- * textStyle: {
447
- * cl: {
448
- * rgb: '#FF0000',
449
- * },
450
- * fs: 14,
451
- * },
452
- * horizontalAlign: 2,
519
+ * const document = univerAPI.getActiveDocument();
520
+ * if (!document) {
521
+ * throw new Error('No active document');
522
+ * }
523
+ * if (!document.isTraditional()) {
524
+ * throw new Error('Traditional document pagination is required');
525
+ * }
526
+ * const heading = document.findParagraphByText('Appendix');
527
+ * const following = document.findParagraphByText('Supporting details');
528
+ * if (!heading || !following) {
529
+ * throw new Error('Expected paragraphs were not found');
530
+ * }
531
+ *
532
+ * const headingUpdated = heading.setStyle({
533
+ * pageBreakBefore: univerAPI.Enum.BooleanNumber.TRUE,
534
+ * keepLines: univerAPI.Enum.BooleanNumber.TRUE,
535
+ * keepNext: univerAPI.Enum.BooleanNumber.TRUE,
536
+ * });
537
+ * const followingUpdated = following.setStyle({
538
+ * // Explicit FALSE terminates this authored keepNext chain even if a named
539
+ * // style or document default enables it.
540
+ * keepNext: univerAPI.Enum.BooleanNumber.FALSE,
541
+ * widowControl: univerAPI.Enum.BooleanNumber.TRUE,
453
542
  * });
454
- * console.log(paragraph?.getInfo().paragraph.paragraphStyle);
543
+ * if (!headingUpdated || !followingUpdated) {
544
+ * throw new Error('Failed to update paragraph pagination');
545
+ * }
455
546
  * ```
456
547
  */
457
548
  setStyle(style) {
458
- const { paragraph, startOffset, endOffset } = this.getInfo();
459
- let result = true;
460
- if (style.textStyle && startOffset < endOffset) result = retainBodyRange({
549
+ const { startOffset, endOffset } = this.getInfo();
550
+ return this._commandService.syncExecuteCommand(UpdateDocumentParagraphStyleCommand.id, {
551
+ unitId: this._document.getId(),
552
+ segmentId: this._segmentId,
553
+ paragraphId: this._paragraphId,
461
554
  startOffset,
462
555
  endOffset,
463
- segmentId: this._segmentId
464
- }, {
465
- dataStream: "",
466
- textRuns: [{
467
- st: 0,
468
- ed: endOffset - startOffset,
469
- ts: style.textStyle
470
- }]
471
- }, UpdateDocsAttributeType.COVER, this._document.getDocumentDataModel(), this._injector);
472
- const updateBody = {
473
- dataStream: "",
474
- paragraphs: [{
475
- ...paragraph,
476
- startIndex: 0,
477
- paragraphStyle: {
478
- ...paragraph.paragraphStyle,
479
- ...style
480
- }
481
- }]
482
- };
483
- this._preserveExplicitParagraphIds(updateBody);
484
- return retainBodyRange({
485
- startOffset: endOffset,
486
- endOffset: endOffset + 1,
487
- segmentId: this._segmentId
488
- }, updateBody, UpdateDocsAttributeType.REPLACE, this._document.getDocumentDataModel(), this._injector) && result;
556
+ style
557
+ });
489
558
  }
490
559
  /**
491
560
  * Check whether this paragraph is a bullet, ordered, or checklist item.
@@ -577,6 +646,7 @@ var FDocumentParagraph = class {
577
646
  body[RESTORE_INSERTED_PARAGRAPH_IDS] = true;
578
647
  }
579
648
  };
649
+ FDocumentParagraph = __decorate([__decorateParam(4, ICommandService)], FDocumentParagraph);
580
650
  function isParagraphFacade(value) {
581
651
  if (typeof value !== "object" || value === null) return false;
582
652
  return typeof value.getId === "function" && typeof value.getSegmentId === "function" && typeof value.getInfo === "function" && typeof value.getRange === "function";
@@ -584,29 +654,30 @@ function isParagraphFacade(value) {
584
654
 
585
655
  //#endregion
586
656
  //#region src/facade/f-document-section.ts
587
- /** Error thrown when traditional section APIs are used to mutate a modern document. */
657
+ function validatePageSetup(pageSetup) {
658
+ const { pageNumberStart, pageSize, pageOrient, marginTop, marginBottom, marginLeft, marginRight } = pageSetup;
659
+ if (pageNumberStart != null && (!Number.isInteger(pageNumberStart) || pageNumberStart < 1)) throw new RangeError("Section page number start must be a positive integer.");
660
+ if (pageSize && [pageSize.width, pageSize.height].some((size) => size != null && (!Number.isFinite(size) || size <= 0))) throw new RangeError("Section page size must be finite and positive.");
661
+ if (pageOrient != null && !Object.values(PageOrientType).includes(pageOrient)) throw new RangeError("Invalid section page orientation.");
662
+ if ([
663
+ marginTop,
664
+ marginBottom,
665
+ marginLeft,
666
+ marginRight
667
+ ].some((margin) => margin != null && (!Number.isFinite(margin) || margin < 0))) throw new RangeError("Section page margins must be finite and non-negative.");
668
+ }
669
+ /** Error thrown when a Traditional-only section API is used with another document flavor. */
588
670
  var DocsSectionUnsupportedDocumentFlavorError = class extends Error {
589
671
  constructor() {
590
- super("Section column APIs are supported only in traditional documents. Use ColumnGroup APIs for modern documents.");
672
+ super("Section column APIs are supported only in traditional documents. Use ColumnGroup APIs for modern documents, or resolve an unspecified document flavor first.");
591
673
  this.name = "DocsSectionUnsupportedDocumentFlavorError";
592
674
  }
593
675
  };
594
- /**
595
- * Facade wrapper for an OOXML-compatible traditional document section.
596
- * Modern documents use ColumnGroup APIs and cannot mutate this facade.
597
- * @example
598
- * ```ts
599
- * const fDocument = univerAPI.getActiveDocument();
600
- * if (fDocument && !fDocument.isModern()) {
601
- * console.log(fDocument.getSection(0)?.describe());
602
- * }
603
- * ```
604
- */
605
- var FDocumentSection = class {
606
- constructor(_document, _sectionId, _injector) {
676
+ let FDocumentSection = class FDocumentSection {
677
+ constructor(_document, _sectionId, _commandService) {
607
678
  this._document = _document;
608
679
  this._sectionId = _sectionId;
609
- this._injector = _injector;
680
+ this._commandService = _commandService;
610
681
  }
611
682
  /**
612
683
  * Returns the persisted section id.
@@ -639,8 +710,7 @@ var FDocumentSection = class {
639
710
  * ```
640
711
  */
641
712
  getConfig() {
642
- const { sectionBreak } = this._resolve();
643
- return Tools.deepClone(sectionBreak);
713
+ return this._getConfigSnapshot();
644
714
  }
645
715
  /**
646
716
  * Returns the section content range, excluding its terminating section-break token.
@@ -651,17 +721,11 @@ var FDocumentSection = class {
651
721
  * ```
652
722
  */
653
723
  getRange() {
654
- const sectionBreaks = getTopLevelSectionBreaks(this._document.getBody());
655
- const { index, sectionBreak } = this._resolve();
656
- return {
657
- startOffset: index === 0 ? 0 : sectionBreaks[index - 1].startIndex + 1,
658
- endOffset: sectionBreak.startIndex,
659
- segmentId: ""
660
- };
724
+ return this._getRange(this._resolve().index);
661
725
  }
662
726
  /**
663
727
  * Returns the explicit columns. An empty array means the normal single-column layout.
664
- * Column widths and trailing spaces are in points (pt).
728
+ * Column widths and trailing spaces are in 96-DPI layout pixels.
665
729
  * @example
666
730
  * ```ts
667
731
  * const fDocument = univerAPI.getActiveDocument();
@@ -669,8 +733,7 @@ var FDocumentSection = class {
669
733
  * ```
670
734
  */
671
735
  getColumns() {
672
- var _this$getConfig$colum;
673
- return Tools.deepClone((_this$getConfig$colum = this.getConfig().columnProperties) !== null && _this$getConfig$colum !== void 0 ? _this$getConfig$colum : []);
736
+ return Tools.deepClone(this._getConfigSnapshot().columnProperties ?? []);
674
737
  }
675
738
  /**
676
739
  * Returns a compact serializable section summary.
@@ -681,9 +744,9 @@ var FDocumentSection = class {
681
744
  * ```
682
745
  */
683
746
  describe() {
684
- var _config$columnPropert, _config$columnSeparat, _config$sectionType;
685
- const config = this.getConfig();
686
- const columns = (_config$columnPropert = config.columnProperties) !== null && _config$columnPropert !== void 0 ? _config$columnPropert : [];
747
+ const { index } = this._resolve();
748
+ const config = this._getConfigSnapshot();
749
+ const columns = config.columnProperties ?? [];
687
750
  const headerFooter = {
688
751
  defaultHeader: this._describeHeaderFooterReference("header", "default"),
689
752
  defaultFooter: this._describeHeaderFooterReference("footer", "default"),
@@ -694,12 +757,12 @@ var FDocumentSection = class {
694
757
  };
695
758
  return {
696
759
  sectionId: this._sectionId,
697
- index: this.getIndex(),
698
- range: this.getRange(),
760
+ index,
761
+ range: this._getRange(index),
699
762
  columnCount: columns.length || 1,
700
763
  columns: Tools.deepClone(columns),
701
- columnSeparatorType: (_config$columnSeparat = config.columnSeparatorType) !== null && _config$columnSeparat !== void 0 ? _config$columnSeparat : ColumnSeparatorType.NONE,
702
- sectionType: (_config$sectionType = config.sectionType) !== null && _config$sectionType !== void 0 ? _config$sectionType : SectionType.SECTION_TYPE_UNSPECIFIED,
764
+ columnSeparatorType: config.columnSeparatorType ?? ColumnSeparatorType.NONE,
765
+ sectionType: config.sectionType ?? SectionType.SECTION_TYPE_UNSPECIFIED,
703
766
  headerFooter,
704
767
  config
705
768
  };
@@ -707,36 +770,36 @@ var FDocumentSection = class {
707
770
  /**
708
771
  * Sets equal or explicitly sized columns for this traditional section.
709
772
  * Use `columnCount = 1` to restore normal single-column layout.
710
- * `gap` and `widths` are in points (pt).
773
+ * `gap` and `widths` are in 96-DPI layout pixels.
711
774
  * @example
712
775
  * ```ts
713
776
  * const fDocument = univerAPI.getActiveDocument();
714
- * if (fDocument && !fDocument.isModern()) {
777
+ * if (fDocument?.isTraditional()) {
715
778
  * fDocument.getSection(0)?.setColumns(2, { gap: 18, separator: true });
716
779
  * }
717
780
  * ```
718
781
  */
719
782
  setColumns(columnCount, options = {}) {
720
- var _options$gap, _options$separator;
721
783
  this._assertTraditionalDocument();
722
784
  if (!Number.isInteger(columnCount) || columnCount < 1) throw new RangeError("Section column count must be a positive integer.");
723
785
  if (options.widths && options.widths.length !== columnCount) throw new RangeError("Section column widths must match the column count.");
724
- const gap = Math.max(0, (_options$gap = options.gap) !== null && _options$gap !== void 0 ? _options$gap : 18);
725
- const config = this.getConfig();
786
+ if (options.gap != null && (!Number.isFinite(options.gap) || options.gap < 0)) throw new RangeError("Section column gap must be finite and non-negative.");
787
+ const gap = Math.max(0, options.gap ?? 18);
788
+ const config = this._getConfigSnapshot();
726
789
  const columns = createSectionColumnProperties(this._document.getDocumentDataModel().getSnapshot().documentStyle, config, columnCount, gap, options.widths);
727
- const separator = typeof options.separator === "boolean" ? options.separator ? ColumnSeparatorType.BETWEEN_EACH_COLUMN : ColumnSeparatorType.NONE : (_options$separator = options.separator) !== null && _options$separator !== void 0 ? _options$separator : ColumnSeparatorType.NONE;
790
+ const separator = typeof options.separator === "boolean" ? options.separator ? ColumnSeparatorType.BETWEEN_EACH_COLUMN : ColumnSeparatorType.NONE : options.separator ?? ColumnSeparatorType.NONE;
791
+ if (!Object.values(ColumnSeparatorType).includes(separator)) throw new RangeError("Invalid section column separator type.");
728
792
  return this._update({
729
793
  columnProperties: columns,
730
- columnSeparatorType: separator,
731
- ...options.sectionType == null ? {} : { sectionType: options.sectionType }
794
+ columnSeparatorType: separator
732
795
  });
733
796
  }
734
797
  /**
735
- * Sets explicit OOXML-compatible column width and trailing-space values in points (pt).
798
+ * Sets explicit OOXML-compatible column width and trailing-space values in 96-DPI layout pixels.
736
799
  * @example
737
800
  * ```ts
738
801
  * const fDocument = univerAPI.getActiveDocument();
739
- * if (fDocument && !fDocument.isModern()) {
802
+ * if (fDocument?.isTraditional()) {
740
803
  * fDocument.getSection(0)?.setColumnProperties([
741
804
  * { width: 240, paddingEnd: 18 },
742
805
  * { width: 240, paddingEnd: 0 },
@@ -746,32 +809,163 @@ var FDocumentSection = class {
746
809
  */
747
810
  setColumnProperties(columns, separator = ColumnSeparatorType.NONE) {
748
811
  this._assertTraditionalDocument();
749
- if (columns.some(({ width, paddingEnd }) => width < 0 || paddingEnd < 0)) throw new RangeError("Section column widths and padding must be non-negative.");
812
+ if (!Object.values(ColumnSeparatorType).includes(separator)) throw new RangeError("Invalid section column separator type.");
813
+ if (columns.some(({ width, paddingEnd }) => !Number.isFinite(width) || !Number.isFinite(paddingEnd) || width < 0 || paddingEnd < 0)) throw new RangeError("Section column widths and padding must be finite and non-negative.");
814
+ const contentWidth = getSectionContentWidth(this._document.getDocumentDataModel().getSnapshot().documentStyle, this._getConfigSnapshot());
815
+ if (columns.reduce((sum, { width, paddingEnd }) => sum + width + paddingEnd, 0) > contentWidth) throw new RangeError("Section columns exceed the available page content width.");
750
816
  return this._update({
751
817
  columnProperties: Tools.deepClone(columns),
752
818
  columnSeparatorType: separator
753
819
  });
754
820
  }
755
821
  /**
756
- * Sets how the next section begins.
822
+ * Sets how this section begins relative to the previous section.
823
+ *
824
+ * The first section has no preceding boundary, so setting its type does not
825
+ * create an initial blank page. Prefer `FDocument.insertSectionBreak` with
826
+ * `nextSectionType` when creating a new boundary; use this method when
827
+ * updating an existing section after resolving it again from the document.
828
+ *
829
+ * @param {SectionType} sectionType How this section begins.
830
+ * @returns {boolean} `true` when the section command was applied.
757
831
  * @example
758
832
  * ```ts
759
- * const fDocument = univerAPI.getActiveDocument();
760
- * if (fDocument && !fDocument.isModern()) {
761
- * fDocument.getSection(0)?.setSectionType(univerAPI.Enum.SectionType.NEXT_PAGE);
833
+ * const document = univerAPI.getActiveDocument();
834
+ * if (!document?.isTraditional()) {
835
+ * throw new Error('A Traditional document is required');
836
+ * }
837
+ *
838
+ * const secondSection = document.getSection(1);
839
+ * if (!secondSection) {
840
+ * throw new Error('The second section does not exist');
841
+ * }
842
+ * if (!secondSection.setSectionType(univerAPI.Enum.SectionType.NEXT_PAGE)) {
843
+ * throw new Error('Failed to update the second section');
762
844
  * }
763
845
  * ```
764
846
  */
765
847
  setSectionType(sectionType) {
766
848
  this._assertTraditionalDocument();
849
+ if (!Object.values(SectionType).includes(sectionType)) throw new RangeError("Invalid section type.");
767
850
  return this._update({ sectionType });
768
851
  }
769
852
  /**
853
+ * Returns this section's explicit page setup overrides.
854
+ * Missing values inherit from the document style. Geometry values use 96-DPI layout pixels.
855
+ *
856
+ * Use `getEffectivePageSetup()` when an agent needs resolved page and content
857
+ * dimensions rather than only the overrides stored on this section.
858
+ *
859
+ * @returns {FDocumentSectionPageSetup} A cloned object containing only explicit section overrides.
860
+ * @example
861
+ * ```ts
862
+ * const document = univerAPI.getActiveDocument();
863
+ * const section = document?.getSection(0);
864
+ * console.log(section?.getPageSetup());
865
+ * ```
866
+ */
867
+ getPageSetup() {
868
+ const { pageNumberStart, pageSize, pageOrient, marginTop, marginBottom, marginLeft, marginRight } = this._getConfigSnapshot();
869
+ return Tools.deepClone({
870
+ pageNumberStart,
871
+ pageSize,
872
+ pageOrient,
873
+ marginTop,
874
+ marginBottom,
875
+ marginLeft,
876
+ marginRight
877
+ });
878
+ }
879
+ /**
880
+ * Returns nominal page geometry after resolving this section's overrides
881
+ * against document defaults. All geometry values use 96-DPI layout pixels.
882
+ *
883
+ * This synchronous model-only API works without `engine-render`. It does not
884
+ * report physical page count, remaining page space, or final coordinates.
885
+ *
886
+ * @returns {IEffectiveSectionPageSetup} A cloned, serializable page setup.
887
+ * @example
888
+ * ```ts
889
+ * const document = univerAPI.getActiveDocument();
890
+ * if (!document) {
891
+ * throw new Error('No active document');
892
+ * }
893
+ * if (!document.isTraditional()) {
894
+ * throw new Error('Traditional document sections are required');
895
+ * }
896
+ *
897
+ * const section = document.getSection(0);
898
+ * if (!section) {
899
+ * throw new Error('The document has no traditional section');
900
+ * }
901
+ *
902
+ * const layout = section.getEffectivePageSetup();
903
+ * console.log({
904
+ * pageWidth: layout.pageSize.width,
905
+ * pageHeight: layout.pageSize.height,
906
+ * contentWidth: layout.contentSize.width,
907
+ * contentHeight: layout.contentSize.height,
908
+ * margins: layout.margins,
909
+ * });
910
+ * ```
911
+ */
912
+ getEffectivePageSetup() {
913
+ this._assertTraditionalDocument();
914
+ const documentStyle = this._document.getDocumentDataModel().getSnapshot().documentStyle;
915
+ return Tools.deepClone(getEffectiveSectionPageSetup(documentStyle, this._getConfigSnapshot()));
916
+ }
917
+ /**
918
+ * Updates this section's page setup through the document section command.
919
+ * Geometry values use 96-DPI layout pixels.
920
+ *
921
+ * This method changes static page geometry; it does not choose where the
922
+ * section begins. Use `setSectionType()` for an existing boundary, or
923
+ * `insertSectionBreak(..., { nextSectionType })` while creating one.
924
+ *
925
+ * @param {FDocumentSectionPageSetup} pageSetup Explicit section overrides to patch.
926
+ * @returns {boolean} `true` when the section command was applied.
927
+ * @example
928
+ * ```ts
929
+ * const document = univerAPI.getActiveDocument();
930
+ * if (!document?.isTraditional()) {
931
+ * throw new Error('A Traditional document is required');
932
+ * }
933
+ *
934
+ * const section = document.getSection(1);
935
+ * if (!section) {
936
+ * throw new Error('The second section does not exist');
937
+ * }
938
+ * const updated = section.setPageSetup({
939
+ * pageSize: { width: 816, height: 1056 },
940
+ * marginTop: 96,
941
+ * marginBottom: 96,
942
+ * marginLeft: 96,
943
+ * marginRight: 96,
944
+ * });
945
+ * if (!updated) {
946
+ * throw new Error('Failed to update section page setup');
947
+ * }
948
+ * console.log(section.getEffectivePageSetup());
949
+ * ```
950
+ */
951
+ setPageSetup(pageSetup) {
952
+ this._assertTraditionalDocument();
953
+ validatePageSetup(pageSetup);
954
+ const definedPageSetup = Tools.deepClone(pageSetup);
955
+ Tools.removeNull(definedPageSetup);
956
+ const documentStyle = this._document.getDocumentDataModel().getSnapshot().documentStyle;
957
+ getEffectiveSectionPageSetup(documentStyle, {
958
+ ...this._getConfigSnapshot(),
959
+ ...definedPageSetup
960
+ });
961
+ return this._update(definedPageSetup);
962
+ }
963
+ /**
770
964
  * Ensures a header segment linked specifically to this section.
771
965
  * @example
772
966
  * ```ts
773
967
  * const fDocument = univerAPI.getActiveDocument();
774
- * if (fDocument && !fDocument.isModern()) {
968
+ * if (fDocument?.isTraditional()) {
775
969
  * const segmentId = fDocument.getSection(0)?.ensureHeader();
776
970
  * if (segmentId) {
777
971
  * fDocument.insertText(0, 'Quarterly report', segmentId);
@@ -787,7 +981,7 @@ var FDocumentSection = class {
787
981
  * @example
788
982
  * ```ts
789
983
  * const fDocument = univerAPI.getActiveDocument();
790
- * if (fDocument && !fDocument.isModern()) {
984
+ * if (fDocument?.isTraditional()) {
791
985
  * const segmentId = fDocument.getSection(0)?.ensureFooter('first');
792
986
  * if (segmentId) {
793
987
  * fDocument.insertText(0, 'Confidential', segmentId);
@@ -807,8 +1001,7 @@ var FDocumentSection = class {
807
1001
  * ```
808
1002
  */
809
1003
  getHeaderId(variant = "default") {
810
- var _this$_getHeaderFoote;
811
- return (_this$_getHeaderFoote = this._getHeaderFooterReference("header", variant).segmentId) !== null && _this$_getHeaderFoote !== void 0 ? _this$_getHeaderFoote : null;
1004
+ return this._getHeaderFooterReference("header", variant).segmentId ?? null;
812
1005
  }
813
1006
  /**
814
1007
  * Returns the effective footer id after resolving links to previous sections.
@@ -819,8 +1012,7 @@ var FDocumentSection = class {
819
1012
  * ```
820
1013
  */
821
1014
  getFooterId(variant = "default") {
822
- var _this$_getHeaderFoote2;
823
- return (_this$_getHeaderFoote2 = this._getHeaderFooterReference("footer", variant).segmentId) !== null && _this$_getHeaderFoote2 !== void 0 ? _this$_getHeaderFoote2 : null;
1015
+ return this._getHeaderFooterReference("footer", variant).segmentId ?? null;
824
1016
  }
825
1017
  /**
826
1018
  * Whether this header variant inherits the previous section's reference.
@@ -849,7 +1041,7 @@ var FDocumentSection = class {
849
1041
  * @example
850
1042
  * ```ts
851
1043
  * const fDocument = univerAPI.getActiveDocument();
852
- * if (fDocument && !fDocument.isModern()) {
1044
+ * if (fDocument?.isTraditional()) {
853
1045
  * fDocument.getSection(1)?.setHeaderLinkedToPrevious(false, 'default');
854
1046
  * }
855
1047
  * ```
@@ -862,7 +1054,7 @@ var FDocumentSection = class {
862
1054
  * @example
863
1055
  * ```ts
864
1056
  * const fDocument = univerAPI.getActiveDocument();
865
- * if (fDocument && !fDocument.isModern()) {
1057
+ * if (fDocument?.isTraditional()) {
866
1058
  * fDocument.getSection(1)?.setFooterLinkedToPrevious(true, 'even');
867
1059
  * }
868
1060
  * ```
@@ -872,11 +1064,11 @@ var FDocumentSection = class {
872
1064
  }
873
1065
  /**
874
1066
  * Updates header/footer switches and margins on this section break.
875
- * `marginHeader` and `marginFooter` are in points (pt).
1067
+ * `marginHeader` and `marginFooter` are in 96-DPI layout pixels.
876
1068
  * @example
877
1069
  * ```ts
878
1070
  * const fDocument = univerAPI.getActiveDocument();
879
- * if (fDocument && !fDocument.isModern()) {
1071
+ * if (fDocument?.isTraditional()) {
880
1072
  * fDocument.getSection(0)?.setHeaderFooterOptions({
881
1073
  * marginHeader: 36,
882
1074
  * marginFooter: 36,
@@ -894,7 +1086,7 @@ var FDocumentSection = class {
894
1086
  * @example
895
1087
  * ```ts
896
1088
  * const fDocument = univerAPI.getActiveDocument();
897
- * if (fDocument && !fDocument.isModern()) {
1089
+ * if (fDocument?.isTraditional()) {
898
1090
  * const sections = fDocument.getSections();
899
1091
  * if (sections.length > 1) {
900
1092
  * sections[0].remove();
@@ -904,14 +1096,14 @@ var FDocumentSection = class {
904
1096
  */
905
1097
  remove() {
906
1098
  this._assertTraditionalDocument();
907
- return this._injector.get(ICommandService).syncExecuteCommand(DeleteDocumentSectionBreakCommand.id, {
1099
+ return this._commandService.syncExecuteCommand(DeleteDocumentSectionBreakCommand.id, {
908
1100
  unitId: this._document.getId(),
909
1101
  sectionId: this._sectionId
910
1102
  });
911
1103
  }
912
1104
  _update(patch) {
913
1105
  const { sectionId: _sectionId, startIndex: _startIndex, ...config } = patch;
914
- return this._injector.get(ICommandService).syncExecuteCommand(UpdateDocumentSectionCommand.id, {
1106
+ return this._commandService.syncExecuteCommand(UpdateDocumentSectionCommand.id, {
915
1107
  unitId: this._document.getId(),
916
1108
  updates: [{
917
1109
  sectionId: this._sectionId,
@@ -922,11 +1114,11 @@ var FDocumentSection = class {
922
1114
  _ensureHeaderFooter(kind, variant) {
923
1115
  this._assertTraditionalDocument();
924
1116
  const { index } = this._resolve();
925
- const existing = this.getConfig()[getSectionHeaderFooterReferenceKey(kind, variant)];
1117
+ const existing = this._getConfigSnapshot()[getSectionHeaderFooterReferenceKey(kind, variant)];
926
1118
  if (typeof existing === "string" && existing) return existing;
927
1119
  if (index > 0) {
928
1120
  const segmentId = generateRandomId(6);
929
- if (!this._injector.get(ICommandService).syncExecuteCommand(SetSectionHeaderFooterLinkCommand.id, {
1121
+ if (!this._commandService.syncExecuteCommand(SetSectionHeaderFooterLinkCommand.id, {
930
1122
  unitId: this._document.getId(),
931
1123
  sectionId: this._sectionId,
932
1124
  kind,
@@ -942,7 +1134,7 @@ var FDocumentSection = class {
942
1134
  even: kind === "header" ? HeaderFooterType.EVEN_PAGE_HEADER : HeaderFooterType.EVEN_PAGE_FOOTER
943
1135
  };
944
1136
  const segmentId = generateRandomId(6);
945
- if (!this._injector.get(ICommandService).syncExecuteCommand(CreateHeaderFooterCommand.id, {
1137
+ if (!this._commandService.syncExecuteCommand(CreateHeaderFooterCommand.id, {
946
1138
  unitId: this._document.getId(),
947
1139
  segmentId,
948
1140
  createType: types[variant],
@@ -955,16 +1147,15 @@ var FDocumentSection = class {
955
1147
  return resolveSectionHeaderFooterReference(this._document.getDocumentDataModel().getSnapshot().documentStyle, getTopLevelSectionBreaks(this._document.getBody()), index, getSectionHeaderFooterReferenceKey(kind, variant));
956
1148
  }
957
1149
  _describeHeaderFooterReference(kind, variant) {
958
- var _reference$segmentId;
959
1150
  const reference = this._getHeaderFooterReference(kind, variant);
960
1151
  return {
961
- segmentId: (_reference$segmentId = reference.segmentId) !== null && _reference$segmentId !== void 0 ? _reference$segmentId : null,
1152
+ segmentId: reference.segmentId ?? null,
962
1153
  linkedToPrevious: reference.linkedToPrevious
963
1154
  };
964
1155
  }
965
1156
  _setHeaderFooterLinkedToPrevious(kind, variant, linkedToPrevious) {
966
1157
  this._assertTraditionalDocument();
967
- return this._injector.get(ICommandService).syncExecuteCommand(SetSectionHeaderFooterLinkCommand.id, {
1158
+ return this._commandService.syncExecuteCommand(SetSectionHeaderFooterLinkCommand.id, {
968
1159
  unitId: this._document.getId(),
969
1160
  sectionId: this._sectionId,
970
1161
  kind,
@@ -976,6 +1167,17 @@ var FDocumentSection = class {
976
1167
  _assertTraditionalDocument() {
977
1168
  if (this._document.getDocumentDataModel().getSnapshot().documentStyle.documentFlavor !== DocumentFlavor.TRADITIONAL) throw new DocsSectionUnsupportedDocumentFlavorError();
978
1169
  }
1170
+ _getConfigSnapshot() {
1171
+ return Tools.deepClone(this._resolve().sectionBreak);
1172
+ }
1173
+ _getRange(index) {
1174
+ const sectionBreaks = getTopLevelSectionBreaks(this._document.getBody());
1175
+ return {
1176
+ startOffset: index === 0 ? 0 : sectionBreaks[index - 1].startIndex + 1,
1177
+ endOffset: sectionBreaks[index].startIndex,
1178
+ segmentId: ""
1179
+ };
1180
+ }
979
1181
  _resolve() {
980
1182
  this._assertTraditionalDocument();
981
1183
  const sectionBreaks = getTopLevelSectionBreaks(this._document.getBody());
@@ -987,9 +1189,10 @@ var FDocumentSection = class {
987
1189
  };
988
1190
  }
989
1191
  };
1192
+ FDocumentSection = __decorate([__decorateParam(2, ICommandService)], FDocumentSection);
990
1193
 
991
1194
  //#endregion
992
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/typeof.js
1195
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/typeof.js
993
1196
  function _typeof(o) {
994
1197
  "@babel/helpers - typeof";
995
1198
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -1000,7 +1203,7 @@ function _typeof(o) {
1000
1203
  }
1001
1204
 
1002
1205
  //#endregion
1003
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPrimitive.js
1206
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPrimitive.js
1004
1207
  function toPrimitive(t, r) {
1005
1208
  if ("object" != _typeof(t) || !t) return t;
1006
1209
  var e = t[Symbol.toPrimitive];
@@ -1013,14 +1216,14 @@ function toPrimitive(t, r) {
1013
1216
  }
1014
1217
 
1015
1218
  //#endregion
1016
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPropertyKey.js
1219
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/toPropertyKey.js
1017
1220
  function toPropertyKey(t) {
1018
1221
  var i = toPrimitive(t, "string");
1019
1222
  return "symbol" == _typeof(i) ? i : i + "";
1020
1223
  }
1021
1224
 
1022
1225
  //#endregion
1023
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/defineProperty.js
1226
+ //#region \0@oxc-project+runtime@0.140.0/helpers/esm/defineProperty.js
1024
1227
  function _defineProperty(e, r, t) {
1025
1228
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
1026
1229
  value: t,
@@ -1030,23 +1233,6 @@ function _defineProperty(e, r, t) {
1030
1233
  }) : e[r] = t, e;
1031
1234
  }
1032
1235
 
1033
- //#endregion
1034
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorateParam.js
1035
- function __decorateParam(paramIndex, decorator) {
1036
- return function(target, key) {
1037
- decorator(target, key, paramIndex);
1038
- };
1039
- }
1040
-
1041
- //#endregion
1042
- //#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorate.js
1043
- function __decorate(decorators, target, key, desc) {
1044
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1045
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1046
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1047
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1048
- }
1049
-
1050
1236
  //#endregion
1051
1237
  //#region src/facade/f-document.ts
1052
1238
  let FDocument = class FDocument extends FBaseInitialable {
@@ -1079,6 +1265,28 @@ let FDocument = class FDocument extends FBaseInitialable {
1079
1265
  return documentDataModel;
1080
1266
  }
1081
1267
  /**
1268
+ * Returns the document's custom blocks in stable model order.
1269
+ *
1270
+ * This method is available in Node/headless environments and does not
1271
+ * perform font measurement, line wrapping, pagination, or rendering.
1272
+ *
1273
+ * @returns {IDocumentCustomBlockLayout} Custom block identifiers and model positions.
1274
+ * @example
1275
+ * ```ts
1276
+ * const document = univerAPI.getActiveDocument();
1277
+ * const layout = document?.getCustomBlockLayout();
1278
+ * console.log(layout?.blocks);
1279
+ * ```
1280
+ */
1281
+ getCustomBlockLayout() {
1282
+ var _this$_documentDataMo;
1283
+ return { blocks: (((_this$_documentDataMo = this._documentDataModel.getBody()) === null || _this$_documentDataMo === void 0 ? void 0 : _this$_documentDataMo.customBlocks) ?? []).map(({ blockId, startIndex }, index) => ({
1284
+ blockId,
1285
+ startIndex,
1286
+ index
1287
+ })) };
1288
+ }
1289
+ /**
1082
1290
  * Get the document body or header/footer body by the segment id.
1083
1291
  * The main body has an empty segment id.
1084
1292
  * The header and footer body have their respective segment ids.
@@ -1094,8 +1302,8 @@ let FDocument = class FDocument extends FBaseInitialable {
1094
1302
  * ```
1095
1303
  */
1096
1304
  getBody(segmentId = "") {
1097
- var _this$_documentDataMo;
1098
- const body = (_this$_documentDataMo = this._documentDataModel.getSelfOrHeaderFooterModel(segmentId)) === null || _this$_documentDataMo === void 0 ? void 0 : _this$_documentDataMo.getBody();
1305
+ var _this$_documentDataMo2;
1306
+ const body = (_this$_documentDataMo2 = this._documentDataModel.getSelfOrHeaderFooterModel(segmentId)) === null || _this$_documentDataMo2 === void 0 ? void 0 : _this$_documentDataMo2.getBody();
1099
1307
  if (!body) throw new Error(segmentId === "" ? "Body is not found in the document." : `Body is not found in the segment: ${segmentId}`);
1100
1308
  return body;
1101
1309
  }
@@ -1127,16 +1335,72 @@ let FDocument = class FDocument extends FBaseInitialable {
1127
1335
  return this._documentDataModel.getTitle() || "";
1128
1336
  }
1129
1337
  /**
1130
- * Whether the document is a modern document or not.
1131
- * @returns {boolean} `true` if the document is a modern document, or `false` if it is not.
1338
+ * Returns the document's explicit layout flavor.
1339
+ *
1340
+ * Use this method when all three states matter. Do not infer a Traditional
1341
+ * document from `!isModern()`: that expression is also true for
1342
+ * `DocumentFlavor.UNSPECIFIED`.
1343
+ *
1344
+ * @returns {DocumentFlavor} `TRADITIONAL`, `MODERN`, or `UNSPECIFIED`.
1345
+ * @example
1346
+ * ```typescript
1347
+ * const document = univerAPI.getActiveDocument();
1348
+ * if (!document) {
1349
+ * throw new Error('No active document');
1350
+ * }
1351
+ *
1352
+ * switch (document.getDocumentFlavor()) {
1353
+ * case univerAPI.Enum.DocumentFlavor.TRADITIONAL:
1354
+ * console.log('Word-compatible physical pagination is available');
1355
+ * break;
1356
+ * case univerAPI.Enum.DocumentFlavor.MODERN:
1357
+ * console.log('Use Modern Doc layout APIs such as ColumnGroup');
1358
+ * break;
1359
+ * default:
1360
+ * console.log('Resolve the unspecified flavor before using flavor-specific APIs');
1361
+ * }
1362
+ * ```
1363
+ */
1364
+ getDocumentFlavor() {
1365
+ return this._resolveDocumentFlavor();
1366
+ }
1367
+ /**
1368
+ * Whether this is a Traditional document with Word-compatible physical pagination.
1369
+ *
1370
+ * Prefer this positive guard before calling section, column-break, page-setup,
1371
+ * or paragraph-pagination APIs.
1372
+ *
1373
+ * @returns {boolean} `true` only for `DocumentFlavor.TRADITIONAL`.
1374
+ * @example
1375
+ * ```typescript
1376
+ * const document = univerAPI.getActiveDocument();
1377
+ * if (document?.isTraditional()) {
1378
+ * console.log(document.getSection(0)?.getEffectivePageSetup());
1379
+ * }
1380
+ * ```
1381
+ */
1382
+ isTraditional() {
1383
+ return this._resolveDocumentFlavor() === DocumentFlavor.TRADITIONAL;
1384
+ }
1385
+ /**
1386
+ * Whether this is a Modern document.
1387
+ *
1388
+ * A `false` result can mean either Traditional or Unspecified. Use
1389
+ * `isTraditional()` before Traditional-only APIs, or `getDocumentFlavor()`
1390
+ * when all three states matter.
1391
+ *
1392
+ * @returns {boolean} `true` only for `DocumentFlavor.MODERN`.
1132
1393
  * @example
1133
1394
  * ```typescript
1134
1395
  * const fDocument = univerAPI.getActiveDocument();
1135
- * console.log(fDocument.isModern());
1396
+ * console.log(fDocument?.isModern());
1136
1397
  * ```
1137
1398
  */
1138
1399
  isModern() {
1139
- return this._documentDataModel.getSnapshot().documentStyle.documentFlavor === DocumentFlavor.MODERN;
1400
+ return this._resolveDocumentFlavor() === DocumentFlavor.MODERN;
1401
+ }
1402
+ _resolveDocumentFlavor() {
1403
+ return this._documentDataModel.getSnapshot().documentStyle.documentFlavor ?? DocumentFlavor.UNSPECIFIED;
1140
1404
  }
1141
1405
  /**
1142
1406
  * Save the document snapshot data, including the document content and resource data, etc.
@@ -1230,7 +1494,7 @@ let FDocument = class FDocument extends FBaseInitialable {
1230
1494
  }, buildPlainTextInsertBody(text), this._documentDataModel, this._injector);
1231
1495
  }
1232
1496
  /**
1233
- * Returns document-level header/footer switches and margins. Margin values are in points (pt).
1497
+ * Returns document-level header/footer switches and margins. Margin values use 96-DPI layout pixels.
1234
1498
  * @example
1235
1499
  * ```ts
1236
1500
  * const fDocument = univerAPI.getActiveDocument();
@@ -1247,12 +1511,18 @@ let FDocument = class FDocument extends FBaseInitialable {
1247
1511
  };
1248
1512
  }
1249
1513
  /**
1250
- * Updates document-level header/footer switches and margins in a traditional document.
1251
- * `marginHeader` and `marginFooter` are in points (pt).
1514
+ * Updates document-level header/footer switches and margins.
1515
+ *
1516
+ * Traditional and Unspecified documents keep the legacy header/footer
1517
+ * behavior. Modern documents reject this API. `marginHeader` and
1518
+ * `marginFooter` use 96-DPI layout pixels.
1252
1519
  * @example
1253
1520
  * ```ts
1254
1521
  * const fDocument = univerAPI.getActiveDocument();
1255
- * if (fDocument && !fDocument.isModern()) {
1522
+ * if (
1523
+ * fDocument &&
1524
+ * fDocument.getDocumentFlavor() !== univerAPI.Enum.DocumentFlavor.MODERN
1525
+ * ) {
1256
1526
  * fDocument.setHeaderFooterOptions({ marginHeader: 36, marginFooter: 36 });
1257
1527
  * }
1258
1528
  * ```
@@ -1293,7 +1563,7 @@ let FDocument = class FDocument extends FBaseInitialable {
1293
1563
  */
1294
1564
  getSections() {
1295
1565
  if (this._documentDataModel.getSnapshot().documentStyle.documentFlavor !== DocumentFlavor.TRADITIONAL) return [];
1296
- return getTopLevelSectionBreaks(this.getBody()).map((sectionBreak) => this._injector.createInstance(FDocumentSection, this, sectionBreak.sectionId, this._injector));
1566
+ return getTopLevelSectionBreaks(this.getBody()).map((sectionBreak) => this._injector.createInstance(FDocumentSection, this, sectionBreak.sectionId));
1297
1567
  }
1298
1568
  /**
1299
1569
  * Returns a traditional section by zero-based index, or `null` in modern documents.
@@ -1305,8 +1575,7 @@ let FDocument = class FDocument extends FBaseInitialable {
1305
1575
  * ```
1306
1576
  */
1307
1577
  getSection(index) {
1308
- var _this$getSections$ind;
1309
- return (_this$getSections$ind = this.getSections()[index]) !== null && _this$getSections$ind !== void 0 ? _this$getSections$ind : null;
1578
+ return this.getSections()[index] ?? null;
1310
1579
  }
1311
1580
  /**
1312
1581
  * Returns the traditional section containing a data-stream offset, or `null` in modern documents.
@@ -1320,45 +1589,84 @@ let FDocument = class FDocument extends FBaseInitialable {
1320
1589
  * ```
1321
1590
  */
1322
1591
  getSectionAt(offset) {
1323
- var _this$getSections$fin;
1324
- return (_this$getSections$fin = this.getSections().find((section) => {
1592
+ return this.getSections().find((section) => {
1325
1593
  const range = section.getRange();
1326
- return offset >= range.startOffset && offset <= range.endOffset;
1327
- })) !== null && _this$getSections$fin !== void 0 ? _this$getSections$fin : null;
1594
+ return offset >= range.startOffset && offset < range.endOffset;
1595
+ }) ?? null;
1328
1596
  }
1329
1597
  /**
1330
1598
  * Inserts a traditional document section break and returns its stable facade.
1331
- * Modern documents must use ColumnGroup and throw `DocsSectionUnsupportedDocumentFlavorError`.
1332
- * Numeric layout values in `config` are in points (pt).
1599
+ *
1600
+ * `options` configures the section created before the inserted break.
1601
+ * Set `options.nextSectionType` to control how the existing section after the
1602
+ * break begins. For example, use `SectionType.NEXT_PAGE` to start a chapter on
1603
+ * a new physical page. Both changes are executed by one command and are
1604
+ * undone or redone together.
1605
+ *
1606
+ * The offset must be a top-level document position. To insert a break before
1607
+ * a table or block such as a callout, use that object's start offset instead
1608
+ * of an offset inside the object.
1609
+ *
1610
+ * Modern documents must use ColumnGroup. Unspecified documents must resolve
1611
+ * their flavor first. Both throw `DocsSectionUnsupportedDocumentFlavorError`.
1612
+ * Numeric layout values in `options` are in 96-DPI layout pixels.
1613
+ *
1614
+ * @param {number} offset Top-level data-stream offset where the section break is inserted.
1615
+ * @param {IFDocumentInsertSectionBreakOptions} [options] Section properties and the optional type of the following section.
1616
+ * @returns {FDocumentSection | null} The section created before the break, or `null` when the command rejects the insertion.
1333
1617
  * @example
1334
1618
  * ```ts
1335
- * const fDocument = univerAPI.getActiveDocument();
1336
- * if (fDocument && !fDocument.isModern()) {
1337
- * const paragraph = fDocument.findParagraphByText('Appendix');
1338
- * const offset = paragraph?.getInfo().startOffset;
1339
- * const section = offset == null ? null : fDocument.insertSectionBreak(offset);
1340
- * console.log(section?.getId());
1619
+ * const document = univerAPI.getActiveDocument();
1620
+ * if (!document) {
1621
+ * throw new Error('No active document');
1622
+ * }
1623
+ * if (!document.isTraditional()) {
1624
+ * throw new Error('Traditional document sections are required');
1341
1625
  * }
1626
+ *
1627
+ * const chapter = document.findParagraphByText('Chapter 2');
1628
+ * if (!chapter) {
1629
+ * throw new Error('Chapter heading not found');
1630
+ * }
1631
+ *
1632
+ * // Insert the boundary immediately before the chapter heading. The command
1633
+ * // also marks the following section as NEXT_PAGE, so the two model changes
1634
+ * // share one undo/redo step.
1635
+ * const sectionBeforeChapter = document.insertSectionBreak(
1636
+ * chapter.getInfo().startOffset,
1637
+ * { nextSectionType: univerAPI.Enum.SectionType.NEXT_PAGE }
1638
+ * );
1639
+ * if (!sectionBeforeChapter) {
1640
+ * throw new Error('The chapter heading is not at a valid top-level offset');
1641
+ * }
1642
+ *
1643
+ * console.log({
1644
+ * insertedSection: sectionBeforeChapter.describe(),
1645
+ * chapterSection: document.getSectionAt(chapter.getInfo().startOffset)?.describe(),
1646
+ * });
1342
1647
  * ```
1343
1648
  */
1344
- insertSectionBreak(offset, config = {}) {
1345
- var _this$getBody$section;
1649
+ insertSectionBreak(offset, options = {}) {
1346
1650
  if (this._documentDataModel.getSnapshot().documentStyle.documentFlavor !== DocumentFlavor.TRADITIONAL) throw new DocsSectionUnsupportedDocumentFlavorError();
1347
- const sectionId = createSectionId(new Set(((_this$getBody$section = this.getBody().sectionBreaks) !== null && _this$getBody$section !== void 0 ? _this$getBody$section : []).map((section) => section.sectionId)));
1651
+ const { nextSectionType, ...config } = options;
1652
+ const sectionId = createSectionId(new Set((this.getBody().sectionBreaks ?? []).map((section) => section.sectionId)));
1348
1653
  return this._commandService.syncExecuteCommand(InsertDocumentSectionBreakCommand.id, {
1349
1654
  unitId: this.getId(),
1350
1655
  offset,
1351
1656
  sectionId,
1352
- config
1353
- }) ? this._injector.createInstance(FDocumentSection, this, sectionId, this._injector) : null;
1657
+ config,
1658
+ nextSectionType
1659
+ }) ? this._injector.createInstance(FDocumentSection, this, sectionId) : null;
1354
1660
  }
1355
1661
  /**
1356
1662
  * Inserts a column-break token in a traditional document.
1357
- * Modern documents must use ColumnGroup and throw `DocsSectionUnsupportedDocumentFlavorError`.
1663
+ * In a single-column section, the traditional renderer advances to the next physical page.
1664
+ * Modern documents must use ColumnGroup. Unspecified documents must resolve
1665
+ * their flavor first. Both throw `DocsSectionUnsupportedDocumentFlavorError`.
1358
1666
  * @example
1359
1667
  * ```ts
1360
1668
  * const fDocument = univerAPI.getActiveDocument();
1361
- * if (fDocument && !fDocument.isModern()) {
1669
+ * if (fDocument?.isTraditional()) {
1362
1670
  * const paragraph = fDocument.findParagraphByText('Continue in next column');
1363
1671
  * const offset = paragraph?.getInfo().startOffset;
1364
1672
  * if (offset != null) {
@@ -1369,7 +1677,10 @@ let FDocument = class FDocument extends FBaseInitialable {
1369
1677
  */
1370
1678
  insertColumnBreak(offset) {
1371
1679
  if (this._documentDataModel.getSnapshot().documentStyle.documentFlavor !== DocumentFlavor.TRADITIONAL) throw new DocsSectionUnsupportedDocumentFlavorError();
1372
- return this.insertText(offset, DataStreamTreeTokenType.COLUMN_BREAK);
1680
+ return this._commandService.syncExecuteCommand(InsertDocumentColumnBreakCommand.id, {
1681
+ unitId: this.getId(),
1682
+ offset
1683
+ });
1373
1684
  }
1374
1685
  /**
1375
1686
  * Inserts a horizontal rule using the existing paragraph `borderBottom` mechanism.
@@ -1592,36 +1903,29 @@ let FDocument = class FDocument extends FBaseInitialable {
1592
1903
  return segmentId;
1593
1904
  }
1594
1905
  _getHeaderFooterCreateInfo(kind, pageIndex) {
1595
- var _documentStyle$defaul, _documentStyle$defaul2;
1596
1906
  const { documentStyle } = this._documentDataModel.getSnapshot();
1597
1907
  const isFirstPage = pageIndex === 0;
1598
1908
  const isEvenPage = (pageIndex + 1) % 2 === 0;
1599
- if (isFirstPage && documentStyle.useFirstPageHeaderFooter === BooleanNumber.TRUE) {
1600
- var _documentStyle$firstP, _documentStyle$firstP2;
1601
- return kind === "header" ? {
1602
- createType: HeaderFooterType.FIRST_PAGE_HEADER,
1603
- segmentId: (_documentStyle$firstP = documentStyle.firstPageHeaderId) !== null && _documentStyle$firstP !== void 0 ? _documentStyle$firstP : ""
1604
- } : {
1605
- createType: HeaderFooterType.FIRST_PAGE_FOOTER,
1606
- segmentId: (_documentStyle$firstP2 = documentStyle.firstPageFooterId) !== null && _documentStyle$firstP2 !== void 0 ? _documentStyle$firstP2 : ""
1607
- };
1608
- }
1609
- if (isEvenPage && documentStyle.evenAndOddHeaders === BooleanNumber.TRUE) {
1610
- var _documentStyle$evenPa, _documentStyle$evenPa2;
1611
- return kind === "header" ? {
1612
- createType: HeaderFooterType.EVEN_PAGE_HEADER,
1613
- segmentId: (_documentStyle$evenPa = documentStyle.evenPageHeaderId) !== null && _documentStyle$evenPa !== void 0 ? _documentStyle$evenPa : ""
1614
- } : {
1615
- createType: HeaderFooterType.EVEN_PAGE_FOOTER,
1616
- segmentId: (_documentStyle$evenPa2 = documentStyle.evenPageFooterId) !== null && _documentStyle$evenPa2 !== void 0 ? _documentStyle$evenPa2 : ""
1617
- };
1618
- }
1909
+ if (isFirstPage && documentStyle.useFirstPageHeaderFooter === BooleanNumber.TRUE) return kind === "header" ? {
1910
+ createType: HeaderFooterType.FIRST_PAGE_HEADER,
1911
+ segmentId: documentStyle.firstPageHeaderId ?? ""
1912
+ } : {
1913
+ createType: HeaderFooterType.FIRST_PAGE_FOOTER,
1914
+ segmentId: documentStyle.firstPageFooterId ?? ""
1915
+ };
1916
+ if (isEvenPage && documentStyle.evenAndOddHeaders === BooleanNumber.TRUE) return kind === "header" ? {
1917
+ createType: HeaderFooterType.EVEN_PAGE_HEADER,
1918
+ segmentId: documentStyle.evenPageHeaderId ?? ""
1919
+ } : {
1920
+ createType: HeaderFooterType.EVEN_PAGE_FOOTER,
1921
+ segmentId: documentStyle.evenPageFooterId ?? ""
1922
+ };
1619
1923
  return kind === "header" ? {
1620
1924
  createType: HeaderFooterType.DEFAULT_HEADER,
1621
- segmentId: (_documentStyle$defaul = documentStyle.defaultHeaderId) !== null && _documentStyle$defaul !== void 0 ? _documentStyle$defaul : ""
1925
+ segmentId: documentStyle.defaultHeaderId ?? ""
1622
1926
  } : {
1623
1927
  createType: HeaderFooterType.DEFAULT_FOOTER,
1624
- segmentId: (_documentStyle$defaul2 = documentStyle.defaultFooterId) !== null && _documentStyle$defaul2 !== void 0 ? _documentStyle$defaul2 : ""
1928
+ segmentId: documentStyle.defaultFooterId ?? ""
1625
1929
  };
1626
1930
  }
1627
1931
  };
@@ -1670,6 +1974,9 @@ FUniver.extend(FUniverDocsMixin);
1670
1974
  * limitations under the License.
1671
1975
  */
1672
1976
  var FDocsEnumMixin = class extends FEnum {
1977
+ get DocumentFlavor() {
1978
+ return DocumentFlavor;
1979
+ }
1673
1980
  get SectionType() {
1674
1981
  return SectionType;
1675
1982
  }