diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index df8bd3dc..b810af9b 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -5,7 +5,7 @@ import { RunEvent, ListRunsResponse } from '@x/shared/src/runs.js'; import type { LanguageModelUsage, ToolUIPart } from 'ai'; import './App.css' import z from 'zod'; -import { CheckIcon, LoaderIcon, PanelLeftIcon, Expand, Shrink, X, ChevronLeftIcon, ChevronRightIcon, SquarePen, SearchIcon } from 'lucide-react'; +import { CheckIcon, LoaderIcon, PanelLeftIcon, Maximize2, Minimize2, ChevronLeftIcon, ChevronRightIcon, SquarePen, SearchIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; import { MarkdownEditor } from './components/markdown-editor'; import { ChatSidebar } from './components/chat-sidebar'; @@ -1655,8 +1655,10 @@ function App() { const restoreChatTabState = useCallback((tabId: string, fallbackRunId: string | null): boolean => { const cached = chatViewStateByTabRef.current[tabId] if (!cached) return false + // Ignore stale cache snapshots that don't match the tab's current run binding. + if (cached.runId !== fallbackRunId) return false - const resolvedRunId = cached.runId ?? fallbackRunId + const resolvedRunId = fallbackRunId setRunId(resolvedRunId) setConversation(cached.conversation) setCurrentAssistantMessage(cached.currentAssistantMessage) @@ -1678,6 +1680,8 @@ function App() { const openChatInNewTab = useCallback((targetRunId: string) => { const existingTab = chatTabs.find(t => t.runId === targetRunId) if (existingTab) { + // Cancel stale in-flight loads from previously focused tabs. + loadRunRequestIdRef.current += 1 setActiveChatTabId(existingTab.id) const restored = restoreChatTabState(existingTab.id, existingTab.runId) if (processingRunIdsRef.current.has(targetRunId) || !restored) { @@ -1696,6 +1700,8 @@ function App() { if (!tab) return if (tabId === activeChatTabId) return saveChatScrollForTab(activeChatTabId) + // Cancel stale in-flight loads from previously focused tabs. + loadRunRequestIdRef.current += 1 setActiveChatTabId(tabId) const restored = restoreChatTabState(tabId, tab.runId) if (tab.runId && processingRunIdsRef.current.has(tab.runId)) { @@ -1732,6 +1738,8 @@ function App() { if (tabId === activeChatTabId && nextTabs.length > 0) { const newIdx = Math.min(idx, nextTabs.length - 1) const newActiveTab = nextTabs[newIdx] + // Cancel stale in-flight loads from the closing tab. + loadRunRequestIdRef.current += 1 setActiveChatTabId(newActiveTab.id) const restored = restoreChatTabState(newActiveTab.id, newActiveTab.runId) if (newActiveTab.runId && processingRunIdsRef.current.has(newActiveTab.runId)) { @@ -2776,7 +2784,6 @@ function App() { onSelectRun: (runIdToLoad) => { if (selectedPath || isGraphOpen) { setIsChatSidebarOpen(true) - setIsRightPaneMaximized(false) } // If already open in a chat tab, switch to it @@ -2886,15 +2893,35 @@ function App() { ) : null} )} + {!selectedPath && !isGraphOpen && !selectedTask && ( + + + + + New chat tab + + )} {!selectedPath && !isGraphOpen && expandedFrom && ( - + + + + + Restore two-pane view + )} {(selectedPath || isGraphOpen) && ( @@ -2905,7 +2932,7 @@ function App() { className="titlebar-no-drag flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground transition-colors -mr-1 self-center shrink-0" aria-label={isChatSidebarOpen ? "Maximize knowledge view" : "Restore two-pane view"} > - {isChatSidebarOpen ? : } + {isChatSidebarOpen ? : } diff --git a/apps/x/apps/renderer/src/components/chat-sidebar.tsx b/apps/x/apps/renderer/src/components/chat-sidebar.tsx index 44fdbafd..1e8610dd 100644 --- a/apps/x/apps/renderer/src/components/chat-sidebar.tsx +++ b/apps/x/apps/renderer/src/components/chat-sidebar.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Expand, Shrink, SquarePen } from 'lucide-react' +import { Maximize2, Minimize2, SquarePen } from 'lucide-react' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' @@ -394,7 +394,7 @@ export function ChatSidebar({ className="titlebar-no-drag my-1 mr-2 h-8 w-8 shrink-0 text-muted-foreground hover:text-foreground" aria-label={isMaximized ? 'Restore two-pane view' : 'Maximize chat view'} > - {isMaximized ? : } + {isMaximized ? : }