skills/ratkit/SKILL.md
Comprehensive guide for the ratkit Rust TUI component library built on ratatui 0.29, including feature flags, APIs, and implementation patterns. Use when building, debugging, or extending ratkit applications and examples.
npx skillsauth add alpha-innovation-labs/ratkit ratkitInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
src/ with 21 feature flags (e.g., button, pane, markdown-preview)features = ["button", "dialog"])tree-view enables widget-event, repo-watcher enables file-watcher and git-watcher)just for all operations: Build (just build), test (just test), check (just check), demos (just demo)--features flag: Examples require their specific features (e.g., --features markdown-preview)use ratkit::primitives::button::Button, use ratkit::widgets::markdown_preview::MarkdownWidget) because crate-root re-exports are not guaranteed for every typecheck_for_changes() calls in the event loopjust check (format + lint + test) before committingexamples/ (moved from crates/ratkit/examples/)examples/cargo run --example button_button_demo --features buttonon_event) and draw path (on_draw)examples/markdown_preview_markdown_preview_demo.rssrc/widgets/document_viewer/widget/document_viewer_widget.rs, src/widgets/code_widget/widget/code_widget.rsjustfilejust demo (interactive picker) or just demo-md, just demo-md-small, just demo-term, etc.Cargo.toml (root level)cargo build --features "button,pane,dialog"cargo build --all-featuresjust check (runs fmt-check, lint, test)# Cargo.toml - enable specific features
[dependencies]
ratkit = { version = "0.2.16", features = ["button", "dialog", "pane"] }
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())
}
The ratkit workspace contains a single crate with 21 feature-gated modules organized into:
src/primitives/
src/widgets/
src/services/
src/core/All modules follow feature-gated compilation. Enable only what you need.
The core runtime provides the application lifecycle, event routing, and element management for terminal UI applications.
Core UI building blocks for TUI applications, located in src/primitives/.
Each primitive has an individual feature flag:
button, pane, dialog, toast, statusline, scrollmenu-bar (enables widget-event)resizable-gridtree-view (enables widget-event)widget-eventtermtuinew() and with_* methodsWidgetEventMenuBar::render_with_offset(frame, area, left_offset) now uses the full available container width for the border: area.width - left_offsetself.areaexamples/menu-bar_menu_bar_demo.rs at fixed 120-column terminal widthHigher-level composite widgets in src/widgets/.
markdown-preview - Most complex (syntax highlighting, TOC, themes, selection)code-diff - VS Code-style diff viewerai-chat - AI chat interface (requires reqwest, serde)hotkey-footer - Keyboard shortcut footerfile-system-tree - File browser with deviconscode-widget - Code viewer with syntax highlighting, symbol outline, and document viewer foundationtheme-picker - Theme selector with 25+ themes| Widget | Dependencies | |--------|-------------| | ai-chat | reqwest, serde, serde_json | | markdown-preview | pulldown-cmark, syntect, syntect-tui, notify, arboard, dirs | | code-diff | similar | | code-widget | syntect, syntect-tui | | file-system-tree | devicons |
When adjusting file-system-tree visuals, keep these conventions to match Yazi-like behavior:
devicons::icon_for_file(...).color (hex) for file icon colors instead of hardcoded extension maps.ratatui::style::Color::Rgb before rendering.Background monitoring services in src/services/.
file-watcher - Watch files/directories for changesgit-watcher - Monitor git repository staterepo-watcher - Combined file + git watching (enables file-watcher and git-watcher)hotkey-service - Global hotkey registration and managementAll watcher services use the notify crate for filesystem events.
use ratkit::prelude::*;CoordinatorAppon_event() to handle eventson_draw() to render UIrun(app, RunnerConfig::default())on_event(), on_draw(), on_layout_changed()Arc<RwLock<>>src/coordinator.rs, src/runner_helper.rsuse ratkit::{run, run_with_diagnostics};CoordinatorAppRunnerConfig::default() or customrun(app, config) or run_with_diagnostics(app, config) for debug overlayrun(), run_with_diagnostics(), RunnerConfigsrc/runner_helper.rsuse ratkit::Element;Element trait for your widgetid(), on_render(), on_keyboard(), on_mouse()ElementMetadata and regionid(), on_render(), on_keyboard(), on_mouse(), on_focus_gain(), on_focus_loss(), on_tick()true when handling eventssrc/registry.rsfeatures = ["button"]use ratkit::Button;Button::new("Label")update_hover(x, y) on mouse moveis_clicked(x, y) on clickrender_with_title()new(), normal_style(), hover_style(), update_hover(), is_clicked()src/primitives/button/widget.rsfeatures = ["pane"]use ratkit::Pane;Pane::new("Title")with_icon(), with_padding(), border_style()new(), with_icon(), with_padding(), with_uniform_padding(), border_style()src/primitives/pane/mod.rsfeatures = ["dialog"]use ratkit::primitives::dialog::{Dialog, DialogWidget, DialogAction, DialogActionsLayout, DialogWrap, DialogShadow, DialogModalMode};Dialog::new(title, message) or Dialog::confirm(...).actions_layout(...), .message_alignment(...), .content_padding(...), .wrap_mode(...), .shadow(...), .overlay(...).buttons(...), .default_selection(...), .next_keys(...), .previous_keys(...), .confirm_keys(...), .cancel_keys(...)dialog.handle_key_event(...) and react to DialogActionDialogWidget::new(&mut dialog)actions_layout(), actions_alignment(), message_alignment(), content_padding(), wrap_mode(), hide_footer(), footer(), footer_style(), shadow(), overlay(), modal_mode(), body_renderer(), handle_key_event(), handle_mouse_confirm(), blocks_background_events()Tab to control inner body UI (for example a list) instead of dialog actions, remove Tab from dialog keymap and handle it in your app event loop; if you want no action row, set .buttons(vec![])src/primitives/dialog/.actions_layout(DialogActionsLayout::Vertical) for stacked action menus.actions_layout(DialogActionsLayout::Horizontal) for classic Yes/No rows.buttons(vec![]) hides the actions row so dialog body content can be primaryDialogBodyRenderer and pass .body_renderer(Box::new(...)) to render a selectable list/menu inside dialog chrome.modal_mode(DialogModalMode::Blocking) plus blocks_background_events() to prevent background input handling.next_keys(...) / .previous_keys(...) to exclude Tab and route Tab to body-level focus/selection logicfeatures = ["toast"]use ratkit::{ToastManager, ToastLevel};ToastManager::new() in app state.success(), .error(), .info(), .warning()cleanup() before renderrender_toasts()ToastManager::new(), .add(), .success(), .error(), .cleanup()cleanup() to remove expired; doesn't auto-expiresrc/primitives/toast/features = ["menu-bar"] (auto-enables widget-event)use ratkit::primitives::menu_bar::{MenuBar, MenuItem};MenuBar::new(vec![MenuItem::new("File", 0), ...]).with_selected(index)update_hover(x, y); on click: call handle_click(x, y) or handle_mouse(x, y)render() or render_with_offset()new(), with_selected(), update_hover(), handle_click(), handle_mouse(), selected(), render_with_offset()src/primitives/menu_bar/menu_bar.rs, examples/menu-bar_menu_bar_demo.rsfeatures = ["tree-view"] (auto-enables widget-event)use ratkit::{TreeNode, TreeView, TreeViewState, TreeNavigator};TreeNode hierarchyTreeView::new(nodes) with render_fnTreeViewState::new() for selection/expansionTreeNavigator for keyboard handlingTreeNode::new(), TreeView::new(), TreeViewState::new(), TreeNavigator::new()src/primitives/tree_view/features = ["markdown-preview"] (complex dependencies)use ratkit::widgets::markdown_preview::{MarkdownWidget, ScrollState, SourceState, ...};MarkdownWidget::new(content, scroll, source, ...)handle_key()new(), handle_key(), handle_mouse(), .show_toc(), .toggle_toc(), .with_frontmatter_collapsed(), set_frontmatter_collapsed(), .show_scrollbar()0); large markdown with many fenced code blocks can increase first-render time if syntax highlighter initialization is repeated (parser now reuses one SyntaxHighlighter per parse call)src/widgets/markdown_preview/widgets/markdown_widget/just demo-md (opencode SDK skill markdown) and just demo-md-small (ratkit skill markdown)target/debug/examples/markdown_preview_markdown_preview_demo --startup-probe (with RATKIT_MD_DEMO_FILE=...) to print MARKDOWN_DEMO_READY_MS=<ms> for repeatable load-time comparisonsexamples/markdown_preview_markdown_preview_demo.rs, justfiles/utilities/demo-md.justfeatures = ["file-system-tree"]use ratkit::widgets::file_system_tree::{FileSystemTree, FileSystemTreeState, FileSystemTreeConfig};FileSystemTree::new(root_path) or with_config(...)FileSystemTreeState in app statehandle_navigation_key(...)handle_filter_key(...) when filter mode is activenew(), with_config(), handle_navigation_key(), enter_filter_mode(), expand_selected(), collapse_selected()src/widgets/file_system_tree/widget.rs, src/widgets/file_system_tree/config.rs, src/widgets/file_system_tree/state.rsfeatures = ["code-widget"] (enables syntect, syntect-tui, pane, statusline)use ratkit::widgets::code_widget::{CodeState, CodeWidget};CodeState::default().source.set_source_file(path)? or .source.set_source(content)CodeWidget::from_state(&state).show_line_numbers(true).show_outline(true).language("rust")frame.render_stateful_widget(widget, area, &mut state).show_line_numbers(), .show_outline(), .language(), .source field for contentsrc/widgets/document_viewer/ with state, widget, extensions, and foundation modules.language("rust") overrides detectionsrc/widgets/code_widget/widget/code_widget.rs, src/widgets/document_viewer/widget/document_viewer_widget.rsfeatures = ["file-watcher"] (uses notify crate)use ratkit::services::file_watcher::FileWatcher;FileWatcher::for_file() or FileWatcher::for_directory()watch(path)check_for_changes() in event loopget_changed_paths()for_file(), for_directory(), watch(), check_for_changes(), get_changed_paths()get_changed_paths() clears queue; debounced (100ms/200ms)src/services/file_watcher/features = ["hotkey-service"]use ratkit::services::hotkey_service::{Hotkey, HotkeyRegistry, HotkeyScope};HotkeyRegistry::new()Hotkey::new(key, description).scope(scope)set_active_scope()lookup(key, scope) in event loopHotkeyRegistry::new(), register(), lookup(), set_active_scope()&'static str for scopes; must handle crossterm events separatelysrc/services/hotkey_service/| 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 |
| 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() |
| 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() |
tree-view enables widget-event; repo-watcher enables file-watcher and git-watchertrue when consuming events, false to propagatecheck_for_changes() regularly on watchers--features markdown-previewjust demo for interactive picker or just demo-* for specific demosUse this section to transfer the demo's responsiveness patterns into other ratkit apps.
Coalesce high-rate mouse move events
MouseEventKind::Moved, skip handling if last processed move was too recent.last_move_processed.elapsed() < Duration::from_millis(24)).Gate redraws to meaningful state changes
CoordinatorAction::Continue by default for move events; return Redraw only when UI state actually changes.MarkdownEvent::TocHoverChanged { .. }.Use differential handling for move vs non-move mouse events
Bound periodic work with moderate tick rate
RunnerConfig { tick_rate: Duration::from_millis(250), .. }.Persist heavy widget state outside draw loop
ScrollState, SourceState, CacheState, CollapseState, ExpandableState, GitStatsState, VimState, SelectionState, DoubleClickState.Keep on_draw render-only
on_draw; do those on state transitions.Continue for non-keydown; map only actionable keys to state changes, then redraw.last_move_processed: Instant to app state and time-gate move handling.Continue unless visible state changed.examples/just help for all available commandsjust build or cargo build -p ratkit --all-featuresjust testdevelopment
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.
development
End-to-end Parallels smoke, upgrade, and rerun workflow for OpenClaw across macOS, Windows, and Linux guests. Use when Codex needs to run, rerun, debug, or interpret VM-based install, onboarding, gateway smoke tests, latest-release-to-main upgrade checks, fresh snapshot retests, or optional Discord roundtrip verification under Parallels.