Coming back to / from /chart, /ephemeris, /reports or /people by client navigation remounted Home from hydrated=false and replayed the whole bootstrap: model catalog, consult status, entry summary and daily card, 3-5 round trips plus up to 4 s of reveal budget, every time. The reveal now counts per document load, not per mount: - lib/home-warm-snapshot.ts: memory-only, per-account snapshot of the model catalog, rectification entry summary, session cursor and today's card (keyed by person + birth fingerprint + day). Account, profile and the session list already survive in SessionListProvider. Cleared on account change, sign-out, provider 401 and redirectToLogin. - lib/home-warm-start.ts: Home's useState initializers take one warm decision per mount. Complete snapshot + settled list + complete profile + a landing computable in memory -> start ready, landing resolved with the cold-path functions (?new=1, ?c=, login-return stash and its person scope from BUG-1038). Anything missing or needing a lookup -> the unchanged cold path (no half-reveal, BUG-1021). - runHomeWarmRefresh commits the landing, then refreshes catalog, account and background-consultation recovery in the background (recovery shares resolveReservedConsultation with the cold path). Summary and daily card refresh through their existing effects. - Next renders the new page before it writes the address bar, so AppLink / navigateAppPath note the target href (lib/client-navigation-target.ts); unknown target -> cold path. Home() useState 33 / useRef 37 unchanged; page.tsx 1329 -> 1373 lines. Tests: home-warm-return-lifecycle (13, real Home + sidebar + provider, navigating in Next's render-then-write-URL order; 11 fail with warm start disabled) and home-warm-snapshot (12). Full suite 3953 / 61 failing, failure names identical to the 3928 / 61 baseline. Build: / stays Static, rootMainFiles gzip 130933 B unchanged. Local Chrome with every /api/* held 1.5 s: /chart -> 新建对话 interactive in 18-23 ms with no loading ring (baseline 5530 ms with ring). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
29 lines
1.0 KiB
TypeScript
29 lines
1.0 KiB
TypeScript
/**
|
|
* Where the client navigation in flight is going (BUG-1040).
|
|
*
|
|
* Next's app router renders the next page first and writes the address bar
|
|
* afterwards (HistoryUpdater, an insertion effect). A page that decides its
|
|
* first frame from the URL — Home's warm return reads `?new=1` / `?c=` — would
|
|
* otherwise read the page it is leaving. `AppLink` and `navigateAppPath` note
|
|
* the href here just before the router starts; the page takes it once.
|
|
*
|
|
* Memory only, no dependencies (it ships with the shared sidebar).
|
|
*/
|
|
|
|
const MAX_AGE_MS = 10_000;
|
|
|
|
let target: { href: string; at: number } | null = null;
|
|
|
|
export function noteClientNavigationTarget(href: string): void {
|
|
if (!href.startsWith("/") || href.startsWith("//")) return;
|
|
target = { href, at: Date.now() };
|
|
}
|
|
|
|
/** The noted href (then forgotten), or null when none is recent enough to trust. */
|
|
export function takeClientNavigationTarget(now = Date.now()): string | null {
|
|
const noted = target;
|
|
target = null;
|
|
if (!noted || now - noted.at > MAX_AGE_MS) return null;
|
|
return noted.href;
|
|
}
|