use super::*;
impl Session {
pub(super) fn is_error_content(content: &str) -> bool {
let lower_content = content.to_lowercase();
let error_indicators = [
"error:",
"error ",
"error\n",
"failed",
"failure",
"exception",
"invalid",
"not found",
"couldn't",
"can't",
"cannot",
"denied",
"forbidden",
"unauthorized",
"timeout",
"connection refused",
"no such",
"does not exist",
];
error_indicators
.iter()
.any(|indicator| lower_content.contains(indicator))
}
pub fn new(theme: InlineTheme, placeholder: Option<String>, view_rows: u16) -> Self {
Self::new_with_logs(
theme,
placeholder,
view_rows,
true,
None,
"Agent TUI".to_string(),
)
}
pub fn new_with_logs(
theme: InlineTheme,
placeholder: Option<String>,
view_rows: u16,
show_logs: bool,
appearance: Option<AppearanceConfig>,
app_name: String,
) -> Self {
Self::new_with_options(
theme,
placeholder,
view_rows,
show_logs,
appearance,
app_name,
)
}
fn new_with_options(
theme: InlineTheme,
placeholder: Option<String>,
view_rows: u16,
show_logs: bool,
appearance: Option<AppearanceConfig>,
app_name: String,
) -> Self {
let resolved_rows = view_rows.max(2);
let initial_header_rows = ui::INLINE_HEADER_HEIGHT;
let reserved_rows = initial_header_rows + Self::input_block_height_for_lines(1);
let initial_transcript_rows = resolved_rows.saturating_sub(reserved_rows).max(1);
let appearance = appearance.unwrap_or_default();
let vim_mode_enabled = appearance.vim_mode;
let mut session = Self {
input_manager: InputManager::new(),
scroll_manager: ScrollManager::new(initial_transcript_rows),
user_scrolled: false,
lines: Vec::with_capacity(64),
collapsed_pastes: Vec::new(),
styles: SessionStyles::new(theme.clone()),
theme,
appearance,
header_context: InlineHeaderContext::default(),
labels: MessageLabels::default(),
prompt_prefix: USER_PREFIX.to_string(),
prompt_style: InlineTextStyle::default(),
placeholder,
placeholder_style: None,
input_status_left: None,
input_status_right: None,
copy_notification_until: None,
input_compact_mode: false,
navigation_state: ListState::default(), input_enabled: true,
cursor_visible: true,
needs_redraw: true,
needs_full_clear: false,
transcript_clear_required: true,
should_exit: false,
scroll_cursor_steady_until: None,
last_shimmer_active: false,
view_rows: resolved_rows,
input_height: Self::input_block_height_for_lines(1),
transcript_rows: initial_transcript_rows,
transcript_width: 0,
transcript_view_top: 0,
transcript_area: None,
input_area: None,
bottom_panel_area: None,
modal_list_area: None,
modal_text_areas: Vec::new(),
transcript_file_link_targets: Vec::new(),
modal_link_targets: Vec::new(),
hovered_transcript_file_link: None,
last_mouse_position: None,
last_link_open: None,
pending_link_open: None,
held_key_modifiers: KeyModifiers::empty(),
log_receiver: None,
log_lines: VecDeque::with_capacity(MAX_LOG_LINES),
log_cached_text: None,
log_evicted: false,
show_logs,
transcript_cache: None,
visible_lines_cache: None,
queued_inputs: Vec::with_capacity(4),
local_agents: Vec::new(),
local_agents_drawer_visible: false,
subprocess_entries: Vec::new(),
subagent_preview: None,
queue_overlay_cache: None,
queue_overlay_version: 0,
active_overlay: None,
overlay_queue: VecDeque::new(),
last_overlay_list_selection: None,
last_overlay_list_was_last: false,
header_rows: initial_header_rows,
line_revision_counter: 0,
first_dirty_line: None,
in_tool_code_fence: false,
suggested_prompt_state: SuggestedPromptState::default(),
inline_prompt_suggestion: InlinePromptSuggestionState::default(),
thinking_spinner: ThinkingSpinner::new(),
shimmer_state: ShimmerState::new(),
reverse_search_state: reverse_search::ReverseSearchState::new(),
active_pty_sessions: None,
clipboard: String::new(),
vim_state: VimState::new(vim_mode_enabled),
mouse_selection: MouseSelectionState::new(),
mouse_drag_target: MouseDragTarget::None,
fullscreen: FullscreenSessionState::default(),
skip_confirmations: false,
header_lines_cache: None,
header_height_cache: hashbrown::HashMap::new(),
queued_inputs_preview_cache: None,
subprocess_entries_preview_cache: None,
app_name,
workspace_root: None,
terminal_title_items: None,
terminal_title_thread_label: None,
terminal_title_git_branch: None,
terminal_title_task_progress: None,
last_terminal_title: None,
is_streaming_final_answer: false,
};
session.ensure_prompt_style_color();
session
}
pub(super) fn clear_thinking_spinner_if_active(&mut self, kind: InlineMessageKind) {
if matches!(
kind,
InlineMessageKind::Agent
| InlineMessageKind::Policy
| InlineMessageKind::Tool
| InlineMessageKind::Error
) && self.thinking_spinner.is_active
{
self.thinking_spinner.stop();
self.needs_redraw = true;
}
}
}