add: near perfect metadata compatibility
This commit is contained in:
parent
b505199492
commit
f8dac8f491
8 changed files with 434 additions and 22 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -84,7 +84,7 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|||
|
||||
[[package]]
|
||||
name = "attobin"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-extra",
|
||||
|
@ -2586,9 +2586,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tetratto-shared"
|
||||
version = "12.0.4"
|
||||
version = "12.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a7a1ea669f94f12da3c007012c6fbac263cfc74c964c460e19da0d9c7f6abf1"
|
||||
checksum = "11c2ba2be9c92a4ac566b9c3b615d7311b3d0e98b175ad84e81b44644b34dd8f"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"chrono",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "attobin"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2024"
|
||||
authors = ["trisuaso"]
|
||||
repository = "https://trisua.com/t/tetratto"
|
||||
|
@ -9,7 +9,7 @@ homepage = "https://tetratto.com"
|
|||
|
||||
[dependencies]
|
||||
tetratto-core = "12.0.0"
|
||||
tetratto-shared = "12.0.4"
|
||||
tetratto-shared = "12.0.5"
|
||||
tokio = { version = "1.46.1", features = ["macros", "rt-multi-thread"] }
|
||||
pathbufd = "0.1.4"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
|
|
|
@ -69,7 +69,7 @@ function check_message() {
|
|||
if (message) {
|
||||
element.style.marginBottom = "1rem";
|
||||
element.style.paddingLeft = "1rem";
|
||||
element.innerHTML += `<li class="${message_good ? "green" : "red"}">${message.replaceAll('"', "")}</li>`;
|
||||
element.innerHTML = `<li class="${message_good ? "green" : "red"}">${message.replaceAll('"', "")}</li>`;
|
||||
}
|
||||
|
||||
// clear cookies
|
||||
|
@ -83,7 +83,7 @@ globalThis.show_message = (message, message_good = true) => {
|
|||
const element = document.getElementById("messages");
|
||||
element.style.marginBottom = "1rem";
|
||||
element.style.paddingLeft = "1rem";
|
||||
element.innerHTML += `<li class="${message_good ? "green" : "red"}">${message.replaceAll('"', "")}</li>`;
|
||||
element.innerHTML = `<li class="${message_good ? "green" : "red"}">${message.replaceAll('"', "")}</li>`;
|
||||
};
|
||||
|
||||
check_message();
|
||||
|
|
|
@ -111,6 +111,11 @@ article {
|
|||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 10px auto 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content_container {
|
||||
margin: var(--pad-2) auto;
|
||||
width: 100%;
|
||||
|
@ -163,7 +168,7 @@ video {
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--gap-2);
|
||||
padding: var(--pad-2) var(--pad-4);
|
||||
padding: var(--pad-2) calc(var(--pad-3) * 1.5);
|
||||
cursor: pointer;
|
||||
background: var(--color-raised);
|
||||
color: var(--color-text);
|
||||
|
@ -198,7 +203,7 @@ video {
|
|||
/* input */
|
||||
input {
|
||||
--h: 36px;
|
||||
padding: var(--pad-2) var(--pad-4);
|
||||
padding: var(--pad-2) calc(var(--pad-3) * 1.5);
|
||||
background: var(--color-raised);
|
||||
color: var(--color-text);
|
||||
outline: none;
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
(title
|
||||
(text "{{ entry.slug }}"))
|
||||
(text "{%- endif %} {{ metadata_head|safe }}")
|
||||
|
||||
(text "{% if metadata.page_icon|length == 0 -%}")
|
||||
(link ("rel" "icon") ("href" "/public/favicon.svg"))
|
||||
(text "{%- endif %}")
|
||||
|
||||
(text "{% endblock %} {% block body %}")
|
||||
(div
|
||||
("class" "flex flex-col gap-2")
|
||||
|
|
187
src/markdown.rs
187
src/markdown.rs
|
@ -1,3 +1,188 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
pub fn render_markdown(input: &str) -> String {
|
||||
tetratto_shared::markdown::render_markdown(input, false)
|
||||
let html = tetratto_shared::markdown::render_markdown_dirty(&parse_text_color(
|
||||
&parse_highlight(input),
|
||||
));
|
||||
|
||||
let mut allowed_attributes = HashSet::new();
|
||||
allowed_attributes.insert("id");
|
||||
allowed_attributes.insert("class");
|
||||
allowed_attributes.insert("ref");
|
||||
allowed_attributes.insert("aria-label");
|
||||
allowed_attributes.insert("lang");
|
||||
allowed_attributes.insert("title");
|
||||
allowed_attributes.insert("align");
|
||||
allowed_attributes.insert("src");
|
||||
allowed_attributes.insert("style");
|
||||
|
||||
tetratto_shared::markdown::clean_html(html, allowed_attributes)
|
||||
}
|
||||
|
||||
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() {
|
||||
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
|
||||
buffer.push('%');
|
||||
in_main_buffer = false;
|
||||
close_1 = false;
|
||||
}
|
||||
|
||||
match char {
|
||||
'%' => {
|
||||
if in_color_buffer {
|
||||
in_color_buffer = false;
|
||||
in_main_buffer = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if in_main_buffer {
|
||||
// ending
|
||||
if !close_1 {
|
||||
close_1 = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// by this point, we have: !
|
||||
// %color_buffer%main_buffer%%
|
||||
output.push_str(&format!(
|
||||
"<span style=\"color: {color_buffer}\">{buffer}</span>\n"
|
||||
));
|
||||
|
||||
color_buffer.clear();
|
||||
buffer.clear();
|
||||
|
||||
// ...
|
||||
in_main_buffer = false;
|
||||
close_1 = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// start
|
||||
// flush buffer
|
||||
output.push_str(&buffer);
|
||||
buffer.clear();
|
||||
|
||||
// toggle open
|
||||
in_color_buffer = true;
|
||||
}
|
||||
' ' => {
|
||||
if in_color_buffer == true {
|
||||
buffer.push_str(&color_buffer);
|
||||
color_buffer.clear();
|
||||
}
|
||||
|
||||
buffer.push(char);
|
||||
}
|
||||
_ => {
|
||||
if in_color_buffer {
|
||||
color_buffer.push(char)
|
||||
} else {
|
||||
buffer.push(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_highlight_line(output: &mut String, buffer: &mut String, line: &str) {
|
||||
let mut open_1 = false;
|
||||
let mut open_2 = false;
|
||||
let mut close_1 = false;
|
||||
let mut is_open = false;
|
||||
|
||||
for char in line.chars() {
|
||||
if close_1 && char != '=' {
|
||||
buffer.push('=');
|
||||
close_1 = false;
|
||||
}
|
||||
|
||||
match char {
|
||||
'=' => {
|
||||
if !is_open {
|
||||
// flush buffer
|
||||
output.push_str(&buffer);
|
||||
buffer.clear();
|
||||
|
||||
// toggle open
|
||||
open_1 = true;
|
||||
is_open = true;
|
||||
} else {
|
||||
if open_1 {
|
||||
// this is the second open we've recieved
|
||||
open_2 = true;
|
||||
open_1 = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if close_1 {
|
||||
// this is the second close we've received
|
||||
output.push_str(&format!("<mark>{buffer}</mark>\n"));
|
||||
buffer.clear();
|
||||
open_1 = false;
|
||||
open_2 = false;
|
||||
close_1 = false;
|
||||
is_open = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
close_1 = true;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if open_1 {
|
||||
open_1 = false;
|
||||
buffer.push('=');
|
||||
}
|
||||
|
||||
if open_2 && is_open {
|
||||
open_2 = false;
|
||||
}
|
||||
|
||||
buffer.push(char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro to quickly allow parsers to ignore fenced code blocks.
|
||||
macro_rules! parser_ignores_pre {
|
||||
($body:ident, $input:ident) => {{
|
||||
let mut in_pre_block = false;
|
||||
let mut output = String::new();
|
||||
let mut buffer = String::new();
|
||||
|
||||
for line in $input.split("\n") {
|
||||
if line.starts_with("```") {
|
||||
in_pre_block = !in_pre_block;
|
||||
output.push_str(&format!("{line}\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if in_pre_block {
|
||||
output.push_str(&format!("{line}\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
$body(&mut output, &mut buffer, line);
|
||||
output.push_str(&format!("{buffer}\n"));
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
output
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn parse_text_color(input: &str) -> String {
|
||||
parser_ignores_pre!(parse_text_color_line, input)
|
||||
}
|
||||
|
||||
pub fn parse_highlight(input: &str) -> String {
|
||||
parser_ignores_pre!(parse_highlight_line, input)
|
||||
}
|
||||
|
|
235
src/model.rs
235
src/model.rs
|
@ -10,6 +10,7 @@ pub struct Entry {
|
|||
pub created: usize,
|
||||
pub edited: usize,
|
||||
pub content: String,
|
||||
#[serde(default)]
|
||||
pub metadata: String,
|
||||
}
|
||||
|
||||
|
@ -69,7 +70,7 @@ pub struct EntryMetadata {
|
|||
#[validate(max_length = 256)]
|
||||
pub page_description: String,
|
||||
/// The favicon of the page.
|
||||
#[serde(default, alias = "PAGE_icon")]
|
||||
#[serde(default, alias = "PAGE_ICON")]
|
||||
#[validate(max_length = 128)]
|
||||
pub page_icon: String,
|
||||
/// The title of the page shown in external embeds.
|
||||
|
@ -99,38 +100,171 @@ pub struct EntryMetadata {
|
|||
#[serde(default, alias = "ACCESS_EASY_READ")]
|
||||
#[validate(max_length = 32)]
|
||||
pub access_easy_read: String,
|
||||
/// The color of the text in the inner container.
|
||||
#[serde(default, alias = "CONTAINER_INNER_FOREGROUND")]
|
||||
/// The padding of the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/padding>
|
||||
#[serde(default, alias = "CONTAINER_PADDING")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_inner_foreground: String,
|
||||
pub container_padding: String,
|
||||
/// The maximum width of the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/max-width>
|
||||
#[serde(default, alias = "CONTAINER_MAX_WIDTH")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_max_width: String,
|
||||
/// The padding of the container.
|
||||
/// The color of the text in the inner container.
|
||||
#[serde(default, alias = "CONTAINER_INNER_FOREGROUND_COLOR")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_inner_foreground_color: String,
|
||||
/// The background of the inner container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background>
|
||||
#[serde(default, alias = "CONTAINER_INNER_BACKGROUND")]
|
||||
#[validate(max_length = 256)]
|
||||
pub container_inner_background: String,
|
||||
/// The color of the text in the outer container.
|
||||
#[serde(default, alias = "CONTAINER_OUTER_FOREGROUND")]
|
||||
/// The background of the inner container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-color>
|
||||
#[serde(default, alias = "CONTAINER_INNER_BACKGROUND_COLOR")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_outer_foreground: String,
|
||||
pub container_inner_background_color: String,
|
||||
/// The background of the inner container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-image>
|
||||
#[serde(default, alias = "CONTAINER_INNER_BACKGROUND_IMAGE")]
|
||||
#[validate(max_length = 128)]
|
||||
pub container_inner_background_image: String,
|
||||
/// The background of the inner container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat>
|
||||
#[serde(default, alias = "CONTAINER_INNER_BACKGROUND_IMAGE_REPEAT")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_inner_background_image_repeat: String,
|
||||
/// The background of the inner container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-position>
|
||||
#[serde(default, alias = "CONTAINER_INNER_BACKGROUND_IMAGE_POSITION")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_inner_background_image_position: String,
|
||||
/// The background of the inner container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-size>
|
||||
#[serde(default, alias = "CONTAINER_INNER_BACKGROUND_IMAGE_SIZE")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_inner_background_image_size: String,
|
||||
/// The color of the text in the outer container.
|
||||
#[serde(default, alias = "CONTAINER_OUTER_FOREGROUND_COLOR")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_outer_foreground_color: String,
|
||||
/// The background of the outer container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background>
|
||||
#[serde(default, alias = "CONTAINER_OUTER_BACKGROUND")]
|
||||
#[validate(max_length = 256)]
|
||||
pub container_outer_background: String,
|
||||
/// The background of the outer container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-color>
|
||||
#[serde(default, alias = "CONTAINER_OUTER_BACKGROUND_COLOR")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_outer_background_color: String,
|
||||
/// The background of the outer container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-image>
|
||||
#[serde(default, alias = "CONTAINER_OUTER_BACKGROUND_IMAGE")]
|
||||
#[validate(max_length = 128)]
|
||||
pub container_outer_background_image: String,
|
||||
/// The background of the outer container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat>
|
||||
#[serde(default, alias = "CONTAINER_OUTER_BACKGROUND_IMAGE_REPEAT")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_outer_background_image_repeat: String,
|
||||
/// The background of the outer container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-position>
|
||||
#[serde(default, alias = "CONTAINER_OUTER_BACKGROUND_IMAGE_POSITION")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_outer_background_image_position: String,
|
||||
/// The background of the outer container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/background-size>
|
||||
#[serde(default, alias = "CONTAINER_OUTER_BACKGROUND_IMAGE_SIZE")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_outer_background_image_size: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border>
|
||||
#[serde(default, alias = "CONTAINER_BORDER")]
|
||||
#[validate(max_length = 256)]
|
||||
pub container_border: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-image>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_IMAGE")]
|
||||
#[validate(max_length = 128)]
|
||||
pub container_border_image: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-image-slice>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_IMAGE_SLICE")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_image_slice: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-image-width>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_IMAGE_WIDTH")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_image_width: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-image-outset>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_IMAGE_OUTSET")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_border_image_outset: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-image-repeat>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_IMAGE_REPEAT")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_image_repeat: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-color>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_COLOR")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_border_color: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-width>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_WIDTH")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_width: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-style>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_STYLE")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_style: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius>
|
||||
#[serde(default, alias = "CONTAINER_BORDER_RADIUS")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_radius: String,
|
||||
/// The shadow around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow>
|
||||
#[serde(default, alias = "CONTAINER_SHADOW")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_shadow: String,
|
||||
/// The shadow under text.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow>
|
||||
#[serde(default, alias = "CONTENT_TEXT_SHADOW")]
|
||||
#[validate(max_length = 32)]
|
||||
pub content_text_shadow: String,
|
||||
/// The name of a font from Google Fonts to use.
|
||||
#[serde(default, alias = "CONTENT_FONT")]
|
||||
#[validate(max_length = 32)]
|
||||
|
@ -166,6 +300,17 @@ macro_rules! metadata_css {
|
|||
));
|
||||
}
|
||||
};
|
||||
|
||||
($selector:literal, $property:literal, $format:literal, $self:ident.$field:ident->$output:ident) => {
|
||||
if !$self.$field.is_empty() {
|
||||
$output.push_str(&format!(
|
||||
"{} {{ {}: {}; }}\n",
|
||||
$selector,
|
||||
$property,
|
||||
format!($format, EntryMetadata::css_escape(&$self.$field))
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl EntryMetadata {
|
||||
|
@ -233,12 +378,34 @@ impl EntryMetadata {
|
|||
pub fn css(&self) -> String {
|
||||
let mut output = "<style>".to_string();
|
||||
|
||||
metadata_css!(".container", "color", self.container_inner_foreground->output);
|
||||
metadata_css!(".container", "padding", self.container_padding->output);
|
||||
metadata_css!(".container", "max-width", self.container_max_width->output);
|
||||
metadata_css!(".container", "color", self.container_inner_foreground_color->output);
|
||||
metadata_css!(".container", "background", self.container_inner_background->output);
|
||||
metadata_css!("body", "color", self.container_outer_foreground->output);
|
||||
metadata_css!(".container", "background-color", self.container_inner_background_color->output);
|
||||
metadata_css!(".container", "background-image", "url('{}')", self.container_inner_background_image->output);
|
||||
metadata_css!(".container", "background-repeat", self.container_inner_background_image_repeat->output);
|
||||
metadata_css!(".container", "background-position", self.container_inner_background_image_position->output);
|
||||
metadata_css!(".container", "background-size", self.container_inner_background_image_size->output);
|
||||
metadata_css!("body", "color", self.container_outer_foreground_color->output);
|
||||
metadata_css!("body", "background", self.container_outer_background->output);
|
||||
metadata_css!("body", "background-color", self.container_outer_background_color->output);
|
||||
metadata_css!("body", "background-image", "url('{}')", self.container_outer_background_image->output);
|
||||
metadata_css!("body", "background-repeat", self.container_outer_background_image_repeat->output);
|
||||
metadata_css!("body", "background-position", self.container_outer_background_image_position->output);
|
||||
metadata_css!("body", "background-size", self.container_outer_background_image_size->output);
|
||||
metadata_css!(".container", "border", self.container_border->output);
|
||||
metadata_css!(".container", "border-image", "url('{}')", self.container_border_image->output);
|
||||
metadata_css!(".container", "border-image-slice", self.container_border_image_slice->output);
|
||||
metadata_css!(".container", "border-image-repeat", self.container_border_image_repeat->output);
|
||||
metadata_css!(".container", "border-image-outset", self.container_border_image_outset->output);
|
||||
metadata_css!(".container", "border-image-width", self.container_border_image_width->output);
|
||||
metadata_css!(".container", "border-color", self.container_border_color->output);
|
||||
metadata_css!(".container", "border-style", self.container_border_style->output);
|
||||
metadata_css!(".container", "border-width", self.container_border_width->output);
|
||||
metadata_css!(".container", "border-radius", self.container_border_radius->output);
|
||||
metadata_css!(".container", "box-shadow", self.container_shadow->output);
|
||||
metadata_css!(".container", "text-shadow", self.content_text_shadow->output);
|
||||
metadata_css!(".container a", "color", self.content_text_link_color->output);
|
||||
|
||||
if self.content_text_align != TextAlignment::Left {
|
||||
|
@ -272,4 +439,54 @@ impl EntryMetadata {
|
|||
|
||||
output + "</style>"
|
||||
}
|
||||
|
||||
pub fn ini_to_toml(input: &str) -> String {
|
||||
let mut output = String::new();
|
||||
|
||||
for line in input.split("\n") {
|
||||
if !line.contains("=") {
|
||||
// no equal sign, skip line (some other toml function)
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut key = String::new();
|
||||
let mut value = String::new();
|
||||
|
||||
let mut chars = line.chars();
|
||||
let mut in_value = false;
|
||||
|
||||
while let Some(char) = chars.next() {
|
||||
if !in_value {
|
||||
key.push(char);
|
||||
|
||||
if char == '=' {
|
||||
in_value = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
value.push(char);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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("\"") {
|
||||
value = format!("\"{value}\"");
|
||||
}
|
||||
|
||||
// push line
|
||||
output.push_str(&format!("{key} {value}\n"));
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,8 @@ async fn view_request(
|
|||
};
|
||||
|
||||
// check metadata
|
||||
let metadata: EntryMetadata = match toml::from_str(&entry.metadata) {
|
||||
let metadata: EntryMetadata = match toml::from_str(&EntryMetadata::ini_to_toml(&entry.metadata))
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
let mut ctx = default_context(&data, &build_code);
|
||||
|
@ -269,7 +270,7 @@ async fn create_request(
|
|||
}
|
||||
|
||||
// check metadata
|
||||
let metadata: EntryMetadata = match toml::from_str(&req.metadata) {
|
||||
let metadata: EntryMetadata = match toml::from_str(&EntryMetadata::ini_to_toml(&req.metadata)) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Json(Error::MiscError(e.to_string()).into()),
|
||||
};
|
||||
|
@ -359,7 +360,7 @@ async fn edit_request(
|
|||
}
|
||||
|
||||
// check metadata
|
||||
let metadata: EntryMetadata = match toml::from_str(&req.metadata) {
|
||||
let metadata: EntryMetadata = match toml::from_str(&EntryMetadata::ini_to_toml(&req.metadata)) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Json(Error::MiscError(e.to_string()).into()),
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue