aboutsummaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/keybind_help.rs143
-rw-r--r--src/ui/menus.rs65
-rw-r--r--src/ui/onboarding.rs2
-rw-r--r--src/ui/panes.rs5
-rw-r--r--src/ui/release_notes.rs10
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..];
}