From 00597fccd63b8e80f8997490f59b1d87238c3abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E6=98=9F=E6=9D=B0?= <1198425718@qq.com> Date: Tue, 12 May 2026 22:27:22 +0800 Subject: [PATCH] fix(webui): default to new chat on load and preserve scroll on settings return - Remove auto-selection of the most recent session on initial load, so the app opens to a blank new-chat page instead of the last session. - Preserve active session state when navigating to/from settings: keep ThreadShell mounted (hidden via CSS) so scroll position, message cache, and streaming state are not lost. - Update onBackToChat to return to blank page when no session was active instead of falling back to the most recent session. - Update related test expectations to match the new navigation behavior. --- webui/src/App.tsx | 46 ++++++++++++++--------------- webui/src/tests/app-layout.test.tsx | 8 ++--- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/webui/src/App.tsx b/webui/src/App.tsx index 1cadcc231..d5b7485a6 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -250,7 +250,6 @@ function Shell({ onModelNameChange, onLogout }: { onModelNameChange: (modelName: key: string; label: string; } | null>(null); - const lastSessionsLen = useRef(0); const restartSawDisconnectRef = useRef(false); const [restartToast, setRestartToast] = useState(null); const [isRestarting, setIsRestarting] = useState(false); @@ -266,13 +265,7 @@ function Shell({ onModelNameChange, onLogout }: { onModelNameChange: (modelName: } }, [desktopSidebarOpen]); - useEffect(() => { - if (activeKey) return; - if (sessions.length > 0 && lastSessionsLen.current === 0) { - setActiveKey(sessions[0].key); - } - lastSessionsLen.current = sessions.length; - }, [sessions, activeKey]); + const activeSession = useMemo(() => { if (!activeKey) return null; @@ -335,9 +328,8 @@ function Shell({ onModelNameChange, onLogout }: { onModelNameChange: (modelName: setView("chat"); setMobileSidebarOpen(false); setActiveKey((current) => { - if (current && sessions.some((session) => session.key === current)) { - return current; - } + if (!current) return null; + if (sessions.some((session) => session.key === current)) return current; return sessions[0]?.key ?? null; }); }, [sessions]); @@ -479,18 +471,13 @@ function Shell({ onModelNameChange, onLogout }: { onModelNameChange: (modelName: ) : null} -
- {view === "settings" ? ( - - ) : ( +
+
+
+ {view === "settings" && ( +
+ +
)}
diff --git a/webui/src/tests/app-layout.test.tsx b/webui/src/tests/app-layout.test.tsx index 561382d18..613ce35d1 100644 --- a/webui/src/tests/app-layout.test.tsx +++ b/webui/src/tests/app-layout.test.tsx @@ -265,7 +265,7 @@ describe("App layout", () => { expect(screen.queryByDisplayValue("unsaved-brave-key")).not.toBeInTheDocument(); }); - it("returns from settings to an available chat instead of the blank start page", async () => { + it("returns from settings to the blank start page when no session was active", async () => { mockSessions = [ { key: "websocket:chat-a", @@ -330,10 +330,8 @@ describe("App layout", () => { expect(await screen.findByRole("heading", { name: "General" })).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Back to chat" })); - await waitFor(() => expect(document.title).toBe("First chat · nanobot")); - const restoredSidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); - fireEvent.click(within(restoredSidebar).getByRole("button", { name: /^Second chat$/ })); - await waitFor(() => expect(document.title).toBe("Second chat · nanobot")); + await waitFor(() => expect(document.title).toBe("nanobot")); + expect(screen.getByText("What can I do for you?")).toBeInTheDocument(); }); it("filters sidebar sessions through the lightweight search row", async () => {