diff options
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/keybind_help.rs | 143 | ||||
| -rw-r--r-- | src/ui/menus.rs | 65 | ||||
| -rw-r--r-- | src/ui/onboarding.rs | 2 | ||||
| -rw-r--r-- | src/ui/panes.rs | 5 | ||||
| -rw-r--r-- | src/ui/release_notes.rs | 10 |
5 files changed, 136 insertions, 89 deletions
diff --git a/src/ui/keybind_help.rs b/src/ui/keybind_help.rs index e7a76155..fe3d7582 100644 --- a/src/ui/keybind_help.rs +++ b/src/ui/keybind_help.rs @@ -14,8 +14,31 @@ use super::widgets::{ }; use crate::app::AppState; -fn optional_keybind_label(label: &Option<String>) -> String { - label.clone().unwrap_or_else(|| "unset".to_string()) +fn keybind_label(bindings: &crate::config::ActionKeybinds) -> String { + bindings.label().unwrap_or_else(|| "unset".to_string()) +} + +fn indexed_label(bindings: &[crate::config::IndexedKeybind]) -> String { + if bindings.is_empty() { + "unset".to_string() + } else if bindings.len() == 9 { + let first = &bindings[0].label; + if first.ends_with('1') { + format!("{}1..9", first.trim_end_matches('1')) + } else { + bindings + .iter() + .map(|binding| binding.label.clone()) + .collect::<Vec<_>>() + .join(" / ") + } + } else { + bindings + .iter() + .map(|binding| binding.label.clone()) + .collect::<Vec<_>>() + .join(" / ") + } } pub(super) fn keybind_help_groups( @@ -29,12 +52,16 @@ pub(super) fn keybind_help_groups( vec![ ( crate::config::format_key_combo((app.prefix_code, app.prefix_mods)), - "navigate mode", + "prefix mode", ), - ("prefix + ?".to_string(), "keybinds"), + (keybind_label(&kb.help), "keybinds"), + (keybind_label(&kb.settings), "settings"), + (keybind_label(&kb.quit), "quit"), + (keybind_label(&kb.detach), "detach from server"), + (keybind_label(&kb.reload_config), "reload config"), ( - optional_keybind_label(&kb.reload_config_label), - "reload config", + keybind_label(&kb.open_notification_target), + "open notification target", ), ], )); @@ -47,85 +74,47 @@ pub(super) fn keybind_help_groups( ("h j k l / arrows".to_string(), "move focus"), ("tab / shift+tab".to_string(), "cycle pane"), ("enter".to_string(), "open workspace"), - ("s".to_string(), "settings"), - ("q".to_string(), "quit"), + ("1..9".to_string(), "switch workspace"), ], )); - let mut workspace_tab = vec![ - (kb.new_workspace_label.clone(), "new workspace"), - (kb.rename_workspace_label.clone(), "rename workspace"), - (kb.close_workspace_label.clone(), "close workspace"), - ( - optional_keybind_label(&kb.open_notification_target_label), - "open notification target", - ), - ( - optional_keybind_label(&kb.previous_workspace_label), - "previous workspace", - ), - ( - optional_keybind_label(&kb.next_workspace_label), - "next workspace", - ), - ( - optional_keybind_label(&kb.indexed_workspaces_label), - "switch workspace 1-9", - ), - ( - optional_keybind_label(&kb.previous_agent_label), - "previous agent", - ), - (optional_keybind_label(&kb.next_agent_label), "next agent"), - ( - optional_keybind_label(&kb.indexed_agents_label), - "focus agent 1-9", - ), - (kb.new_tab_label.clone(), "new tab"), - (optional_keybind_label(&kb.rename_tab_label), "rename tab"), - ( - optional_keybind_label(&kb.previous_tab_label), - "previous tab", - ), - (optional_keybind_label(&kb.next_tab_label), "next tab"), - ( - optional_keybind_label(&kb.indexed_tabs_label), - "switch tab 1-9", - ), - (optional_keybind_label(&kb.close_tab_label), "close tab"), + let workspace_tab = vec![ + (keybind_label(&kb.workspace_picker), "workspace navigation"), + (keybind_label(&kb.new_workspace), "new workspace"), + (keybind_label(&kb.rename_workspace), "rename workspace"), + (keybind_label(&kb.close_workspace), "close workspace"), + (keybind_label(&kb.previous_workspace), "previous workspace"), + (keybind_label(&kb.next_workspace), "next workspace"), + (indexed_label(&kb.switch_workspace), "switch workspace 1-9"), + (keybind_label(&kb.previous_agent), "previous agent"), + (keybind_label(&kb.next_agent), "next agent"), + (indexed_label(&kb.focus_agent), "focus agent 1-9"), + (keybind_label(&kb.new_tab), "new tab"), + (keybind_label(&kb.rename_tab), "rename tab"), + (keybind_label(&kb.previous_tab), "previous tab"), + (keybind_label(&kb.next_tab), "next tab"), + (indexed_label(&kb.switch_tab), "switch tab 1-9"), + (keybind_label(&kb.close_tab), "close tab"), ]; - if let Some(label) = &kb.detach_label { - workspace_tab.insert(3, (label.clone(), "detach from server")); - } groups.push(("workspaces / tabs", workspace_tab)); let panes = vec![ - (kb.split_vertical_label.clone(), "split vertical"), - (kb.split_horizontal_label.clone(), "split horizontal"), - (kb.close_pane_label.clone(), "close pane"), - (optional_keybind_label(&kb.rename_pane_label), "rename pane"), - ( - optional_keybind_label(&kb.edit_scrollback_label), - "edit scrollback", - ), - (kb.zoom_label.clone(), "zoom pane"), - (kb.resize_mode_label.clone(), "resize mode"), - (kb.toggle_sidebar_label.clone(), "toggle sidebar"), - ( - optional_keybind_label(&kb.focus_pane_left_label), - "focus pane left", - ), - ( - optional_keybind_label(&kb.focus_pane_down_label), - "focus pane down", - ), - ( - optional_keybind_label(&kb.focus_pane_up_label), - "focus pane up", - ), + (keybind_label(&kb.split_vertical), "split vertical"), + (keybind_label(&kb.split_horizontal), "split horizontal"), + (keybind_label(&kb.close_pane), "close pane"), + (keybind_label(&kb.rename_pane), "rename pane"), + (keybind_label(&kb.edit_scrollback), "edit scrollback"), + (keybind_label(&kb.zoom), "zoom pane"), + (keybind_label(&kb.resize_mode), "resize mode"), + (keybind_label(&kb.toggle_sidebar), "toggle sidebar"), + (keybind_label(&kb.focus_pane_left), "focus pane left"), + (keybind_label(&kb.focus_pane_down), "focus pane down"), + (keybind_label(&kb.focus_pane_up), "focus pane up"), + (keybind_label(&kb.focus_pane_right), "focus pane right"), + (keybind_label(&kb.cycle_pane_next), "cycle pane next"), ( - optional_keybind_label(&kb.focus_pane_right_label), - "focus pane right", + keybind_label(&kb.cycle_pane_previous), + "cycle pane previous", ), ]; groups.push(("panes", panes)); diff --git a/src/ui/menus.rs b/src/ui/menus.rs index 7a074c68..d17aa897 100644 --- a/src/ui/menus.rs +++ b/src/ui/menus.rs @@ -9,6 +9,12 @@ use ratatui::{ use super::widgets::{panel_contrast_fg, render_panel_shell}; use crate::app::AppState; +fn prefix_rhs_label(bindings: &crate::config::ActionKeybinds) -> String { + bindings + .prefix_rhs_label() + .unwrap_or_else(|| "unset".to_string()) +} + fn render_bottom_bar(frame: &mut Frame, area: Rect, line: Line<'_>, bg: ratatui::style::Color) { frame.render_widget(Clear, area); let buf = frame.buffer_mut(); @@ -18,6 +24,38 @@ fn render_bottom_bar(frame: &mut Frame, area: Rect, line: Line<'_>, bg: ratatui: frame.render_widget(Paragraph::new(line), area); } +pub(super) fn render_prefix_overlay(app: &AppState, frame: &mut Frame, area: Rect) { + let key = Style::default() + .fg(app.palette.accent) + .add_modifier(Modifier::BOLD); + let dim = Style::default().fg(app.palette.overlay0); + let mode_style = Style::default() + .fg(panel_contrast_fg(&app.palette)) + .bg(app.palette.accent) + .add_modifier(Modifier::BOLD); + + let workspace_picker = prefix_rhs_label(&app.keybinds.workspace_picker); + let help = prefix_rhs_label(&app.keybinds.help); + let prefix = crate::config::format_key_combo((app.prefix_code, app.prefix_mods)); + + let line = Line::from(vec![ + Span::styled(" PREFIX ", mode_style), + Span::raw(" "), + Span::styled("esc", key), + Span::styled(" cancel ", dim), + Span::styled(prefix, key), + Span::styled(" send prefix ", dim), + Span::styled(workspace_picker, key), + Span::styled(" workspace nav ", dim), + Span::styled(help, key), + Span::styled(" keybinds", dim), + ]); + + let overlay_y = area.y + area.height.saturating_sub(1); + let overlay_area = Rect::new(area.x, overlay_y, area.width, 1); + render_bottom_bar(frame, overlay_area, line, app.palette.panel_bg); +} + pub(super) fn render_navigate_overlay(app: &AppState, frame: &mut Frame, area: Rect) { let key = Style::default() .fg(app.palette.accent) @@ -30,6 +68,15 @@ pub(super) fn render_navigate_overlay(app: &AppState, frame: &mut Frame, area: R .add_modifier(Modifier::BOLD); let kb = &app.keybinds; + let new_tab = prefix_rhs_label(&kb.new_tab); + let split_vertical = prefix_rhs_label(&kb.split_vertical); + let split_horizontal = prefix_rhs_label(&kb.split_horizontal); + let close_pane = prefix_rhs_label(&kb.close_pane); + let zoom = prefix_rhs_label(&kb.zoom); + let resize = prefix_rhs_label(&kb.resize_mode); + let help = prefix_rhs_label(&kb.help); + let settings = prefix_rhs_label(&kb.settings); + let quit = prefix_rhs_label(&kb.quit); let line = Line::from(vec![ Span::styled(" NAVIGATE ", mode_style), Span::raw(" "), @@ -39,23 +86,23 @@ pub(super) fn render_navigate_overlay(app: &AppState, frame: &mut Frame, area: R Span::styled(" ws ", dim), Span::styled("⇥", key), Span::styled(" pane ", dim), - Span::styled(kb.new_tab_label.as_str(), key), + Span::styled(new_tab, key), Span::styled(" new tab ", dim), - Span::styled(kb.split_vertical_label.as_str(), key), + Span::styled(split_vertical, key), Span::styled(" split│ ", dim), - Span::styled(kb.split_horizontal_label.as_str(), key), + Span::styled(split_horizontal, key), Span::styled(" split─ ", dim), - Span::styled(kb.close_pane_label.as_str(), key), + Span::styled(close_pane, key), Span::styled(" close ", dim), - Span::styled(kb.zoom_label.as_str(), key), + Span::styled(zoom, key), Span::styled(" zoom ", dim), - Span::styled(kb.resize_mode_label.as_str(), key), + Span::styled(resize, key), Span::styled(" resize ", dim), - Span::styled("?", key), + Span::styled(help, key), Span::styled(" keybinds ", dim), - Span::styled("s", key), + Span::styled(settings, key), Span::styled(" settings ", dim), - Span::styled("q", key), + Span::styled(quit, key), Span::styled(" quit", dim), ]); diff --git a/src/ui/onboarding.rs b/src/ui/onboarding.rs index 104f737f..8829acc7 100644 --- a/src/ui/onboarding.rs +++ b/src/ui/onboarding.rs @@ -78,7 +78,7 @@ fn render_onboarding_welcome(app: &AppState, frame: &mut Frame, area: Rect) { .add_modifier(Modifier::BOLD), ), Span::styled( - " enters navigate mode · ", + " enters prefix mode · ", Style::default().fg(app.palette.overlay1), ), Span::styled( diff --git a/src/ui/panes.rs b/src/ui/panes.rs index d9108575..550d34ce 100644 --- a/src/ui/panes.rs +++ b/src/ui/panes.rs @@ -355,7 +355,10 @@ fn render_empty(app: &AppState, frame: &mut Frame, area: Rect) { Line::from(vec![ Span::styled(" Press ", Style::default().fg(p.overlay0)), Span::styled( - app.keybinds.new_workspace_label.to_string(), + app.keybinds + .new_workspace + .label() + .unwrap_or_else(|| "unset".to_string()), Style::default().fg(p.accent).add_modifier(Modifier::BOLD), ), Span::styled(" to create one", Style::default().fg(p.overlay0)), diff --git a/src/ui/release_notes.rs b/src/ui/release_notes.rs index 9d069828..e6d20885 100644 --- a/src/ui/release_notes.rs +++ b/src/ui/release_notes.rs @@ -281,7 +281,15 @@ fn release_notes_inline_spans<'a>( let (code, after_end) = after_start.split_at(end); width += code.chars().count(); if !code.is_empty() { - spans.push(Span::styled(code.to_string(), code_style)); + // Keep short config examples together when Paragraph wraps. + // Snippets like `new_tab = "prefix+c"` read poorly when they + // split at the spaces around `=` in narrow announcement modals. + let display_code = if code.contains('=') { + code.replace(' ', "\u{00a0}") + } else { + code.to_string() + }; + spans.push(Span::styled(display_code, code_style)); } remaining = &after_end[1..]; } |
