feat(web): workspace components — ComparePane, Memory page, McpDialog, error boundaries, message-parts
- Add ComparePane.tsx: side-by-side AI response comparison - Add Memory.tsx: memory management page with CRUD UI - Add McpPermissionDialog.tsx: MCP tool permission approval dialog - Add McpResponseDisplay.tsx: MCP response visualization - Add MessageBoundary.tsx + MessageListErrorBoundary.tsx: error resilience - Add EmptyState.tsx: contextual empty state component - Add KeyboardShortcutsDialog.tsx: keyboard shortcut reference - Add message-parts/: ActionRow, CompactCard, MistakeRecoverySentinel, ReasoningBlock, SendToTerminalMenu, StatsLine, SummaryCard - Add useDraftPersistence.ts: draft message persistence hook - Add useTerminals.ts: terminal session management hook - Add keyboard-shortcuts.ts + tool-utils.ts: shared utilities - Extend components: ChatInput, MessageBubble, MessageList, Workspace, panes - Extend hooks: useTerminalSocket, useSessionStream test suite - Update pages: Home, Project — workspace layout and session flow
This commit is contained in:
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { Archive, ChevronLeft, Code, MessageSquare, RotateCcw, Terminal, Trash2 } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import mascot from '@/assets/brand/banner-mascot.png';
|
||||
import { EmptyState } from '@/components/EmptyState';
|
||||
import { ChatInput } from '@/components/ChatInput';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@@ -167,9 +168,11 @@ export function SessionLandingPage({
|
||||
<h2 className="text-sm font-medium ml-auto mr-1">Session history</h2>
|
||||
</div>
|
||||
{isEmpty ? (
|
||||
<p className="text-sm text-muted-foreground text-center py-8">
|
||||
No conversations yet. Send a message to start.
|
||||
</p>
|
||||
<EmptyState
|
||||
icon={<MessageSquare size={40} strokeWidth={1.5} />}
|
||||
title="No conversations"
|
||||
description="Your chat history will appear here"
|
||||
/>
|
||||
) : (<>
|
||||
{openChats.length > 0 && (
|
||||
<>
|
||||
@@ -200,7 +203,7 @@ export function SessionLandingPage({
|
||||
{formatRelative(c.updated_at)}
|
||||
</span>
|
||||
</button>
|
||||
<div className="shrink-0 flex items-center gap-0.5 opacity-0 group-hover/row:opacity-100 focus-within:opacity-100 max-md:opacity-100 transition-opacity">
|
||||
<div className="shrink-0 flex items-center gap-0.5 opacity-0 group-hover/row:opacity-100 focus-within:opacity-100 max-md:opacity-100 motion-reduce:transition-none transition-opacity">
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => { e.stopPropagation(); void onArchiveChat(c.id); }}
|
||||
@@ -254,7 +257,7 @@ export function SessionLandingPage({
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => { e.stopPropagation(); setDeleteConfirm({ id: c.id, name: c.name }); }}
|
||||
className="shrink-0 inline-flex items-center justify-center size-7 rounded hover:bg-destructive/20 hover:text-destructive max-md:size-9 opacity-0 group-hover/arch:opacity-100 focus-within:opacity-100 max-md:opacity-100 transition-opacity"
|
||||
className="shrink-0 inline-flex items-center justify-center size-7 rounded hover:bg-destructive/20 hover:text-destructive max-md:size-9 opacity-0 group-hover/arch:opacity-100 focus-within:opacity-100 max-md:opacity-100 motion-reduce:transition-none transition-opacity"
|
||||
aria-label="Delete chat"
|
||||
title="Delete"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user