Files
pad/web/src/lib/scroll
xarmian 11c7e1e98c feat(web): parameterize scroll-target in createScrollRestoration (TASK-2171) (#976)
* feat(web): parameterize scroll-target in createScrollRestoration (TASK-2171)

Add an optional `scrollTarget` getter to `ScrollRestorationOptions` and
route both the capture and restore-loop call sites through a local
`resolveScrollTarget = opts.scrollTarget ?? getScrollTarget`. When omitted
(all 12 existing callers), behavior is byte-identical — the module default
still resolves `.main-content`, falling back to `window`.

This unblocks PLAN-2154 Phase 2 (Architecture E): the full-page item host
scrolls its own `.item-page` overflow column once a detail pane docks
beside it, and will pass that container via the new getter in TASK-2174.

Verified: `npm run check` (0 errors), `make install` web+go build green.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra

* fix(web): retry scroll restore when a custom scrollTarget is not yet mounted (TASK-2171)

Codex review caught a doc/code gap: the scrollTarget getter's documented
per-frame-retry contract wasn't delivered — the tryScroll loop aborted
permanently when resolveScrollTarget() returned null, and since restoredKey
is already set for the key, the gated effect never re-fires, stranding the
parked offset. Now a null target re-schedules the RAF within the existing 2s
budget until the element mounts. The default .main-content/window resolver
never returns null in-browser, so existing callers are unaffected.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-19 11:21:47 -04:00
..