Files
boocode/apps/web/src/components/EmptyState.tsx
indifferentketchup aec209310e 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
2026-06-08 03:49:22 +00:00

69 lines
2.0 KiB
TypeScript

import type { ReactNode } from 'react';
import { Inbox } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
interface EmptyStateProps {
/** Optional icon node shown above the title. Defaults to a muted Inbox icon. */
icon?: ReactNode;
/** Main heading text (bold, base font-size). */
title: string;
/** Optional descriptive text shown below the title (muted, sm font-size). */
description?: string;
/** Optional CTA button rendered below the description. */
action?: {
label: string;
onClick: () => void;
variant?: 'default' | 'outline';
};
className?: string;
}
/**
* Reusable empty state for lists, search results, and landing pages.
*
* Renders a centered column with:
* 1. Optional icon (default: `Inbox` from lucide-react, drawn at 50% muted
* opacity so it sits subtly in the background).
* 2. Bold title.
* 3. Optional description (constrained to `max-w-sm` for readability).
* 4. Optional action button (outline by default).
*
* Design follows The Data Terminal aesthetic: charcoal canvas, low-chrome
* muted icon, high-contrast text, and an outline button that gets an ember
* glow on interaction.
*/
export function EmptyState({
icon,
title,
description,
action,
className,
}: EmptyStateProps) {
return (
<div
className={cn(
'flex flex-col items-center justify-center text-center gap-3 px-4 py-12',
className,
)}
>
<div className="text-muted-foreground/50">
{icon ?? <Inbox size={40} strokeWidth={1.5} />}
</div>
<h3 className="text-base font-semibold text-foreground">{title}</h3>
{description && (
<p className="text-sm text-muted-foreground max-w-sm">{description}</p>
)}
{action && (
<Button
variant={action.variant ?? 'outline'}
onClick={action.onClick}
className="mt-1"
>
{action.label}
</Button>
)}
</div>
);
}