fix: allow interleaved spans and variables in footer (#577)
Some checks failed
Deploy docs / build-and-deploy (push) Has been cancelled
Merge checks / Checks (push) Has been cancelled
Merge checks / Validate nix flake (push) Has been cancelled
Merge checks / Validate bat assets (push) Has been cancelled
Merge checks / Validate JSON schemas (push) Has been cancelled

This allows including spans that contain variables in footers. This does
mean that if you pull in a variable like `{title}` that contains `<` or
`>` it will be included as-is and would allow creating valid HTML tags,
but this is harmless so I don't see a problem with it.

Fixes #574
This commit is contained in:
Matias Fontanini 2025-05-03 13:25:21 -07:00 committed by GitHub
commit 2a4ea80a46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -178,10 +178,9 @@ impl FooterLine {
palette: &ColorPalette,
) -> Result<Self, InvalidFooterTemplateError> {
use FooterTemplateChunk::*;
let mut line = Line::default();
let FooterVariables { current_slide, total_slides, author, title, sub_title, event, location, date } = vars;
let arena = Arena::default();
let parser = MarkdownParser::new(&arena);
let mut reassembled = String::new();
for chunk in template.0 {
let raw_text = match chunk {
CurrentSlide => Cow::Owned(current_slide.to_string()),
@ -199,20 +198,22 @@ impl FooterLine {
if raw_text.lines().count() != 1 {
return Err(InvalidFooterTemplateError::NoNewlines);
}
let starting_length = raw_text.len();
let raw_text = raw_text.trim_start();
reassembled.push_str(&raw_text);
}
// Inline parsing loses leading/trailing whitespaces so re-add them ourselves
let starting_length = reassembled.len();
let raw_text = reassembled.trim_start();
let left_whitespace = starting_length - raw_text.len();
let raw_text = raw_text.trim_end();
let right_whitespace = starting_length - raw_text.len() - left_whitespace;
let inlines = parser.parse_inlines(raw_text)?;
let mut contents = inlines.resolve(palette)?;
let parser = MarkdownParser::new(&arena);
let inlines = parser.parse_inlines(&reassembled)?;
let mut line = inlines.resolve(palette)?;
if left_whitespace != 0 {
contents.0.insert(0, " ".repeat(left_whitespace).into());
line.0.insert(0, " ".repeat(left_whitespace).into());
}
if right_whitespace != 0 {
contents.0.push(" ".repeat(right_whitespace).into());
}
line.0.extend(contents.0);
line.0.push(" ".repeat(right_whitespace).into());
}
line.apply_style(style);
Ok(Self(line))
@ -320,4 +321,25 @@ mod tests {
let template = FooterTemplate(vec![chunk]);
FooterLine::new(template, &Default::default(), &VARIABLES, &PALETTE).expect_err("render succeeded");
}
#[test]
fn interleaved_spans() {
let chunks = vec![
FooterTemplateChunk::Literal("<span style=\"color: palette:red\">".into()),
FooterTemplateChunk::CurrentSlide,
FooterTemplateChunk::Literal(" / ".into()),
FooterTemplateChunk::TotalSlides,
FooterTemplateChunk::Literal("</span>".into()),
FooterTemplateChunk::Literal("<span style=\"color: green\">".into()),
FooterTemplateChunk::Title,
FooterTemplateChunk::Literal("</span>".into()),
];
let template = FooterTemplate(chunks);
let line = FooterLine::new(template, &Default::default(), &VARIABLES, &PALETTE).expect("render failed");
let expected = &[
Text::new("1 / 5", TextStyle::default().fg_color(Color::new(255, 0, 0))),
Text::new("hi", TextStyle::default().fg_color(Color::Green)),
];
assert_eq!(line.0.0, expected);
}
}