use ratatui::Frame;
use super::roadmap::ActiveProjectRoadmap;
use super::state::{App, UpdateStatus};
use crate::app::session;
use crate::app::tab::Tab;
use crate::domain::session::{PublishedBranchSyncStatus, Session, Status};
use crate::ui;
use crate::ui::state::app_mode::{AppMode, ConfirmationViewMode, HelpContext};
use crate::ui::style;
impl App {
pub fn active_project_id(&self) -> i64 {
self.projects.active_project_id()
}
pub fn working_dir(&self) -> &std::path::Path {
self.projects.working_dir()
}
pub fn git_branch(&self) -> Option<&str> {
self.projects.git_branch()
}
pub fn git_upstream_ref(&self) -> Option<&str> {
self.projects.git_upstream_ref()
}
pub fn git_status_info(&self) -> Option<(u32, u32)> {
self.projects.git_status()
}
pub(crate) fn prompt_slash_state(&self) -> crate::ui::state::prompt::PromptSlashState {
crate::ui::state::prompt::PromptSlashState::with_available_agent_kinds(
self.services.available_agent_kinds(),
)
}
pub fn latest_available_version(&self) -> Option<&str> {
self.latest_available_version.as_deref()
}
pub fn update_status(&self) -> Option<&UpdateStatus> {
self.update_status.as_ref()
}
pub(crate) fn has_visible_tick_driven_ui(&self) -> bool {
match &self.mode {
AppMode::List
| AppMode::SessionCreation { .. }
| AppMode::Confirmation { .. }
| AppMode::SyncBlockedPopup { .. } => {
self.list_background_has_tick_driven_ui()
|| matches!(
&self.mode,
AppMode::SyncBlockedPopup {
is_loading: true,
..
}
)
}
AppMode::View { session_id, .. }
| AppMode::Prompt { session_id, .. }
| AppMode::Question { session_id, .. }
| AppMode::OpenCommandSelector {
restore_view: ConfirmationViewMode { session_id, .. },
..
}
| AppMode::PublishBranchInput {
restore_view: ConfirmationViewMode { session_id, .. },
..
} => self.session_has_tick_driven_ui(session_id),
AppMode::ViewInfoPopup {
is_loading,
restore_view,
..
} => *is_loading || self.session_has_tick_driven_ui(&restore_view.session_id),
AppMode::Diff { .. } => false,
AppMode::Help { context, .. } => self.help_overlay_has_tick_driven_ui(context),
}
}
pub fn draw(&mut self, frame: &mut Frame) {
let has_tasks_tab = self.active_project_has_tasks_tab();
self.tabs.normalize(has_tasks_tab);
let active_project_id = self.projects.active_project_id();
let current_tab = self.tabs.current();
let working_dir = self.projects.working_dir().to_path_buf();
let git_branch = self.projects.git_branch().map(str::to_string);
let git_upstream_ref = self.projects.git_upstream_ref().map(str::to_string);
let git_status = self.projects.git_status();
let latest_available_version = self.latest_available_version.as_deref().map(str::to_string);
let task_roadmap = self.active_project_roadmap.as_ref().and_then(|roadmap| {
if let ActiveProjectRoadmap::Loaded(content) = roadmap {
return Some(content.clone());
}
None
});
let task_roadmap_error = self.active_project_roadmap.as_ref().and_then(|roadmap| {
if let ActiveProjectRoadmap::LoadError(message) = roadmap {
return Some(message.clone());
}
None
});
let session_git_statuses = self.sessions.session_git_statuses().clone();
let session_branch_names = self.sessions.session_branch_names().clone();
let session_index_by_id = self.sessions.state().session_index_by_id().clone();
let session_worktree_availability = self.sessions.session_worktree_availability().clone();
let active_prompt_outputs = self.sessions.active_prompt_outputs().clone();
let session_progress_messages = self.session_progress_messages.clone();
let update_status = self.update_status().cloned();
let wall_clock_unix_seconds =
session::unix_timestamp_from_system_time(self.sessions.state().clock.now_system_time());
let status_bar_fyi_rotation_index =
u64::try_from(wall_clock_unix_seconds.div_euclid(60)).unwrap_or_default();
let projects = self.projects.project_items().to_vec();
let review_comment_cache = self.services.review_comment_cache();
let mode = &self.mode;
let project_table_state = self.projects.project_table_state_mut();
let (sessions, stats_activity, table_state) = self.sessions.render_parts();
let settings = &mut self.settings;
let _theme_scope = style::scoped_active_theme(settings.theme);
ui::render(
frame,
ui::RenderContext {
active_project_id,
current_tab,
has_tasks_tab,
git_branch: git_branch.as_deref(),
git_upstream_ref: git_upstream_ref.as_deref(),
git_status,
latest_available_version: latest_available_version.as_deref(),
markdown_render_cache: &self.markdown_render_cache,
update_status: update_status.as_ref(),
mode,
output_layout_cache: &self.session_output_layout_cache,
project_table_state,
projects: &projects,
review_comment_cache: &review_comment_cache,
task_roadmap: task_roadmap.as_deref(),
task_roadmap_error: task_roadmap_error.as_deref(),
task_roadmap_scroll_offset: self.task_roadmap_scroll_offset,
active_prompt_outputs: &active_prompt_outputs,
session_branch_names: &session_branch_names,
session_git_statuses: &session_git_statuses,
session_index_by_id: &session_index_by_id,
session_progress_messages: &session_progress_messages,
session_update_versions: &self.last_seen_session_update_versions,
session_worktree_availability: &session_worktree_availability,
settings,
stats_activity,
sessions,
status_bar_fyi_rotation_index,
table_state,
working_dir: &working_dir,
wall_clock_unix_seconds,
},
);
}
fn list_background_has_tick_driven_ui(&self) -> bool {
self.tabs.current() == Tab::Sessions
&& self
.sessions
.state()
.sessions
.iter()
.any(Self::session_tick_driven_ui_active)
}
fn help_overlay_has_tick_driven_ui(&self, context: &HelpContext) -> bool {
match context {
HelpContext::List { .. } => self.list_background_has_tick_driven_ui(),
HelpContext::View { session_id, .. } => self.session_has_tick_driven_ui(session_id),
HelpContext::Diff { .. } => false,
}
}
fn session_has_tick_driven_ui(&self, session_id: &str) -> bool {
self.sessions
.state()
.session_for_id(session_id)
.is_some_and(Self::session_tick_driven_ui_active)
}
fn session_tick_driven_ui_active(session: &Session) -> bool {
session.in_progress_started_at.is_some()
|| matches!(
session.status,
Status::AgentReview | Status::InProgress | Status::Merging | Status::Rebasing
)
|| session.published_branch_sync_status == PublishedBranchSyncStatus::InProgress
}
}