Feat: UI / UX

This commit is contained in:
2026-02-16 01:22:40 +01:00
parent b23dd85d0f
commit af6732db1c
37 changed files with 1045 additions and 64 deletions

View File

@@ -4,7 +4,7 @@ use std::time::Duration;
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Cell, Clear, Padding, Paragraph, Row, Table};
use ratatui::widgets::{Block, Borders, Cell, Clear, Padding, Paragraph, Row, Table, Wrap};
use ratatui::Frame;
use crate::app::App;
@@ -418,13 +418,20 @@ fn render_footer(frame: &mut Frame, app: &App, area: Rect) {
("Space", "Play"),
("?", "Keys"),
],
Page::Help => vec![
("↑↓", "Scroll"),
("Tab", "Topic"),
("PgUp/Dn", "Page"),
("/", "Search"),
("?", "Keys"),
],
Page::Help => match app.ui.help_focus {
crate::state::HelpFocus::Content => vec![
("n", "Next Example"),
("p", "Previous Example"),
("Enter", "Evaluate"),
("Tab", "Topics"),
],
crate::state::HelpFocus::Topics => vec![
("↑↓", "Navigate"),
("Tab", "Content"),
("/", "Search"),
("?", "Keys"),
],
},
Page::Dict => vec![
("Tab", "Focus"),
("↑↓", "Navigate"),
@@ -594,6 +601,60 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
inner
}
Modal::Onboarding => {
let (desc, keys) = app.page.onboarding();
let text_width = 51usize; // inner width minus 2 for padding
let desc_lines = {
let mut lines = 0u16;
for line in desc.split('\n') {
lines += (line.len() as u16).max(1).div_ceil(text_width as u16);
}
lines
};
let key_lines = keys.len() as u16;
let modal_height = (3 + desc_lines + 1 + key_lines + 2).min(term.height.saturating_sub(4)); // border + pad + desc + gap + keys + pad + hint
let inner = ModalFrame::new(&format!(" {} ", app.page.name()))
.width(57)
.height(modal_height)
.border_color(theme.modal.confirm)
.render_centered(frame, term);
let content_width = inner.width.saturating_sub(4);
let mut y = inner.y + 1;
let desc_area = Rect::new(inner.x + 2, y, content_width, desc_lines);
let body = Paragraph::new(desc)
.style(Style::new().fg(theme.ui.text_primary))
.wrap(Wrap { trim: true });
frame.render_widget(body, desc_area);
y += desc_lines + 1;
for &(key, action) in keys {
if y >= inner.y + inner.height - 1 {
break;
}
let line = Line::from(vec![
Span::raw(" "),
Span::styled(
format!("{:>8}", key),
Style::new().fg(theme.hint.key),
),
Span::styled(
format!(" {action}"),
Style::new().fg(theme.hint.text),
),
]);
frame.render_widget(Paragraph::new(line), Rect::new(inner.x + 1, y, inner.width.saturating_sub(2), 1));
y += 1;
}
let hint_area = Rect::new(inner.x, inner.y + inner.height - 1, inner.width, 1);
let hints = hint_line(&[("Enter", "don't show again"), ("any key", "dismiss")]);
frame.render_widget(Paragraph::new(hints).alignment(Alignment::Center), hint_area);
inner
}
Modal::KeybindingsHelp { scroll } => render_modal_keybindings(frame, app, *scroll, term),
Modal::EuclideanDistribution {
source_step,