--- # claudbg-ut9q title: 'TUI: terminal setup, event loop, and teardown' status: completed type: task priority: normal created_at: 2026-03-30T04:45:32Z updated_at: 2026-03-30T16:47:13Z parent: claudbg-i6l2 blocked_by: - claudbg-nq36 --- Implement terminal initialization, the main event loop, and graceful teardown. ## Responsibilities 1. **Setup:** enable raw mode (`crossterm::terminal::enable_raw_mode`), switch to alternate screen (`EnterAlternateScreen`), create `CrosstermBackend`, instantiate `ratatui::Terminal`. 2. **Panic hook:** install a panic hook that restores the terminal before printing the panic message (prevents garbled terminal on crash). 3. **Event loop:** `loop { terminal.draw(|f| render(f, &state)); if event::poll(Duration::from_millis(50))? { handle_event(event::read()?, &mut state); } if state.should_quit { break; } }` 4. **Teardown:** disable raw mode, leave alternate screen. ## Entry point Replaces the stub in `src/commands/stubs.rs` (the `Tui` command handler). The `tui()` async function should call into a synchronous `run_tui()` function since ratatui is sync. ## Notes - Use `crossterm::event::Event` for keyboard input — no mouse needed for this milestone. - The 50 ms poll timeout gives ~20 fps without busy-spinning. - Teardown must run even on error: use a RAII guard or wrap in a closure. ## Blocked by - ratatui deps (claudbg-78xt), app state (claudbg-nq36) ## Summary of Changes\n\nCreated src/tui/run.rs with TerminalGuard (RAII cleanup), install_panic_hook, run_tui() event loop at 50ms poll. Placeholder render draws a bordered box. Handles q/Esc to quit. Added should_quit field to AppState. Wired into stubs.rs replacing the coming-soon stub. cargo check and clippy pass clean.