chore: reformat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user