Skip to content

perf: keep typing responsive in large docs (fixes #590)#789

Closed
devmc12 wants to merge 3 commits into
eigenpal:mainfrom
devmc12:perf/large-doc-lazy-pm-remap
Closed

perf: keep typing responsive in large docs (fixes #590)#789
devmc12 wants to merge 3 commits into
eigenpal:mainfrom
devmc12:perf/large-doc-lazy-pm-remap

Conversation

@devmc12

@devmc12 devmc12 commented Jun 13, 2026

Copy link
Copy Markdown

Summary

Typing in large documents was sluggish: every edit that shifted ProseMirror offsets rewrote the data-pm-* attributes on every visually-unchanged page, and the full Document was re-materialized (whole-tree walk) on every keystroke. Repro: a ~10 MB / 72-page document was noticeably laggy to type in, while a 6-page document typed fine — the cost scaled with page count, because every visually-unchanged page was rewritten on each @edit.

  • Visually-unchanged virtualized pages are no longer repainted. Page fingerprints compare visual content (block id / pmStart / pmEnd excluded); a per-page in-memory PmPositionMapper carries the offset delta and DOM readers remap positions on read via readMappedPmStart/End.
  • Paragraph block ids are stable (para:<paraId>) so inserting earlier content doesn't churn every later page's fingerprint into a full re-render.
  • Full-Document materialization (onDocumentChange) is debounced (500ms); a new lightweight onEditorStateChange(state, tx) feeds per-transaction consumers (toolbar/outline) without walking the tree.
  • React and Vue both flush the pending document change on blur and on unmount.
    All body PM-position readers (click-to-position, caret/selection rects, visual-line nav, scroll-to-position, cell-selection highlight, table-insert hover, image NodeSelection, RenderedDomContext) go through the mapper and are scoped to .layout-page-content, so header/footer positions (a separate PM doc) are never remapped with the body mapper.

Test plan

  • bun run typecheck, bun run api:check, bun run check:parity-contract
  • core: render-all-pages-now.test.ts — asserts 0 DOM mutations on visually-unchanged pages and correct remapped positions
  • e2e: text-editing.spec.ts (React)
  • manual: ~10 MB / 72-page document now types without the prior lag
  • manual: click-to-caret after edits; HF caret stays in header/footer

Known limitations

Only paragraph block ids are stable. Tables/images still use a counter id, so inserting content before them degrades those pages to the previous full-repaint path (no correctness issue — just less speedup).

Associated issues

#590 Editing becomes slow in large documents


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Edits that don't change a page's appearance no longer rewrite every
PM-position attribute on that page; the page carries an in-memory
position mapper and remaps on read instead. The full Document is now
materialized on a debounce rather than on every keystroke.
@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

@devmc12 is attempting to deploy a commit to the EigenPal Team on Vercel.

A member of the Team first needs to authorize it.

@eigenpal-release-pal

eigenpal-release-pal Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅

Posted by the CLA bot.

@devmc12

devmc12 commented Jun 13, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

eigenpal-release-pal Bot added a commit that referenced this pull request Jun 13, 2026
@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docx-editor Ready Ready Preview, Comment Jun 13, 2026 3:50pm

Request Review

devmc12 added 2 commits June 14, 2026 00:02
The earlier api:extract ran against a stale dist and dropped
insertImageFromFile / INSERT_IMAGE_MAX_WIDTH_PX from the snapshot.
Re-extracted from a clean build so api:check passes.
…m-remap

# Conflicts:
#	packages/core/src/layout-bridge/clickToPositionDom.ts
@jedrazb jedrazb self-requested a review June 15, 2026 17:34
@jedrazb

jedrazb commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Thanks will have a look soon ! :)

@devmc12

devmc12 commented Jun 17, 2026

Copy link
Copy Markdown
Author

Closing this PR for now after a deeper React/Vue comparison.

The investigation changed the diagnosis: Vue did not show the same large-document typing lag before these changes, so the primary bottleneck is not the shared virtualized page DOM / data-pm-* rewrite path alone.

The React-specific hot path is heavier:

PM transaction → PagedEditor.handleTransaction → hiddenPM.getDocument() → fromProseDoc() whole-tree materialization → DocxEditor.handleDocumentChange → history.push() → useDocumentHistory deep comparison via JSON.stringify(package.document) → React state/render updates

That path ran on every keystroke. In large documents, both fromProseDoc() and the document-history comparison scale with the full document, so typing becomes sluggish. Vue’s adapter did have a fromProseDoc() call too, but it only updated a shallowRef and emitted change events; it did not also push the full document through an internal React document history/deep-compare/render chain.

@devmc12 devmc12 closed this Jun 17, 2026
@jedrazb

jedrazb commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Thansk for analysis @devmc12 ! Are you planning to look into react optimisation?

@devmc12

devmc12 commented Jun 17, 2026

Copy link
Copy Markdown
Author

Thansk for analysis @devmc12 ! Are you planning to look into react optimisation?

Yes, I ran into this issue while using docx-editor-react, so I’m actively looking for a good fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants