UI Components
Overview
The UI layer uses React + Ink for terminal user interface. There are ~140 React components organized in a modular, reusable architecture.
Component Directory Structure
src/components/
├── design-system/ - Base design primitives
├── MessageSelector.tsx - Message filtering UI
├── Spinner.js - Loading indicator
├── [140+ component files]
└── index.ts - Component registry
Design System
The design system provides base primitives for consistent UI:
src/components/design-system/
├── Box.tsx - Layout container
├── Text.tsx - Text rendering
├── Button.tsx - Interactive button
├── Input.tsx - Text input field
├── Modal.tsx - Modal dialog
├── Card.tsx - Content container
├── Badge.tsx - Status indicator
└── [more primitives]
Core Component Categories
Layout Components
Box- Flexible layout containerFlex- Flexbox layoutGrid- Grid layout systemSpacer- Spacing utility
Interactive Components
Button- Clickable actionInput- Text inputCheckbox- Boolean selectionSelect- Dropdown selection
Feedback Components
Spinner- Loading indicatorAlert- Notification displayProgress- Progress barToast- Temporary message
Content Components
Card- Content containerModal- Modal dialogPanel- Collapsible sectionTooltip- Hover information
Component Pattern
All components follow a consistent pattern:
// src/components/ExampleComponent.tsx
import React from 'react';
import { Box } from './design-system/Box.js';
export function ExampleComponent(props: ExampleProps) {
return (
<Box>
{/* Component content */}
</Box>
);
}
Component State Management
Components use React hooks for state:
import { useState, useEffect } from 'react';
export function InteractiveComponent() {
const [state, setState] = useState<StateType>(initialState);
useEffect(() => {
// Side effects
}, [dependencies]);
return (
<Box>
{/* Render based on state */}
</Box>
);
}
Component Integration with Tools
Tool UI components are organized per-tool:
src/tools/<ToolName>/
├── <ToolName>Tool.ts - Tool logic
├── UI.tsx - Tool-specific UI
└── prompt.ts - System prompt
Example: BashTool UI
// src/tools/BashTool/UI.tsx
import React from 'react';
import { Box, Text } from '../../components/design-system';
export function BashToolUI(props: BashToolUIProps) {
return (
<Box>
<Text>Bash command output: {props.output}</Text>
</Box>
);
}
Component Best Practices
- Composability: Components designed for nesting
- Props Validation: Strict TypeScript typing
- State Management: React hooks for local state
- Styling: CSS-in-JS via Ink
- Accessibility: ARIA labels for screen readers
Component Registry
Components are registered for discovery:
// src/components/index.ts
export { Box } from './design-system/Box.js';
export { Text } from './design-system/Text.js';
export { Button } from './design-system/Button.js';
// ... all other components
Component Performance
- Memoization:
React.memofor expensive renders - Lazy Loading: Heavy components loaded on demand
- Conditional Rendering: Render only when needed
- Event Debouncing: Reduce re-renders
Component Security
- Error Boundaries: Isolate component failures
- Permission Checks: Components respect permission modes
- Feature Flags: Conditional component availability
- State Isolation: Component state separate from global
Component Examples
Spinner Component
// src/components/Spinner.js
export function Spinner(props: { mode?: SpinnerMode }) {
const mode = props.mode ?? 'dots';
// Render spinning animation
return <Box>{/* spinner animation */}</Box>;
}
Message Selector Component
// src/components/MessageSelector.tsx
export function MessageSelector(props: MessageSelectorProps) {
const { messages, onSelect } = props;
return (
<Box>
{messages.map(msg => (
<div key={msg.id} onClick={() => onSelect(msg)}>
{msg.content}
</div>
))}
</Box>
);
}
Component Testing
Components are tested via:
- Unit Tests: Individual component behavior
- Integration Tests: Component interactions
- E2E Tests: Full UI flow
Component Documentation
Each component has inline documentation:
/**
* Box - Flexible layout container
*
* @param children - Content to render
* @param style - CSS-in-JS styles
* @param className - Optional class name
*/
export function Box(props: BoxProps) {
// Implementation
}
Component Security Considerations
- Working Directory: Components respect cwd restrictions
- Permission Modes: Components adapt to permission settings
- Feature Flags: Conditional component availability
- Error Handling: Graceful degradation on failures