feat: update chat and app UI with new maximize/minimize icons

This commit is contained in:
tusharmagar 2026-02-19 16:19:13 +05:30
parent e8d8332e34
commit bf5f6f16de
2 changed files with 41 additions and 14 deletions

View file

@ -5,7 +5,7 @@ import { RunEvent, ListRunsResponse } from '@x/shared/src/runs.js';
import type { LanguageModelUsage, ToolUIPart } from 'ai'; import type { LanguageModelUsage, ToolUIPart } from 'ai';
import './App.css' import './App.css'
import z from 'zod'; 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 { cn } from '@/lib/utils';
import { MarkdownEditor } from './components/markdown-editor'; import { MarkdownEditor } from './components/markdown-editor';
import { ChatSidebar } from './components/chat-sidebar'; import { ChatSidebar } from './components/chat-sidebar';
@ -1655,8 +1655,10 @@ function App() {
const restoreChatTabState = useCallback((tabId: string, fallbackRunId: string | null): boolean => { const restoreChatTabState = useCallback((tabId: string, fallbackRunId: string | null): boolean => {
const cached = chatViewStateByTabRef.current[tabId] const cached = chatViewStateByTabRef.current[tabId]
if (!cached) return false 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) setRunId(resolvedRunId)
setConversation(cached.conversation) setConversation(cached.conversation)
setCurrentAssistantMessage(cached.currentAssistantMessage) setCurrentAssistantMessage(cached.currentAssistantMessage)
@ -1678,6 +1680,8 @@ function App() {
const openChatInNewTab = useCallback((targetRunId: string) => { const openChatInNewTab = useCallback((targetRunId: string) => {
const existingTab = chatTabs.find(t => t.runId === targetRunId) const existingTab = chatTabs.find(t => t.runId === targetRunId)
if (existingTab) { if (existingTab) {
// Cancel stale in-flight loads from previously focused tabs.
loadRunRequestIdRef.current += 1
setActiveChatTabId(existingTab.id) setActiveChatTabId(existingTab.id)
const restored = restoreChatTabState(existingTab.id, existingTab.runId) const restored = restoreChatTabState(existingTab.id, existingTab.runId)
if (processingRunIdsRef.current.has(targetRunId) || !restored) { if (processingRunIdsRef.current.has(targetRunId) || !restored) {
@ -1696,6 +1700,8 @@ function App() {
if (!tab) return if (!tab) return
if (tabId === activeChatTabId) return if (tabId === activeChatTabId) return
saveChatScrollForTab(activeChatTabId) saveChatScrollForTab(activeChatTabId)
// Cancel stale in-flight loads from previously focused tabs.
loadRunRequestIdRef.current += 1
setActiveChatTabId(tabId) setActiveChatTabId(tabId)
const restored = restoreChatTabState(tabId, tab.runId) const restored = restoreChatTabState(tabId, tab.runId)
if (tab.runId && processingRunIdsRef.current.has(tab.runId)) { if (tab.runId && processingRunIdsRef.current.has(tab.runId)) {
@ -1732,6 +1738,8 @@ function App() {
if (tabId === activeChatTabId && nextTabs.length > 0) { if (tabId === activeChatTabId && nextTabs.length > 0) {
const newIdx = Math.min(idx, nextTabs.length - 1) const newIdx = Math.min(idx, nextTabs.length - 1)
const newActiveTab = nextTabs[newIdx] const newActiveTab = nextTabs[newIdx]
// Cancel stale in-flight loads from the closing tab.
loadRunRequestIdRef.current += 1
setActiveChatTabId(newActiveTab.id) setActiveChatTabId(newActiveTab.id)
const restored = restoreChatTabState(newActiveTab.id, newActiveTab.runId) const restored = restoreChatTabState(newActiveTab.id, newActiveTab.runId)
if (newActiveTab.runId && processingRunIdsRef.current.has(newActiveTab.runId)) { if (newActiveTab.runId && processingRunIdsRef.current.has(newActiveTab.runId)) {
@ -2776,7 +2784,6 @@ function App() {
onSelectRun: (runIdToLoad) => { onSelectRun: (runIdToLoad) => {
if (selectedPath || isGraphOpen) { if (selectedPath || isGraphOpen) {
setIsChatSidebarOpen(true) setIsChatSidebarOpen(true)
setIsRightPaneMaximized(false)
} }
// If already open in a chat tab, switch to it // If already open in a chat tab, switch to it
@ -2886,15 +2893,35 @@ function App() {
) : null} ) : null}
</div> </div>
)} )}
{!selectedPath && !isGraphOpen && !selectedTask && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleNewChatTab}
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 self-center shrink-0"
aria-label="New chat tab"
>
<SquarePen className="size-5" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom">New chat tab</TooltipContent>
</Tooltip>
)}
{!selectedPath && !isGraphOpen && expandedFrom && ( {!selectedPath && !isGraphOpen && expandedFrom && (
<button <Tooltip>
type="button" <TooltipTrigger asChild>
onClick={handleCloseFullScreenChat} <button
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 self-center shrink-0" type="button"
aria-label="Return to file" onClick={handleCloseFullScreenChat}
> 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 self-center shrink-0"
<X className="size-5" /> aria-label="Restore two-pane view"
</button> >
<Minimize2 className="size-5" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom">Restore two-pane view</TooltipContent>
</Tooltip>
)} )}
{(selectedPath || isGraphOpen) && ( {(selectedPath || isGraphOpen) && (
<Tooltip> <Tooltip>
@ -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" 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"} aria-label={isChatSidebarOpen ? "Maximize knowledge view" : "Restore two-pane view"}
> >
{isChatSidebarOpen ? <Expand className="size-5" /> : <Shrink className="size-5" />} {isChatSidebarOpen ? <Maximize2 className="size-5" /> : <Minimize2 className="size-5" />}
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="bottom"> <TooltipContent side="bottom">

View file

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' 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 { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils' 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" 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'} aria-label={isMaximized ? 'Restore two-pane view' : 'Maximize chat view'}
> >
{isMaximized ? <Shrink className="size-5" /> : <Expand className="size-5" />} {isMaximized ? <Minimize2 className="size-5" /> : <Maximize2 className="size-5" />}
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="bottom"> <TooltipContent side="bottom">