import React from 'react'; import { TOOL_ITEM_MUTED } from '@renderer/constants/cssVariables'; import { getTriggerColorDef, type TriggerColor } from '@shared/constants/triggerColors'; import { ChevronRight } from 'lucide-react'; import { formatDuration, formatTokens, getStatusDotColor } from './baseItemHelpers'; // ============================================================================= // Types // ============================================================================= export type ItemStatus = 'ok' | 'error' | 'pending' | 'orphaned'; interface BaseItemProps { /** Icon component to display */ icon: React.ReactNode; /** Primary label (e.g., "Thinking", "Output", tool name) */ label: string; /** Summary text shown after the label */ summary?: string; /** Token count to display */ tokenCount?: number; /** Label for tokens (default: "tokens") */ tokenLabel?: string; /** Status indicator (green/red/gray dot) */ status?: ItemStatus; /** Duration in milliseconds */ durationMs?: number; /** Click handler for toggling */ onClick: () => void; /** Whether the item is expanded */ isExpanded: boolean; /** Whether the item has expandable content */ hasExpandableContent?: boolean; /** Additional classes for highlighting (e.g., error deep linking) */ highlightClasses?: string; /** Inline styles for highlighting (used by custom hex colors) */ highlightStyle?: React.CSSProperties; /** Notification dot color for custom triggers */ notificationDotColor?: TriggerColor; /** Children rendered when expanded */ children?: React.ReactNode; } // ============================================================================= // Helper Components // ============================================================================= /** * Small status dot indicator. */ export const StatusDot: React.FC<{ status: ItemStatus }> = ({ status }) => { return ( ); }; // ============================================================================= // Main Component // ============================================================================= /** * BaseItem provides a consistent layout for all expandable items in the chat view. * * Layout: * - Clickable header row with icon, label, summary, tokens, status, and chevron * - Expanded content area with left border indent * * Used by: ThinkingItem, TextItem, LinkedToolItem, SlashItem, SubagentItem */ export const BaseItem: React.FC = ({ icon, label, summary, tokenCount, tokenLabel = 'tokens', status, durationMs, onClick, isExpanded, hasExpandableContent = true, highlightClasses = '', highlightStyle, notificationDotColor, children, }) => { return (
{/* Clickable Header */}
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } }} className="group flex cursor-pointer items-center gap-2 rounded px-2 py-1.5" style={{ backgroundColor: 'transparent' }} onMouseEnter={(e) => Object.assign(e.currentTarget.style, { backgroundColor: 'var(--tool-item-hover-bg)' }) } onMouseLeave={(e) => Object.assign(e.currentTarget.style, { backgroundColor: 'transparent' }) } > {/* Icon */} {icon} {/* Label */} {label} {/* Separator and Summary */} {summary && ( <> - {summary} )} {/* Spacer if no summary */} {!summary && } {/* Token count badge */} {tokenCount != null && tokenCount > 0 && ( ~{formatTokens(tokenCount)} {tokenLabel} )} {/* Status indicator - hidden when notification dot replaces it */} {status && !notificationDotColor && } {/* Notification dot (replaces status dot when present) */} {notificationDotColor && ( )} {/* Duration */} {durationMs !== undefined && ( {formatDuration(durationMs)} )} {/* Expand/collapse chevron */} {hasExpandableContent && ( )}
{/* Expanded Content */} {isExpanded && children && (
{children}
)}
); };