Skip to content

Commit 0da2bb6

Browse files
hieu-leeclaude
andauthored
perf(web): composer serializes each draft doc once per keystroke (#26720)
Co-authored-by: Claude Opus 5.5 <[email protected]>
1 parent c2985a7 commit 0da2bb6

2 files changed

Lines changed: 20 additions & 6 deletions

File tree

‎apps/web/src/components/ComposerPromptEditorTiptap.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ function ComposerPromptEditorTiptapInner(
10181018

10191019
const handleEditorChange = (updated: TiptapEditor) => {
10201020
const map = serializeEditorDoc(updated.state.doc);
1021-
const contextIds = literalText ? [] : map.contextIds;
1021+
const contextIds = literalText ? [] : [...map.contextIds];
10221022
const { from, to } = updated.state.selection;
10231023
const fromFlat = pmToFlat(map, from);
10241024
const toFlat = pmToFlat(map, to);
@@ -1473,7 +1473,7 @@ function ComposerPromptEditorTiptapInner(
14731473
value: map.value,
14741474
cursor: collapseExpandedComposerCursor(map.value, expandedCursor, literalText),
14751475
expandedCursor,
1476-
contextIds: literalText ? [] : map.contextIds,
1476+
contextIds: literalText ? [] : [...map.contextIds],
14771477
};
14781478
const toFlat = pmToFlat(map, to);
14791479
selectionRangeRef.current = {

‎apps/web/src/composer-rich-text-doc.ts‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -879,11 +879,12 @@ export interface RichRun {
879879
nodeName?: string;
880880
}
881881

882+
// Shared by every caller that serializes the same doc, so it must not be mutated.
882883
export interface RichDocMap {
883-
value: string;
884-
runs: RichRun[];
885-
docLength: number;
886-
contextIds: string[];
884+
readonly value: string;
885+
readonly runs: ReadonlyArray<Readonly<RichRun>>;
886+
readonly docLength: number;
887+
readonly contextIds: ReadonlyArray<string>;
887888
}
888889

889890
function readAtomSource(node: ProseMirrorNode): string {
@@ -1354,7 +1355,20 @@ export function serializeSelection(doc: ProseMirrorNode, from: number, to: numbe
13541355
return serializeEditorDoc(doc.type.create(null, content)).value;
13551356
}
13561357

1358+
// Docs are immutable, and one keystroke serializes the same doc from the marker
1359+
// plugin, onUpdate, and onSelectionUpdate.
1360+
const serializedDocs = new WeakMap<ProseMirrorNode, RichDocMap>();
1361+
13571362
export function serializeEditorDoc(doc: ProseMirrorNode): RichDocMap {
1363+
let map = serializedDocs.get(doc);
1364+
if (!map) {
1365+
map = serializeDoc(doc);
1366+
serializedDocs.set(doc, map);
1367+
}
1368+
return map;
1369+
}
1370+
1371+
function serializeDoc(doc: ProseMirrorNode): RichDocMap {
13581372
const acc: RichAccumulator = { runs: [], value: "", flat: 0, md: 0 };
13591373
const blocks: ProseMirrorNode[] = [];
13601374
doc.content.forEach((node) => {

0 commit comments

Comments
 (0)