perf(ui): isolate sidebar session selection

This commit is contained in:
c_w_xiaohei
2026-08-26 00:42:36 +08:00
parent 77da97953f
commit ed37d8e249
8 changed files with 351 additions and 159 deletions
+32 -28
View File
@@ -37,6 +37,7 @@ import { setActionRefs } from "./session-actions"
import { setSyncRefs, getAllSyncSessions } from "./sync-refs"
import { useSessionUIStore } from "./session-ui-store"
import { stripSessionDiffSnapshots } from "./sanitize"
import { upsertSessionRecord } from "./session-records"
import { applySessionEventToGlobalSessions } from "./session-event-router"
import { syncDebug } from "./debug"
import { getReconnectCandidateSessionIds, mergeBootstrapSessions } from "./reconnect-recovery"
@@ -86,22 +87,29 @@ import {
// Context
// ---------------------------------------------------------------------------
type SyncSystem = {
type SyncRuntime = {
childStores: ChildStoreManager
messageLoader: SessionMessageLoader
runtimeKey: string
sdk: OpencodeClient
}
type SyncSystem = SyncRuntime & {
directory: string
}
const SYNC_CONTEXT_GLOBAL_KEY = "__openchamber_sync_context__"
const SYNC_RUNTIME_CONTEXT_GLOBAL_KEY = "__openchamber_sync_runtime_context__"
type SyncGlobal = typeof globalThis & {
[SYNC_CONTEXT_GLOBAL_KEY]?: React.Context<SyncSystem | null>
[SYNC_RUNTIME_CONTEXT_GLOBAL_KEY]?: React.Context<SyncRuntime | null>
}
const syncGlobal = globalThis as SyncGlobal
const SyncContext = syncGlobal[SYNC_CONTEXT_GLOBAL_KEY] ?? createContext<SyncSystem | null>(null)
syncGlobal[SYNC_CONTEXT_GLOBAL_KEY] = SyncContext
const SyncRuntimeContext = syncGlobal[SYNC_RUNTIME_CONTEXT_GLOBAL_KEY] ?? createContext<SyncRuntime | null>(null)
syncGlobal[SYNC_RUNTIME_CONTEXT_GLOBAL_KEY] = SyncRuntimeContext
type SdkResult<T> = {
data?: T
@@ -137,6 +145,12 @@ function useSyncSystem() {
return ctx
}
export function useSyncRuntime() {
const ctx = useContext(SyncRuntimeContext)
if (!ctx) throw new Error("useSyncRuntime must be used within <SyncProvider>")
return ctx
}
function getLiveStates(childStores: ChildStoreManager): State[] {
return Array.from(childStores.children.values(), (store) => store.getState())
}
@@ -1408,28 +1422,13 @@ async function resyncDirectoryAfterReconnect(
const nextSession = stripSessionDiffSnapshots(session)
store.setState((state: DirectoryStore) => {
const sessionIndex = state.session.findIndex((item) => item.id === nextSession.id)
let sessions = state.session
let sessionChanged = false
const sessions = upsertSessionRecord(state.session, nextSession)
let sessionTotal = state.sessionTotal
if (sessionIndex >= 0) {
if (!haveEquivalentSyncSnapshots(sessions[sessionIndex], nextSession)) {
sessions = [...state.session]
sessions[sessionIndex] = nextSession
sessionChanged = true
}
} else {
sessions = [...state.session]
sessions.push(nextSession)
sessions.sort((a, b) => cmp(a.id, b.id))
if (!nextSession.parentID) sessionTotal += 1
sessionChanged = true
}
if (!sessionChanged) {
if (sessions === state.session) {
return state
}
if (!state.session.some((item) => item.id === nextSession.id) && !nextSession.parentID) sessionTotal += 1
return {
session: sessions,
@@ -2024,15 +2023,13 @@ export function SyncProvider(props: {
const pipelineHasConnectedRef = useRef(false)
const pipelineDisconnectedBeforeFirstConnectRef = useRef(false)
const runtime = useMemo<SyncRuntime>(
() => ({ childStores, messageLoader, runtimeKey, sdk: props.sdk }),
[childStores, messageLoader, props.sdk, runtimeKey],
)
const system = useMemo<SyncSystem>(
() => ({
childStores,
messageLoader,
runtimeKey,
sdk: props.sdk,
directory: props.directory,
}),
[childStores, messageLoader, props.sdk, props.directory, runtimeKey],
() => ({ ...runtime, directory: props.directory }),
[props.directory, runtime],
)
const triggerDirectoryResync = useCallback((directory: string, reason: SessionMaterializationReason) => {
@@ -2547,7 +2544,14 @@ export function SyncProvider(props: {
return unsubscribe
}, [props.directory, childStores])
return <SyncContext.Provider value={system}>{props.children}</SyncContext.Provider>
// Directory navigation must not republish stable runtime dependencies.
return (
<SyncContext.Provider value={system}>
<SyncRuntimeContext.Provider value={runtime}>
{props.children}
</SyncRuntimeContext.Provider>
</SyncContext.Provider>
)
}
// ---------------------------------------------------------------------------