only show tool detail when running

This commit is contained in:
Dhanji Prasanna
2025-10-06 14:33:15 +11:00
parent 340ba78eb3
commit c9487db5e7

View File

@@ -71,6 +71,10 @@ struct TerminalState {
scroll_offset: usize,
/// Cursor blink state
cursor_blink: bool,
/// Animation state for activity area (0.0 = hidden, 1.0 = fully shown)
activity_animation: f32,
/// Target animation state
activity_animation_target: f32,
/// Tool activity history (left side of activity box)
tool_activity: Vec<String>,
/// Track if tool activity should auto-scroll
@@ -121,6 +125,8 @@ impl TerminalState {
],
scroll_offset: 0,
cursor_blink: true,
activity_animation: 0.0,
activity_animation_target: 0.0,
tool_activity: Vec::new(),
tool_activity_auto_scroll: true,
tool_activity_scroll: 0,
@@ -371,6 +377,8 @@ impl RetroTui {
let was_processing = state.status_line == "PROCESSING";
state.status_line = status;
state.is_processing = state.status_line == "PROCESSING";
// Set animation target based on processing state
state.activity_animation_target = if state.is_processing { 1.0 } else { 0.0 };
// Remove cursor when exiting PROCESSING mode
if was_processing && !state.is_processing {
@@ -439,6 +447,18 @@ impl RetroTui {
state.last_status_blink = Instant::now();
}
}
// Update activity area animation
let animation_speed = 0.15; // Adjust for faster/slower animation
if (state.activity_animation - state.activity_animation_target).abs() > 0.01 {
// Smoothly interpolate towards target
state.activity_animation += (state.activity_animation_target - state.activity_animation) * animation_speed;
// Clamp to valid range
state.activity_animation = state.activity_animation.clamp(0.0, 1.0);
} else {
// Snap to target when close enough
state.activity_animation = state.activity_animation_target;
}
}
// Redraw at ~60fps
@@ -476,16 +496,31 @@ impl RetroTui {
terminal.draw(|f| {
let size = f.area();
// Create main layout - header, input, output
let chunks = Layout::default()
// Calculate activity area height based on animation (0 to 8)
let activity_height = (8.0 * state.activity_animation).round() as u16;
// Create main layout - dynamically adjust based on whether activity area is shown
let chunks = if activity_height > 0 {
Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(5), // Header/input area
Constraint::Min(10), // Main output area (will be further split)
Constraint::Length(8), // Activity area
Constraint::Length(activity_height), // Activity area (animated)
Constraint::Length(1), // Status bar
])
.split(size);
.split(size)
} else {
// When activity area is hidden, give more space to output
Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(5), // Header/input area
Constraint::Min(10), // Main output area gets all remaining space
Constraint::Length(1), // Status bar
])
.split(size)
};
// IMPORTANT: Update the last known visible height BEFORE drawing
// This ensures auto-scroll calculations use the correct height
@@ -513,13 +548,22 @@ impl RetroTui {
// Draw main output area
Self::draw_output_area(f, chunks[1], &state.output_history, state.scroll_offset);
// Draw activity area (tool output)
Self::draw_activity_area(f, chunks[2], state);
// Draw activity area only if it's visible (during animation or when shown)
if activity_height > 0 {
// Apply fade effect by adjusting opacity through color intensity
let opacity = state.activity_animation;
Self::draw_activity_area(f, chunks[2], state, opacity);
}
// Draw status bar
// Draw status bar - use the last chunk which is either index 2 or 3
let status_bar_chunk = if activity_height > 0 {
chunks[3]
} else {
chunks[2]
};
Self::draw_status_bar(
f,
chunks[3],
status_bar_chunk,
&state.status_line,
state.context_info,
&state.provider_info,
@@ -704,9 +748,23 @@ impl RetroTui {
f: &mut Frame,
area: Rect,
state: &TerminalState,
opacity: f32,
) {
// Note: scroll_offset is managed by the state and auto-scrolls to show latest content when new data arrives
// Apply fade effect by adjusting colors based on opacity
let fade_color = |color: Color| -> Color {
match color {
Color::Rgb(r, g, b) => {
let faded_r = ((r as f32 * opacity) as u8).max(0);
let faded_g = ((g as f32 * opacity) as u8).max(0);
let faded_b = ((b as f32 * opacity) as u8).max(0);
Color::Rgb(faded_r, faded_g, faded_b)
}
_ => color,
}
};
// Split the activity area into left and right halves
let chunks = Layout::default()
.direction(Direction::Horizontal)
@@ -732,7 +790,7 @@ impl RetroTui {
let visible_lines: Vec<Line> = if state.tool_activity.is_empty() {
vec![Line::from(Span::styled(
" No tool activity yet",
Style::default().fg(TERMINAL_DIM_GREEN).add_modifier(Modifier::ITALIC),
Style::default().fg(fade_color(TERMINAL_DIM_GREEN)).add_modifier(Modifier::ITALIC),
))]
} else {
state.tool_activity
@@ -742,11 +800,11 @@ impl RetroTui {
.map(|line| {
// Style the header lines differently
let style = if line.starts_with('[') && line.contains(']') {
Style::default().fg(TERMINAL_CYAN).add_modifier(Modifier::BOLD)
Style::default().fg(fade_color(TERMINAL_CYAN)).add_modifier(Modifier::BOLD)
} else if line.is_empty() {
Style::default()
} else {
Style::default().fg(TERMINAL_GREEN)
Style::default().fg(fade_color(TERMINAL_GREEN))
};
Line::from(Span::styled(format!(" {}", line), style))
})
@@ -759,7 +817,7 @@ impl RetroTui {
.title(" TOOL DETAIL ")
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.border_style(Style::default().fg(TERMINAL_DIM_GREEN))
.border_style(Style::default().fg(fade_color(TERMINAL_DIM_GREEN)))
.style(Style::default().bg(TERMINAL_BG)),
)
.wrap(Wrap { trim: false });
@@ -767,7 +825,7 @@ impl RetroTui {
f.render_widget(tool_output, chunks[0]);
// Draw right half - Token Chart
Self::draw_token_chart(f, chunks[1], &state.token_rate_history, state.is_processing);
Self::draw_token_chart(f, chunks[1], &state.token_rate_history, state.is_processing, opacity);
}
/// Draw a line chart showing tokens received over time
@@ -776,13 +834,27 @@ impl RetroTui {
area: Rect,
token_history: &VecDeque<(f64, f64)>,
is_processing: bool,
opacity: f32,
) {
// Apply fade effect by adjusting colors based on opacity
let fade_color = |color: Color| -> Color {
match color {
Color::Rgb(r, g, b) => {
let faded_r = ((r as f32 * opacity) as u8).max(0);
let faded_g = ((g as f32 * opacity) as u8).max(0);
let faded_b = ((b as f32 * opacity) as u8).max(0);
Color::Rgb(faded_r, faded_g, faded_b)
}
_ => color,
}
};
// Create the chart block
let block = Block::default()
.title(" TOKENS RECEIVED ")
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.border_style(Style::default().fg(TERMINAL_DIM_GREEN))
.border_style(Style::default().fg(fade_color(TERMINAL_DIM_GREEN)))
.style(Style::default().bg(TERMINAL_BG));
// Calculate inner area for chart
@@ -795,7 +867,7 @@ impl RetroTui {
if token_history.is_empty() || inner.width < 10 || inner.height < 3 {
let placeholder = Paragraph::new(vec![Line::from(Span::styled(
" Waiting for token data...",
Style::default().fg(TERMINAL_DIM_GREEN).add_modifier(Modifier::ITALIC),
Style::default().fg(fade_color(TERMINAL_DIM_GREEN)).add_modifier(Modifier::ITALIC),
))])
.alignment(Alignment::Center);
f.render_widget(placeholder, inner);
@@ -826,9 +898,9 @@ impl RetroTui {
lines.push(Line::from(vec![
Span::styled(
format!("{:>5.0}", max_tokens),
Style::default().fg(TERMINAL_AMBER),
Style::default().fg(fade_color(TERMINAL_AMBER)),
),
Span::styled("", Style::default().fg(TERMINAL_DIM_GREEN)),
Span::styled("", Style::default().fg(fade_color(TERMINAL_DIM_GREEN))),
]));
// Draw the sparkline chart
@@ -851,16 +923,16 @@ impl RetroTui {
}
}
let color = if is_processing { TERMINAL_CYAN } else { TERMINAL_GREEN };
let color = if is_processing { fade_color(TERMINAL_CYAN) } else { fade_color(TERMINAL_GREEN) };
lines.push(Line::from(Span::styled(chart_line, Style::default().fg(color))));
// Add bottom axis
lines.push(Line::from(vec![
Span::styled(" 0", Style::default().fg(TERMINAL_AMBER)),
Span::styled("", Style::default().fg(TERMINAL_DIM_GREEN)),
Span::styled(" 0", Style::default().fg(fade_color(TERMINAL_AMBER))),
Span::styled("", Style::default().fg(fade_color(TERMINAL_DIM_GREEN))),
Span::styled(
format!("{}T (seconds)", "".repeat(chart_width.saturating_sub(15))),
Style::default().fg(TERMINAL_DIM_GREEN),
Style::default().fg(fade_color(TERMINAL_DIM_GREEN)),
),
]));
}