feat(ui): simplify AppMode minibuffer handling and panel rendering
Refactor AppMode to use MinibufferConfig for all text-entry modes. Update command implementations to use new mode constructors. Introduce PanelContent trait and replace panel structs with content types. Adjust rendering to use Panel::new and minibuffer configuration. Update imports and add MinibufferConfig struct. No functional changes; all behavior preserved. Co-Authored-By: fiddlerwoaroof/git-smart-commit (bartowski/nvidia_Nemotron-Cascade-2-30B-A3B-GGUF)
This commit is contained in:
@ -2,12 +2,12 @@ use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Color, Modifier, Style},
|
||||
widgets::{Block, Borders, Widget},
|
||||
};
|
||||
|
||||
use crate::model::Model;
|
||||
use crate::ui::app::AppMode;
|
||||
use crate::ui::cat_tree::{build_cat_tree, CatTreeEntry};
|
||||
use crate::ui::panel::PanelContent;
|
||||
use crate::view::Axis;
|
||||
|
||||
fn axis_display(axis: Axis) -> (&'static str, Color) {
|
||||
@ -19,112 +19,89 @@ fn axis_display(axis: Axis) -> (&'static str, Color) {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CategoryPanel<'a> {
|
||||
pub model: &'a Model,
|
||||
pub mode: &'a AppMode,
|
||||
pub cursor: usize,
|
||||
pub expanded: &'a std::collections::HashSet<String>,
|
||||
pub struct CategoryContent<'a> {
|
||||
model: &'a Model,
|
||||
tree: Vec<CatTreeEntry>,
|
||||
}
|
||||
|
||||
impl<'a> CategoryPanel<'a> {
|
||||
pub fn new(
|
||||
model: &'a Model,
|
||||
mode: &'a AppMode,
|
||||
cursor: usize,
|
||||
expanded: &'a std::collections::HashSet<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
model,
|
||||
mode,
|
||||
cursor,
|
||||
expanded,
|
||||
}
|
||||
impl<'a> CategoryContent<'a> {
|
||||
pub fn new(model: &'a Model, expanded: &'a std::collections::HashSet<String>) -> Self {
|
||||
let tree = build_cat_tree(model, expanded);
|
||||
Self { model, tree }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for CategoryPanel<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let is_item_add = matches!(self.mode, AppMode::ItemAdd { .. });
|
||||
let is_cat_add = matches!(self.mode, AppMode::CategoryAdd { .. });
|
||||
let is_active = matches!(self.mode, AppMode::CategoryPanel) || is_item_add || is_cat_add;
|
||||
impl PanelContent for CategoryContent<'_> {
|
||||
fn is_active(&self, mode: &AppMode) -> bool {
|
||||
matches!(
|
||||
mode,
|
||||
AppMode::CategoryPanel | AppMode::ItemAdd { .. } | AppMode::CategoryAdd { .. }
|
||||
)
|
||||
}
|
||||
|
||||
let (border_color, title) = if is_active {
|
||||
(Color::Cyan, " Categories ")
|
||||
fn active_color(&self) -> Color {
|
||||
Color::Cyan
|
||||
}
|
||||
|
||||
fn title(&self) -> &str {
|
||||
" Categories "
|
||||
}
|
||||
|
||||
fn item_count(&self) -> usize {
|
||||
self.tree.len()
|
||||
}
|
||||
|
||||
fn empty_message(&self) -> &str {
|
||||
"(no categories — use :add-cat <name>)"
|
||||
}
|
||||
|
||||
fn render_item(&self, index: usize, is_selected: bool, inner: Rect, buf: &mut Buffer) {
|
||||
let y = inner.y + index as u16;
|
||||
let view = self.model.active_view();
|
||||
|
||||
let base_style = if is_selected {
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
(Color::DarkGray, " Categories ")
|
||||
Style::default()
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(border_color))
|
||||
.title(title);
|
||||
let inner = block.inner(area);
|
||||
block.render(area, buf);
|
||||
|
||||
let view = self.model.active_view();
|
||||
let tree = build_cat_tree(self.model, self.expanded);
|
||||
|
||||
if tree.is_empty() {
|
||||
buf.set_string(
|
||||
inner.x,
|
||||
inner.y,
|
||||
"(no categories — use :add-cat <name>)",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
);
|
||||
return;
|
||||
if is_selected {
|
||||
let fill = " ".repeat(inner.width as usize);
|
||||
buf.set_string(inner.x, y, &fill, base_style);
|
||||
}
|
||||
|
||||
for (i, entry) in tree.iter().enumerate() {
|
||||
if i as u16 >= inner.height {
|
||||
break;
|
||||
}
|
||||
let y = inner.y + i as u16;
|
||||
let is_selected = i == self.cursor && is_active;
|
||||
match &self.tree[index] {
|
||||
CatTreeEntry::Category {
|
||||
name,
|
||||
item_count,
|
||||
expanded,
|
||||
} => {
|
||||
let indicator = if *expanded { "▼" } else { "▶" };
|
||||
let (axis_str, axis_color) = axis_display(view.axis_of(name));
|
||||
let name_part = format!("{indicator} {name} ({item_count})");
|
||||
let axis_part = format!(" [{axis_str}]");
|
||||
|
||||
let base_style = if is_selected {
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default()
|
||||
};
|
||||
|
||||
if is_selected {
|
||||
let fill = " ".repeat(inner.width as usize);
|
||||
buf.set_string(inner.x, y, &fill, base_style);
|
||||
}
|
||||
|
||||
match entry {
|
||||
CatTreeEntry::Category {
|
||||
name,
|
||||
item_count,
|
||||
expanded,
|
||||
} => {
|
||||
let indicator = if *expanded { "▼" } else { "▶" };
|
||||
let (axis_str, axis_color) = axis_display(view.axis_of(name));
|
||||
let name_part = format!("{indicator} {name} ({item_count})");
|
||||
let axis_part = format!(" [{axis_str}]");
|
||||
|
||||
buf.set_string(inner.x, y, &name_part, base_style);
|
||||
if name_part.len() + axis_part.len() < inner.width as usize {
|
||||
buf.set_string(
|
||||
inner.x + name_part.len() as u16,
|
||||
y,
|
||||
&axis_part,
|
||||
if is_selected {
|
||||
base_style
|
||||
} else {
|
||||
Style::default().fg(axis_color)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
CatTreeEntry::Item { item_name, .. } => {
|
||||
let label = format!(" · {item_name}");
|
||||
buf.set_string(inner.x, y, &label, base_style);
|
||||
buf.set_string(inner.x, y, &name_part, base_style);
|
||||
if name_part.len() + axis_part.len() < inner.width as usize {
|
||||
buf.set_string(
|
||||
inner.x + name_part.len() as u16,
|
||||
y,
|
||||
&axis_part,
|
||||
if is_selected {
|
||||
base_style
|
||||
} else {
|
||||
Style::default().fg(axis_color)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
CatTreeEntry::Item { item_name, .. } => {
|
||||
let label = format!(" · {item_name}");
|
||||
buf.set_string(inner.x, y, &label, base_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user