fix(ui): preserve user-selected session on reconnect and tab switch (#59611) thanks @loong0306

Fixes #57072 — chat UI state desync after route navigation.

- applySessionDefaults() now detects user-selected sessions and preserves them on reconnect
- Chat tab session switching consolidated to use switchChatSession() helper
- Overview session-key handler uses shared resetChatStateForSessionSwitch to prevent stale state leaks
- Session select dropdowns now set ?selected to reflect actual state

Co-authored-by: loong0306 <loong0306@gmail.com>
Co-authored-by: Nova <nova@openknot.ai>
This commit is contained in:
Byron
2026-04-14 04:24:56 +08:00
committed by GitHub
parent 10a92e2ff4
commit 891e42beec
3 changed files with 44 additions and 5 deletions

View File

@@ -177,6 +177,27 @@ function applySessionDefaults(host: GatewayHost, defaults?: SessionDefaultsSnaps
if (!defaults?.mainSessionKey) {
return;
}
// Detect if user has already selected a specific session (not an alias like "main").
// If normalization doesn't change the value, it's a user-selected session.
const normalizedSessionKey = normalizeSessionKeyForDefaults(host.sessionKey, defaults);
const isUserSelectedSession = normalizedSessionKey === host.sessionKey;
if (isUserSelectedSession) {
// User has selected a specific session; preserve their choice
// Only normalize lastActiveSessionKey, don't override current sessionKey
const resolvedLastActiveSessionKey = normalizeSessionKeyForDefaults(
host.settings.lastActiveSessionKey,
defaults,
);
if (resolvedLastActiveSessionKey !== host.settings.lastActiveSessionKey) {
applySettings(host as unknown as Parameters<typeof applySettings>[0], {
...host.settings,
lastActiveSessionKey: resolvedLastActiveSessionKey,
});
}
return; // Keep user's session selection
}
const resolvedSessionKey = normalizeSessionKeyForDefaults(host.sessionKey, defaults);
const resolvedSettingsSessionKey = normalizeSessionKeyForDefaults(
host.settings.sessionKey,

View File

@@ -117,9 +117,11 @@ export function renderTab(state: AppViewState, tab: Tab, opts?: { collapsed?: bo
}
event.preventDefault();
if (tab === "chat") {
const mainSessionKey = resolveSidebarChatSessionKey(state);
if (state.sessionKey !== mainSessionKey) {
if (!state.sessionKey) {
const mainSessionKey = resolveSidebarChatSessionKey(state);
resetChatStateForSessionSwitch(state, mainSessionKey);
}
if (state.tab !== "chat") {
void state.loadAssistantIdentity();
}
}
@@ -202,7 +204,13 @@ export function renderChatSessionSelect(state: AppViewState) {
group.options,
(entry) => entry.key,
(entry) =>
html`<option value=${entry.key} title=${entry.title}>${entry.label}</option>`,
html`<option
value=${entry.key}
title=${entry.title}
?selected=${entry.key === state.sessionKey}
>
${entry.label}
</option>`,
)}
</optgroup>`,
)}
@@ -474,7 +482,13 @@ export function renderChatMobileToggle(state: AppViewState) {
<optgroup label=${group.label}>
${group.options.map(
(opt) => html`
<option value=${opt.key} title=${opt.title}>${opt.label}</option>
<option
value=${opt.key}
title=${opt.title}
?selected=${opt.key === state.sessionKey}
>
${opt.label}
</option>
`,
)}
</optgroup>

View File

@@ -1113,13 +1113,17 @@ export function renderApp(state: AppViewState) {
onSessionKeyChange: (next) => {
state.sessionKey = next;
state.chatMessage = "";
state.chatMessages = [];
state.chatToolMessages = [];
state.chatStream = null;
state.chatRunId = null;
state.chatQueue = [];
state.resetToolStream();
state.applySettings({
...state.settings,
sessionKey: next,
lastActiveSessionKey: next,
});
void state.loadAssistantIdentity();
},
onToggleGatewayTokenVisibility: () => {
state.overviewShowGatewayToken = !state.overviewShowGatewayToken;