mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-15 21:11:08 +02:00
fix: chat sidebar buttons and sidebar collapse behaviour
This commit is contained in:
parent
2bb27e477f
commit
2efc80a7e2
2 changed files with 51 additions and 15 deletions
|
|
@ -6,7 +6,7 @@ import type { LanguageModelUsage, ToolUIPart } from 'ai';
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
import { Button } from './components/ui/button';
|
import { Button } from './components/ui/button';
|
||||||
import { CheckIcon, LoaderIcon, ArrowUp, PanelLeftIcon, PanelRightIcon, Square } from 'lucide-react';
|
import { CheckIcon, LoaderIcon, ArrowUp, PanelLeftIcon, PanelRightIcon, Square, X } 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 { ChatInputBar } from './components/chat-button';
|
import { ChatInputBar } from './components/chat-button';
|
||||||
|
|
@ -497,6 +497,7 @@ function App() {
|
||||||
const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set())
|
const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set())
|
||||||
const [recentWikiFiles, setRecentWikiFiles] = useState<string[]>([])
|
const [recentWikiFiles, setRecentWikiFiles] = useState<string[]>([])
|
||||||
const [isGraphOpen, setIsGraphOpen] = useState(false)
|
const [isGraphOpen, setIsGraphOpen] = useState(false)
|
||||||
|
const [expandedFrom, setExpandedFrom] = useState<{ path: string | null; graph: boolean } | null>(null)
|
||||||
const [graphData, setGraphData] = useState<{ nodes: GraphNode[]; edges: GraphEdge[] }>({
|
const [graphData, setGraphData] = useState<{ nodes: GraphNode[]; edges: GraphEdge[] }>({
|
||||||
nodes: [],
|
nodes: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
|
|
@ -1303,9 +1304,28 @@ function App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpenFullScreenChat = useCallback(() => {
|
const handleOpenFullScreenChat = useCallback(() => {
|
||||||
|
// Remember where we came from so the close button can return
|
||||||
|
if (selectedPath || isGraphOpen) {
|
||||||
|
setExpandedFrom({ path: selectedPath, graph: isGraphOpen })
|
||||||
|
}
|
||||||
|
// Copy sidebar input text to full-screen input (keep sidebar message intact for return)
|
||||||
|
if (message.trim()) {
|
||||||
|
setPresetMessage(message)
|
||||||
|
}
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
}, [])
|
}, [selectedPath, isGraphOpen, message])
|
||||||
|
|
||||||
|
const handleCloseFullScreenChat = useCallback(() => {
|
||||||
|
if (expandedFrom) {
|
||||||
|
if (expandedFrom.graph) {
|
||||||
|
setIsGraphOpen(true)
|
||||||
|
} else if (expandedFrom.path) {
|
||||||
|
setSelectedPath(expandedFrom.path)
|
||||||
|
}
|
||||||
|
setExpandedFrom(null)
|
||||||
|
}
|
||||||
|
}, [expandedFrom])
|
||||||
|
|
||||||
// File navigation with history tracking
|
// File navigation with history tracking
|
||||||
const navigateToFile = useCallback((path: string | null) => {
|
const navigateToFile = useCallback((path: string | null) => {
|
||||||
|
|
@ -1320,6 +1340,7 @@ function App() {
|
||||||
setSelectedPath(path)
|
setSelectedPath(path)
|
||||||
// Clear background task selection when navigating to a file
|
// Clear background task selection when navigating to a file
|
||||||
setSelectedBackgroundTask(null)
|
setSelectedBackgroundTask(null)
|
||||||
|
setExpandedFrom(null)
|
||||||
}, [selectedPath])
|
}, [selectedPath])
|
||||||
|
|
||||||
const navigateBack = useCallback(() => {
|
const navigateBack = useCallback(() => {
|
||||||
|
|
@ -1394,17 +1415,22 @@ function App() {
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl+L to open main chat view
|
// Keyboard shortcut: Ctrl+L to toggle main chat view
|
||||||
|
const isFullScreenChat = !selectedPath && !isGraphOpen && !selectedBackgroundTask
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'l') {
|
if ((e.ctrlKey || e.metaKey) && e.key === 'l') {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleOpenFullScreenChat()
|
if (isFullScreenChat && expandedFrom) {
|
||||||
|
handleCloseFullScreenChat()
|
||||||
|
} else {
|
||||||
|
handleOpenFullScreenChat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
return () => document.removeEventListener('keydown', handleKeyDown)
|
return () => document.removeEventListener('keydown', handleKeyDown)
|
||||||
}, [handleOpenFullScreenChat])
|
}, [handleOpenFullScreenChat, handleCloseFullScreenChat, isFullScreenChat, expandedFrom])
|
||||||
|
|
||||||
const toggleExpand = (path: string, kind: 'file' | 'dir') => {
|
const toggleExpand = (path: string, kind: 'file' | 'dir') => {
|
||||||
if (kind === 'file') {
|
if (kind === 'file') {
|
||||||
|
|
@ -1889,6 +1915,16 @@ function App() {
|
||||||
Close Graph
|
Close Graph
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{!selectedPath && !isGraphOpen && expandedFrom && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleCloseFullScreenChat}
|
||||||
|
className="titlebar-no-drag flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
||||||
|
aria-label="Return to file"
|
||||||
|
>
|
||||||
|
<X className="size-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{(selectedPath || isGraphOpen) && (
|
{(selectedPath || isGraphOpen) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { ArrowUp, Expand, LoaderIcon, Plus, Square } from 'lucide-react'
|
import { ArrowUp, Expand, LoaderIcon, SquarePen, Square } from 'lucide-react'
|
||||||
import type { ToolUIPart } from 'ai'
|
import type { ToolUIPart } from 'ai'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
@ -465,24 +465,24 @@ export function ChatSidebar({
|
||||||
<>
|
<>
|
||||||
{/* Header - minimal, expand and new chat buttons */}
|
{/* Header - minimal, expand and new chat buttons */}
|
||||||
<header className="titlebar-drag-region flex h-10 shrink-0 items-center justify-end gap-1 px-2 bg-sidebar">
|
<header className="titlebar-drag-region flex h-10 shrink-0 items-center justify-end gap-1 px-2 bg-sidebar">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon" onClick={onNewChat} className="titlebar-no-drag h-8 w-8 text-muted-foreground hover:text-foreground">
|
||||||
|
<SquarePen className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">New chat</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
{onOpenFullScreen && (
|
{onOpenFullScreen && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" onClick={onOpenFullScreen} className="h-8 w-8 text-muted-foreground hover:text-foreground">
|
<Button variant="ghost" size="icon" onClick={onOpenFullScreen} className="titlebar-no-drag h-8 w-8 text-muted-foreground hover:text-foreground">
|
||||||
<Expand className="h-4 w-4" />
|
<Expand className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="bottom">Full screen chat</TooltipContent>
|
<TooltipContent side="bottom">Full screen chat</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button variant="ghost" size="icon" onClick={onNewChat} className="h-8 w-8 text-muted-foreground hover:text-foreground">
|
|
||||||
<Plus className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom">New chat</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Conversation area */}
|
{/* Conversation area */}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue