fix: keep a deleted session's data in its components until they unmount (#12062)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Guilhem
2026-10-28 18:49:16 +01:00
committed by GitHub
co-authored by Claude Opus 5.5
parent bfd31df5d5
commit 9c6c2018a4
2 changed files with 16 additions and 10 deletions
@@ -106,8 +106,10 @@
setContext<AIChatManager>('aiChatManager', runtime.manager)
}
// Reactive session reference (mutations to summary/target propagate via the $state proxy)
const session = $derived(sessionState.sessions.find((s) => s.id === sessionId))
// Reactive session reference (mutations to summary/target propagate via the $state proxy).
// Falls back to the mounted session once it is deleted: the layout's header reads this
// through PageHeaderContent, outside the page's tree, once more before the wrapper unmounts.
const session = $derived(sessionState.sessions.find((s) => s.id === sessionId) ?? initialSession)
// Only a session that has never sent opens as the starter block. Read off the record, not
// the transcript: a cold session's history is still loading from storage when it mounts,
@@ -94,11 +94,7 @@
tabsToReload,
viewerTabsToReload
} from '$lib/components/sessions/previewReload'
import {
isPreviewListPath,
barePath,
agentPagePath
} from '$lib/components/sessions/previewPaths'
import { isPreviewListPath, barePath, agentPagePath } from '$lib/components/sessions/previewPaths'
import { listWorkspaceAgents } from '$lib/components/sessions/workspaceAgents'
import {
leafKeyFor,
@@ -268,6 +264,15 @@
.map((r) => sessionState.sessions.find((s) => s.id === r.sessionId))
.filter((s): s is NonNullable<typeof s> => s != null)
)
// The preview hosts take their runtime from the item rather than a lookup by id: a
// removed session's hosts are torn down after its runtime has left the map, and their
// teardowns (an editor unregistering from the runtime) still read the prop.
const warmHosts = $derived(
listRuntimes().flatMap((runtime) => {
const session = sessionState.sessions.find((s) => s.id === runtime.sessionId)
return session ? [{ session, runtime }] : []
})
)
// Chat columns stay mounted (stacked, visibility-toggled) so switching back
// keeps scroll position, MRU-capped like preview tabs: every mounted column
@@ -1272,9 +1277,8 @@
<div class="relative flex-1 min-h-0">
{#if previewTabHostComponent.current}
{@const TabHost = previewTabHostComponent.current}
{#each warmSessions as s (s.id)}
{@const rt = getRuntime(s.id)}
{@const tabs = rt?.previewTabs}
{#each warmHosts as { session: s, runtime: rt } (s.id)}
{@const tabs = rt.previewTabs}
{#each tabs?.tabs ?? [] as tab (tab.id)}
<!-- tabHosts is an imperative ref-bag (only tabHosts[key]?.reload() in
reloadTabs); it is intentionally a plain object so component