mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-30 20:39:46 +02:00
feat: update chat and app UI with new maximize/minimize icons
This commit is contained in:
parent
e8d8332e34
commit
bf5f6f16de
2 changed files with 41 additions and 14 deletions
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue