Skip to content

Commit e4e5fbb

Browse files
committed
fix: cache fix for empty runs
1 parent f8b190e commit e4e5fbb

3 files changed

Lines changed: 57 additions & 11 deletions

File tree

packages/super-editor/src/editors/v1/core/layout-adapter/converters/paragraph.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,22 +1147,23 @@ export function handleParagraphNode(node: PMNode, context: NodeHandlerContext):
11471147
// get() returns both the entry (if hit) and pre-computed nodeJson to avoid double serialization
11481148
const { entry: cached, nodeJson, nodeRev } = flowBlockCache.get(prefixedStableId, node);
11491149
if (cached) {
1150-
// Cache hit: reuse blocks with position adjustment
1151-
// Cache hit reuses previously-converted blocks as-is. That means we don't
1152-
// recompute previousParagraphFont (used for empty list items without
1153-
// explicit run properties). If the user changes the font on the prior
1154-
// paragraph (e.g. paragraph A), an empty list item (paragraph B) can keep
1155-
// the old font until the cache entry is invalidated. Narrow case, but
1156-
// avoids confusing incremental-edit behavior.
1150+
// Cache hit: reuse blocks with position adjustment, then re-sync marker font
1151+
// from live PM state. Empty list items have no textStyle marks, so pass
1152+
// previousParagraphFont instead of falling back to stale cached runs.
11571153
const delta = pmStart - cached.pmStart;
11581154
const reusedBlocks = shiftCachedBlocks(cached.blocks, delta);
1155+
const paragraphProps = node.attrs?.paragraphProperties as ParagraphProperties | undefined;
1156+
const previousParagraphFont = !hasExplicitParagraphRunProperties(paragraphProps)
1157+
? getLastParagraphFont(blocks)
1158+
: undefined;
11591159
reusedBlocks.forEach((block) => {
11601160
if (block.kind === 'paragraph') {
11611161
syncListMarkerFontFromParagraphRuns({
11621162
block,
11631163
converterContext,
11641164
para: node,
11651165
contentFontSource: 'paragraph',
1166+
previousParagraphFont,
11661167
});
11671168
}
11681169
});

packages/super-editor/src/editors/v1/core/layout-adapter/list-marker-font.test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ describe('syncListMarkerFontFromParagraphRuns', () => {
123123
expect(block.attrs.wordLayout?.marker?.run?.fontSize).toBe(40);
124124
});
125125

126-
it('merges partial PM textStyle with cached runs on cache hits', () => {
126+
it('syncs only live PM textStyle properties on cache hits without using cached runs', () => {
127127
const block = listBlock({
128128
runs: [{ text: 'item', fontFamily: 'Georgia, serif', fontSize: 12 }],
129129
});
@@ -135,10 +135,46 @@ describe('syncListMarkerFontFromParagraphRuns', () => {
135135
contentFontSource: 'paragraph',
136136
});
137137

138-
expect(block.attrs.wordLayout?.marker?.run?.fontFamily).toContain('Georgia');
138+
expect(block.attrs.wordLayout?.marker?.run?.fontFamily).toContain('Times New Roman');
139139
expect(block.attrs.wordLayout?.marker?.run?.fontSize).toBe(40);
140140
});
141141

142+
it('uses previousParagraphFont on cache hits when empty list items have no textStyle marks', () => {
143+
const block = listBlock({
144+
runs: [{ text: '', fontFamily: 'Times New Roman, serif', fontSize: 12 }],
145+
markerSize: 12,
146+
});
147+
148+
syncListMarkerFontFromParagraphRuns({
149+
block,
150+
converterContext: minimalContext as never,
151+
para: { content: { forEach: () => {} } } as never,
152+
contentFontSource: 'paragraph',
153+
previousParagraphFont: { fontFamily: 'Georgia, serif', fontSize: 30 },
154+
});
155+
156+
expect(block.attrs.wordLayout?.marker?.run?.fontFamily).toContain('Georgia');
157+
expect(block.attrs.wordLayout?.marker?.run?.fontSize).toBe(30);
158+
});
159+
160+
it('does not fall back to stale cached runs on cache hits without textStyle or previousParagraphFont', () => {
161+
const block = listBlock({
162+
runs: [{ text: '', fontFamily: 'Times New Roman, serif', fontSize: 12 }],
163+
markerFamily: 'Times New Roman, serif',
164+
markerSize: 12,
165+
});
166+
167+
syncListMarkerFontFromParagraphRuns({
168+
block,
169+
converterContext: minimalContext as never,
170+
para: { content: { forEach: () => {} } } as never,
171+
contentFontSource: 'paragraph',
172+
});
173+
174+
expect(block.attrs.wordLayout?.marker?.run?.fontFamily).toContain('Times New Roman');
175+
expect(block.attrs.wordLayout?.marker?.run?.fontSize).toBe(12);
176+
});
177+
142178
it('preserves numbering-defined marker font family but still syncs font size when size is unset', () => {
143179
const block = listBlock({
144180
runs: [{ text: 'item', fontFamily: 'Georgia, serif', fontSize: 30 }],

packages/super-editor/src/editors/v1/core/layout-adapter/list-marker-font.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export type SyncListMarkerFontParams = {
1515
converterContext?: ConverterContext;
1616
para?: PMNode;
1717
contentFontSource?: ListMarkerContentFontSource;
18+
/** Used on cache hits for empty list items with no live textStyle marks. */
19+
previousParagraphFont?: ParagraphFont;
1820
};
1921

2022
const isTextRun = (run: Run): run is TextRun => 'text' in run;
@@ -88,10 +90,16 @@ const resolveContentFont = (
8890
block: { runs: ReadonlyArray<Run> },
8991
para: PMNode | undefined,
9092
source: ListMarkerContentFontSource,
93+
previousParagraphFont?: ParagraphFont,
9194
): Partial<ParagraphFont> | undefined => {
9295
const fromRuns = getFontFromRuns(block.runs);
9396
const fromPara = para ? getFontFromParagraphContent(para) : undefined;
94-
return source === 'paragraph' ? mergeContentFont(fromPara, fromRuns) : (fromRuns ?? fromPara);
97+
if (source === 'paragraph') {
98+
// Cache hits must not fall back to stale cached runs. Empty list items have no
99+
// textStyle marks, so inherit from the preceding paragraph when available.
100+
return mergeContentFont(fromPara, previousParagraphFont);
101+
}
102+
return fromRuns ?? fromPara;
95103
};
96104

97105
const getNumberingMarkerFontOverrides = (
@@ -118,11 +126,12 @@ export const syncListMarkerFontFromParagraphRuns = ({
118126
converterContext,
119127
para,
120128
contentFontSource = 'runs',
129+
previousParagraphFont,
121130
}: SyncListMarkerFontParams): void => {
122131
const markerRun = block.attrs?.wordLayout?.marker?.run;
123132
if (!markerRun) return;
124133

125-
const contentFont = resolveContentFont(block, para, contentFontSource);
134+
const contentFont = resolveContentFont(block, para, contentFontSource, previousParagraphFont);
126135
if (!contentFont) return;
127136

128137
const numberingOverrides = getNumberingMarkerFontOverrides(block.attrs?.numberingProperties, converterContext);

0 commit comments

Comments
 (0)