mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
314fbea508
* fix(app-router): align RSC navigation scroll targeting with Next.js App Router RSC navigations scrolled the document after appNavigate resolved, which lost Next.js segment targeting and focus semantics. That left persistent layout offsets and horizontal scroll behaviour coarser than Next.js during normal navigation. Next.js treats scroll as a navigation intent consumed by the newly navigated segment after commit. This adds a small intent state, wraps the page slot in a client scroll target, and keeps the existing hash-only and popstate paths separate. Adds focused Playwright coverage ported from Next.js autoscroll and navigation focus tests. * test(form): stop asserting app router scroll detail * fix(app-router): prevent focus from changing scroll * fix(app-router): share scroll intent across Vite module instances The App Router scroll intent was stored in plain module-level variables in app-router-scroll-state.ts. navigateClientSide stages the intent and the committed AppRouterScrollTarget consumes it, but next/navigation and the scroll-target module can resolve to separate Vite module instances. If the writer and consumer hold different copies, the staged intent is invisible to the consumer and the post-commit scroll/focus silently no-ops. This is the same multi-instance hazard the rest of the navigation state already guards against with Symbol.for globals (see _CLIENT_NAV_STATE_KEY in navigation.ts and AGENTS.md "RSC and SSR Are Separate Vite Environments"). Move the single pending intent and the id counter onto a Symbol.for global so every module instance shares one slot, matching the established navigation state pattern. Function signatures are unchanged, so callers need no edits. Also document why AppRouterScrollTarget must remain a class component: findDOMNode needs a mounted class instance to locate the first rendered DOM node without a wrapper element, mirroring Next's default scroll handler.