chore: reformat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-03-31 00:07:22 -07:00
parent 37584670eb
commit 183b2350f7
17 changed files with 1112 additions and 471 deletions

View File

@ -6,14 +6,14 @@ use ratatui::{
};
use crate::model::Model;
use crate::view::Axis;
use crate::ui::app::AppMode;
use crate::view::Axis;
fn axis_display(axis: Axis) -> (&'static str, Color) {
match axis {
Axis::Row => ("Row ↕", Color::Green),
Axis::Row => ("Row ↕", Color::Green),
Axis::Column => ("Col ↔", Color::Blue),
Axis::Page => ("Page ☰", Color::Magenta),
Axis::Page => ("Page ☰", Color::Magenta),
}
}
@ -25,20 +25,30 @@ pub struct CategoryPanel<'a> {
impl<'a> CategoryPanel<'a> {
pub fn new(model: &'a Model, mode: &'a AppMode, cursor: usize) -> Self {
Self { model, mode, cursor }
Self {
model,
mode,
cursor,
}
}
}
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;
let is_cat_add = matches!(self.mode, AppMode::CategoryAdd { .. });
let is_active = matches!(self.mode, AppMode::CategoryPanel) || is_item_add || is_cat_add;
let (border_color, title) = if is_cat_add {
(Color::Yellow, " Categories — New category (Enter:add Esc:done) ")
(
Color::Yellow,
" Categories — New category (Enter:add Esc:done) ",
)
} else if is_item_add {
(Color::Green, " Categories — Adding items (Enter:add Esc:done) ")
(
Color::Green,
" Categories — Adding items (Enter:add Esc:done) ",
)
} else if is_active {
(Color::Cyan, " Categories n:new a:add-items Space:axis ")
} else {
@ -56,9 +66,12 @@ impl<'a> Widget for CategoryPanel<'a> {
let cat_names: Vec<&str> = self.model.category_names();
if cat_names.is_empty() {
buf.set_string(inner.x, inner.y,
buf.set_string(
inner.x,
inner.y,
"(no categories — use :add-cat <name>)",
Style::default().fg(Color::DarkGray));
Style::default().fg(Color::DarkGray),
);
return;
}
@ -67,24 +80,35 @@ impl<'a> Widget for CategoryPanel<'a> {
let list_height = inner.height.saturating_sub(prompt_rows);
for (i, cat_name) in cat_names.iter().enumerate() {
if i as u16 >= list_height { break; }
if i as u16 >= list_height {
break;
}
let y = inner.y + i as u16;
let (axis_str, axis_color) = axis_display(view.axis_of(cat_name));
let item_count = self.model.category(cat_name).map(|c| c.items.len()).unwrap_or(0);
let item_count = self
.model
.category(cat_name)
.map(|c| c.items.len())
.unwrap_or(0);
// Highlight the selected category both in CategoryPanel and ItemAdd modes
let is_selected_cat = if is_item_add {
if let AppMode::ItemAdd { category, .. } = self.mode {
*cat_name == category.as_str()
} else { false }
} else {
false
}
} else {
i == self.cursor && is_active
};
let base_style = if is_selected_cat {
Style::default().fg(Color::Black).bg(Color::Cyan).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Black)
.bg(Color::Cyan)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
};
@ -99,19 +123,23 @@ impl<'a> Widget for CategoryPanel<'a> {
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_cat { base_style } else { Style::default().fg(axis_color) });
buf.set_string(
inner.x + name_part.len() as u16,
y,
&axis_part,
if is_selected_cat {
base_style
} else {
Style::default().fg(axis_color)
},
);
}
}
// Inline prompt at the bottom for CategoryAdd or ItemAdd
let (prompt_color, prompt_text) = match self.mode {
AppMode::CategoryAdd { buffer } => {
(Color::Yellow, format!(" + category: {buffer}"))
}
AppMode::ItemAdd { buffer, .. } => {
(Color::Green, format!(" + item: {buffer}"))
}
AppMode::CategoryAdd { buffer } => (Color::Yellow, format!(" + category: {buffer}")),
AppMode::ItemAdd { buffer, .. } => (Color::Green, format!(" + item: {buffer}")),
_ => return,
};
@ -122,8 +150,14 @@ impl<'a> Widget for CategoryPanel<'a> {
buf.set_string(inner.x, sep_y, &sep, Style::default().fg(prompt_color));
}
if prompt_y < inner.y + inner.height {
buf.set_string(inner.x, prompt_y, &prompt_text,
Style::default().fg(prompt_color).add_modifier(Modifier::BOLD));
buf.set_string(
inner.x,
prompt_y,
&prompt_text,
Style::default()
.fg(prompt_color)
.add_modifier(Modifier::BOLD),
);
}
}
}

View File

@ -16,13 +16,20 @@ pub struct FormulaPanel<'a> {
impl<'a> FormulaPanel<'a> {
pub fn new(model: &'a Model, mode: &'a AppMode, cursor: usize) -> Self {
Self { model, mode, cursor }
Self {
model,
mode,
cursor,
}
}
}
impl<'a> Widget for FormulaPanel<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
let is_active = matches!(self.mode, AppMode::FormulaPanel | AppMode::FormulaEdit { .. });
let is_active = matches!(
self.mode,
AppMode::FormulaPanel | AppMode::FormulaEdit { .. }
);
let border_style = if is_active {
Style::default().fg(Color::Yellow)
} else {
@ -39,17 +46,25 @@ impl<'a> Widget for FormulaPanel<'a> {
let formulas = self.model.formulas();
if formulas.is_empty() {
buf.set_string(inner.x, inner.y,
buf.set_string(
inner.x,
inner.y,
"(no formulas — press 'n' to add)",
Style::default().fg(Color::DarkGray));
Style::default().fg(Color::DarkGray),
);
return;
}
for (i, formula) in formulas.iter().enumerate() {
if inner.y + i as u16 >= inner.y + inner.height { break; }
if inner.y + i as u16 >= inner.y + inner.height {
break;
}
let is_selected = i == self.cursor && is_active;
let style = if is_selected {
Style::default().fg(Color::Black).bg(Color::Yellow).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Black)
.bg(Color::Yellow)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(Color::Green)
};
@ -65,9 +80,12 @@ impl<'a> Widget for FormulaPanel<'a> {
// Formula edit mode
if let AppMode::FormulaEdit { buffer } = self.mode {
let y = inner.y + inner.height.saturating_sub(2);
buf.set_string(inner.x, y,
buf.set_string(
inner.x,
y,
"┄ Enter formula (Name = expr): ",
Style::default().fg(Color::Yellow));
Style::default().fg(Color::Yellow),
);
let y = y + 1;
let prompt = format!("> {buffer}");
buf.set_string(inner.x, y, &prompt, Style::default().fg(Color::Green));

View File

@ -5,8 +5,8 @@ use ratatui::{
widgets::{Block, Borders, Clear, Widget},
};
use crate::import::wizard::{ImportWizard, WizardStep};
use crate::import::analyzer::FieldKind;
use crate::import::wizard::{ImportWizard, WizardStep};
pub struct ImportWizardWidget<'a> {
pub wizard: &'a ImportWizard,
@ -52,20 +52,31 @@ impl<'a> Widget for ImportWizardWidget<'a> {
let summary = self.wizard.pipeline.preview_summary();
buf.set_string(x, y, truncate(&summary, w), Style::default());
y += 2;
buf.set_string(x, y,
buf.set_string(
x,
y,
"Press Enter to continue\u{2026}",
Style::default().fg(Color::Yellow));
Style::default().fg(Color::Yellow),
);
}
WizardStep::SelectArrayPath => {
buf.set_string(x, y,
buf.set_string(
x,
y,
"Select the path containing records:",
Style::default().fg(Color::Yellow));
Style::default().fg(Color::Yellow),
);
y += 1;
for (i, path) in self.wizard.pipeline.array_paths.iter().enumerate() {
if y >= inner.y + inner.height { break; }
if y >= inner.y + inner.height {
break;
}
let is_sel = i == self.wizard.cursor;
let style = if is_sel {
Style::default().fg(Color::Black).bg(Color::Magenta).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Black)
.bg(Color::Magenta)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
};
@ -74,19 +85,36 @@ impl<'a> Widget for ImportWizardWidget<'a> {
y += 1;
}
y += 1;
buf.set_string(x, y, "\u{2191}\u{2193} select Enter confirm", Style::default().fg(Color::DarkGray));
buf.set_string(
x,
y,
"\u{2191}\u{2193} select Enter confirm",
Style::default().fg(Color::DarkGray),
);
}
WizardStep::ReviewProposals => {
buf.set_string(x, y,
buf.set_string(
x,
y,
"Review field proposals (Space toggle, c cycle kind):",
Style::default().fg(Color::Yellow));
Style::default().fg(Color::Yellow),
);
y += 1;
let header = format!(" {:<20} {:<22} {:<6}", "Field", "Kind", "Accept");
buf.set_string(x, y, truncate(&header, w), Style::default().fg(Color::Gray).add_modifier(Modifier::UNDERLINED));
buf.set_string(
x,
y,
truncate(&header, w),
Style::default()
.fg(Color::Gray)
.add_modifier(Modifier::UNDERLINED),
);
y += 1;
for (i, proposal) in self.wizard.pipeline.proposals.iter().enumerate() {
if y >= inner.y + inner.height - 2 { break; }
if y >= inner.y + inner.height - 2 {
break;
}
let is_sel = i == self.wizard.cursor;
let kind_color = match proposal.kind {
@ -96,14 +124,23 @@ impl<'a> Widget for ImportWizardWidget<'a> {
FieldKind::Label => Color::DarkGray,
};
let accept_str = if proposal.accepted { "[\u{2713}]" } else { "[ ]" };
let row = format!(" {:<20} {:<22} {}",
let accept_str = if proposal.accepted {
"[\u{2713}]"
} else {
"[ ]"
};
let row = format!(
" {:<20} {:<22} {}",
truncate(&proposal.field, 20),
truncate(proposal.kind_label(), 22),
accept_str);
accept_str
);
let style = if is_sel {
Style::default().fg(Color::Black).bg(Color::Cyan).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Black)
.bg(Color::Cyan)
.add_modifier(Modifier::BOLD)
} else if proposal.accepted {
Style::default().fg(kind_color)
} else {
@ -114,23 +151,34 @@ impl<'a> Widget for ImportWizardWidget<'a> {
y += 1;
}
let hint_y = inner.y + inner.height - 1;
buf.set_string(x, hint_y, "Enter: next Space: toggle c: cycle kind Esc: cancel",
Style::default().fg(Color::DarkGray));
buf.set_string(
x,
hint_y,
"Enter: next Space: toggle c: cycle kind Esc: cancel",
Style::default().fg(Color::DarkGray),
);
}
WizardStep::NameModel => {
buf.set_string(x, y, "Model name:", Style::default().fg(Color::Yellow));
y += 1;
let name_str = format!("> {}\u{2588}", self.wizard.pipeline.model_name);
buf.set_string(x, y, truncate(&name_str, w),
Style::default().fg(Color::Green));
buf.set_string(
x,
y,
truncate(&name_str, w),
Style::default().fg(Color::Green),
);
y += 2;
buf.set_string(x, y, "Enter to import, Esc to cancel",
Style::default().fg(Color::DarkGray));
buf.set_string(
x,
y,
"Enter to import, Esc to cancel",
Style::default().fg(Color::DarkGray),
);
if let Some(msg) = &self.wizard.message {
let msg_y = inner.y + inner.height - 1;
buf.set_string(x, msg_y, truncate(msg, w),
Style::default().fg(Color::Red));
buf.set_string(x, msg_y, truncate(msg, w), Style::default().fg(Color::Red));
}
}
WizardStep::Done => {
@ -141,7 +189,11 @@ impl<'a> Widget for ImportWizardWidget<'a> {
}
fn truncate(s: &str, max: usize) -> String {
if s.len() <= max { s.to_string() }
else if max > 1 { format!("{}\u{2026}", &s[..max-1]) }
else { s[..max].to_string() }
if s.len() <= max {
s.to_string()
} else if max > 1 {
format!("{}\u{2026}", &s[..max - 1])
} else {
s[..max].to_string()
}
}

View File

@ -1,9 +1,8 @@
pub mod app;
pub mod grid;
pub mod formula_panel;
pub mod category_panel;
pub mod view_panel;
pub mod tile_bar;
pub mod import_wizard_ui;
pub mod formula_panel;
pub mod grid;
pub mod help;
pub mod import_wizard_ui;
pub mod tile_bar;
pub mod view_panel;

View File

@ -6,14 +6,14 @@ use ratatui::{
};
use crate::model::Model;
use crate::view::Axis;
use crate::ui::app::AppMode;
use crate::view::Axis;
fn axis_display(axis: Axis) -> (&'static str, Color) {
match axis {
Axis::Row => ("", Color::Green),
Axis::Row => ("", Color::Green),
Axis::Column => ("", Color::Blue),
Axis::Page => ("", Color::Magenta),
Axis::Page => ("", Color::Magenta),
}
}
@ -49,12 +49,17 @@ impl<'a> Widget for TileBar<'a> {
let is_selected = selected_cat_idx == Some(i);
let style = if is_selected {
Style::default().fg(Color::Black).bg(Color::Cyan).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Black)
.bg(Color::Cyan)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(axis_color)
};
if x + label.len() as u16 > area.x + area.width { break; }
if x + label.len() as u16 > area.x + area.width {
break;
}
buf.set_string(x, area.y, &label, style);
x += label.len() as u16;
}

View File

@ -16,7 +16,11 @@ pub struct ViewPanel<'a> {
impl<'a> ViewPanel<'a> {
pub fn new(model: &'a Model, mode: &'a AppMode, cursor: usize) -> Self {
Self { model, mode, cursor }
Self {
model,
mode,
cursor,
}
}
}
@ -40,23 +44,33 @@ impl<'a> Widget for ViewPanel<'a> {
let active = &self.model.active_view;
for (i, view_name) in view_names.iter().enumerate() {
if inner.y + i as u16 >= inner.y + inner.height { break; }
if inner.y + i as u16 >= inner.y + inner.height {
break;
}
let is_selected = i == self.cursor && is_active;
let is_active_view = *view_name == active.as_str();
let style = if is_selected {
Style::default().fg(Color::Black).bg(Color::Blue).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Black)
.bg(Color::Blue)
.add_modifier(Modifier::BOLD)
} else if is_active_view {
Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD)
Style::default()
.fg(Color::Blue)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
};
let prefix = if is_active_view { "" } else { " " };
buf.set_string(inner.x, inner.y + i as u16,
buf.set_string(
inner.x,
inner.y + i as u16,
format!("{prefix}{view_name}"),
style);
style,
);
}
}
}