# Chapter 5: Design System Components Pre-built theme-aware UI components for common terminal interface patterns. ## Dialog Modal dialog with border, title, and keyboard navigation. ```tsx import { Dialog } from '@anthropic/ink' setShowDialog(false)} color="warning" > Are you sure you want to proceed? ``` ### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `title` | `ReactNode` | - | Dialog title (required) | | `subtitle` | `ReactNode` | - | Optional subtitle | | `children` | `ReactNode` | - | Dialog body content | | `onCancel` | `() => void` | - | Called on Esc/n (required) | | `color` | `keyof Theme` | `'permission'` | Title and border color | | `hideInputGuide` | `boolean` | `false` | Hide the keyboard hint footer | | `hideBorder` | `boolean` | `false` | Render without Pane border | | `inputGuide` | `(exitState) => ReactNode` | - | Custom input guide footer | | `isCancelActive` | `boolean` | `true` | Enable/disable cancel keybindings | ### Keyboard Shortcuts - **Enter** -- Confirm (consumer handles this) - **Esc / n** -- Cancel (calls `onCancel`) - **Ctrl+C / Ctrl+D** -- Double-press to exit ### Custom Input Guide ```tsx ( exitState.pending ? Press {exitState.keyName} again to exit : Press Enter to save, Esc to cancel )} > ... ``` ## Pane Bordered container with themed top border. ```tsx import { Pane } from '@anthropic/ink' Content inside a bordered pane ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `children` | `ReactNode` | - | Content | | `color` | `keyof Theme` | `'permission'` | Top border color | ## ProgressBar Visual progress indicator. ```tsx import { ProgressBar } from '@anthropic/ink' ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `ratio` | `number` | - | Progress 0..1 (required) | | `width` | `number` | - | Character width (required) | | `fillColor` | `keyof Theme` | - | Filled portion color | | `emptyColor` | `keyof Theme` | - | Empty portion color | ## Spinner Animated loading spinner. No props. ```tsx import { Spinner } from '@anthropic/ink' Loading... ``` ## LoadingState Loading message with spinner and optional subtitle. ```tsx import { LoadingState } from '@anthropic/ink' ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `message` | `string` | - | Loading message (required) | | `bold` | `boolean` | `false` | Bold message | | `dimColor` | `boolean` | `false` | Dimmed message | | `subtitle` | `string` | - | Secondary text below | ## StatusIcon Semantic status indicator with icon and color. ```tsx import { StatusIcon } from '@anthropic/ink' Build complete ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `status` | `'success' \| 'error' \| 'warning' \| 'info' \| 'pending' \| 'loading'` | - | Status type (required) | | `withSpace` | `boolean` | `false` | Add trailing space | Status icons: - `success` -- Green checkmark - `error` -- Red cross - `warning` -- Yellow warning - `info` -- Blue info - `pending` -- Dimmed circle - `loading` -- Dimmed ellipsis ## FuzzyPicker Full-featured fuzzy search selector with preview support. ```tsx import { FuzzyPicker } from '@anthropic/ink' f.path} renderItem={(f, focused) => {f.name}} onQueryChange={(q) => setFilteredFiles(filterFiles(q))} onSelect={(f) => openFile(f)} onCancel={() => setShowPicker(false)} /> ``` ### Props | Prop | Type | Description | |------|------|-------------| | `title` | `string` | Picker title (required) | | `items` | `readonly T[]` | Items to display (required) | | `getKey` | `(item: T) => string` | Unique key extractor (required) | | `renderItem` | `(item: T, isFocused: boolean) => ReactNode` | Item renderer (required) | | `onQueryChange` | `(query: string) => void` | Filter callback (required) | | `onSelect` | `(item: T) => void` | Enter key handler (required) | | `onCancel` | `() => void` | Esc handler (required) | | `renderPreview` | `(item: T) => ReactNode` | Preview panel renderer | | `previewPosition` | `'bottom' \| 'right'` | Preview placement | | `visibleCount` | `number` | Max visible items | | `direction` | `'down' \| 'up'` | Item ordering | | `onTab` | `PickerAction` | Tab key handler | | `onShiftTab` | `PickerAction` | Shift+Tab handler | | `onFocus` | `(item: T \| undefined) => void` | Focus change callback | | `emptyMessage` | `string \| ((query: string) => string)` | Empty state message | | `matchLabel` | `string` | Status line below list | | `placeholder` | `string` | Input placeholder | | `initialQuery` | `string` | Initial search query | | `selectAction` | `string` | Action label for byline | | `extraHints` | `ReactNode` | Additional keyboard hints | ## Tabs / Tab Tabbed interface with keyboard navigation. ```tsx import { Tabs, Tab } from '@anthropic/ink' ``` ### Tabs Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `children` | `ReactElement[]` | - | Tab elements | | `title` | `string` | - | Header title | | `color` | `keyof Theme` | - | Active tab indicator color | | `defaultTab` | `string` | - | Initial tab id | | `selectedTab` | `string` | - | Controlled selected tab | | `onTabChange` | `(tabId: string) => void` | - | Tab change callback | | `hidden` | `boolean` | `false` | Hide tab headers | | `useFullWidth` | `boolean` | `false` | Use full terminal width | | `banner` | `ReactNode` | - | Banner below tab headers | | `disableNavigation` | `boolean` | `false` | Disable keyboard nav | | `initialHeaderFocused` | `boolean` | `true` | Start with header focused | | `contentHeight` | `number` | - | Fixed content height | | `navFromContent` | `boolean` | `false` | Allow Tab/Arrow from content | ### Tab Props | Prop | Type | Description | |------|------|-------------| | `title` | `string` | Tab label (required) | | `id` | `string` | Tab identifier | | `children` | `ReactNode` | Tab content | ### Tab Hooks ```tsx import { useTabsWidth, useTabHeaderFocus } from '@anthropic/ink' const width = useTabsWidth() // Available content width const focused = useTabHeaderFocus() // Whether tab header is focused ``` ## ListItem Selectable list item with focus/selection indicators. ```tsx import { ListItem } from '@anthropic/ink' {item.label} ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `isFocused` | `boolean` | - | Keyboard focus (required) | | `isSelected` | `boolean` | `false` | Checked/active state | | `children` | `ReactNode` | - | Content | | `description` | `string` | - | Secondary text below | | `styled` | `boolean` | `true` | Auto-style based on state | | `disabled` | `boolean` | `false` | Dimmed, non-interactive | | `showScrollDown` | `boolean` | `false` | Scroll-down hint arrow | | `showScrollUp` | `boolean` | `false` | Scroll-up hint arrow | | `declareCursor` | `boolean` | `true` | Declare terminal cursor | ## SearchBox Search input with theme-aware styling. ```tsx import { SearchBox } from '@anthropic/ink' ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `query` | `string` | - | Current search text | | `placeholder` | `string` | - | Placeholder text | | `isFocused` | `boolean` | - | Focus state | | `isTerminalFocused` | `boolean` | - | Terminal focus state | | `prefix` | `string` | - | Input prefix label | | `width` | `number \| string` | - | Input width | | `cursorOffset` | `number` | - | Cursor position offset | | `borderless` | `boolean` | `false` | Remove border | ## Divider Horizontal/vertical divider line. ```tsx import { Divider } from '@anthropic/ink' ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `width` | `number` | Terminal width | Divider width | | `color` | `keyof Theme` | Dimmed | Line color | | `char` | `string` | `'─'` | Line character | | `padding` | `number` | `0` | Width reduction | | `title` | `string` | - | Centered title text | ## Byline Footer with middot-separated items. ```tsx import { Byline } from '@anthropic/ink' ``` ## KeyboardShortcutHint Display a keyboard shortcut with its action. ```tsx import { KeyboardShortcutHint } from '@anthropic/ink' ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `shortcut` | `string` | - | Key or chord to display | | `action` | `string` | - | Action description | | `parens` | `boolean` | `false` | Wrap in parentheses | | `bold` | `boolean` | `false` | Bold shortcut text | ## ConfigurableShortcutHint Displays a shortcut hint that reads the actual keybinding from config. ```tsx import { ConfigurableShortcutHint } from '@anthropic/ink' ``` | Prop | Type | Description | |------|------|-------------| | `action` | `string` | Keybinding action name | | `context` | `string` | Keybinding context | | `fallback` | `string` | Default shortcut if unbound | | `description` | `string` | Action description | | `parens` | `boolean` | Wrap in parentheses | | `bold` | `boolean` | Bold shortcut text | ## Ratchet Animated counter component that prevents layout jumps. ```tsx import { Ratchet } from '@anthropic/ink' {count} ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `children` | `ReactNode` | - | Content | | `lock` | `'always' \| 'offscreen'` | `'always'` | Width locking strategy. `'always'` locks always; `'offscreen'` only locks when the element is scrolled off-screen |