use super::types::{DemoteAction, DemoteSnapshot, PostRenderActions};
use crate::agent_commands_store::AgentCommandStore;
use crate::command_palette::CommandPalette;
use crate::config::Config;
use crate::crash_triage::CrashTriageState;
use crate::pane::{PaneBounds, SplitDirection};
use crate::status_bar::StatusBarUI;
use arc_swap::ArcSwap;
#[cfg(feature = "mux")]
use crate::app::tmux_handler::tmux_state::TmuxState;
pub(super) fn render_notifications(
ctx: &egui::Context,
overlay_state: &crate::app::window_state::OverlayState,
pane_hint_banner: Option<&str>,
top_inset: f32,
demote: (DemoteSnapshot, Option<PaneBounds>),
actions: &mut PostRenderActions,
) {
use crate::app::overlay::toast;
actions.toast_events = toast::render_toasts(ctx, &overlay_state.toasts, top_inset);
let mode_banner = pane_hint_banner.or(overlay_state.mode_banner);
crate::app::leader::which_key::render(ctx, overlay_state.which_key.as_ref());
let demote_banner = match demote.0 {
DemoteSnapshot::PickTab => Some("Click a tab to merge into · Esc cancels"),
DemoteSnapshot::PickPane => Some("Click a pane to merge into · Esc cancels"),
_ => None,
};
toast::render_mode_banner(ctx, demote_banner.or(mode_banner), top_inset);
render_demote_overlays(ctx, demote.0, demote.1, actions);
}
fn render_demote_overlays(
ctx: &egui::Context,
snapshot: DemoteSnapshot,
pane_bounds: Option<PaneBounds>,
actions: &mut PostRenderActions,
) {
match snapshot {
DemoteSnapshot::PickTab | DemoteSnapshot::PickPane => {}
DemoteSnapshot::ChooseDirection {
source_tab_id,
target_tab_id,
target_pane_id,
} => {
if let Some(bounds) = pane_bounds {
let center_x = bounds.x + bounds.width / 2.0;
let center_y = bounds.y + bounds.height / 2.0;
egui::Area::new(egui::Id::new("demote_direction_overlay"))
.fixed_pos(egui::pos2(center_x - 100.0, center_y - 30.0))
.order(egui::Order::Foreground)
.show(ctx, |ui| {
egui::Frame::NONE
.fill(egui::Color32::from_rgba_unmultiplied(30, 30, 30, 240))
.inner_margin(egui::Margin::symmetric(16, 10))
.corner_radius(8.0)
.stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(80, 80, 80)))
.show(ui, |ui| {
ui.style_mut().visuals.override_text_color =
Some(egui::Color32::from_rgb(255, 255, 255));
ui.vertical_centered(|ui| {
ui.label(egui::RichText::new("Split direction:").size(14.0));
ui.add_space(4.0);
ui.horizontal(|ui| {
if ui
.button(egui::RichText::new("Horizontal").size(14.0))
.clicked()
{
actions.demote = DemoteAction::Execute {
source_tab_id,
target_tab_id,
target_pane_id,
direction: SplitDirection::Horizontal,
};
}
if ui
.button(egui::RichText::new("Vertical").size(14.0))
.clicked()
{
actions.demote = DemoteAction::Execute {
source_tab_id,
target_tab_id,
target_pane_id,
direction: SplitDirection::Vertical,
};
}
});
});
});
});
}
}
DemoteSnapshot::Idle => {}
}
}
#[allow(clippy::too_many_arguments)] pub(super) fn open_command_palette_with_runtime_rows(
status_bar_ui: &StatusBarUI,
agent_commands: &AgentCommandStore,
crash_triage: &mut CrashTriageState,
config: &ArcSwap<Config>,
command_palette: &mut CommandPalette,
profiles: &crate::profile::ProfileManager,
keybinding_registry: &par_term_keybindings::KeybindingRegistry,
#[cfg(feature = "mux")] tmux_state: &TmuxState,
) {
let rows = crate::app::overlay::palette_rows::palette_runtime_rows(
status_bar_ui,
agent_commands,
crash_triage,
&config.load(),
profiles,
#[cfg(feature = "mux")]
tmux_state,
);
command_palette.open(rows, keybinding_registry);
}
pub(super) fn render_update_dialog(
ctx: &egui::Context,
update_state: &mut crate::app::window_state::UpdateState,
status_bar_ui: &mut StatusBarUI,
config: &ArcSwap<Config>,
actions: &mut PostRenderActions,
) {
if !update_state.show_dialog {
return;
}
if let Some(ref rx) = update_state.install_receiver
&& let Ok(result) = rx.try_recv()
{
match result {
Ok(update_result) => {
update_state.install_status = Some(format!(
"Updated to v{}! Restart par-term to use the new version.",
update_result.new_version
));
update_state.installing = false;
status_bar_ui.update_available_version = None;
}
Err(e) => {
update_state.install_status = Some(format!("Update failed: {}", e));
update_state.installing = false;
}
}
update_state.install_receiver = None;
}
if let Some(ref update_result) = update_state.last_result {
let dialog_action = crate::update_dialog::render(
ctx,
update_result,
env!("CARGO_PKG_VERSION"),
update_state.installation_type,
update_state.installing,
update_state.install_status.as_deref(),
);
match dialog_action {
crate::update_dialog::UpdateDialogAction::Dismiss => {
if !update_state.installing {
update_state.show_dialog = false;
update_state.install_status = None;
}
}
crate::update_dialog::UpdateDialogAction::SkipVersion(v) => {
config.rcu(|old| {
let mut new = (**old).clone();
new.updates.skipped_version = Some(v.clone());
std::sync::Arc::new(new)
});
update_state.show_dialog = false;
status_bar_ui.update_available_version = None;
update_state.install_status = None;
actions.save_config = true;
}
crate::update_dialog::UpdateDialogAction::InstallUpdate(v) => {
if !update_state.installing {
update_state.installing = true;
update_state.install_status = Some("Downloading update...".to_string());
let (tx, rx) = std::sync::mpsc::channel();
update_state.install_receiver = Some(rx);
let version = v.clone();
let current_version = crate::VERSION.to_string();
std::thread::spawn(move || {
let result = par_term_update::self_updater::perform_update(
&version,
¤t_version,
);
let _ = tx.send(result);
});
}
}
crate::update_dialog::UpdateDialogAction::None => {}
}
} else {
update_state.show_dialog = false;
}
}
pub(super) fn render_profile_launcher(
ctx: &mut egui::Ui,
overlay_ui: &mut crate::app::window_state::overlay_ui_state::OverlayUiState,
config: &Config,
custom_status_bar_height: f32,
) -> Option<crate::profile_launcher_ui::LauncherChoice> {
let popup = overlay_ui.profile_launcher_ui.show(ctx.ctx());
let bottom_margin =
if config.status_bar.status_bar_position == par_term_config::StatusBarPosition::Bottom {
custom_status_bar_height
} else {
0.0
};
let pinned = overlay_ui.profile_drawer_ui.render(
ctx,
&mut overlay_ui.profile_launcher_ui,
config,
bottom_margin,
);
popup.or(pinned)
}