用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/mudrii/hom --skill hom-tui-testing命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Use this skill when building, reviewing, or refactoring Rust code that requires strong maintainability discipline: SRP, DRY, OCP, explicit dependency injection, TDD/ATDD workflow, architecture review, and clean project structure. Complements the project's Rust CLAUDE.md with process rigor.
Use when adding a new harness adapter or modifying an existing one in hom-adapters
Use when working on terminal emulation, the TerminalBackend trait, libghostty integration, or PTY management
基于 SOC 职业分类
正在显示 SKILL.md
| name | hom-tui-testing |
| description | Use when modifying TUI rendering, input routing, layout, command bar, or pane management |
Invoke this skill when:
crates/hom-tui/src/pane_render.rscrates/hom-tui/src/layout.rscrates/hom-tui/src/command_bar.rscrates/hom-tui/src/input.rscrates/hom-tui/src/render.rsApp state machine in crates/hom-tui/src/app.rsThe TUI has a clear data flow:
User Input → InputRouter → Action → App state mutation → render() → Frame
↓
pane.terminal.screen_snapshot() → pane_render → ratatui Buffer
Breaking any link in this chain means the user sees nothing, gets stuck, or loses input.
Command bar parsing — Pure function, no side effects:
#[test]
fn test_parse_spawn_command() {
let cmd = parse_command("spawn claude opus");
assert!(matches!(cmd, Ok(Command::Spawn { harness: Some(HarnessType::ClaudeCode), .. })));
}
#[test]
fn test_parse_unknown_harness() {
let cmd = parse_command("spawn nonexistent");
assert!(cmd.is_err());
}
Layout computation — Pure geometry:
#[test]
fn test_grid_layout_4_panes() {
let area = Rect::new(0, 0, 100, 50);
let panes = vec![1, 2, 3, 4];
let areas = compute_pane_areas(area, &panes, &LayoutKind::Grid);
assert_eq!(areas.len(), 4);
// 2x2 grid: each pane gets ~50x25
for (_, rect) in &areas {
assert!(rect.width >= 49);
assert!(rect.height >= 24);
}
}
Input encoding — Verify key events produce correct PTY bytes:
#[test]
fn test_ctrl_c_encoding() {
let key = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
let bytes = encode_key_event(&key);
assert_eq!(bytes, vec![0x03]);
}
Pane hit testing — Click coordinates → pane ID:
#[test]
fn test_pane_at_position() {
let pane_areas = vec![
(1, Rect::new(0, 0, 50, 25)),
(2, Rect::new(50, 0, 50, 25)),
];
assert_eq!(pane_at_position(&pane_areas, 25, 12), Some(1));
assert_eq!(pane_at_position(&pane_areas, 75, 12), Some(2));
}
Full render cycle — Create App, add panes, render to TestBackend, assert buffer contents:
use ratatui::backend::TestBackend;
use ratatui::Terminal;
#[test]
fn test_empty_state_renders_welcome() {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend).unwrap();
let app = App::new(HomConfig::default());
terminal.draw(|f| render(f, &app)).unwrap();
let buf = terminal.backend().buffer().clone();
// Check that "Welcome to HOM" appears in the buffer
let text: String = buf.content().iter().map(|c| c.symbol().chars().next().unwrap_or(' ')).collect();
assert!(text.contains("Welcome to HOM"));
}
For these, use cargo run and test manually. Document what you tested.
| Pitfall | Why it breaks | Prevention |
|---|---|---|
| Off-by-one in pane border rendering | Pane content overflows into border or adjacent pane | Always use block.inner(area) for content area |
| Forgetting to handle empty pane list | Index panic in layout computation | Guard with if pane_ids.is_empty() { return } |
| Cursor position outside visible area | Terminal shows cursor in wrong pane | Clamp cursor to inner.right() / inner.bottom() |
| Command bar eating Escape key | User can't exit command mode | Test mode transitions explicitly |
| InputRouter not updating mode | Clicks do nothing, keys go to wrong pane | Every Action that changes focus must also update self.mode |
cargo test -p hom-tui passescargo run, spawn a pane, type into it, switch panescargo clippy clean