A ton of bug fixes
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
use minimad::{Composite, CompositeStyle, Compound, Line};
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::style::{Color, Modifier, Style, Stylize};
|
||||
use ratatui::text::{Line as RLine, Span};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Padding, Paragraph, Wrap};
|
||||
use ratatui::Frame;
|
||||
use tui_big_text::{BigText, PixelSize};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::views::highlight;
|
||||
@@ -11,8 +12,10 @@ use crate::views::highlight;
|
||||
// To add a new help topic: drop a .md file in docs/ and add one line here.
|
||||
const DOCS: &[(&str, &str)] = &[
|
||||
("Welcome", include_str!("../../docs/welcome.md")),
|
||||
("Audio Engine", include_str!("../../docs/audio_engine.md")),
|
||||
("Keybindings", include_str!("../../docs/keybindings.md")),
|
||||
("Sequencer", include_str!("../../docs/sequencer.md")),
|
||||
("About", include_str!("../../docs/about.md")),
|
||||
];
|
||||
|
||||
pub fn topic_count() -> usize {
|
||||
@@ -47,8 +50,36 @@ fn render_topics(frame: &mut Frame, app: &App, area: Rect) {
|
||||
frame.render_widget(list, area);
|
||||
}
|
||||
|
||||
const WELCOME_TOPIC: usize = 0;
|
||||
const BIG_TITLE_HEIGHT: u16 = 6;
|
||||
|
||||
fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let (name, md) = DOCS[app.ui.help_topic];
|
||||
let (_, md) = DOCS[app.ui.help_topic];
|
||||
|
||||
let is_welcome = app.ui.help_topic == WELCOME_TOPIC;
|
||||
let md_area = if is_welcome {
|
||||
let [title_area, rest] =
|
||||
Layout::vertical([Constraint::Length(BIG_TITLE_HEIGHT), Constraint::Fill(1)])
|
||||
.areas(area);
|
||||
let big_title = BigText::builder()
|
||||
.pixel_size(PixelSize::Quadrant)
|
||||
.style(Style::new().cyan().bold())
|
||||
.lines(vec!["CAGIRE".into()])
|
||||
.centered()
|
||||
.build();
|
||||
let subtitle = Paragraph::new(RLine::from(Span::styled(
|
||||
"A Forth Sequencer",
|
||||
Style::new().fg(Color::White),
|
||||
)))
|
||||
.alignment(ratatui::layout::Alignment::Center);
|
||||
let [big_area, subtitle_area] =
|
||||
Layout::vertical([Constraint::Length(4), Constraint::Length(2)]).areas(title_area);
|
||||
frame.render_widget(big_title, big_area);
|
||||
frame.render_widget(subtitle, subtitle_area);
|
||||
rest
|
||||
} else {
|
||||
area
|
||||
};
|
||||
let query = &app.ui.help_search_query;
|
||||
let has_query = !query.is_empty();
|
||||
let query_lower = query.to_lowercase();
|
||||
@@ -57,7 +88,7 @@ fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
|
||||
let has_search_bar = app.ui.help_search_active || has_query;
|
||||
let search_bar_height: u16 = u16::from(has_search_bar);
|
||||
let visible_height = area.height.saturating_sub(6 + search_bar_height) as usize;
|
||||
let visible_height = md_area.height.saturating_sub(6 + search_bar_height) as usize;
|
||||
let max_scroll = lines.len().saturating_sub(visible_height);
|
||||
let scroll = app.ui.help_scroll().min(max_scroll);
|
||||
|
||||
@@ -76,18 +107,17 @@ fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
|
||||
let content_area = if has_search_bar {
|
||||
let [content, search] =
|
||||
Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).areas(area);
|
||||
Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).areas(md_area);
|
||||
render_search_bar(frame, app, search);
|
||||
content
|
||||
} else {
|
||||
area
|
||||
md_area
|
||||
};
|
||||
|
||||
let para = Paragraph::new(visible)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(name)
|
||||
.padding(Padding::new(2, 2, 2, 2)),
|
||||
)
|
||||
.wrap(Wrap { trim: false });
|
||||
@@ -158,8 +188,52 @@ fn code_border_style() -> Style {
|
||||
Style::new().fg(Color::Rgb(60, 60, 70))
|
||||
}
|
||||
|
||||
fn preprocess_underscores(md: &str) -> String {
|
||||
let mut out = String::with_capacity(md.len());
|
||||
for line in md.lines() {
|
||||
let mut result = String::with_capacity(line.len());
|
||||
let mut chars = line.char_indices().peekable();
|
||||
let bytes = line.as_bytes();
|
||||
while let Some((i, c)) = chars.next() {
|
||||
if c == '`' {
|
||||
result.push(c);
|
||||
for (_, ch) in chars.by_ref() {
|
||||
result.push(ch);
|
||||
if ch == '`' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if c == '_' {
|
||||
let before_is_space = i == 0 || bytes[i - 1] == b' ';
|
||||
if before_is_space {
|
||||
if let Some(end) = line[i + 1..].find('_') {
|
||||
let inner = &line[i + 1..i + 1 + end];
|
||||
if !inner.is_empty() {
|
||||
result.push('*');
|
||||
result.push_str(inner);
|
||||
result.push('*');
|
||||
for _ in 0..end {
|
||||
chars.next();
|
||||
}
|
||||
chars.next(); // skip closing _
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push(c);
|
||||
}
|
||||
out.push_str(&result);
|
||||
out.push('\n');
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn parse_markdown(md: &str) -> Vec<RLine<'static>> {
|
||||
let text = minimad::Text::from(md);
|
||||
let processed = preprocess_underscores(md);
|
||||
let text = minimad::Text::from(processed.as_str());
|
||||
let mut lines = Vec::new();
|
||||
|
||||
let mut code_line_nr: usize = 0;
|
||||
@@ -218,13 +292,13 @@ fn composite_to_line(composite: Composite) -> RLine<'static> {
|
||||
}
|
||||
|
||||
for compound in composite.compounds {
|
||||
spans.push(compound_to_span(compound, base_style));
|
||||
compound_to_spans(compound, base_style, &mut spans);
|
||||
}
|
||||
|
||||
RLine::from(spans)
|
||||
}
|
||||
|
||||
fn compound_to_span(compound: Compound, base: Style) -> Span<'static> {
|
||||
fn compound_to_spans(compound: Compound, base: Style, out: &mut Vec<Span<'static>>) {
|
||||
let mut style = base;
|
||||
|
||||
if compound.bold {
|
||||
@@ -240,5 +314,39 @@ fn compound_to_span(compound: Compound, base: Style) -> Span<'static> {
|
||||
style = style.add_modifier(Modifier::CROSSED_OUT);
|
||||
}
|
||||
|
||||
Span::styled(compound.src.to_string(), style)
|
||||
let src = compound.src.to_string();
|
||||
let link_style = Style::new()
|
||||
.fg(Color::Rgb(120, 200, 180))
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
|
||||
let mut rest = src.as_str();
|
||||
while let Some(start) = rest.find('[') {
|
||||
let after_bracket = &rest[start + 1..];
|
||||
if let Some(text_end) = after_bracket.find("](") {
|
||||
let url_start = start + 1 + text_end + 2;
|
||||
if let Some(url_end) = rest[url_start..].find(')') {
|
||||
if start > 0 {
|
||||
out.push(Span::styled(rest[..start].to_string(), style));
|
||||
}
|
||||
let text = &rest[start + 1..start + 1 + text_end];
|
||||
let url = &rest[url_start..url_start + url_end];
|
||||
if text == url {
|
||||
out.push(Span::styled(url.to_string(), link_style));
|
||||
} else {
|
||||
out.push(Span::styled(text.to_string(), link_style));
|
||||
out.push(Span::styled(
|
||||
format!(" ({url})"),
|
||||
Style::new().fg(Color::Rgb(100, 100, 100)),
|
||||
));
|
||||
}
|
||||
rest = &rest[url_start + url_end + 1..];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.push(Span::styled(rest[..start + 1].to_string(), style));
|
||||
rest = &rest[start + 1..];
|
||||
}
|
||||
if !rest.is_empty() {
|
||||
out.push(Span::styled(rest.to_string(), style));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user