use ratatui::Frame;
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::ReviewDetail { .. }
| 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 current_tab = self.tabs.current();
let available_agent_clis = self.services.available_agent_clis();
let latest_available_version = self.latest_available_version.as_deref();
let session_progress_messages = &self.session_progress_messages;
let update_status = self.update_status.as_ref();
let wall_clock_unix_seconds =
session::unix_timestamp_from_system_time(self.sessions.state().now_system_time());
let status_bar_fyi_rotation_index =
u64::try_from(wall_clock_unix_seconds.div_euclid(60)).unwrap_or_default();
let review_comment_cache = self.services.review_comment_cache();
let requested_review_selected_index = self.requested_review_selected_index();
let mode = &self.mode;
let requested_review_table_state = &mut self.requested_review_table_state;
let project_render_parts = self.projects.render_parts();
let session_render_parts = self.sessions.render_parts();
let render_cache_store = &self.render_cache_store;
let settings = &mut self.settings;
let _theme_scope = style::scoped_active_theme(settings.theme);
ui::render(
frame,
ui::RenderContext {
active_project_id: project_render_parts.active_project_id,
available_agent_clis: &available_agent_clis,
current_tab,
git_branch: project_render_parts.git_branch,
diff_layout_cache: render_cache_store.diff_layout_cache(),
git_upstream_ref: project_render_parts.git_upstream_ref,
git_status: project_render_parts.git_status,
latest_available_version,
markdown_render_cache: render_cache_store.markdown_render_cache(),
update_status,
mode,
output_layout_cache: render_cache_store.session_output_layout_cache(),
project_table_state: project_render_parts.table_state,
projects: project_render_parts.project_items,
review_comment_cache: &review_comment_cache,
requested_reviews: &self.requested_reviews,
requested_review_selected_index,
requested_review_table_state,
active_prompt_outputs: session_render_parts.active_prompt_outputs,
session_branch_names: session_render_parts.session_branch_names,
session_git_statuses: session_render_parts.session_git_statuses,
session_index_by_id: session_render_parts.session_index_by_id,
session_progress_messages,
session_update_versions: &self.last_seen_session_update_versions,
session_worktree_availability: session_render_parts.session_worktree_availability,
settings,
system_log_tail_offset: self.system_log_tail_offset,
system_logs: &self.system_logs,
stats_activity: session_render_parts.stats_activity,
sessions: session_render_parts.sessions,
status_bar_fyi_rotation_index,
table_state: session_render_parts.table_state,
working_dir: project_render_parts.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
}
}