Skip to content

Commit 20a1341

Browse files
committed
fix(super-editor): resolve fontSize per-block via style chain in get_content
1 parent 739a18f commit 20a1341

1 file changed

Lines changed: 54 additions & 20 deletions

File tree

packages/super-editor/src/editors/v1/document-api-adapters/plan-engine/blocks-wrappers.ts

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,38 +67,68 @@ function extractTextPreview(node: ProseMirrorNode): string | null {
6767

6868
const HEADING_PATTERN = /^Heading(\d)$/;
6969

70-
/** OOXML implicit default font size when neither Normal style nor docDefaults specifies one. */
70+
/** OOXML implicit default font size when neither styles nor docDefaults specifies one. */
7171
const OOXML_DEFAULT_FONT_SIZE_PT = 10;
7272

73+
/** Pre-built style context for fontSize resolution across all blocks in a list call. */
74+
interface StyleContext {
75+
styles: Record<string, { runProperties?: { fontSize?: unknown }; basedOn?: string }>;
76+
docDefaultsFontSizeHp: number | undefined;
77+
}
78+
79+
function buildStyleContext(editor: Editor): StyleContext | null {
80+
const styleProps = readTranslatedLinkedStyles(editor);
81+
if (!styleProps) return null;
82+
return {
83+
styles: styleProps.styles ?? {},
84+
docDefaultsFontSizeHp:
85+
typeof styleProps.docDefaults?.runProperties?.fontSize === 'number'
86+
? styleProps.docDefaults.runProperties.fontSize
87+
: undefined,
88+
};
89+
}
90+
7391
/**
74-
* Resolve the document's default font size (in points) from the style catalog.
75-
* Falls back through: Normal style rPr → docDefaults rPrOOXML implicit default (10pt).
92+
* Resolve the effective font size (in points) for a block by walking its style chain.
93+
* Cascade: inline mark → block's paragraph style → basedOn chainNormal → docDefaults → 10pt.
7694
* OOXML stores fontSize as half-points (w:sz val), so we divide by 2.
7795
*/
78-
function resolveDefaultFontSizePt(editor: Editor): number {
79-
const styleProps = readTranslatedLinkedStyles(editor);
80-
if (!styleProps) return OOXML_DEFAULT_FONT_SIZE_PT;
96+
function resolveBlockFontSizePt(styleCtx: StyleContext | null, styleId: string | null | undefined): number {
97+
if (!styleCtx) return OOXML_DEFAULT_FONT_SIZE_PT;
98+
99+
// Walk the style's basedOn chain (limit depth to avoid infinite loops)
100+
let currentId = styleId ?? 'Normal';
101+
const visited = new Set<string>();
102+
while (currentId && !visited.has(currentId)) {
103+
visited.add(currentId);
104+
const style = styleCtx.styles[currentId];
105+
if (!style) break;
106+
const fs = style.runProperties?.fontSize;
107+
if (typeof fs === 'number') return fs / 2;
108+
currentId = style.basedOn ?? '';
109+
}
81110

82-
// Try Normal style first
83-
const normalStyle = styleProps.styles?.['Normal'];
84-
const normalFontSize = normalStyle?.runProperties?.fontSize;
85-
if (typeof normalFontSize === 'number') return normalFontSize / 2;
111+
// Try Normal if we haven't already
112+
if (!visited.has('Normal')) {
113+
const normal = styleCtx.styles['Normal'];
114+
const fs = normal?.runProperties?.fontSize;
115+
if (typeof fs === 'number') return fs / 2;
116+
}
86117

87-
// Fall back to docDefaults
88-
const defaultFontSize = styleProps.docDefaults?.runProperties?.fontSize;
89-
if (typeof defaultFontSize === 'number') return defaultFontSize / 2;
118+
// docDefaults
119+
if (typeof styleCtx.docDefaultsFontSizeHp === 'number') return styleCtx.docDefaultsFontSizeHp / 2;
90120

91121
return OOXML_DEFAULT_FONT_SIZE_PT;
92122
}
93123

94124
/**
95125
* Extract key formatting from a block node's first text run marks.
96-
* When defaultFontSizePt is provided, it's used as fallback when the
97-
* inline marks don't specify a fontSize (common for inherited styles).
126+
* When styleCtx is provided, resolves fontSize from the block's paragraph style
127+
* chain when inline marks don't specify one (common for inherited styles).
98128
*/
99129
function extractBlockFormatting(
100130
node: ProseMirrorNode,
101-
defaultFontSizePt?: number,
131+
styleCtx?: StyleContext | null,
102132
): {
103133
styleId?: string | null;
104134
fontFamily?: string;
@@ -156,7 +186,11 @@ function extractBlockFormatting(
156186
return {
157187
...(styleId ? { styleId } : {}),
158188
...(fontFamily ? { fontFamily } : {}),
159-
...((fontSize ?? defaultFontSizePt) !== undefined ? { fontSize: fontSize ?? defaultFontSizePt } : {}),
189+
...(fontSize !== undefined
190+
? { fontSize }
191+
: styleCtx
192+
? { fontSize: resolveBlockFontSizePt(styleCtx, styleId) }
193+
: {}),
160194
...(bold ? { bold } : {}),
161195
...(underline ? { underline } : {}),
162196
...(color ? { color } : {}),
@@ -270,8 +304,8 @@ export function blocksListWrapper(editor: Editor, input?: BlocksListInput): Bloc
270304

271305
const rev = getRevision(editor);
272306

273-
// Resolve document's default fontSize once for all blocks
274-
const defaultFontSizePt = resolveDefaultFontSizePt(editor);
307+
// Build style context once — used to resolve fontSize per-block via style chain
308+
const styleCtx = buildStyleContext(editor);
275309

276310
const blocks: BlockListEntry[] = paged.map((candidate, i) => {
277311
const textLength = computeTextContentLength(candidate.node);
@@ -294,7 +328,7 @@ export function blocksListWrapper(editor: Editor, input?: BlocksListInput): Bloc
294328
nodeType: candidate.nodeType,
295329
textPreview: extractTextPreview(candidate.node),
296330
isEmpty: textLength === 0,
297-
...extractBlockFormatting(candidate.node, defaultFontSizePt),
331+
...extractBlockFormatting(candidate.node, styleCtx),
298332
...(ref ? { ref } : {}),
299333
};
300334
});

0 commit comments

Comments
 (0)