Recreate repository due to Git object corruption (all files preserved)
This commit is contained in:
322
README.md
Normal file
322
README.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# TUI Orchestrator
|
||||
|
||||
A complete, **ready-to-use TUI framework** that handles input routing, focus management, page navigation, and lifecycle hooks—so you can define your pages, buttons, and logic, and it just works.
|
||||
|
||||
## Features
|
||||
|
||||
- **Zero boilerplate** - Define components, library handles everything else
|
||||
- **Ready to use** - Register pages and run, no manual wiring needed
|
||||
- **Sensible defaults** - Works without configuration
|
||||
- **Fully extendable** - Customize via traits when needed
|
||||
- **no_std compatible** - Works on embedded systems and WebAssembly
|
||||
- **Backend-agnostic** - No crossterm/ratatui dependencies
|
||||
- **Zero unsafe** - Pure Rust, no unsafe code
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Define Your Component
|
||||
|
||||
```rust
|
||||
extern crate alloc;
|
||||
|
||||
use tui_orchestrator::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum LoginFocus {
|
||||
Username,
|
||||
Password,
|
||||
LoginButton,
|
||||
CancelButton,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum LoginEvent {
|
||||
AttemptLogin { username: String, password: String },
|
||||
Cancel,
|
||||
}
|
||||
|
||||
struct LoginPage {
|
||||
username: alloc::string::String,
|
||||
password: alloc::string::String,
|
||||
}
|
||||
|
||||
impl Component for LoginPage {
|
||||
type Focus = LoginFocus;
|
||||
type Action = ComponentAction;
|
||||
type Event = LoginEvent;
|
||||
|
||||
fn targets(&self) -> &[Self::Focus] {
|
||||
&[
|
||||
LoginFocus::Username,
|
||||
LoginFocus::Password,
|
||||
LoginFocus::LoginButton,
|
||||
LoginFocus::CancelButton,
|
||||
]
|
||||
}
|
||||
|
||||
fn handle(&mut self, focus: &Self::Focus, action: Self::Action) -> Result<Option<Self::Event>> {
|
||||
match (focus, action) {
|
||||
(LoginFocus::LoginButton, ComponentAction::Select) => {
|
||||
Ok(Some(LoginEvent::AttemptLogin {
|
||||
username: self.username.clone(),
|
||||
password: self.password.clone(),
|
||||
}))
|
||||
}
|
||||
(LoginFocus::CancelButton, ComponentAction::Select) => {
|
||||
Ok(Some(LoginEvent::Cancel))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_text(&mut self, focus: &Self::Focus, ch: char) -> Result<Option<Self::Event>> {
|
||||
match focus {
|
||||
LoginFocus::Username => {
|
||||
self.username.push(ch);
|
||||
Ok(None)
|
||||
}
|
||||
LoginFocus::Password => {
|
||||
self.password.push(ch);
|
||||
Ok(None)
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Register and Run
|
||||
|
||||
```rust
|
||||
use tui_orchestrator::prelude::*;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut orch = Orchestrator::builder()
|
||||
.with_page("login", LoginPage::new())
|
||||
.with_default_bindings()
|
||||
.build()?;
|
||||
|
||||
orch.navigate_to("login")?;
|
||||
|
||||
orch.run(&mut MyInputSource)?;
|
||||
}
|
||||
```
|
||||
|
||||
**That's it.** The library handles:
|
||||
- Input processing (read keys, route to actions)
|
||||
- Focus management (next/prev navigation)
|
||||
- Page navigation (on_exit, swap, on_enter)
|
||||
- Default keybindings (Tab=Next, Enter=Select)
|
||||
- Event collection and routing
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Component
|
||||
|
||||
The main abstraction in tui_orchestrator. A component represents a page or UI section with focusable elements.
|
||||
|
||||
```rust
|
||||
pub trait Component {
|
||||
type Focus: FocusId; // What can receive focus
|
||||
type Action: Action; // What actions this handles
|
||||
type Event: Clone + Debug; // Events this component emits
|
||||
|
||||
fn targets(&self) -> &[Self::Focus];
|
||||
fn handle(&mut self, focus: &Self::Focus, action: Self::Action) -> Result<Option<Self::Event>>;
|
||||
}
|
||||
```
|
||||
|
||||
**Optional methods** (all have defaults):
|
||||
- `on_enter()` - Called when component becomes active
|
||||
- `on_exit()` - Called when component becomes inactive
|
||||
- `on_focus()` - Called when a focus target gains focus
|
||||
- `on_blur()` - Called when a focus target loses focus
|
||||
- `handle_text()` - Called when character is typed
|
||||
- `can_navigate_forward/backward()` - Control focus movement
|
||||
|
||||
### Component Actions
|
||||
|
||||
Standard actions the library provides:
|
||||
|
||||
```rust
|
||||
pub enum ComponentAction {
|
||||
Next, // Tab
|
||||
Prev, // Shift+Tab
|
||||
First, // Home
|
||||
Last, // End
|
||||
Select, // Enter
|
||||
Cancel, // Esc
|
||||
TypeChar(char), // Any character
|
||||
Backspace, // Backspace
|
||||
Delete, // Delete
|
||||
Custom(usize), // User-defined
|
||||
}
|
||||
```
|
||||
|
||||
### Focus Management
|
||||
|
||||
Focus tracks which element is currently active. The library provides:
|
||||
|
||||
- `FocusManager<F>` - Generic focus tracking
|
||||
- `FocusQuery` - Read-only focus state for rendering
|
||||
- Automatic navigation (next, prev, first, last)
|
||||
|
||||
### Orchestrator
|
||||
|
||||
The complete TUI runtime that wires everything together:
|
||||
|
||||
- `Orchestrator<C>` - Main framework struct
|
||||
- `process_frame()` - Process one input frame
|
||||
- `run()` - Complete main loop
|
||||
- Extension points for custom behavior
|
||||
|
||||
---
|
||||
|
||||
## Extension Points
|
||||
|
||||
For complex applications (like komp_ac), the library provides extension points to customize behavior:
|
||||
|
||||
### ModeResolver
|
||||
|
||||
Customize how modes are resolved (dynamic vs static).
|
||||
|
||||
```rust
|
||||
impl ModeResolver for CustomResolver {
|
||||
fn resolve(&self, focus: &dyn Any) -> Vec<ModeName> { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### OverlayManager
|
||||
|
||||
Customize overlay types (dialogs, command palettes, search).
|
||||
|
||||
```rust
|
||||
impl OverlayManager for CustomOverlayManager {
|
||||
fn is_active(&self) -> bool { ... }
|
||||
fn handle_input(&mut self, key: Key) -> Option<OverlayResult> { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### EventHandler
|
||||
|
||||
Customize how events are routed to handlers.
|
||||
|
||||
```rust
|
||||
impl EventHandler<AppEvent> for CustomHandler {
|
||||
fn handle(&mut self, event: AppEvent) -> Result<HandleResult> { ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example: Multi-Page App
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
enum MyPage {
|
||||
Login(LoginPage),
|
||||
Home(HomePage),
|
||||
Settings(SettingsPage),
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut orch = Orchestrator::builder()
|
||||
.with_page("login", LoginPage::new())
|
||||
.with_page("home", HomePage::new())
|
||||
.with_page("settings", SettingsPage::new())
|
||||
.with_default_bindings()
|
||||
.build()?;
|
||||
|
||||
orch.navigate_to("login")?;
|
||||
|
||||
orch.run()?;
|
||||
}
|
||||
```
|
||||
|
||||
Navigation with history:
|
||||
```rust
|
||||
orch.navigate_to("home")?;
|
||||
orch.navigate_to("settings")?;
|
||||
orch.back()? // Return to home
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feature Flags
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tui_orchestrator = { version = "0.1", features = ["std"] }
|
||||
|
||||
# Optional features
|
||||
sequences = ["alloc"] # Enable multi-key sequences
|
||||
```
|
||||
|
||||
- `default` - No features (pure no_std)
|
||||
- `std` - Enable std library support
|
||||
- `alloc` - Enable alloc support (needed for collections)
|
||||
|
||||
---
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
1. **Plugin-play model** - Library is runtime, components are plugins
|
||||
2. **Sensible defaults** - Zero configuration works
|
||||
3. **Optional everything** - Define only what you need
|
||||
4. **Extension points** - Override defaults when needed
|
||||
5. **User-focused** - "register page" not "bind chord to registry"
|
||||
6. **no_std first** - Works on embedded, opt-in std
|
||||
|
||||
---
|
||||
|
||||
## For komp_ac Integration
|
||||
|
||||
komp_ac can:
|
||||
1. Implement `Component` trait for all pages
|
||||
2. Use library's `Orchestrator` as runtime
|
||||
3. Extend with custom `ModeResolver` for dynamic Canvas-style modes
|
||||
4. Extend with custom `OverlayManager` for command palette, find file, search
|
||||
5. Extend with custom `EventHandler` for page/global/canvas routing
|
||||
|
||||
**Result:** komp_ac uses library's core while keeping all custom behavior.
|
||||
|
||||
See [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md) for details.
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
If you're migrating from a TUI built with manual wiring:
|
||||
|
||||
1. **Identify components** - What are your pages/sections?
|
||||
2. **Implement Component trait** - `targets()`, `handle()`, optional hooks
|
||||
3. **Remove manual orchestration** - Delete manual focus/binding/router setup
|
||||
4. **Use Orchestrator** - Register pages and run
|
||||
5. **Add extensions if needed** - ModeResolver, OverlayManager, EventHandler
|
||||
|
||||
The library handles everything else.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
See `examples/` directory for complete working applications:
|
||||
- `simple_app.rs` - Basic multi-page TUI
|
||||
- `form_app.rs` - Form with text input
|
||||
- `extended_app.rs` - Using extension points
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
- [PLAN.md](PLAN.md) - Complete implementation plan
|
||||
- [REDESIGN.md](REDESIGN.md) - Framework architecture deep dive
|
||||
- [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md) - Integration examples and patterns
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT OR Apache-2.0
|
||||
Reference in New Issue
Block a user