Keep the view across a tab switch
Perspective's auto-pause is on by default, and pausing deletes the view: AutoPauseState::apply() fires on the viewer's own IntersectionObserver and on the document's visibilitychange, and set_pause(true) does view_sub.take().delete(). Coming back is therefore not a redraw but restore_and_render() -- a fresh view and a fresh traversal of the whole grain -- which is the multi-second chug on every tab switch, and takes any per-node expansion with it. Nothing updates the table while the tab is hidden; every operation is driven from this page. So the pause bought nothing and cost a rebuild. Also drops a comment that still described the old set_depth re-apply on refocus, whose machinery is long gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a028cdb79a
commit
d14036d1b5
15
CLAUDE.md
15
CLAUDE.md
@ -272,6 +272,21 @@ gone:
|
|||||||
|
|
||||||
`split_full` is still *read* on load, for layouts saved under the old scheme.
|
`split_full` is still *read* on load, for layouts saved under the old scheme.
|
||||||
|
|
||||||
|
### Auto-pause is off
|
||||||
|
|
||||||
|
`<perspective-viewer>` auto-pauses by default: an `IntersectionObserver` on
|
||||||
|
itself plus the document's `visibilitychange` drive `AutoPauseState::apply()`,
|
||||||
|
and pausing **deletes the view** (`session.set_pause(true)` →
|
||||||
|
`view_sub.take().delete()`). Returning to the tab is therefore not a redraw but
|
||||||
|
`restore_and_render()` — a fresh view and a fresh traversal of the whole grain,
|
||||||
|
which on a large one is a multi-second chug on every tab switch, and takes any
|
||||||
|
per-node expansion with it.
|
||||||
|
|
||||||
|
`initViewer()` calls `viewer.setAutoPause(false)` right after `viewer.load()`.
|
||||||
|
Nothing updates the table while the tab is hidden — every operation is driven
|
||||||
|
from this page — so the pause bought nothing and cost a rebuild. The view is now
|
||||||
|
held while the tab is backgrounded.
|
||||||
|
|
||||||
### Still not solved: per-node expansion
|
### Still not solved: per-node expansion
|
||||||
|
|
||||||
Expanding one specific branch is view state with no config representation, and
|
Expanding one specific branch is view state with no config representation, and
|
||||||
|
|||||||
@ -259,12 +259,6 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
// the current selection, readable from event handlers that were registered once
|
// the current selection, readable from event handlers that were registered once
|
||||||
const slicesRef = useRef([])
|
const slicesRef = useRef([])
|
||||||
|
|
||||||
// Perspective's ROLLUP view contains every level of the hierarchy; view.set_depth()
|
|
||||||
// is the only thing hiding the deeper ones, and it lives on the view rather than in
|
|
||||||
// the saved config. The viewer rebuilds its view when it redraws — which its
|
|
||||||
// Intersection/ResizeObserver triggers when you switch back to the tab — and the
|
|
||||||
// fresh view has no depth set, so the whole tree appears expanded. Re-apply the
|
|
||||||
// depth we last set once the redraw has settled.
|
|
||||||
// perspective-click fires as a CustomEvent with no modifier state of its own,
|
// perspective-click fires as a CustomEvent with no modifier state of its own,
|
||||||
// and carries no cell coordinates either, so record both from the mousedown that
|
// and carries no cell coordinates either, so record both from the mousedown that
|
||||||
// precedes it. The grid lives in a shadow root, so the real target is the head of
|
// precedes it. The grid lives in a shadow root, so the real target is the head of
|
||||||
@ -822,6 +816,22 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
// that occurs when viewer.load(worker) + restore({ table: name }) can't resolve
|
// that occurs when viewer.load(worker) + restore({ table: name }) can't resolve
|
||||||
// the named table in time.
|
// the named table in time.
|
||||||
await viewer.load(tableRef.current)
|
await viewer.load(tableRef.current)
|
||||||
|
|
||||||
|
// Auto-pause is on by default, and pausing *deletes the view*:
|
||||||
|
// AutoPauseState::apply() fires on visibilitychange and on the viewer's own
|
||||||
|
// IntersectionObserver, and set_pause(true) does
|
||||||
|
// `view_sub.take().delete()`. Coming back is therefore not a redraw but
|
||||||
|
// `restore_and_render()` -- a fresh view, a fresh traversal of the whole
|
||||||
|
// grain -- which is the multi-second chug on every tab switch, and why any
|
||||||
|
// per-node expansion is gone when you return.
|
||||||
|
//
|
||||||
|
// Nothing updates the table while the tab is hidden (operations are driven
|
||||||
|
// from this page), so the pause buys nothing here and costs a rebuild.
|
||||||
|
// Guarded: it is a method on the custom element, absent on older builds.
|
||||||
|
try { if (viewer.setAutoPause) await viewer.setAutoPause(false) } catch (err) {
|
||||||
|
console.error('[setAutoPause]', err)
|
||||||
|
}
|
||||||
|
|
||||||
viewer.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
viewer.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
||||||
if (!hideSplitTotal()) setTimeout(hideSplitTotal, 400)
|
if (!hideSplitTotal()) setTimeout(hideSplitTotal, 400)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user