Enhance logging and documentation across utility tools and TUI overlays
- Added tracing instrumentation and improved logging messages in the Pong, Todofinish, and Todowrite tools for better debugging and monitoring. - Enhanced documentation comments for clarity on tool functionalities and workflows. - Implemented tracing in WorkflowRun, NoteFinding, ReadFindings, and HiveMind tools to track execution phases and findings. - Updated TUI overlays (e.g., Bash, Clear Confirm, Editor, Effort Level, Help, Key Input, Learning, Loading, MCP, Model Selector, Plan, Quit Confirm, Rewind, Settings, Todo, Usage) with debug logging to capture rendering details. - Improved the status bar and workflow panel rendering with additional debug information. - Added tracing to various utility functions to facilitate better performance monitoring and error tracking.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
//! *how* state is updated — only *what* action to produce.
|
||||
|
||||
use crate::state::Overlay;
|
||||
use tracing::debug;
|
||||
|
||||
/// A single well-typed event in the TUI that mutates `AppStateRest`.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -92,8 +93,9 @@ pub enum Action {
|
||||
/// This is the single chokepoint for all state mutations.
|
||||
///
|
||||
/// Return: nothing; `state` is mutated in place.
|
||||
#[tracing::instrument(skip(state))]
|
||||
pub fn apply_action(state: &mut crate::state::AppStateRest, action: Action) {
|
||||
tracing::debug!("apply_action: {:?}", action);
|
||||
debug!("apply_action: {:?}", action);
|
||||
match action {
|
||||
Action::ForceQuit => {
|
||||
state.quit = true;
|
||||
@@ -195,7 +197,7 @@ pub fn apply_action(state: &mut crate::state::AppStateRest, action: Action) {
|
||||
state.dirty = true;
|
||||
}
|
||||
_ => {
|
||||
tracing::debug!("unhandled turn event variant");
|
||||
debug!("unhandled turn event variant");
|
||||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
//! on every `/`-prefixed line, then maps the resulting `Command` to an
|
||||
//! `Action` for the event loop to apply to `AppStateRest`.
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
/// A parsed slash command from the TUI input buffer.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Command {
|
||||
@@ -43,6 +45,7 @@ pub enum Command {
|
||||
/// Flow: trim -> check for leading `/` -> split on space (max 3 parts) ->
|
||||
/// match the first token against known commands -> extract arguments from
|
||||
/// the remaining parts.
|
||||
#[tracing::instrument]
|
||||
pub fn parse_command(text: &str) -> Command {
|
||||
let text = text.trim();
|
||||
if !text.starts_with('/') {
|
||||
@@ -89,11 +92,12 @@ pub fn parse_command(text: &str) -> Command {
|
||||
_ => Command::Unknown(cmd.to_string()),
|
||||
};
|
||||
|
||||
tracing::debug!(%text, command = ?result, "parse_command");
|
||||
debug!(%text, command = ?result, "parse_command");
|
||||
result
|
||||
}
|
||||
|
||||
/// Map a parsed `Command` into `Action` values for the event loop.
|
||||
#[tracing::instrument]
|
||||
pub fn apply_command(cmd: Command) -> Vec<crate::action::Action> {
|
||||
match cmd {
|
||||
Command::Help => {
|
||||
@@ -155,11 +159,13 @@ pub fn apply_command(cmd: Command) -> Vec<crate::action::Action> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Verify that `parse_command` recognises the `/todo` command.
|
||||
#[test]
|
||||
fn parses_todo_open() {
|
||||
assert_eq!(parse_command("/todo"), Command::TodoOpen);
|
||||
}
|
||||
|
||||
/// Verify that `parse_command` recognises the `/usage` command.
|
||||
#[test]
|
||||
fn parses_usage_open() {
|
||||
assert_eq!(parse_command("/usage"), Command::UsageOpen);
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
//! 3. The main match handles navigation, auto-complete, editing, and shortcuts.
|
||||
//! 4. Multi-key actions return `Vec<Action>`.
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
use crate::action::Action;
|
||||
@@ -25,8 +27,9 @@ fn mark(state: &mut AppStateRest) -> Vec<Action> {
|
||||
/// based on the current application state.
|
||||
///
|
||||
/// Return: `Vec<Action>` so a single key can produce multiple queued actions.
|
||||
#[tracing::instrument(skip(state))]
|
||||
pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
||||
tracing::debug!(code = ?key.code, mods = ?key.modifiers, overlay = ?state.misc.overlay, "handle_key");
|
||||
debug!(code = ?key.code, mods = ?key.modifiers, overlay = ?state.misc.overlay, "handle_key");
|
||||
|
||||
// ── Editor overlay ───────────────────────────────────────────────────
|
||||
if state.misc.overlay == Overlay::Editor {
|
||||
@@ -299,8 +302,17 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
||||
}
|
||||
|
||||
/// Handle pressing Enter while a modal overlay is active.
|
||||
///
|
||||
/// Each overlay variant has its own Enter semantics:
|
||||
/// - `QuitConfirm` → set `quit = true`
|
||||
/// - `KeyInput` → save API key from buffer
|
||||
/// - `ModelSelector` → switch provider/model from selected index
|
||||
/// - `ClearConfirm` → clear transcript cache
|
||||
/// - `Rewind` → rewind to selected message index
|
||||
/// - `Bash` / `Settings` / `Todo` / `Mcp` → no-ops (placeholder)
|
||||
#[tracing::instrument(skip(state))]
|
||||
fn handle_overlay_enter(state: &mut AppStateRest) -> Vec<Action> {
|
||||
tracing::debug!(overlay = ?state.misc.overlay, "handle_overlay_enter");
|
||||
debug!(overlay = ?state.misc.overlay, "handle_overlay_enter");
|
||||
match state.misc.overlay {
|
||||
Overlay::Bash => {
|
||||
let command = state.input.buffer.clone();
|
||||
@@ -399,12 +411,15 @@ fn handle_overlay_enter(state: &mut AppStateRest) -> Vec<Action> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Build a minimal `AppStateRest` in a temp directory for test isolation.
|
||||
fn test_state() -> AppStateRest {
|
||||
let tmp = std::env::temp_dir().join(format!("zesdex-input-test-{}", uuid::Uuid::new_v4()));
|
||||
std::fs::create_dir_all(&tmp).unwrap();
|
||||
AppStateRest::new(vec![tmp.clone()], &tmp, tmp.join("memory"))
|
||||
}
|
||||
|
||||
/// Verify that Ctrl+Y sets `pending_clipboard_copy` to the most
|
||||
/// recent assistant message (skipping non-assistant roles like Tool).
|
||||
#[test]
|
||||
fn ctrl_y_sets_pending_clipboard_copy_to_last_assistant_message() {
|
||||
let mut state = test_state();
|
||||
@@ -434,6 +449,8 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// Verify that Ctrl+Y with no assistant messages in the transcript
|
||||
/// pushes an info toast instead of setting `pending_clipboard_copy`.
|
||||
#[test]
|
||||
fn ctrl_y_with_no_assistant_message_pushes_info_toast() {
|
||||
let mut state = test_state();
|
||||
|
||||
@@ -16,6 +16,8 @@ use ratatui::Terminal;
|
||||
use std::io::{self, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::action::{apply_action, Action};
|
||||
use crate::controller::input::handle_key;
|
||||
use crate::state::AppStateRest;
|
||||
@@ -26,8 +28,10 @@ use crate::view;
|
||||
/// Flow: build `AppStateRest` → enter raw mode / alternate screen →
|
||||
/// run the event loop → always restore the terminal (even on error) →
|
||||
/// save settings.
|
||||
#[tracing::instrument]
|
||||
pub fn run_single_process() -> Result<()> {
|
||||
// Create session state
|
||||
info!("starting single-process TUI");
|
||||
let (_store, mut state) = create_local_session()?;
|
||||
|
||||
// Enter raw mode and alternate screen for the TUI
|
||||
@@ -60,10 +64,15 @@ pub fn run_single_process() -> Result<()> {
|
||||
}
|
||||
|
||||
/// Run the event loop, guaranteeing terminal restoration on error.
|
||||
///
|
||||
/// Wraps `run_loop_inner` so that if it panics or returns an error the
|
||||
/// terminal is restored to a usable state before propagating the error.
|
||||
#[tracing::instrument(skip(state, terminal))]
|
||||
fn run_loop(
|
||||
state: &mut AppStateRest,
|
||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||
) -> Result<()> {
|
||||
debug!("entering run_loop");
|
||||
let result = run_loop_inner(state, terminal);
|
||||
if let Err(ref _e) = result {
|
||||
let _ = terminal.clear();
|
||||
@@ -83,12 +92,15 @@ fn run_loop(
|
||||
/// smooth updates), 200ms when idle (no reason to busy-loop)
|
||||
/// - Toast expiry is drained once here instead of in two places
|
||||
/// - Chat display lines are cached and rebuilt only when content changes
|
||||
#[tracing::instrument(skip(state, terminal))]
|
||||
fn run_loop_inner(
|
||||
state: &mut AppStateRest,
|
||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||
) -> Result<()> {
|
||||
info!("TUI event loop started");
|
||||
loop {
|
||||
if state.quit {
|
||||
info!("TUI event loop exiting (quit=true)");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -165,8 +177,13 @@ fn run_loop_inner(
|
||||
}
|
||||
|
||||
/// Create session state with real infrastructure wired in.
|
||||
///
|
||||
/// Flow: initialise `Store` → ensure data directories → generate session ID →
|
||||
/// load `Settings` and `AppConfig` from disk → construct `AppStateRest`.
|
||||
#[tracing::instrument]
|
||||
fn create_local_session() -> Result<(zesdex_domain::core::Store, AppStateRest)> {
|
||||
let store = zesdex_domain::core::Store::new();
|
||||
debug!("store created at {:?}", store.base_dir);
|
||||
store.ensure_dirs()?;
|
||||
|
||||
let session_id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
@@ -721,6 +721,7 @@ pub enum LearningItem {
|
||||
///
|
||||
/// Reads lesson markdown files from the `lessons/` subdirectory
|
||||
/// under the memory directory.
|
||||
#[tracing::instrument(skip(state))]
|
||||
pub fn get_learning_items(state: &AppStateRest) -> Vec<LearningItem> {
|
||||
let lessons_dir = state.memory_dir.join("lessons");
|
||||
if !lessons_dir.exists() {
|
||||
@@ -779,6 +780,7 @@ pub fn rewind_count(state: &AppStateRest) -> usize {
|
||||
///
|
||||
/// Uses the model name from settings to determine max context window,
|
||||
/// falling back to settings-configured max or 128k default.
|
||||
#[tracing::instrument(skip(_app_config, settings))]
|
||||
pub fn resolve_context_window(
|
||||
_app_config: &zesdex_domain::cms::AppConfig,
|
||||
settings: &zesdex_domain::cms::Settings,
|
||||
@@ -793,6 +795,11 @@ pub fn resolve_context_window(
|
||||
}
|
||||
|
||||
/// Count tokens using tiktoken, fall back to character estimation.
|
||||
///
|
||||
/// Flow: try tiktoken-rs `cl100k_base` BPE encoding → return accurate count.
|
||||
/// On failure (~4 chars per token heuristic), fall back to character-based
|
||||
/// estimation so the UI never blocks on an unavailable tokeniser.
|
||||
#[tracing::instrument]
|
||||
pub fn count_tokens(text: &str) -> usize {
|
||||
// Try tiktoken for accurate counting
|
||||
if let Ok(bpe) = tiktoken_rs::cl100k_base() {
|
||||
|
||||
@@ -18,6 +18,11 @@ use zesdex_infrastructure::TurnEvent;
|
||||
use crate::state::AppStateRest;
|
||||
|
||||
/// Spawn an agent turn on a background OS thread.
|
||||
///
|
||||
/// Flow: compare-exchange the in-flight flag → snapshot state fields →
|
||||
/// clone session runtime messages → push user message → spawn OS thread
|
||||
/// that runs `run_turn`.
|
||||
#[tracing::instrument(skip(state))]
|
||||
pub fn spawn_agent_turn(state: &mut AppStateRest, text: String) {
|
||||
// compare_exchange: only mark in-flight if not already running
|
||||
if state.turn_in_flight_flag
|
||||
@@ -86,6 +91,10 @@ pub fn spawn_agent_turn(state: &mut AppStateRest, text: String) {
|
||||
}
|
||||
|
||||
/// Parameters for a turn, grouped to avoid too-many-arguments lint.
|
||||
///
|
||||
/// Holds all the references and owned values that `run_turn` needs:
|
||||
/// message history, turn-event queue, abort/in-flight flags, API credentials,
|
||||
/// and environment paths.
|
||||
struct TurnParams<'a> {
|
||||
messages: &'a mut Vec<ChatMessage>,
|
||||
session_dir: &'a Path,
|
||||
@@ -99,6 +108,12 @@ struct TurnParams<'a> {
|
||||
}
|
||||
|
||||
/// The core agent turn — LLM call → tool execution → repeat.
|
||||
///
|
||||
/// Flow: build `LlmClient` → compile tools → prepend system message →
|
||||
/// loop (max 50 iterations): abort check → stream LLM response →
|
||||
/// push events → execute tool calls → push results → break on
|
||||
/// no tool calls or error → emit final `Compacted` + `Done`.
|
||||
#[tracing::instrument(skip(params))]
|
||||
fn run_turn(params: TurnParams) {
|
||||
let client = LlmClient::new(params.api_key, params.model, params.api_base);
|
||||
|
||||
|
||||
@@ -4,14 +4,17 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Bash Jobs overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Bash Jobs overlay");
|
||||
let block = super::overlay_block(block, "Bash Jobs", Theme::ACCENT_ORANGE);
|
||||
let lines: Vec<Line> = state
|
||||
.session_runtime
|
||||
|
||||
@@ -4,14 +4,17 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Clear Transcript confirmation dialog.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
_state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Clear Transcript confirmation overlay");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Clear Transcript ",
|
||||
|
||||
@@ -5,14 +5,17 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Editor overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Editor overlay, buffer length: {}", state.input.buffer.len());
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Editor ",
|
||||
|
||||
@@ -5,14 +5,18 @@ use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Effort Level overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let current_idx = current_effort(state);
|
||||
debug!("Rendering Effort Level overlay, current idx: {current_idx}");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Effort Level ",
|
||||
|
||||
@@ -3,14 +3,17 @@ use ratatui::style::Style;
|
||||
use ratatui::widgets::{Block, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Help overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Help overlay, content length: {}", state.help_text.len());
|
||||
let block = super::overlay_block(block, "Help", Theme::INFO);
|
||||
let content = state.help_text;
|
||||
let paragraph = Paragraph::new(content)
|
||||
|
||||
@@ -5,14 +5,18 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the API Key input overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let char_count = state.input.buffer.chars().count();
|
||||
debug!("Rendering API Key overlay, char count: {char_count}");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" API Key ",
|
||||
|
||||
@@ -7,14 +7,18 @@ use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Learning overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let items = get_learning_items(state);
|
||||
debug!("Rendering Learning overlay, {} items", items.len());
|
||||
drop(block);
|
||||
|
||||
let h_chunks = Layout::default()
|
||||
|
||||
@@ -4,14 +4,17 @@ use ratatui::text::Span;
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Loading overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Loading overlay, tick: {}", state.misc.tick_count);
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Loading ",
|
||||
|
||||
@@ -4,14 +4,17 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the MCP Servers overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering MCP Servers overlay, session dir: {}", state.session_dir.display());
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" MCP Servers ",
|
||||
|
||||
@@ -26,6 +26,7 @@ use ratatui::text::Span;
|
||||
use ratatui::widgets::{Block, Borders, Clear};
|
||||
use ratatui::Frame;
|
||||
use super::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Decorate an overlay block with a styled title and matching border color.
|
||||
pub fn overlay_block(block: Block<'static>, title: &str, color: ratatui::style::Color) -> Block<'static> {
|
||||
@@ -53,12 +54,17 @@ pub fn centered_rect(area: Rect, percent_x: u16, percent_y: u16) -> Rect {
|
||||
}
|
||||
|
||||
/// Render the active modal overlay as a centered panel.
|
||||
///
|
||||
/// Dispatches to the appropriate overlay module's `render` function based on the
|
||||
/// `Overlay` variant. No-ops for `Overlay::None`.
|
||||
#[tracing::instrument(skip(frame, state))]
|
||||
pub fn render_overlay(
|
||||
frame: &mut Frame,
|
||||
area: Rect,
|
||||
overlay: Overlay,
|
||||
state: &AppStateRest,
|
||||
) {
|
||||
debug!("Rendering overlay: {overlay:?}");
|
||||
let overlay_area = centered_rect(area, 75, 70);
|
||||
frame.render_widget(Clear, overlay_area);
|
||||
|
||||
|
||||
@@ -5,14 +5,17 @@ use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Model Selector overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Model Selector overlay, current provider: {}, model: {}", state.settings.provider, state.settings.model);
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Model Selector ",
|
||||
|
||||
@@ -4,14 +4,18 @@ use ratatui::text::Span;
|
||||
use ratatui::widgets::{Block, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Project Plan overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let has_content = !state.misc.plan_content.is_empty();
|
||||
debug!("Rendering Project Plan overlay, has content: {has_content}");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Project Plan ",
|
||||
|
||||
@@ -4,14 +4,17 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Quit confirmation overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
_state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Quit confirmation overlay");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Quit ",
|
||||
|
||||
@@ -5,15 +5,19 @@ use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use tracing::debug;
|
||||
use zesdex_domain::core::Role;
|
||||
|
||||
/// Render the Rewind overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let msg_count = state.transcript_cache.messages.len();
|
||||
debug!("Rendering Rewind overlay, {} messages", msg_count);
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Rewind ",
|
||||
|
||||
@@ -5,14 +5,17 @@ use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Settings overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("Rendering Settings overlay, provider: {}, model: {}", state.settings.provider, state.settings.model);
|
||||
let block = super::overlay_block(block, "Settings", Theme::PRIMARY);
|
||||
let lines = vec![
|
||||
Line::from(Span::styled(
|
||||
|
||||
@@ -4,14 +4,18 @@ use ratatui::text::Span;
|
||||
use ratatui::widgets::{Block, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
use crate::view::theme::Theme;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Tasks / Todo overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let has_content = !state.misc.todo_content.is_empty();
|
||||
debug!("Rendering Tasks overlay, has content: {has_content}");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Tasks ",
|
||||
|
||||
@@ -6,14 +6,18 @@ use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::Frame;
|
||||
use tracing::debug;
|
||||
|
||||
/// Render the Usage overlay.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn render(
|
||||
frame: &mut Frame,
|
||||
area: ratatui::layout::Rect,
|
||||
block: Block<'static>,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
let has_runtime = state.session_runtime.is_some();
|
||||
debug!("Rendering Usage overlay, has runtime: {has_runtime}");
|
||||
let block = block
|
||||
.title(Span::styled(
|
||||
" Usage ",
|
||||
|
||||
@@ -134,6 +134,10 @@ fn draw_usage_widget(frame: &mut Frame, area: Rect, state: &crate::state::AppSta
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
/// Aggregated usage statistics for the current session.
|
||||
///
|
||||
/// Separates main (chat) and self-learning (review) token counts from
|
||||
/// the raw `UsageStats` and adds a human-readable elapsed-time breakdown.
|
||||
pub(crate) struct UsageSummary {
|
||||
pub main_tokens: u64,
|
||||
pub self_learning_tokens: u64,
|
||||
@@ -144,6 +148,11 @@ pub(crate) struct UsageSummary {
|
||||
pub elapsed_seconds: i64,
|
||||
}
|
||||
|
||||
/// Compute a human-friendly usage summary from raw `UsageStats`.
|
||||
///
|
||||
/// Fields: total_tokens = tokens_in + tokens_out, self_learning = review_tokens,
|
||||
/// main = total - self_learning. Elapsed time is broken into hours/minutes/seconds.
|
||||
#[tracing::instrument]
|
||||
pub(crate) fn compute_usage_summary(
|
||||
usage: &zesdex_domain::core::UsageStats,
|
||||
session_start: i64,
|
||||
|
||||
@@ -10,8 +10,13 @@ use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::Block;
|
||||
use ratatui::Frame;
|
||||
use tracing::instrument;
|
||||
|
||||
/// Render the single-line status bar.
|
||||
/// Render the single-line status bar at the bottom of the terminal.
|
||||
///
|
||||
/// Three visual segments: left (app name + PROG/READY/NOAPI badge),
|
||||
/// center (lesson indicator), right (token count, provider, model).
|
||||
#[instrument(skip_all)]
|
||||
pub fn draw_status_bar(frame: &mut Frame, area: Rect, state: &crate::state::AppStateRest) {
|
||||
use ratatui::layout::{Alignment, Constraint, Direction, Layout};
|
||||
let spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
|
||||
@@ -6,6 +6,7 @@ use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
fn state_icon(state: AgentState) -> &'static str {
|
||||
match state {
|
||||
@@ -34,12 +35,18 @@ fn state_color(state: AgentState) -> Color {
|
||||
}
|
||||
}
|
||||
|
||||
/// Render the workflow status panel.
|
||||
/// Render the workflow status panel showing agent cards and a header.
|
||||
///
|
||||
/// Flow: render titled block → split into header (command hint + status)
|
||||
/// and body (agent cards with icon/name/label/duration, or session stats
|
||||
/// when no workflow is running).
|
||||
#[instrument(skip_all)]
|
||||
pub fn draw_workflow_panel(
|
||||
frame: &mut Frame,
|
||||
area: Rect,
|
||||
state: &crate::state::AppStateRest,
|
||||
) {
|
||||
debug!("draw_workflow_panel — rendering workflow panel");
|
||||
use ratatui::layout::{Constraint, Direction, Layout};
|
||||
|
||||
let title = Span::styled(
|
||||
@@ -157,6 +164,11 @@ pub fn draw_workflow_panel(
|
||||
}
|
||||
}
|
||||
|
||||
/// Build placeholder lines for the workflow panel body when no agents are running.
|
||||
///
|
||||
/// Shows session stats (message count, tool calls, pending queue, bash jobs)
|
||||
/// or a "(no active session)" fallback.
|
||||
#[instrument(skip(state))]
|
||||
fn build_session_lines(state: &crate::state::AppStateRest) -> Vec<Line<'static>> {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
lines.push(Line::from(Span::styled(
|
||||
|
||||
Reference in New Issue
Block a user