ratkit 0.2.6

A comprehensive collection of reusable TUI components for ratatui including resizable splits, tree views, markdown rendering, toast notifications, dialogs, and terminal embedding
Documentation
# ratkit

> Comprehensive Rust TUI component library built on ratatui 0.29, providing 21 feature-gated modules (primitives, widgets, services) for building rich terminal applications.

This file provides a complete reference for working with the ratkit codebase. The repository is organized as a single crate at the root level with feature-based modularity. Use this guide to understand component relationships, find APIs, and follow established patterns when implementing new features.

## Agent Operating Rules

1. **Single crate at root**: All code is in `src/` with 21 feature flags (e.g., `button`, `pane`, `markdown-preview`)
2. **Enable features explicitly**: No default features; add required features to Cargo.toml (e.g., `features = ["button", "dialog"]`)
3. **Cross-feature dependencies**: Some features auto-enable others (e.g., `tree-view` enables `widget-event`, `repo-watcher` enables `file-watcher` and `git-watcher`)
4. **Use `just` for all operations**: Build (`just build`), test (`just test`), check (`just check`), demos (`just demo`)
5. **Run examples with `--features` flag**: Examples require their specific features (e.g., `--features markdown-preview`)
6. **Import from crate root**: All public APIs are re-exported from `ratkit::` (e.g., `use ratkit::Button`)
7. **StatefulWidget pattern**: Complex widgets require separate state structs persisted in app state
8. **Event loop polling**: Services require regular `check_for_changes()` calls in the event loop
9. **Mouse capture required**: Enable crossterm mouse capture for interactive widgets
10. **Persist widget state**: Never create widget state in render loops - store in app struct
11. **Validate before commits**: Run `just check` (format + lint + test) before committing
12. **Verify feature flags**: Compilation errors often indicate missing feature flags in Cargo.toml

## Environment and Version Constraints

- Rust 1.70+ required (workspace.rust-version in Cargo.toml)
- ratatui 0.29 as the underlying rendering library
- crossterm 0.28 for terminal input/events
- tokio for async runtime
- Single crate at root with 21 feature flags (no workspace members)
- 23 examples in `examples/` (moved from `crates/ratkit/examples/`)
- Optional external deps: notify (file watching), reqwest (ai-chat), pulldown-cmark/syntect (markdown), similar (code-diff)

## Quick Task Playbooks

### Run an example
- **Where to edit**: N/A
- **Related files**: `examples/`
- **Validation**: `cargo run --example button_button_demo --features button`

### Run with just
- **Where to edit**: N/A
- **Related files**: `justfile`
- **Validation**: `just demo` (interactive picker) or `just demo-md`, `just demo-term`, etc.

### Build with specific features
- **Where to edit**: `Cargo.toml` (root level)
- **Related files**: Feature definitions
- **Validation**: `cargo build --features "button,pane,dialog"`

### Build all features
- **Where to edit**: N/A
- **Related files**: All source files
- **Validation**: `cargo build --all-features`

### Run full verification
- **Where to edit**: N/A
- **Related files**: All source files
- **Validation**: `just check` (runs fmt-check, lint, test)

## Getting Started

```toml
# Cargo.toml - enable specific features
[dependencies]
ratkit = { version = "0.2", features = ["button", "dialog", "pane"] }
```

```rust
use ratkit::prelude::*;
use ratatui::Frame;

struct MyApp;

impl CoordinatorApp for MyApp {
    fn on_event(&mut self, event: CoordinatorEvent) -> LayoutResult<CoordinatorAction> {
        match event {
            CoordinatorEvent::Keyboard(keyboard) => {
                if keyboard.is_escape() {
                    return Ok(CoordinatorAction::Quit);
                }
            }
            _ => {}
        }
        Ok(CoordinatorAction::Continue)
    }

    fn on_draw(&mut self, frame: &mut Frame) {
        // Render your UI here
    }
}

fn main() -> std::io::Result<()> {
    let app = MyApp;
    run(app, RunnerConfig::default())
}
```

## Workspace Overview

The ratkit workspace contains a **single crate** with 21 feature-gated modules organized into:

- **Primitives** (11 modules): Core UI building blocks in `src/primitives/`
  - button, pane, dialog, toast, statusline, scroll, menu_bar, resizable_grid, tree_view, widget_event, termtui
- **Widgets** (6 modules): Higher-level composite widgets in `src/widgets/`
  - markdown_preview, code_diff, ai_chat, hotkey_footer, file_system_tree, theme_picker
- **Services** (4 modules): Background monitoring services in `src/services/`
  - file_watcher, git_watcher, repo_watcher, hotkey_service
- **Core Runtime** (1 module): Application lifecycle in `src/core/`

All modules follow feature-gated compilation. Enable only what you need.

## Core Runtime

The core runtime provides the application lifecycle, event routing, and element management for terminal UI applications.

### Key Components

- **CoordinatorApp trait**: Applications implement this to receive events and render
- **run() / run_with_diagnostics()**: Entry points to start the event loop
- **Element trait**: Implement for custom widgets that integrate with the coordinator
- **RunnerConfig**: Configuration for tick rate, layout debounce, mouse capture

### Architecture

- Three-region layout: Top, Center, Bottom
- Focus management with stack and traversal
- Mouse routing with z-order hit testing
- Element registry with weak references

## UI Primitives

Core UI building blocks for TUI applications, located in `src/primitives/`.

### Feature Flags

Each primitive has an individual feature flag:
- `button`, `pane`, `dialog`, `toast`, `statusline`, `scroll`
- `menu-bar` (enables `widget-event`)
- `resizable-grid`
- `tree-view` (enables `widget-event`)
- `widget-event`
- `termtui`

### Common Patterns

- Builder pattern with `new()` and `with_*` methods
- StatefulWidget pattern for complex state
- Event emission via `WidgetEvent`
- Mouse/keyboard interaction support

## Complex Widgets

Higher-level composite widgets in `src/widgets/`.

### Feature Flags

- `markdown-preview` - Most complex (syntax highlighting, TOC, themes, selection)
- `code-diff` - VS Code-style diff viewer
- `ai-chat` - AI chat interface (requires reqwest, serde)
- `hotkey-footer` - Keyboard shortcut footer
- `file-system-tree` - File browser with devicons
- `theme-picker` - Theme selector with 25+ themes

### External Dependencies

| Widget | Dependencies |
|--------|-------------|
| ai-chat | reqwest, serde, serde_json |
| markdown-preview | pulldown-cmark, syntect, syntect-tui, notify, arboard, dirs |
| code-diff | similar |
| file-system-tree | devicons |

## Services

Background monitoring services in `src/services/`.

### Feature Flags

- `file-watcher` - Watch files/directories for changes
- `git-watcher` - Monitor git repository state
- `repo-watcher` - Combined file + git watching (enables file-watcher and git-watcher)
- `hotkey-service` - Global hotkey registration and management

### Common Dependencies

All watcher services use the `notify` crate for filesystem events.

## Usage Cards

### CoordinatorApp
- **Use when**: Building any ratkit TUI application
- **Enable/Install**: Core runtime, no feature flag needed
- **Import/Invoke**: `use ratkit::prelude::*;`
- **Minimal flow**:
  1. Define struct implementing `CoordinatorApp`
  2. Implement `on_event()` to handle events
  3. Implement `on_draw()` to render UI
  4. Call `run(app, RunnerConfig::default())`
- **Key APIs**: `on_event()`, `on_draw()`, `on_layout_changed()`
- **Pitfalls**: Runner takes ownership; wrap shared state in `Arc<RwLock<>>`
- **Source**: `src/coordinator.rs`, `src/runner_helper.rs`

### run()
- **Use when**: Starting the main application event loop
- **Enable/Install**: Core runtime, no feature flag
- **Import/Invoke**: `use ratkit::{run, run_with_diagnostics};`
- **Minimal flow**:
  1. Create app implementing `CoordinatorApp`
  2. Create `RunnerConfig::default()` or custom
  3. Call `run(app, config)` or `run_with_diagnostics(app, config)` for debug overlay
- **Key APIs**: `run()`, `run_with_diagnostics()`, `RunnerConfig`
- **Pitfalls**: Blocks until exit; handles terminal init/cleanup
- **Source**: `src/runner_helper.rs`

### Element
- **Use when**: Creating custom widgets that integrate with coordinator
- **Enable/Install**: Core runtime
- **Import/Invoke**: `use ratkit::Element;`
- **Minimal flow**:
  1. Implement `Element` trait for your widget
  2. Define `id()`, `on_render()`, `on_keyboard()`, `on_mouse()`
  3. Register with `ElementMetadata` and region
- **Key APIs**: `id()`, `on_render()`, `on_keyboard()`, `on_mouse()`, `on_focus_gain()`, `on_focus_loss()`, `on_tick()`
- **Pitfalls**: Registry stores weak refs - keep strong refs in app state; return `true` when handling events
- **Source**: `src/registry.rs`

### Button
- **Use when**: Clickable button with hover states
- **Enable/Install**: `features = ["button"]`
- **Import/Invoke**: `use ratkit::Button;`
- **Minimal flow**:
  1. Create `Button::new("Label")`
  2. Call `update_hover(x, y)` on mouse move
  3. Call `is_clicked(x, y)` on click
  4. Render with `render_with_title()`
- **Key APIs**: `new()`, `normal_style()`, `hover_style()`, `update_hover()`, `is_clicked()`
- **Pitfalls**: State must persist in app struct
- **Source**: `src/primitives/button/widget.rs`

### Pane
- **Use when**: Styled panel container with title/icon/padding
- **Enable/Install**: `features = ["pane"]`
- **Import/Invoke**: `use ratkit::Pane;`
- **Minimal flow**:
  1. Create `Pane::new("Title")`
  2. Chain builder methods: `with_icon()`, `with_padding()`, `border_style()`
  3. Render as widget
- **Key APIs**: `new()`, `with_icon()`, `with_padding()`, `with_uniform_padding()`, `border_style()`
- **Pitfalls**: Padding reduces inner content area
- **Source**: `src/primitives/pane/mod.rs`

### Dialog
- **Use when**: Modal dialogs for confirmation/information
- **Enable/Install**: `features = ["dialog"]`
- **Import/Invoke**: `use ratkit::{Dialog, DialogState, DialogType};`
- **Minimal flow**:
  1. Create `Dialog::new(title, message)` or use builder (`Dialog::confirm()`)
  2. Set buttons with `.buttons(vec!["Yes", "No"])`
  3. Create `DialogState::new()` for button selection
  4. Render with `DialogWidget`
- **Key APIs**: `new()`, `info()`, `warning()`, `error()`, `success()`, `confirm()`, `buttons()`
- **Pitfalls**: DialogState must persist; uses StatefulWidget pattern
- **Source**: `src/primitives/dialog/`

### Toast
- **Use when**: Auto-dismissing notifications
- **Enable/Install**: `features = ["toast"]`
- **Import/Invoke**: `use ratkit::{ToastManager, ToastLevel};`
- **Minimal flow**:
  1. Create `ToastManager::new()` in app state
  2. Add toasts via `.success()`, `.error()`, `.info()`, `.warning()`
  3. Call `cleanup()` before render
  4. Render with `render_toasts()`
- **Key APIs**: `ToastManager::new()`, `.add()`, `.success()`, `.error()`, `.cleanup()`
- **Pitfalls**: Must call `cleanup()` to remove expired; doesn't auto-expire
- **Source**: `src/primitives/toast/`

### TreeView
- **Use when**: Hierarchical data with expand/collapse/selection
- **Enable/Install**: `features = ["tree-view"]` (auto-enables widget-event)
- **Import/Invoke**: `use ratkit::{TreeNode, TreeView, TreeViewState, TreeNavigator};`
- **Minimal flow**:
  1. Build `TreeNode` hierarchy
  2. Create `TreeView::new(nodes)` with render_fn
  3. Create `TreeViewState::new()` for selection/expansion
  4. Use `TreeNavigator` for keyboard handling
- **Key APIs**: `TreeNode::new()`, `TreeView::new()`, `TreeViewState::new()`, `TreeNavigator::new()`
- **Pitfalls**: TreeViewState must persist; TreeNavigator handles all keyboard nav
- **Source**: `src/primitives/tree_view/`

### MarkdownWidget
- **Use when**: Rendering markdown with syntax highlighting, TOC, themes
- **Enable/Install**: `features = ["markdown-preview"]` (complex dependencies)
- **Import/Invoke**: `use ratkit::widgets::markdown_preview::{MarkdownWidget, ScrollState, SourceState, ...};`
- **Minimal flow**:
  1. Create state structs (ScrollState, SourceState, etc.) in app state
  2. Create `MarkdownWidget::new(content, scroll, source, ...)`
  3. Handle keyboard with `handle_key()`
  4. Render with ratatui
- **Key APIs**: `new()`, `handle_key()`, `handle_mouse()`, `.show_toc()`, `.show_scrollbar()`
- **Pitfalls**: Requires mouse capture enabled; state must persist across renders
- **Source**: `src/widgets/markdown_preview/widgets/markdown_widget/`

### FileWatcher
- **Use when**: Detecting file/directory changes
- **Enable/Install**: `features = ["file-watcher"]` (uses notify crate)
- **Import/Invoke**: `use ratkit::services::file_watcher::FileWatcher;`
- **Minimal flow**:
  1. Create `FileWatcher::for_file()` or `FileWatcher::for_directory()`
  2. Call `watch(path)`
  3. Poll `check_for_changes()` in event loop
  4. Get changes with `get_changed_paths()`
- **Key APIs**: `for_file()`, `for_directory()`, `watch()`, `check_for_changes()`, `get_changed_paths()`
- **Pitfalls**: Must poll regularly; `get_changed_paths()` clears queue; debounced (100ms/200ms)
- **Source**: `src/services/file_watcher/`

### HotkeyService
- **Use when**: Centralized hotkey management with scope filtering
- **Enable/Install**: `features = ["hotkey-service"]`
- **Import/Invoke**: `use ratkit::services::hotkey_service::{Hotkey, HotkeyRegistry, HotkeyScope};`
- **Minimal flow**:
  1. Create `HotkeyRegistry::new()`
  2. Register hotkeys with `Hotkey::new(key, description).scope(scope)`
  3. Set active scope with `set_active_scope()`
  4. Query with `lookup(key, scope)` in event loop
- **Key APIs**: `HotkeyRegistry::new()`, `register()`, `lookup()`, `set_active_scope()`
- **Pitfalls**: Uses `&'static str` for scopes; must handle crossterm events separately
- **Source**: `src/services/hotkey_service/`

## API Reference

### Core Runtime

| Component | Key APIs |
|-----------|----------|
| CoordinatorApp | `on_event()`, `on_draw()`, `on_layout_changed()` |
| run | `run()`, `run_with_diagnostics()` |
| Element | `id()`, `on_render()`, `on_keyboard()`, `on_mouse()`, `on_focus_gain()`, `on_focus_loss()`, `on_tick()` |
| RunnerConfig | `tick_rate`, `layout_debounce`, `mouse_router_config` |

### Primitives

| Primitive | Key APIs |
|-----------|----------|
| Button | `new()`, `normal_style()`, `hover_style()`, `update_hover()`, `is_clicked()` |
| Pane | `new()`, `with_icon()`, `with_padding()`, `with_uniform_padding()`, `border_style()` |
| Dialog | `new()`, `info()`, `warning()`, `error()`, `success()`, `confirm()`, `buttons()` |
| Toast | `ToastManager::new()`, `.add()`, `.success()`, `.error()`, `.cleanup()` |
| TreeView | `TreeNode::new()`, `TreeView::new()`, `TreeViewState::new()`, `TreeNavigator::new()` |
| Scroll | `calculate_scroll_offset()` |

### Services

| Service | Key APIs |
|---------|----------|
| FileWatcher | `for_file()`, `for_directory()`, `watch()`, `check_for_changes()`, `get_changed_paths()` |
| GitWatcher | `new()`, `with_config()`, `watch()`, `check_for_changes()` |
| RepoWatcher | `new()`, `with_config()`, `watch()`, `check_for_changes()`, `get_change_set()` |
| HotkeyRegistry | `new()`, `register()`, `lookup()`, `set_active_scope()` |

## Common Pitfalls

### Feature Flags

1. **No default features**: Must explicitly enable every feature you use
2. **Cross-feature deps**: `tree-view` enables `widget-event`; `repo-watcher` enables `file-watcher` and `git-watcher`
3. **Missing feature errors**: "unresolved import" usually means missing feature flag

### State Management

1. **StatefulWidget pattern**: Complex widgets require persistent state in app struct
2. **Never create state in render**: Always store widget state in app struct
3. **Weak references**: Element registry stores weak refs - keep strong refs in app

### Event Handling

1. **Return values**: Return `true` when consuming events, `false` to propagate
2. **Mouse capture**: Must enable crossterm mouse capture for interactions
3. **Poll services**: Must call `check_for_changes()` regularly on watchers

### Examples

1. **Feature flags required**: Examples need their specific features: `--features markdown-preview`
2. **Just commands**: Use `just demo` for interactive picker or `just demo-*` for specific demos

## Optional

### Additional Resources

- **Examples**: 23 examples in `examples/`
- **Just commands**: Run `just help` for all available commands
- **Build**: `just build` or `cargo build -p ratkit --all-features`
- **Test**: `just test`

### Version

- Current: 0.2.5
- Rust: 1.70+