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 ? : }