diff options
| author | soryu <soryu@soryu.co> | 2026-01-19 13:47:32 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-19 13:47:32 +0000 |
| commit | 0833fb1f30c0c3b920157deb882e0e902c3af02a (patch) | |
| tree | 45110fb8cb9277dfbaccfeb53ed9c1f76975022b /makima/src/bin | |
| parent | 786510379bed060db2b3742b7dfca671552d2c34 (diff) | |
| download | soryu-0833fb1f30c0c3b920157deb882e0e902c3af02a.tar.gz soryu-0833fb1f30c0c3b920157deb882e0e902c3af02a.zip | |
Add interactive TUI browser for tasks, contracts, and files (makima view) (#7)
* feat(tui): Implement fuzzy search with real-time filtering and highlighting
Adds comprehensive fuzzy search functionality to the TUI browser:
## Fuzzy Matching (fuzzy.rs)
- FuzzyMatcher wrapper using SkimMatcherV2 from fuzzy-matcher crate
- fuzzy_match() returns score and matched character indices
- fuzzy_match_all() supports multi-term search (space-separated)
- Recency-aware scoring to boost recent items in results
- Unit tests for all matching scenarios
## App State (app.rs)
- FilteredItem struct with index, score, and matched_indices
- apply_filter() uses fuzzy matching with score-based sorting
- match_count() and has_no_matches() helper methods
- Results sorted by match score (highest first)
## List View (list_view.rs)
- Highlighted matched characters in search results
- Yellow bold styling for matched chars
- Status icons with color coding
## Search Input (search_input.rs)
- Real-time match count display (X/Y matches)
- Visual feedback for no matches (red border)
- Placeholder text when search is empty
- Active search mode indication (yellow border)
## Event Handling (event.rs)
- Arrow key navigation while in search mode
- Ctrl+K/J for vim-style navigation during search
- Delete key support alongside backspace
- Ctrl+U to clear search query
- Tab toggles preview while searching
- Escape clears search and exits search mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Task completion checkpoint
* [WIP] Heartbeat checkpoint - 2026-01-19 11:20:34 UTC
* Task completion checkpoint
* [WIP] Heartbeat checkpoint - 2026-01-19 11:31:19 UTC
* Task completion checkpoint
* [WIP] Heartbeat checkpoint - 2026-01-19 11:39:07 UTC
* fix(tui): Fix module exports and main binary integration
- Update mod.rs to properly export app, event, fuzzy, and ui modules
- Add run() function for TUI entry point
- Fix run_view() to use ViewCommand enum instead of ViewArgs
- Fix event handling to use poll_event and handle_key_event
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/src/bin')
| -rw-r--r-- | makima/src/bin/makima.rs | 67 |
1 files changed, 66 insertions, 1 deletions
diff --git a/makima/src/bin/makima.rs b/makima/src/bin/makima.rs index 6ed1761..8fc8b60 100644 --- a/makima/src/bin/makima.rs +++ b/makima/src/bin/makima.rs @@ -6,8 +6,9 @@ use std::sync::Arc; use makima::daemon::api::ApiClient; use makima::daemon::cli::{ - Cli, Commands, ContractCommand, SupervisorCommand, + Cli, Commands, ContractCommand, SupervisorCommand, ViewCommand, ViewArgs, }; +use makima::daemon::tui::{self, App, ListItem, ViewType}; use makima::daemon::config::{DaemonConfig, RepoEntry}; use makima::daemon::db::LocalDb; use makima::daemon::error::DaemonError; @@ -26,6 +27,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { Commands::Daemon(args) => run_daemon(args).await, Commands::Supervisor(cmd) => run_supervisor(cmd).await, Commands::Contract(cmd) => run_contract(cmd).await, + Commands::View(args) => run_view(args).await, } } @@ -530,6 +532,69 @@ async fn run_contract( Ok(()) } +/// Run the TUI view command. +async fn run_view(cmd: ViewCommand) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { + // Extract view type and args from command + let (view_type, args) = match cmd { + ViewCommand::Tasks(args) => (ViewType::Tasks, args), + ViewCommand::Contracts(args) => (ViewType::Contracts, args), + ViewCommand::Files(args) => (ViewType::Files, args), + }; + + // Create API client + let client = ApiClient::new(args.api_url.clone(), args.api_key.clone())?; + + // Fetch initial data based on view type + let items = match view_type { + ViewType::Tasks => { + let contract_id = args.contract_id + .ok_or("Contract ID is required for tasks view (use --contract-id or MAKIMA_CONTRACT_ID)")?; + let result = client.supervisor_tasks(contract_id).await?; + // Parse tasks from JSON array + result.0.as_array() + .map(|arr| arr.iter().filter_map(ListItem::from_task).collect()) + .unwrap_or_default() + } + ViewType::Contracts => { + // For contracts, we would need a list contracts endpoint + // For now, return empty or fetch from a different endpoint + eprintln!("Contracts view not yet implemented - requires list contracts endpoint"); + Vec::new() + } + ViewType::Files => { + let contract_id = args.contract_id + .ok_or("Contract ID is required for files view (use --contract-id or MAKIMA_CONTRACT_ID)")?; + let result = client.contract_files(contract_id).await?; + // Parse files from JSON array + result.0.as_array() + .map(|arr| arr.iter().filter_map(ListItem::from_file).collect()) + .unwrap_or_default() + } + }; + + // Create TUI app + let mut app = App::new(view_type); + app.contract_id = args.contract_id; + app.set_items(items); + + // Run TUI + match tui::run(app) { + Ok(Some(path)) => { + // Output the path for shell integration (e.g., cd $(makima view tasks)) + tui::print_path(&path); + } + Ok(None) => { + // Normal exit, no output needed + } + Err(e) => { + eprintln!("TUI error: {}", e); + std::process::exit(1); + } + } + + Ok(()) +} + fn init_logging(level: &str, format: &str) { let filter = EnvFilter::try_from_default_env() .or_else(|_| EnvFilter::try_new(level)) |
