pub(crate) mod confirm;
pub(crate) mod inline_edit;
pub(crate) mod key_routing;
pub(crate) mod palette_rows;
pub(crate) mod picker;
pub(crate) mod routing;
pub(crate) mod stack_state;
pub(crate) mod theme;
pub(crate) mod toast;
#[cfg(test)]
mod routing_live_tests;
#[cfg(test)]
mod stack_tests;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum OverlayKind {
Modal,
Popup,
Panel,
Mode,
}
impl OverlayKind {
pub(crate) const fn layer(self) -> u8 {
match self {
OverlayKind::Panel => 0,
OverlayKind::Mode => 1,
OverlayKind::Popup => 2,
OverlayKind::Modal => 3,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum OverlayId {
AiInspector,
ProfileDrawer,
CopyMode,
CustomActionPrefix,
Leader,
PluginOverlayFocus,
PaneHints,
ResizeMode,
DemotePick,
Help,
ClipboardHistory,
CommandHistory,
PasteSpecial,
Search,
CommandPalette,
AgentUsage,
PaneContextMenu,
TreePicker,
SessionPicker,
TabContextMenu,
ProfileLauncher,
AppMenu,
PaneRename,
SshConnect,
ShaderInstall,
Integrations,
RemoteShellInstall,
UpdateDialog,
TriggerConfirm,
AgentCommandConfirm,
CloseConfirm,
MuxLastTab,
QuitConfirm,
}
impl OverlayId {
pub(crate) const ALL: &'static [OverlayId] = &[
OverlayId::AiInspector,
OverlayId::ProfileDrawer,
OverlayId::CopyMode,
OverlayId::CustomActionPrefix,
OverlayId::Leader,
OverlayId::PluginOverlayFocus,
OverlayId::PaneHints,
OverlayId::ResizeMode,
OverlayId::DemotePick,
OverlayId::Help,
OverlayId::ClipboardHistory,
OverlayId::CommandHistory,
OverlayId::PasteSpecial,
OverlayId::Search,
OverlayId::CommandPalette,
OverlayId::AgentUsage,
OverlayId::PaneContextMenu,
OverlayId::TreePicker,
OverlayId::SessionPicker,
OverlayId::TabContextMenu,
OverlayId::ProfileLauncher,
OverlayId::AppMenu,
OverlayId::PaneRename,
OverlayId::SshConnect,
OverlayId::ShaderInstall,
OverlayId::Integrations,
OverlayId::RemoteShellInstall,
OverlayId::UpdateDialog,
OverlayId::TriggerConfirm,
OverlayId::AgentCommandConfirm,
OverlayId::CloseConfirm,
OverlayId::MuxLastTab,
OverlayId::QuitConfirm,
];
pub(crate) const fn kind(self) -> OverlayKind {
use OverlayId::*;
match self {
AiInspector | ProfileDrawer => OverlayKind::Panel,
CopyMode | CustomActionPrefix | Leader | PluginOverlayFocus | PaneHints
| ResizeMode | DemotePick => OverlayKind::Mode,
Help | ClipboardHistory | CommandHistory | PasteSpecial | Search | CommandPalette
| AgentUsage | PaneContextMenu | TreePicker | SessionPicker | TabContextMenu
| ProfileLauncher | AppMenu | PaneRename | SshConnect => OverlayKind::Popup,
ShaderInstall | Integrations | RemoteShellInstall | UpdateDialog | TriggerConfirm
| AgentCommandConfirm | CloseConfirm | MuxLastTab | QuitConfirm => OverlayKind::Modal,
}
}
pub(crate) const fn name(self) -> &'static str {
use OverlayId::*;
match self {
AiInspector => "ai_inspector",
ProfileDrawer => "profile_drawer",
CopyMode => "copy_mode",
CustomActionPrefix => "custom_action_prefix",
Leader => "leader",
PluginOverlayFocus => "plugin_overlay_focus",
PaneHints => "pane_hints",
ResizeMode => "resize_mode",
DemotePick => "demote_chooser",
Help => "help_ui",
ClipboardHistory => "clipboard_history_ui",
CommandHistory => "command_history_ui",
PasteSpecial => "paste_special_ui",
Search => "search_ui",
CommandPalette => "command_palette",
AgentUsage => "agent_usage_panel",
PaneContextMenu => "pane_context_menu",
TreePicker => "tree_picker_ui",
SessionPicker => "tmux_session_picker_ui",
TabContextMenu => "tab_context_menu",
ProfileLauncher => "profile_launcher",
AppMenu => "app_menu",
PaneRename => "pane_rename",
SshConnect => "ssh_connect_ui",
ShaderInstall => "shader_install_ui",
Integrations => "integrations_ui",
RemoteShellInstall => "remote_shell_install_ui",
UpdateDialog => "update_dialog",
TriggerConfirm => "trigger_confirm",
AgentCommandConfirm => "agent_command_confirm",
CloseConfirm => "close_confirmation_ui",
MuxLastTab => "mux_last_tab_ui",
QuitConfirm => "quit_confirmation_ui",
}
}
pub(crate) const fn guards_terminal(self) -> bool {
match self.kind() {
OverlayKind::Modal => true,
OverlayKind::Popup => !matches!(
self,
OverlayId::AppMenu | OverlayId::PasteSpecial | OverlayId::PaneRename
),
OverlayKind::Panel => false,
OverlayKind::Mode => matches!(self, OverlayId::DemotePick),
}
}
pub(crate) const fn may_hold_egui_focus(self) -> bool {
self.guards_terminal()
|| matches!(self.kind(), OverlayKind::Panel)
|| matches!(self, OverlayId::PaneRename | OverlayId::AppMenu)
}
pub(crate) const fn toggle_action(self) -> Option<&'static str> {
use OverlayId::*;
Some(match self {
AiInspector => "toggle_ai_inspector",
ProfileLauncher => "toggle_profile_drawer",
ProfileDrawer => "toggle_profiles_panel",
CopyMode => "toggle_copy_mode",
Help => "toggle_help",
ClipboardHistory => "toggle_clipboard_history",
CommandHistory => "toggle_command_history",
Search => "toggle_search",
CommandPalette => "toggle_command_palette",
AgentUsage => "toggle_agent_usage_panel",
TreePicker => "toggle_tree_picker",
SessionPicker => "toggle_session_picker",
AppMenu => "toggle_menu",
_ => return None,
})
}
pub(crate) fn opened_by_action(action: &str) -> Option<OverlayId> {
OverlayId::ALL
.iter()
.copied()
.find(|id| id.toggle_action() == Some(action))
.or(match action {
"ssh_quick_connect" => Some(OverlayId::SshConnect),
"paste_special" => Some(OverlayId::PasteSpecial),
_ => None,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct OverlayStack {
entries: Vec<OverlayId>,
}
impl OverlayStack {
pub(crate) fn from_open(open: impl IntoIterator<Item = OverlayId>) -> Self {
let mut entries: Vec<OverlayId> = open.into_iter().collect();
entries.sort_by_key(|id| id.kind().layer());
Self { entries }
}
pub(crate) fn entries(&self) -> &[OverlayId] {
&self.entries
}
pub(crate) fn top_key_owner(&self, panel_focused: bool) -> Option<OverlayId> {
self.entries
.iter()
.rev()
.copied()
.find(|id| id.kind() != OverlayKind::Panel || panel_focused)
}
pub(crate) fn guard_names(&self) -> Vec<String> {
self.entries
.iter()
.filter(|id| id.guards_terminal())
.map(|id| id.name().to_string())
.collect()
}
}