fix(ui): panel toggle in header bar + message spacing
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- Move side panel toggle from floating button to chat header right side (Trae Solo style) via new PanelToggleButton component - Add px-6 py-4 padding to message list container - Add mb-5 gap between messages for readable vertical spacing
This commit is contained in:
@@ -12,7 +12,7 @@ import { type UnlistenFn } from '@tauri-apps/api/event';
|
|||||||
import { safeListenEvent } from '../lib/safe-tauri';
|
import { safeListenEvent } from '../lib/safe-tauri';
|
||||||
import { Paperclip, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon, Search, ClipboardList, Square } from 'lucide-react';
|
import { Paperclip, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon, Search, ClipboardList, Square } from 'lucide-react';
|
||||||
import { Button, EmptyState, MessageListSkeleton, LoadingDots } from './ui';
|
import { Button, EmptyState, MessageListSkeleton, LoadingDots } from './ui';
|
||||||
import { ResizableChatLayout } from './ai/ResizableChatLayout';
|
import { ResizableChatLayout, PanelToggleButton } from './ai/ResizableChatLayout';
|
||||||
import { ArtifactPanel } from './ai/ArtifactPanel';
|
import { ArtifactPanel } from './ai/ArtifactPanel';
|
||||||
import { ToolCallChain, type ToolCallStep } from './ai/ToolCallChain';
|
import { ToolCallChain, type ToolCallStep } from './ai/ToolCallChain';
|
||||||
import { TaskProgress, type Subtask } from './ai/TaskProgress';
|
import { TaskProgress, type Subtask } from './ai/TaskProgress';
|
||||||
@@ -361,6 +361,13 @@ export function ChatArea({ compact, onOpenDetail }: { compact?: boolean; onOpenD
|
|||||||
<Search className="w-3.5 h-3.5" />
|
<Search className="w-3.5 h-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{/* Side panel toggle — Trae Solo style, in header */}
|
||||||
|
{!compact && (
|
||||||
|
<PanelToggleButton
|
||||||
|
panelOpen={artifactPanelOpen}
|
||||||
|
onToggle={() => setArtifactPanelOpen(!artifactPanelOpen)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/* 详情按钮 (简洁模式) */}
|
{/* 详情按钮 (简洁模式) */}
|
||||||
{compact && onOpenDetail && (
|
{compact && onOpenDetail && (
|
||||||
<Button
|
<Button
|
||||||
@@ -398,7 +405,7 @@ export function ChatArea({ compact, onOpenDetail }: { compact?: boolean; onOpenD
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<Conversation className="flex-1 bg-white dark:bg-gray-900">
|
<Conversation className="flex-1 bg-white dark:bg-gray-900 px-6 py-4">
|
||||||
<AnimatePresence mode="popLayout">
|
<AnimatePresence mode="popLayout">
|
||||||
{/* Loading skeleton */}
|
{/* Loading skeleton */}
|
||||||
{isLoading && messages.length === 0 && (
|
{isLoading && messages.length === 0 && (
|
||||||
@@ -467,6 +474,7 @@ export function ChatArea({ compact, onOpenDetail }: { compact?: boolean; onOpenD
|
|||||||
animate="visible"
|
animate="visible"
|
||||||
layout
|
layout
|
||||||
transition={defaultTransition}
|
transition={defaultTransition}
|
||||||
|
className="mb-5"
|
||||||
>
|
>
|
||||||
<MessageBubble message={message} onRetry={createRetryHandler(message.id)} />
|
<MessageBubble message={message} onRetry={createRetryHandler(message.id)} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import { X, PanelRightOpen, PanelRightClose } from 'lucide-react';
|
|||||||
* - Right panel: Artifact/detail viewer (collapsible)
|
* - Right panel: Artifact/detail viewer (collapsible)
|
||||||
* - Draggable resize handle between panels
|
* - Draggable resize handle between panels
|
||||||
* - Persisted panel sizes via localStorage
|
* - Persisted panel sizes via localStorage
|
||||||
|
*
|
||||||
|
* Side-panel toggle is injected into the chat header via `headerAction`
|
||||||
|
* so it sits cleanly in the top-right corner (Trae Solo style).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface ResizableChatLayoutProps {
|
interface ResizableChatLayoutProps {
|
||||||
@@ -61,16 +64,10 @@ export function ResizableChatLayout({
|
|||||||
}, [rightPanelOpen, onRightPanelToggle]);
|
}, [rightPanelOpen, onRightPanelToggle]);
|
||||||
|
|
||||||
if (!rightPanelOpen || !rightPanel) {
|
if (!rightPanelOpen || !rightPanel) {
|
||||||
|
// Panel closed: just render chat panel, no floating button
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col overflow-hidden relative">
|
<div className="h-full flex flex-col overflow-hidden">
|
||||||
{chatPanel}
|
{chatPanel}
|
||||||
<button
|
|
||||||
onClick={handleToggle}
|
|
||||||
className="absolute top-20 right-4 z-10 p-1.5 rounded-md bg-white/80 dark:bg-gray-800/80 border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-white dark:hover:bg-gray-800 transition-colors shadow-sm"
|
|
||||||
title="打开侧面板"
|
|
||||||
>
|
|
||||||
<PanelRightOpen className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -87,15 +84,8 @@ export function ResizableChatLayout({
|
|||||||
defaultSize={sizes.left}
|
defaultSize={sizes.left}
|
||||||
minSize="40%"
|
minSize="40%"
|
||||||
>
|
>
|
||||||
<div className="h-full flex flex-col relative">
|
<div className="h-full flex flex-col">
|
||||||
{chatPanel}
|
{chatPanel}
|
||||||
<button
|
|
||||||
onClick={handleToggle}
|
|
||||||
className="absolute top-20 right-4 z-10 p-1.5 rounded-md bg-white/80 dark:bg-gray-800/80 border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-white dark:hover:bg-gray-800 transition-colors shadow-sm"
|
|
||||||
title="关闭侧面板"
|
|
||||||
>
|
|
||||||
<PanelRightClose className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
@@ -134,3 +124,28 @@ export function ResizableChatLayout({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle button for embedding in chat header.
|
||||||
|
* Renders PanelRightOpen/PanelRightClose icon — Trae Solo style.
|
||||||
|
*/
|
||||||
|
export function PanelToggleButton({
|
||||||
|
panelOpen,
|
||||||
|
onToggle,
|
||||||
|
}: {
|
||||||
|
panelOpen: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onToggle}
|
||||||
|
className="p-1.5 rounded-md text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
||||||
|
title={panelOpen ? '关闭侧面板' : '打开侧面板'}
|
||||||
|
>
|
||||||
|
{panelOpen
|
||||||
|
? <PanelRightClose className="w-4 h-4" />
|
||||||
|
: <PanelRightOpen className="w-4 h-4" />
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user