fix(app): keep React session sync alive across chat switches

Mount the workspace-scoped React session sync above the per-chat session island so switching chats no longer tears down the event subscription that keeps transcript state updating in the background.
This commit is contained in:
Benjamin Shafii
2026-04-05 20:30:15 -07:00
parent 49df59d6ef
commit 15f37a09c1
3 changed files with 35 additions and 9 deletions

View File

@@ -115,6 +115,7 @@ import {
import { ReactIsland } from "../../react/island";
import { reactSessionEnabled } from "../../react/feature-flag";
import { SessionSurface } from "../../react/session/session-surface.react";
import { SessionSyncProvider } from "../../react/session/session-sync-provider.react";
export type SessionViewProps = {
selectedSessionId: string | null;
@@ -3200,6 +3201,18 @@ export default function SessionView(props: SessionViewProps) {
<div class="flex-1 flex overflow-hidden">
<div class="relative min-w-0 flex-1 overflow-hidden bg-dls-surface">
<Show when={showReactSessionSurface()}>
<ReactIsland
class="contents"
instanceKey={props.runtimeWorkspaceId!}
component={SessionSyncProvider}
props={{
workspaceId: props.runtimeWorkspaceId!,
opencodeBaseUrl: reactSessionOpencodeBaseUrl(),
openworkToken: reactSessionToken(),
}}
/>
</Show>
<div
class={`h-full overflow-y-auto px-4 sm:px-6 lg:px-10 ${showWorkspaceSetupEmptyState() ? "pt-20" : "pt-10"} bg-dls-surface`}
style={{ contain: "layout paint style" }}

View File

@@ -10,7 +10,6 @@ import { SessionDebugPanel } from "./debug-panel.react";
import { deriveSessionRenderModel } from "./transition-controller";
import { SessionTranscript } from "./message-list.react";
import {
ensureWorkspaceSessionSync,
seedSessionState,
statusKey as reactStatusKey,
todoKey as reactTodoKey,
@@ -102,14 +101,6 @@ export function SessionSurface(props: SessionSurfaceProps) {
gcTime: 1000 * 60 * 60,
});
useEffect(() => {
return ensureWorkspaceSessionSync({
workspaceId: props.workspaceId,
baseUrl: props.opencodeBaseUrl,
openworkToken: props.openworkToken,
});
}, [props.workspaceId, props.opencodeBaseUrl, props.openworkToken]);
useEffect(() => {
if (!currentSnapshot) return;
setRendered({ sessionId: props.sessionId, snapshot: currentSnapshot });

View File

@@ -0,0 +1,22 @@
/** @jsxImportSource react */
import { useEffect } from "react";
import { ensureWorkspaceSessionSync } from "./session-sync";
type SessionSyncProviderProps = {
workspaceId: string;
opencodeBaseUrl: string;
openworkToken: string;
};
export function SessionSyncProvider(props: SessionSyncProviderProps) {
useEffect(() => {
return ensureWorkspaceSessionSync({
workspaceId: props.workspaceId,
baseUrl: props.opencodeBaseUrl,
openworkToken: props.openworkToken,
});
}, [props.workspaceId, props.opencodeBaseUrl, props.openworkToken]);
return null;
}