refactor: update readme and remove unusedfiles.

This commit is contained in:
simosmik
2026-02-09 18:18:34 +00:00
committed by Haileyesus
parent 5f0676bdb3
commit d8e7a9e944
5 changed files with 153 additions and 432 deletions

View File

@@ -2,7 +2,9 @@
## Overview
This folder contains a **config-driven architecture** for rendering tool executions in the chat interface. Instead of scattered conditional logic, all tool rendering is centralized through configurations and reusable display components.
Config-driven architecture for rendering tool executions in chat. All tool display behavior is defined in `toolConfigs.ts` — no scattered conditionals. Two base display patterns: **OneLineDisplay** for compact tools, **CollapsibleDisplay** for tools with expandable content.
Non-error tool results route through `ToolRenderer` with `mode="result"` (single source of truth). Error results are handled inline in `MessageComponent` with a red error box.
---
@@ -10,407 +12,213 @@ This folder contains a **config-driven architecture** for rendering tool executi
```
tools/
├── components/ # Reusable display components
│ ├── OneLineDisplay.tsx # Simple one-line tool displays
│ ├── CollapsibleDisplay.tsx # Expandable tool displays
│ ├── ContentRenderers/ # Content-specific renderers
│ ├── DiffViewer.tsx # File diff viewer
│ │ ├── MarkdownContent.tsx # Markdown renderer
│ │ ├── FileListContent.tsx # File list for search results
│ │ ├── TodoListContent.tsx # Todo list renderer
│ │ ── TextContent.tsx # Plain text/JSON/code
│ ├── FilePathButton.tsx # Clickable file paths
│ └── CollapsibleSection.tsx # Collapsible wrapper
├── configs/ # Tool configurations
── types.ts # TypeScript interfaces
│ └── toolConfigs.ts # All tool configs (10 tools)
── ToolRenderer.tsx # Main router component
└── README.md # This file
├── components/
│ ├── OneLineDisplay.tsx # Compact one-line tool display
│ ├── CollapsibleDisplay.tsx # Expandable tool display (uses children pattern)
│ ├── CollapsibleSection.tsx # <details>/<summary> wrapper
│ ├── ContentRenderers/
│ │ ├── DiffViewer.tsx # File diff viewer (memoized)
│ │ ├── MarkdownContent.tsx # Markdown renderer
│ │ ├── FileListContent.tsx # Comma-separated clickable file list
│ │ ── TodoListContent.tsx # Todo items with status badges
│ ├── TaskListContent.tsx # Task tracker with progress bar
│ └── TextContent.tsx # Plain text / JSON / code
├── configs/
── toolConfigs.ts # All tool configs + ToolDisplayConfig type
── ToolRenderer.tsx # Main router (React.memo wrapped)
── README.md
```
---
## Core Concepts
## Display Patterns
### 1. Display Components
### OneLineDisplay
All tools use one of two base display patterns:
Used by: Bash, Read, Grep, Glob, TodoRead, TaskCreate, TaskUpdate, TaskGet
#### **OneLineDisplay** - For simple tools
Used by: Bash, Read, Grep, Glob, TodoRead
Renders as a single line with `border-l-2` accent. Supports multiple rendering modes based on `action`:
- **terminal** (`style: 'terminal'`) — Dark pill around command text, green `$` prompt
- **open-file** — Shows filename only (truncated from full path), clickable to open
- **jump-to-results** — Shows pattern with anchor link to result section
- **copy** — Shows value with hover copy button
- **none** — Plain display
```tsx
<OneLineDisplay
icon="$" // Optional icon
label="Read" // Optional label
value="command text" // Main value to display
secondary="(desc)" // Optional secondary text
action="copy" // Action type: copy | open-file | jump-to-results | none
onAction={() => ...} // Action callback
colorScheme={{ // Optional color customization
primary: "text-...",
secondary: "text-..."
toolName="Read"
icon="terminal" // Optional icon or style keyword
label="Read" // Tool label
value="/path/to/file.ts" // Main display value
secondary="description" // Optional secondary text (italic)
action="open-file" // Action type
onAction={() => ...} // Click handler
colorScheme={{ // Per-tool colors
primary: 'text-...',
border: 'border-...',
icon: 'text-...'
}}
resultId="tool-result-x" // For jump-to-results anchor
toolResult={...} // For conditional jump arrow
toolId="x" // Tool use ID
/>
```
#### **CollapsibleDisplay** - For complex tools
Used by: Edit, Write, Plan, TodoWrite, Grep/Glob results
### CollapsibleDisplay
Used by: Edit, Write, ApplyPatch, Grep/Glob results, TodoWrite, TaskList/TaskGet results, ExitPlanMode, Default
Wraps `CollapsibleSection` (`<details>`/`<summary>`) with a `border-l-2` accent colored by tool category. Accepts **children** directly (not contentProps).
```tsx
<CollapsibleDisplay
title="View edit diff" // Section title
defaultOpen={false} // Expand by default?
action={<FilePathButton />} // Optional action button
contentType="diff" // Type of content to render
contentProps={{...}} // Props for content renderer
showRawParameters={true} // Show raw JSON?
rawContent="..." // Raw JSON content
/>
toolName="Edit"
toolId="123"
title="filename.ts" // Section title (can be clickable)
defaultOpen={false}
onTitleClick={() => ...} // Makes title a clickable link (for edit tools)
showRawParameters={true} // Show raw JSON toggle
rawContent="..." // Raw JSON string
toolCategory="edit" // Drives border color
>
<DiffViewer {...} /> // Content as children
</CollapsibleDisplay>
```
### 2. Content Renderers
Different content types are handled by specialized renderers:
- **diff** → `DiffViewer` - Shows before/after file changes
- **markdown** → `MarkdownContent` - Renders markdown with styling
- **file-list** → `FileListContent` - Clickable file list
- **todo-list** → `TodoListContent` - Todo items with status
- **text** → `TextContent` - Plain text, JSON, or code
### 3. Configuration-Driven
Every tool is defined by a config object. No code changes needed to add/modify tools!
**Tool category colors** (via `border-l-2`):
| Category | Tools | Color |
|----------|-------|-------|
| `edit` | Edit, Write, ApplyPatch | amber |
| `bash` | Bash | green |
| `search` | Grep, Glob | gray |
| `todo` | TodoWrite, TodoRead | violet |
| `task` | TaskCreate/Update/List/Get | violet |
| `plan` | ExitPlanMode | indigo |
| `default` | everything else | neutral gray |
---
## How to Add a New Tool
## Content Renderers
### Example: Adding a "Format" tool
Specialized components for different content types, rendered as children of `CollapsibleDisplay`:
| contentType | Component | Used by |
|---|---|---|
| `diff` | `DiffViewer` | Edit, Write, ApplyPatch |
| `markdown` | `MarkdownContent` | ExitPlanMode |
| `file-list` | `FileListContent` | Grep/Glob results |
| `todo-list` | `TodoListContent` | TodoWrite, TodoRead |
| `task` | `TaskListContent` | TaskList, TaskGet results |
| `text` | `TextContent` | Default fallback |
| `success-message` | inline SVG | TodoWrite result |
---
## Adding a New Tool
**Step 1:** Add config to `configs/toolConfigs.ts`
```typescript
Format: {
MyTool: {
input: {
type: 'one-line', // or 'collapsible'
label: 'Format',
getValue: (input) => input.file_path,
label: 'MyTool',
getValue: (input) => input.some_field,
action: 'open-file',
colorScheme: {
primary: 'text-purple-600 dark:text-purple-400'
primary: 'text-purple-600 dark:text-purple-400',
border: 'border-purple-400 dark:border-purple-500'
}
},
result: {
hideOnSuccess: true // Hide successful results
hideOnSuccess: true // Only show errors
}
}
```
**Step 2:** That's it! No other files to touch.
**Step 2:** If the tool needs a category color, add it to `getToolCategory()` in `ToolRenderer.tsx`.
The ToolRenderer automatically:
- Parses the tool input
- Selects the right display component
- Passes the correct props
- Handles callbacks (file opening, copy, etc.)
**That's it.** The ToolRenderer auto-routes based on config.
---
## Configuration Reference
### Input Configuration
### ToolDisplayConfig
```typescript
input: {
// Display type (required)
type: 'one-line' | 'collapsible' | 'hidden'
// One-line specific
icon?: string // Icon to display (e.g., "$", "✓")
label?: string // Text label (e.g., "Read", "Grep")
getValue?: (input) => string // Extract main value from input
getSecondary?: (input) => string // Extract secondary text (description)
action?: 'copy' | 'open-file' | 'jump-to-results' | 'none'
colorScheme?: {
primary?: string // Tailwind classes for main text
secondary?: string // Tailwind classes for secondary text
}
// Collapsible specific
title?: string | ((input) => string) // Section title
defaultOpen?: boolean // Auto-expand?
contentType?: 'diff' | 'markdown' | 'file-list' | 'todo-list' | 'text'
getContentProps?: (input, helpers) => any // Extract props for content renderer
actionButton?: 'file-button' | 'none' // Show file path button?
}
```
### Result Configuration
```typescript
result?: {
hidden?: boolean // Never show results
hideOnSuccess?: boolean // Only show errors
type?: 'one-line' | 'collapsible' | 'special'
contentType?: 'markdown' | 'file-list' | 'todo-list' | 'text' | 'success-message'
getMessage?: (result) => string // For success messages
getContentProps?: (result) => any // Extract content props
}
```
---
## Real-World Examples
### Simple One-Line Tool (Bash)
```typescript
Bash: {
interface ToolDisplayConfig {
input: {
type: 'one-line',
icon: '$',
getValue: (input) => input.command,
getSecondary: (input) => input.description,
action: 'copy'
},
result: {
hideOnSuccess: true
}
}
```
type: 'one-line' | 'collapsible' | 'hidden';
**Renders:**
```
$ npm install (Install dependencies) [Copy Button]
```
// One-line
icon?: string;
label?: string;
getValue?: (input) => string;
getSecondary?: (input) => string | undefined;
action?: 'copy' | 'open-file' | 'jump-to-results' | 'none';
style?: string; // 'terminal' for Bash
wrapText?: boolean;
colorScheme?: {
primary?: string;
secondary?: string;
background?: string;
border?: string;
icon?: string;
};
### Collapsible Diff Tool (Edit)
// Collapsible
title?: string | ((input) => string);
defaultOpen?: boolean;
contentType?: 'diff' | 'markdown' | 'file-list' | 'todo-list' | 'text' | 'task';
getContentProps?: (input, helpers?) => any;
actionButton?: 'none';
};
```typescript
Edit: {
input: {
type: 'collapsible',
title: 'View edit diff for',
contentType: 'diff',
actionButton: 'file-button',
getContentProps: (input) => ({
oldContent: input.old_string,
newContent: input.new_string,
filePath: input.file_path
})
},
result: {
hideOnSuccess: true
}
}
```
**Renders:**
```
▶ View edit diff for [file.ts]
┌─────────────────────────┐
│ - old line │
│ + new line │
└─────────────────────────┘
```
### Search Results (Grep)
```typescript
Grep: {
input: {
type: 'one-line',
label: 'Grep',
getValue: (input) => input.pattern,
getSecondary: (input) => input.path ? `in ${input.path}` : undefined,
action: 'jump-to-results'
},
result: {
type: 'collapsible',
contentType: 'file-list',
getContentProps: (result) => ({
files: result.toolUseResult?.filenames || [],
title: `Found ${count} files`
})
}
}
```
**Renders:**
```
Input: Grep "TODO" in src/ [Search results ↓]
Result: Found 5 files
📄 app.ts (src/)
📄 utils.ts (src/utils/)
...
```
---
## Advanced Features
### Dynamic Content Props with Helpers
For tools that need API calls or complex logic:
```typescript
getContentProps: (input, helpers) => {
const { selectedProject, createDiff, onFileOpen } = helpers;
// Use helpers for complex operations
return {
filePath: input.file_path,
onFileClick: () => onFileOpen(input.file_path)
result?: {
hidden?: boolean; // Never show
hideOnSuccess?: boolean; // Only show errors
type?: 'one-line' | 'collapsible' | 'special';
title?: string | ((result) => string);
defaultOpen?: boolean;
contentType?: 'markdown' | 'file-list' | 'todo-list' | 'text' | 'success-message' | 'task';
getMessage?: (result) => string;
getContentProps?: (result) => any;
};
}
```
### File Opening Logic
---
The ToolRenderer handles file opening automatically for Edit/Write tools:
## All Configured Tools
1. Fetches current file via API
2. Reverse-applies edits (for Edit)
3. Opens file in diff view
4. Falls back gracefully on errors
No config needed - handled by `actionButton: 'file-button'`.
### Custom Color Schemes
Override default colors per tool:
```typescript
colorScheme: {
primary: 'text-purple-600 dark:text-purple-400',
secondary: 'text-purple-400 dark:text-purple-500'
}
```
| Tool | Input | Result | Notes |
|------|-------|--------|-------|
| Bash | terminal one-line | hide success | Dark command pill, green accent |
| Read | one-line (open-file) | hidden | Shows filename, clicks to open |
| Edit | collapsible (diff) | hide success | Amber border, clickable filename |
| Write | collapsible (diff) | hide success | "New" badge on diff |
| ApplyPatch | collapsible (diff) | hide success | "Patch" badge on diff |
| Grep | one-line (jump) | collapsible file-list | Collapsed by default |
| Glob | one-line (jump) | collapsible file-list | Collapsed by default |
| TodoWrite | collapsible (todo-list) | success message | |
| TodoRead | one-line | collapsible todo-list | |
| TaskCreate | one-line | hide success | Shows task subject |
| TaskUpdate | one-line | hide success | Shows `#id → status` |
| TaskList | one-line | collapsible task | Progress bar, status icons |
| TaskGet | one-line | collapsible task | Task details with status |
| ExitPlanMode | collapsible (markdown) | collapsible markdown | Also registered as `exit_plan_mode` |
| Default | collapsible (code) | collapsible text | Fallback for unknown tools |
---
## Component Props Reference
### ToolRenderer (Main Entry Point)
```typescript
<ToolRenderer
toolName="Bash" // Tool identifier
toolInput={...} // Tool input (string or object)
toolResult={...} // Tool result (for mode='result')
mode="input" | "result" // Rendering mode
// Callbacks
onFileOpen={(path, diff) => ...}
createDiff={(old, new) => [...]}
// Context
selectedProject={...}
// Display options
autoExpandTools={false}
showRawParameters={false}
rawToolInput="..."
/>
```
### OneLineDisplay
```typescript
interface OneLineDisplayProps {
icon?: string;
label?: string;
value: string; // Required
secondary?: string;
action?: ActionType;
onAction?: () => void;
colorScheme?: { primary, secondary };
resultId?: string; // For jump-to-results
}
```
### CollapsibleDisplay
```typescript
interface CollapsibleDisplayProps {
title: string; // Required
defaultOpen?: boolean;
action?: React.ReactNode;
contentType: ContentType; // Required
contentProps: any; // Required
showRawParameters?: boolean;
rawContent?: string;
className?: string;
}
```
---
## Testing Your Tool
### 1. Add Config
Add your tool to `toolConfigs.ts`
### 2. Test Input Rendering
Trigger the tool and verify:
- ✅ Correct display component used (one-line vs collapsible)
- ✅ Values extracted correctly from input
- ✅ Actions work (copy, file open, jump)
- ✅ Colors and styling correct
### 3. Test Result Rendering
Check tool results:
- ✅ Results hidden when appropriate
- ✅ Error results always shown
- ✅ Content rendered correctly
- ✅ Interactive elements work
### 4. Test Edge Cases
- Empty inputs
- Missing fields
- Parse errors
- API failures
---
## Performance Notes
### Config Loading
- Configs are loaded once at module import
- No runtime overhead for config lookups
- Tree-shaking removes unused configs in production
### Component Rendering
- Display components are memoized where appropriate
- Content renderers only render when props change
- Collapsible sections lazy-load content
### API Calls
- File opening uses async/await with error handling
- Failures gracefully fall back to simple file open
- API module dynamically imported to reduce bundle size
---
## Quick Reference
### All Configured Tools
| Tool | Type | Display | Action | Result |
|------|------|---------|--------|--------|
| Bash | one-line | $ command | copy | hide success |
| Read | one-line | Read file.ts | open-file | hidden |
| Edit | collapsible | diff viewer | file-button | hide success |
| Write | collapsible | diff viewer | file-button | hide success |
| ApplyPatch | collapsible | diff viewer | file-button | hide success |
| Grep | one-line | pattern | jump | file-list |
| Glob | one-line | pattern | jump | file-list |
| TodoWrite | collapsible | todo-list | none | success msg |
| TodoRead | one-line | Read todo | none | todo-list |
| exit_plan_mode | collapsible | markdown | none | markdown |
| Default | collapsible | text/code | none | text |
## Performance
- **ToolRenderer** is wrapped with `React.memo` — skips re-render when props haven't changed
- **parsedData** is memoized with `useMemo` — JSON parsing only runs when input changes
- **DiffViewer** memoizes `createDiff()` — expensive diff computation cached
- **MessageComponent** caches `localStorage` reads and timestamp formatting via `useMemo`
- Tool results route through `ToolRenderer` (no duplicate rendering paths)
- `CollapsibleDisplay` uses children pattern (no wasteful contentProps indirection)
- Configs are static module-level objects — zero runtime overhead for lookups

View File

@@ -1,44 +0,0 @@
import React from 'react';
interface FilePathButtonProps {
filePath: string;
onClick: () => void;
variant?: 'button' | 'link';
showFullPath?: boolean;
className?: string;
}
/**
* Clickable file path — inline link style
*/
export const FilePathButton: React.FC<FilePathButtonProps> = ({
filePath,
onClick,
variant = 'button',
showFullPath = false,
className = ''
}) => {
const filename = filePath.split('/').pop() || filePath;
const displayText = showFullPath ? filePath : filename;
if (variant === 'link') {
return (
<button
onClick={onClick}
className={`text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-mono text-xs hover:underline transition-colors ${className}`}
>
{displayText}
</button>
);
}
return (
<button
onClick={onClick}
className={`text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-mono text-xs hover:underline transition-colors ${className}`}
title={filePath}
>
{displayText}
</button>
);
};

View File

@@ -1,5 +1,4 @@
export { CollapsibleSection } from './CollapsibleSection';
export { FilePathButton } from './FilePathButton';
export { DiffViewer } from './DiffViewer';
export { OneLineDisplay } from './OneLineDisplay';
export { CollapsibleDisplay } from './CollapsibleDisplay';

View File

@@ -1,2 +0,0 @@
export * from './types';
export * from './toolConfigs';

View File

@@ -1,40 +0,0 @@
import type { ToolResult } from '../../types';
export type ToolCategory = 'command' | 'file-operation' | 'search' | 'todo' | 'plan' | 'default';
export interface ToolConfig {
/** Tool display name */
displayName: string;
/** Tool category for grouping and styling */
category: ToolCategory;
/** Icon identifier (can be emoji or icon name) */
icon?: string;
/** Whether to show minimized display by default */
minimized?: boolean;
/** Name of the renderer component to use */
renderer: string;
/** Whether to hide successful results (show only errors) */
hideSuccessfulResult?: boolean;
/** Whether this tool requires file system access */
requiresFileAccess?: boolean;
/** Whether this tool supports copy to clipboard */
supportsCopy?: boolean;
/** Custom function to determine if result should be hidden */
shouldHideResult?: (result: ToolResult | null) => boolean;
/** Color scheme for the tool display */
colorScheme?: {
primary: string;
secondary: string;
};
}
export type ToolConfigRegistry = Record<string, ToolConfig>;