Conversation
Clicking in the vertical gap between two pages caused the viewer to jump to a different page. PointerNormalization returns pageIndex=undefined when elementsFromPoint finds no .superdoc-page under the cursor (the gap area), but the existing isOutsidePageBodyContent guard from SD-2356 only fires when pageIndex is finite. The pointer flow then fell through to the geometry fallback, which always snaps to the nearest fragment, moving the selection and triggering a scrollIntoView. Treat "no page under cursor" the same as "click in page margin": prevent default, focus the editor, do not change selection or scroll. Also harden isOutsidePageBodyContent against a missing layout.pages array. Tests: - New regression test in EditorInputManager.pageMarginClick.test.ts asserts that a click with undefined pageIndex does not call into the position resolver or set a selection. - Existing pointer tests had incomplete normalizeClientPoint mocks that did not return pageIndex. Updated them to mirror the production contract so in-page click paths still exercise position resolution. - Two PresentationEditor integration tests stub elementsFromPoint to simulate a .superdoc-page under the click point, since happy-dom does not compute layout. Verified end-to-end: clicking in the 24px gap between pages no longer moves selection or scroll, while in-page text clicks still place the caret normally.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
… H/F (SD-2749) After double-clicking a header/footer to enter editing mode and then clicking back into a body paragraph, ProseMirror was dispatching the new selection on the now-stale active (header/footer) editor. With the H/F surface scrolled out of view, ProseMirror's scrollIntoView pulled the viewport back to the H/F surface, jumping the user away from where they clicked. The captured `sessionMode`, `useActiveSurfaceHitTest`, and `editor` locals in #handlePointerDown were captured before #handleClickInHeaderFooterMode exited the session for a body-content click. Re-derive them from the fresh getHeaderFooterSession() reading once the H/F handler returns false (meaning it didn't claim the click), so subsequent hit testing and selection dispatch target the body editor. Adds EditorInputManager.headerFooterBodyClick.test.ts covering the regression. Verified end-to-end in the browser: scrollTop and selection are preserved when clicking body after editing a header.
|
Follow-up: extended fix for the header→body click variant of SD-2749. Repro confirmed on origin/main (and on previous commit on this branch):
Root cause: Fix (commit 57c46e5): convert the three locals to Tests added Verified end-to-end in the browser:
Full |
💡 Codex Reviewdocument.elementsFromPoint after stubbing
This test mutates the global ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Address two P2 review findings on PR #3181: 1. Refresh activeStorySession after exiting H/F (codex-connector inline review). exitHeaderFooterMode synchronously clears the backing StoryPresentationSessionManager, but the previous patch recomputed useActiveSurfaceHitTest from the pre-exit activeStorySession variable. In production H/F sessions are story-backed, so that stale value kept the first body click on the active-surface path, skipping the new page-gap guard. Re-read getActiveStorySession() after the exit before recomputing the locals. The headerFooterBodyClick test now mocks a story-backed session so it would fail without the re-read, and a second test asserts that an inter-page gap click (pageIndex undefined) after exiting an H/F session still preserves the selection. 2. Restore document.elementsFromPoint after stubbing (Codex review). The two PresentationEditor.test.ts tests that fake elementsFromPoint now save and restore the original (or delete the stub if there was none) via try/finally, so subsequent tests do not inherit the fake hit chain.
|
Addressed both P2 review comments in 8ddc44e. 1. Strengthened the existing unit test to mock a story-backed session, and added a second test that exercises the inter-page gap path ( 2. Verified end-to-end:
|
Pins the implicit invariant that an active note session is exited before useActiveSurfaceHitTest is computed, so the new pointerOffAnyPage bail also catches inter-page gap clicks during footnote/endnote editing. Fails on main, passes on this branch.
caio-pizzol
left a comment
There was a problem hiding this comment.
@tupizz good call re-reading the story session after H/F exit, that path was easy to miss :)
i pushed a sibling regression test for the note-session × gap path, and ran render-parity against the merge-base (no rendering delta).
lgtm.
|
🎉 This PR is included in @superdoc-dev/mcp v0.3.0-next.63 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/react v1.2.0-next.105 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.8.0-next.79 The release is available on GitHub release |
|
🎉 This PR is included in vscode-ext v2.3.0-next.107 |
|
🎉 This PR is included in superdoc v1.30.0-next.61 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.8.0-next.61 |
|
🎉 This PR is included in superdoc-cli v0.9.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc v1.32.0 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/mcp v0.4.0 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/react v1.3.0 The release is available on GitHub release |
|
🎉 This PR is included in vscode-ext v2.4.0 |
Summary
EditorInputManager.#handlePointerDownthat bails when no.superdoc-pageis under the cursor (matches the existing in-margin guard from SD-2356).layout?.pages?.[pageIndex]access inisOutsidePageBodyContentto prevent a latent crash whenlayout.pagesis missing.Root cause
PointerNormalizationuseselementsFromPointto find the.superdoc-pageunder the cursor. In the inter-page gap there is none, so it returnspageIndex: undefined. The existingisOutsidePageBodyContentguard from SD-2356 short-circuits tofalsewhenpageIndexis not finite, so the pointer flow fell through to the geometry fallback (clickToPositionGeometry→snapToNearestFragment), which always returns some nearest fragment.TextSelection.create+ ProseMirrorscrollIntoViewthen jumped the page.The fix is symmetric with SD-2356: clicking in the gap is conceptually the same as clicking in a page margin — preserve selection and scroll.
Files changed
EditorInputManager.tspageIndexis undefined; added defensivelayout?.pages?.[pageIndex]EditorInputManager.pageMarginClick.test.tsEditorInputManager.{activeCommentClick,dragAutoScroll,footnoteClick,structuredContent}.test.ts{ x, y, pageIndex, pageLocalY }matching the production contractPresentationEditor.test.tsdocument.elementsFromPointso happy-dom returns the simulated.superdoc-pageunder the click pointTest plan
pnpm --filter super-editor test— 18,421/18,421 pass (1,240 files)EditorInputManager.pageMarginClick.test.ts(3 tests pass; new SD-2749 test fails onmain, passes on this branch)IT-923 Headers.docx(7 pages):(600, 366)between pages 0 & 1:scrollTopandselection.fromUNCHANGED(600, 500): selection moves to clicked text, no scroll jumpNVCA-Model-IRA.docx)