From 2cc9ed74452fafdb80fc144314abcac3216e7a5f Mon Sep 17 00:00:00 2001 From: trisua Date: Thu, 24 Jul 2025 15:10:59 -0400 Subject: [PATCH] fix: scan ahead panic --- app/public/style.css | 2 +- src/markdown.rs | 42 ++++++++++++++++--- src/model.rs | 96 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 127 insertions(+), 13 deletions(-) diff --git a/app/public/style.css b/app/public/style.css index 6fd2537..4afe582 100644 --- a/app/public/style.css +++ b/app/public/style.css @@ -323,7 +323,7 @@ hr.margin { } span.img_sizer { - display: block; + display: inline-block; } p, diff --git a/src/markdown.rs b/src/markdown.rs index e209cd2..07510b2 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -27,13 +27,29 @@ pub fn render_markdown(input: &str) -> String { .replace(":temp_style", "") } +pub(crate) fn is_numeric(value: &str) -> bool { + let mut is_numeric = false; + + for char in value.chars() { + is_numeric = char.is_numeric(); + } + + is_numeric +} + +pub(crate) fn slice(x: &str, range: core::ops::RangeFrom) -> String { + (&x.chars().collect::>()[range]) + .iter() + .collect::() +} + fn parse_text_color_line(output: &mut String, buffer: &mut String, line: &str) { let mut in_color_buffer = false; let mut in_main_buffer = false; let mut color_buffer = String::new(); let mut close_1 = false; - for char in line.chars() { + for (i, char) in line.chars().enumerate() { if close_1 && char != '%' { // we expected to see another percentage to close the main buffer, // not getting that means this wasn't meant to be a color @@ -60,7 +76,7 @@ fn parse_text_color_line(output: &mut String, buffer: &mut String, line: &str) { // by this point, we have: ! // %color_buffer%main_buffer%% output.push_str(&format!( - "{buffer}\n" + "{buffer}" )); color_buffer.clear(); @@ -73,6 +89,13 @@ fn parse_text_color_line(output: &mut String, buffer: &mut String, line: &str) { } // start + // scan ahead + let ahead = slice(line, i..); + if !ahead.contains("%%") { + // no closing sequence, we're done + continue; + } + // flush buffer output.push_str(&buffer); buffer.clear(); @@ -336,7 +359,17 @@ fn parse_image_size_line(output: &mut String, buffer: &mut String, line: &str) { if in_size && in_size_rhs { // end output.push_str(&format!( - "![{buffer}" + "![{buffer}", + if is_numeric(&size_lhs) { + format!("{size_lhs}px") + } else { + size_lhs + }, + if is_numeric(&size_rhs) { + format!("{size_rhs}px") + } else { + size_rhs + } )); size_lhs = String::new(); @@ -487,8 +520,7 @@ fn parse_link_line(output: &mut String, buffer: &mut String, line: &str) { buffer.clear(); // scan for closing, otherwise quit - let haystack = &line[i..]; - + let haystack = slice(line, i..); if !haystack.contains("]") { output.push('['); continue; diff --git a/src/model.rs b/src/model.rs index c743fc1..2d1e041 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,3 +1,4 @@ +use crate::markdown::is_numeric; use serde::{Deserialize, Serialize}; use serde_valid::Validate; use std::fmt::Display; @@ -259,12 +260,64 @@ pub struct EntryMetadata { #[serde(default, alias = "CONTAINER_SHADOW")] #[validate(max_length = 32)] pub container_shadow: String, + /// If the container is a flexbox. + #[serde(default, alias = "CONTAINER_FLEX")] + pub container_flex: bool, + /// The direction of the container's content (if container has flex enabled). + #[serde(default, alias = "CONTAINER_FLEX_DIRECTION")] + #[validate(max_length = 16)] + pub container_flex_direction: String, + /// The gap of the container's content (if container has flex enabled). + /// + /// Syntax: + #[serde(default, alias = "CONTAINER_FLEX_GAP")] + #[validate(max_length = 16)] + pub container_flex_gap: String, + /// If the container's content wraps (if container has flex enabled). + /// + /// Syntax: + #[serde(default, alias = "CONTAINER_FLEX_WRAP")] + pub container_flex_wrap: bool, + /// The alignment of the container's content horizontally (if container has flex enabled). + /// + /// Swapped to vertical when direction is `column*`. + /// + /// Syntax: + #[serde(default, alias = "CONTAINER_FLEX_ALIGN_HORIZONTAL")] + #[validate(max_length = 16)] + pub container_flex_align_horizontal: String, + /// The alignment of the container's content vertically (if container has flex enabled). + /// + /// Swapped to horizontal when direction is `column*`. + /// + /// Syntax: + #[serde(default, alias = "CONTAINER_FLEX_ALIGN_VERTICAL")] + #[validate(max_length = 16)] + pub container_flex_align_vertical: String, /// The shadow under text. /// /// Syntax: #[serde(default, alias = "CONTENT_TEXT_SHADOW")] #[validate(max_length = 32)] pub content_text_shadow: String, + /// The shadow under text. + /// + /// Syntax: + #[serde(default, alias = "CONTENT_TEXT_SHADOW_COLOR")] + #[validate(max_length = 32)] + pub content_text_shadow_color: String, + /// The shadow under text. + /// + /// Syntax: + #[serde(default, alias = "CONTENT_TEXT_SHADOW_OFFSET")] + #[validate(max_length = 32)] + pub content_text_shadow_offset: String, + /// The shadow under text. + /// + /// Syntax: + #[serde(default, alias = "CONTENT_TEXT_SHADOW_BLUR")] + #[validate(max_length = 32)] + pub content_text_shadow_blur: String, /// The name of a font from Google Fonts to use. #[serde(default, alias = "CONTENT_FONT")] #[validate(max_length = 32)] @@ -287,6 +340,9 @@ pub struct EntryMetadata { /// The color of links. #[serde(default, alias = "CONTENT_TEXT_LINK_COLOR")] pub content_text_link_color: String, + /// If paragraph elements have a margin below them. + #[serde(default, alias = "CONTENT_DISABLE_PARAGRAPH_MARGIN")] + pub content_disable_paragraph_margin: bool, } macro_rules! metadata_css { @@ -446,6 +502,31 @@ impl EntryMetadata { )); } + if !self.content_text_shadow_color.is_empty() { + output.push_str(&format!( + ".container {{ text-shadow: {} {} {}; }}", + self.content_text_shadow_offset, + self.content_text_shadow_blur, + self.content_text_shadow_color + )); + } + + if self.container_flex { + output.push_str(".container { display: flex; }"); + metadata_css!(".container", "gap", self.container_flex_gap->output); + metadata_css!(".container", "flex-direction", self.container_flex_direction->output); + metadata_css!(".container", "align-items", self.container_flex_align_vertical->output); + metadata_css!(".container", "justify-content", self.container_flex_align_horizontal->output); + + if self.container_flex_wrap { + output.push_str(".container { flex-wrap: wrap; }"); + } + } + + if self.content_disable_paragraph_margin { + output.push_str(".container p { margin: 0; }"); + } + output + "" } @@ -482,13 +563,14 @@ impl EntryMetadata { value = value.trim().to_string(); // determine if we need to stringify - let mut is_numeric = false; - - for char in value.chars() { - is_numeric = char.is_numeric(); - } - - if !is_numeric && !value.starts_with("[") && !value.starts_with("\"") { + let is_numeric = is_numeric(&value); + if !is_numeric + && !value.starts_with("[") + && !value.starts_with("\"") + && value != "true" + && value != "false" + || value.starts_with("#") + { value = format!("\"{value}\""); }