docs: clean up comments

This commit is contained in:
Steffen Becker committed 2026-07-08 19:21:12 +00:00
1 parent 15f7e50976
commit d11fc065f8
5 files changed
+33 -57

No files matched your search

+6 -9
View File
@@ -3,10 +3,9 @@ import type { Node as PMNode } from '@tiptap/pm/model';
import { TextSelection } from '@tiptap/pm/state';
import type { EditorState, Transaction } from '@tiptap/pm/state';
// A multi-column (newspaper) section: a block region whose content flows through
// balanced CSS columns. The document may hold it as several adjacent fragments with
// equal attrs — a "chain" — so the flow can continue across page breaks
// (columnsFlow.ts splits/joins fragments); export merges a chain into one section.
// A multi-column (newspaper) section: content flows through balanced CSS columns.
// The doc may hold it as adjacent equal-attr fragments (a "chain") so columnsFlow.ts
// can split/join across page breaks; export merges a chain into one section.
export const DEFAULT_COLUMN_GAP_CM = 0.5;
export const MAX_COLUMN_GAP_CM = 5;
@@ -26,11 +25,9 @@ declare module '@tiptap/core' {
}
}
// Shared fit margin for the "does the first block fit the remaining page space"
// test: the balanced-height estimate (block height / count) undershoots the real
// fragment height by margins + line quantization. pageBreaks.ts (push vs. wait for
// the flow) and columnsFlow.ts (split vs. leave) MUST use the same margin, or the
// two disagree at the boundary and split/push/join cycle forever.
// Fit margin for "does the first block fit the remaining page space": the
// balanced-height estimate undershoots real height by margins + line quantization.
// pageBreaks.ts and columnsFlow.ts MUST share this or split/push/join cycles forever.
export const COLUMNS_FIT_MARGIN_PX = 32;
export function clampColumnCount(n: number): number {
+12 -27
View File
@@ -7,20 +7,9 @@ import type { Node as PMNode } from '@tiptap/pm/model';
import { readVerticalMargins, FORCE_PAGE_RECALC } from './pageBreaks';
import { sameColumnsAttrs, COLUMNS_FIT_MARGIN_PX } from './columns';
// Cross-page column flow: the document holds a columns section as adjacent
// equal-attr fragments ("chain"). This layout pass keeps the fragmentation in sync
// with the page grid — an overflowing fragment is split at a block boundary (or,
// for a paragraph taller than the page slot, at a LINE boundary: the second part
// carries the layout-internal joinPrev attr and is re-merged by joins/export), and
// a fragment with room pulls the continuation back. Splits/joins are pure layout:
// they never enter the undo history, and export merges a chain into one section.
//
// Fill order is sequential like Word (column 1 fills to the page bottom, then
// column 2, then the next page): fragments get a node decoration with an explicit
// height + column-fill:auto — full page slot for fragments with a continuation,
// content height for the open last fragment of a document-final chain (so typing
// grows column 1 first). A chain followed by real content keeps CSS balance on its
// last fragment (Word balances the end of a continuous section).
// Cross-page column flow: keeps a columns chain's fragmentation in sync with the
// page grid (split an overflowing fragment at a block or line boundary, pull a
// fragment with room back) via layout-only transactions that never enter undo history.
const flowKey = new PluginKey<number>('columnsFlow');
const FLOW_TX = 'columnsFlowTx';
@@ -208,11 +197,9 @@ export const ColumnsFlow = Extension.create({
return open;
}
// Split the paragraph at child `blockIndex` at the line whose prefix
// consumes `budgetPx` of content flow (Word's mid-paragraph page break);
// the second part is marked joinPrev so joins/export restore the original.
// `force` takes at least one line even when the budget is tiny (progress
// guard for a paragraph taller than the whole page slot).
// Split the paragraph at `blockIndex` at the line consuming `budgetPx` (Word's
// mid-paragraph page break); the second part is marked joinPrev so joins/export
// restore the original. `force` takes at least one line for an over-tall paragraph.
function splitParagraphInBlock(
frag: Fragment, children: Element[], blockIndex: number, budgetPx: number,
scale: number, force: boolean,
@@ -346,10 +333,9 @@ export const ColumnsFlow = Extension.create({
const headIsJoinPrev =
headBlock.type.name === 'paragraph' && headBlock.attrs.joinPrev &&
node.child(node.childCount - 1).type.name === 'paragraph';
// A joinPrev head needs only one more line row to make progress; a
// multi-line paragraph head can be pulled PARTIALLY (the overflow
// split hands the excess back via a line split), so ~two line rows
// of room justify the pull; any other block needs its full share.
// A joinPrev head needs only one more line row; a multi-line head can be
// pulled PARTIALLY (the overflow split hands the excess back), so ~two
// line rows justify the pull — any other block needs its full share.
const headLines = headBlock.type.name === 'paragraph' ? lineRects(nextFirst) : [];
const headAdvance = (lineAdvance(headLines) || DEFAULT_LINE_PX * scale) / scale;
const need = headIsJoinPrev
@@ -373,10 +359,9 @@ export const ColumnsFlow = Extension.create({
return false;
}
// Sequential-fill heights: a fragment with a continuation spans its page
// slot; the open last fragment of a document-final chain hugs its content
// (plus slack) so typing fills column 1 first. A chain followed by real
// content keeps its last fragment undecorated (CSS balance).
// Sequential-fill heights: a fragment with a continuation spans its page slot;
// an open last fragment hugs its content (plus slack) so typing fills column 1
// first. Otherwise the last fragment stays undecorated (CSS balance).
function updateDecorations(): void {
const frags = fragments();
const vm = readVerticalMargins(editorView.dom);
+8 -11
View File
@@ -130,9 +130,8 @@ export type VMargins = {
};
// Reads vertical page margins + page height (px) from the --user-* props the Layout
// panel writes. Document-px values, so they pair directly with offsetTop below. Side
// margins don't affect pagination (only line wrapping), so they're ignored.
// Exported for columnsFlow.ts, which shares this geometry.
// panel writes (document-px, pairing directly with offsetTop below); side margins
// are ignored (they affect line wrapping, not pagination). Shared with columnsFlow.ts.
export function readVerticalMargins(dom: HTMLElement): VMargins {
const cs = getComputedStyle(dom);
const top = parseFloat(cs.getPropertyValue('--user-margin-top'));
@@ -190,10 +189,9 @@ type Leaf = {
cellStart?: boolean;
rowStart?: boolean;
rowEnd?: boolean;
// A multi-column section fragment (columns.ts). columnsFlow.ts splits an
// overflowing fragment at a block boundary, so pagination must NOT push it when
// the flow can act: it only pushes when the first block alone can't fit the
// remaining page space (then the flow splits it at the next page top).
// A multi-column section fragment (columns.ts): pagination only pushes it when
// the first block alone can't fit the remaining space (else columnsFlow.ts splits
// the overflow at a block boundary).
columnsFragment?: { blockCount: number; firstBlockNeededPx: number };
};
@@ -817,10 +815,9 @@ export const PageBreaks = Extension.create({
// Set when a leaf overflows but no spacer can bridge it (block > one page).
let noPushReason: string | null = null;
// The empty trailing paragraph after a document-final columns chain: its
// fragment box may run to the page bottom (sequential fill), which would
// shove this invisible paragraph onto a phantom extra page. Leave it in
// place and keep it out of the page count; typed text re-enables it.
// A sequential-fill chain's fragment box may run to the page bottom, which
// would shove this trailing empty paragraph onto a phantom extra page —
// leave it in place and out of the page count; typed text re-enables it.
if (
i === leaves.length - 1 &&
leaf.el.tagName === 'P' &&
+6 -8
View File
@@ -83,10 +83,9 @@ const TOC_SENT = '';
// <draw:frame>/<draw:text-box> or <draw:custom-shape>. U+E008.
const TBX = '';
// Sentinel bracketing a hoisted multi-column section's blocks (marker paragraphs
// COL S{i} COL … COL E{i} COL): replaceColumns hoists like replaceTextBoxes;
// applyColumns wraps the region into a <text:section> with a minted section style
// carrying <style:columns>. U+E009.
// Sentinel bracketing a hoisted multi-column section's blocks, same mechanism as
// TBX above; applyColumns wraps the region into a <text:section> with a minted
// section style carrying <style:columns>. U+E009.
const COL = '';
const EXT_BY_MIME: Record<string, string> = {
@@ -346,10 +345,9 @@ export function mergeJoinedParagraphsJson(blocks: TiptapNode[]): TiptapNode[] {
return out;
}
// Swap each top-level columns section for marker paragraphs bracketing its hoisted
// child blocks (same mechanism as replaceTextBoxes); applyColumns re-wraps the
// serialized region into a <text:section>. Adjacent equal-attr fragments are a
// columnsFlow.ts page-split chain — one section, so they coalesce here.
// Swap each top-level columns section for marker paragraphs (same mechanism as
// replaceTextBoxes). Adjacent equal-attr fragments are a columnsFlow.ts page-split
// chain — one section, so they coalesce here.
function replaceColumns(doc: TiptapNode, cols: ColumnsExport[]): TiptapNode {
if (!doc.content?.length) return doc;
const content: TiptapNode[] = [];
+1 -2
View File
@@ -859,8 +859,7 @@ function flattenTable(tbl: Element, ctx: Ctx): Node[] {
// ---- section: page geometry + headers/footers ------------------------------
// Split body children into sections: a <w:p> whose pPr carries a sectPr ends the
// group before it (a continuous section break). The marker paragraph is dropped
// unless it has run content (ours and Word's are empty). The trailing group has
// group before it (dropped unless it has run content). The trailing group has
// sectPr null — the caller pairs it with the body-final <w:sectPr>.
function splitBodySections(children: Element[]): {
groups: { els: Element[]; sectPr: Element | null }[];