feat(ui): implement which-key popup for command completions
Implement `WhichKey` popup to show available command completions. - Added `format_key_label` to convert `KeyCode` to human-readable strings. - Added `binding_hints` to `Keymap` to extract available commands for a given prefix. - Added `src/ui/which_key.rs` for the widget implementation. - Updated `src/ui/mod.rs` to export the new module. Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
This commit is contained in:
@ -9,3 +9,4 @@ pub mod import_wizard_ui;
|
||||
pub mod panel;
|
||||
pub mod tile_bar;
|
||||
pub mod view_panel;
|
||||
pub mod which_key;
|
||||
|
||||
67
src/ui/which_key.rs
Normal file
67
src/ui/which_key.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Color, Modifier, Style},
|
||||
widgets::{Block, Borders, Clear, Widget},
|
||||
};
|
||||
|
||||
/// A compact popup showing available key completions after a prefix key,
|
||||
/// Emacs which-key style.
|
||||
pub struct WhichKeyWidget<'a> {
|
||||
hints: &'a [(String, &'static str)],
|
||||
}
|
||||
|
||||
impl<'a> WhichKeyWidget<'a> {
|
||||
pub fn new(hints: &'a [(String, &'static str)]) -> Self {
|
||||
Self { hints }
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for WhichKeyWidget<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
if self.hints.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Size: width fits the longest "key command" line, height = hint count + border
|
||||
let content_width = self
|
||||
.hints
|
||||
.iter()
|
||||
.map(|(k, cmd)| k.len() + 2 + cmd.len())
|
||||
.max()
|
||||
.unwrap_or(10);
|
||||
let popup_w = (content_width as u16 + 4).min(area.width); // +4 for border + padding
|
||||
let popup_h = (self.hints.len() as u16 + 2).min(area.height); // +2 for border
|
||||
|
||||
// Position: bottom-center, above the status bar
|
||||
let x = area.x + area.width.saturating_sub(popup_w) / 2;
|
||||
let y = area.y + area.height.saturating_sub(popup_h + 2); // 2 lines above bottom
|
||||
|
||||
let popup_area = Rect::new(x, y, popup_w, popup_h);
|
||||
Clear.render(popup_area, buf);
|
||||
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::DarkGray))
|
||||
.title(" which-key ");
|
||||
let inner = block.inner(popup_area);
|
||||
block.render(popup_area, buf);
|
||||
|
||||
let key_style = Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let cmd_style = Style::default().fg(Color::Gray);
|
||||
|
||||
for (i, (key_label, cmd_name)) in self.hints.iter().enumerate() {
|
||||
if i >= inner.height as usize {
|
||||
break;
|
||||
}
|
||||
let y = inner.y + i as u16;
|
||||
buf.set_string(inner.x + 1, y, key_label, key_style);
|
||||
let cmd_x = inner.x + 4; // fixed column for command names
|
||||
if cmd_x < inner.x + inner.width {
|
||||
buf.set_string(cmd_x, y, cmd_name, cmd_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user