1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//! Terminal UI for long-running operations.
//!
//! This module provides a ratatui-based terminal UI for Google Books import
//! and other long-running operations. It subscribes to domain events and
//! renders them without coupling to import internals.
//!
//! ## Architecture
//!
//! The TUI follows a reactive, event-driven architecture:
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────────────┐
//! │ Import Layer │
//! │ GoogleBooksImporter emits ImportEvent via broadcast channel │
//! └───────────────────────────────────────┬─────────────────────────────┘
//! │ ImportEvent
//! ▼
//! ┌─────────────────────────────────────────────────────────────────────┐
//! │ TUI Layer │
//! │ - TuiState: Applies events to derive display state │
//! │ - ImportTui: Renders state with ratatui, handles key input │
//! │ - Sends ImportCommand back to importer via mpsc channel │
//! └─────────────────────────────────────────────────────────────────────┘
//! ```
//!
//! ## Key Bindings
//!
//! | Key | Action |
//! |-----|--------|
//! | `p` | Pause/resume import |
//! | `q` | Graceful quit (save checkpoint) |
//! | `+` | Increase parallelism |
//! | `-` | Decrease parallelism |
//! | `↑`/`↓` | Scroll log |
//! | `Esc` | Force quit (no checkpoint) |
//!
//! ## Example
//!
//! ```ignore
//! use tokio::sync::{broadcast, mpsc};
//! use libgrammstein::sources::google_books::{ImportEvent, ImportCommand};
//! use libgrammstein::cli::tui::ImportTui;
//!
//! let (event_tx, _) = broadcast::channel::<ImportEvent>(1024);
//! let (command_tx, command_rx) = mpsc::channel::<ImportCommand>(16);
//!
//! // Run import in background
//! let import_handle = tokio::spawn(async move {
//! importer.import_http_reactive(event_tx.clone(), command_rx).await
//! });
//!
//! // Run TUI in foreground (subscribes to events)
//! let tui = ImportTui::new(command_tx, parallel_downloads);
//! tui.run(event_tx.subscribe())?;
//!
//! // TUI exits when import completes or user quits
//! let stats = import_handle.await??;
//! ```
pub use ImportTui;
pub use ;