11 KiB
TUI Orchestrator - Progress Summary
Current Status
Completed Features ✅
Phase 1: Core Foundation
-
Input handling (
src/input/)- Key types (KeyCode, KeyModifiers, Key)
- Bindings (key → action mappings)
- SequenceHandler (multi-key sequences, feature-gated)
- MatchResult (action/pending/no-match)
-
Focus management (
src/focus/)- FocusId trait (generic focus identifiers)
- FocusManager (focus tracking, navigation, overlays)
- FocusQuery (read-only focus state for rendering)
- Focusable trait (components declare focusable elements)
- FocusError (error types)
Documentation ✅
- PLAN.md - Complete implementation plan for framework approach
- REDESIGN.md - Deep dive into framework architecture
- INTEGRATION_GUIDE.md - User guide for building TUI apps
- README.md - Project overview and quick start
Tests ✅
- Input tests: 12 tests passing
- Focus tests: 18 tests passing
- Total: 30 tests covering all core functionality
Next Steps
Phase 2: Component System
The foundation for the entire framework. This is the highest priority.
Files to create:
src/component/mod.rs- Module routingsrc/component/trait.rs- Component trait definitionsrc/component/action.rs- Standard component actionssrc/component/error.rs- Component-specific errors
Component trait design:
pub trait Component {
type Focus: FocusId + Clone;
type Action: Action + Clone;
type Event: Clone + core::fmt::Debug;
// REQUIRED
fn targets(&self) -> &[Self::Focus];
fn handle(&mut self, focus: &Self::Focus, action: Self::Action) -> Result<Option<Self::Event>>;
// OPTIONAL (all with defaults)
fn on_enter(&mut self) -> Result<()>;
fn on_exit(&mut self) -> Result<()>;
fn on_focus(&mut self, focus: &Self::Focus) -> Result<()>;
fn on_blur(&mut self, focus: &Self::Focus) -> Result<()>;
fn handle_text(&mut self, focus: &Self::Focus, ch: char) -> Result<Option<Self::Event>>;
fn can_navigate_forward(&self, focus: &Self::Focus) -> bool;
fn can_navigate_backward(&self, focus: &Self::Focus) -> bool;
}
ComponentAction enum:
pub enum ComponentAction {
Next, // Tab → move focus forward
Prev, // Shift+Tab → move focus backward
First, // Home → move to first
Last, // End → move to last
Select, // Enter → activate current focus
Cancel, // Esc → cancel/close
TypeChar(char), // Character → type text
Backspace, // Backspace → delete before cursor
Delete, // Delete → delete at cursor
Custom(usize), // User-defined action
}
Priority: HIGHEST - This enables all other functionality
Phase 3: Router & Lifecycle
Page navigation with automatic lifecycle hook invocation.
Files to create:
src/router/mod.rs- Module routingsrc/router/router.rs- Router implementationsrc/router/history.rs- Navigation history
Router API:
pub struct Router<C: Component> {
pages: alloc::collections::HashMap<String, C>,
current: Option<String>,
history: alloc::vec::Vec<String>,
future: alloc::vec::Vec<String>, // For forward navigation
}
impl<C: Component> Router<C> {
pub fn new() -> Self;
pub fn navigate(&mut self, id: &str) -> Result<()>;
pub fn back(&mut self) -> Result<Option<()>>;
pub fn forward(&mut self) -> Result<Option<()>>;
pub fn current(&self) -> Option<&C>;
}
Automatic behavior:
navigate()callsold_page.on_exit()→ swaps → callsnew_page.on_enter()back()/forward()manage history stack
Priority: HIGH - Essential for multi-page apps
Phase 4: Orchestrator Core
The complete runtime that wires everything together.
Files to create:
src/orchestrator/mod.rs- Module routingsrc/orchestrator/core.rs- Main Orchestrator structsrc/orchestrator/bindings.rs- Default + custom bindingssrc/orchestrator/modes.rs- Mode stack and resolutionsrc/orchestrator/overlays.rs- Overlay stacksrc/orchestrator/events.rs- Event bus
Orchestrator API:
pub struct Orchestrator<C: Component> {
registry: ComponentRegistry<C>,
focus: FocusManager<C::Focus>,
bindings: Bindings<ComponentAction>,
router: Router<C>,
modes: ModeStack<ModeName>,
overlays: OverlayStack<C::Event>,
events: EventBus<C::Event>,
}
impl<C: Component> Orchestrator<C> {
pub fn new() -> Self;
pub fn register_page(&mut self, id: &str, page: C) -> Result<()>;
pub fn navigate_to(&mut self, id: &str) -> Result<()>;
pub fn process_frame(&mut self, key: Key) -> Result<alloc::vec::Vec<C::Event>>;
pub fn run<I: InputSource>(&mut self, input: I) -> Result<()>;
// Extension points
pub fn set_mode_resolver<R: ModeResolver + 'static>(&mut self, resolver: R);
pub fn set_overlay_manager<O: OverlayManager + 'static>(&mut self, manager: O);
pub fn set_event_handler<H: EventHandler<C::Event> + 'static>(&mut self, handler: H);
}
Process flow:
- Check overlay active → route to overlay
- Get current mode + focus
- Lookup binding → get action
- Get current component
- Call
component.handle(action, focus) - Collect events
- Handle internal events (focus changes, page nav)
- Return external events
Priority: HIGH - This makes the framework "ready to use"
Phase 5: Extension Traits
Extension points for komp_ac and other complex apps.
Files to create:
src/extension/mod.rs- Module routingsrc/extension/mode.rs- ModeResolver traitsrc/extension/overlay.rs- OverlayManager traitsrc/extension/event.rs- EventHandler trait
ModeResolver trait:
pub trait ModeResolver {
fn resolve(&self, focus: &dyn core::any::Any) -> alloc::vec::Vec<ModeName>;
}
pub struct DefaultModeResolver;
impl ModeResolver for DefaultModeResolver { ... }
OverlayManager trait:
pub trait OverlayManager {
fn is_active(&self) -> bool;
fn handle_input(&mut self, key: Key) -> Option<OverlayResult>;
}
pub enum OverlayResult {
Dismissed,
Selected(OverlayData),
Continue,
}
EventHandler trait:
pub trait EventHandler<E> {
fn handle(&mut self, event: E) -> Result<HandleResult>;
}
pub enum HandleResult {
Consumed,
Forward,
Navigate(String),
}
Priority: MEDIUM - Defaults work for most apps, komp_ac needs these
Phase 6: Builder & Defaults
Easy setup pattern with sensible defaults.
Files to create:
src/builder/mod.rs- Module routingsrc/builder/builder.rs- Builder patternsrc/builder/defaults.rs- Preset keybindings
Builder API:
pub struct Builder<C: Component> {
orchestrator: Orchestrator<C>,
}
impl<C: Component> Builder<C> {
pub fn new() -> Self;
pub fn with_page(mut self, id: &str, page: C) -> Result<Self>;
pub fn with_default_bindings(mut self) -> Self;
pub fn with_mode_resolver<R: ModeResolver + 'static>(mut self, resolver: R) -> Self;
pub fn with_overlay_manager<O: OverlayManager + 'static>(mut self, manager: O) -> Self;
pub fn build(self) -> Result<Orchestrator<C>>;
}
Default bindings:
pub fn default_bindings() -> Bindings<ComponentAction> {
let mut bindings = Bindings::new();
bindings.bind(Key::tab(), ComponentAction::Next);
bindings.bind(Key::shift_tab(), ComponentAction::Prev);
bindings.bind(Key::enter(), ComponentAction::Select);
bindings.bind(Key::esc(), ComponentAction::Cancel);
bindings.bind(Key::ctrl('c'), ComponentAction::Custom(0)); // Common quit
}
Priority: MEDIUM - Improves developer experience
Phase 7: Integration with komp_ac
Adapters and integration helpers for seamless komp_ac migration.
Files to create:
src/integration/mod.rs- Module routingsrc/integration/komp_ac.rs- komp_ac-specific adapters
Integration pattern:
impl Component for komp_ac::LoginPage {
type Focus = komp_ac::FocusTarget;
type Action = komp_ac::ResolvedAction;
type Event = komp_ac::AppEvent;
fn targets(&self) -> &[Self::Focus] { ... }
fn handle(&mut self, focus: &Self::Focus, action: Self::Action) -> Result<Option<Self::Event>> { ... }
}
komp_ac setup:
let mut orch = Orchestrator::new()
.with_mode_resolver(CanvasModeResolver::new(app_state))
.with_overlay_manager(KompAcOverlayManager::new())
.with_event_handler(KompAcEventHandler::new(router, focus));
Priority: LOW - Not needed for general library users, but essential for komp_ac
File Structure After All Phases
src/
├── lib.rs # Routing
├── prelude.rs # Common imports
│
├── input/ # Phase 1 ✅
│ ├── mod.rs
│ ├── key.rs
│ ├── bindings.rs
│ ├── handler.rs
│ ├── result.rs
│ └── action.rs
│
├── focus/ # Phase 1 ✅
│ ├── mod.rs
│ ├── id.rs
│ ├── manager.rs
│ ├── query.rs
│ ├── error.rs
│ └── traits.rs
│
├── component/ # Phase 2 (NEXT)
│ ├── mod.rs
│ ├── trait.rs
│ ├── action.rs
│ └── error.rs
│
├── router/ # Phase 3
│ ├── mod.rs
│ ├── router.rs
│ └── history.rs
│
├── orchestrator/ # Phase 4
│ ├── mod.rs
│ ├── core.rs
│ ├── bindings.rs
│ ├── modes.rs
│ ├── overlays.rs
│ └── events.rs
│
├── extension/ # Phase 5
│ ├── mod.rs
│ ├── mode.rs
│ ├── overlay.rs
│ └── event.rs
│
├── builder/ # Phase 6
│ ├── mod.rs
│ ├── builder.rs
│ └── defaults.rs
│
└── integration/ # Phase 7
├── mod.rs
└── komp_ac.rs
tests/
├── input/
├── focus/
├── component/
├── router/
├── orchestrator/
└── integration/
Testing Strategy
For each phase:
- Write tests first - Define expected behavior
- Implement to pass - Code should make tests pass
- Run cargo test - Verify all pass
- Run cargo clippy - Ensure code quality
- Run cargo fmt - Ensure formatting
Target: 100% test coverage for all public APIs
Dependencies Update
Cargo.toml (after Phase 4)
[package]
name = "tui_orchestrator"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[features]
default = ["std"]
std = []
alloc = ["hashbrown"]
sequences = ["alloc"]
[dependencies]
hashbrown = { version = "0.15", optional = true }
[dev-dependencies]
Next Action
Implement Phase 2: Component System
This is the foundation that enables:
- Page/component registration
- Button logic definition
- Lifecycle hooks
- Everything the framework needs
Tasks:
- Create
src/component/mod.rs - Create
src/component/trait.rswith Component trait - Create
src/component/action.rswith ComponentAction enum - Create
src/component/error.rswith ComponentError enum - Write tests in
tests/component/ - Update
src/lib.rsto export component module - Update
src/prelude.rsto include Component types - Run
cargo test --all-features - Run
cargo clippy --all-features - Update documentation if needed
Ready to implement?