add: cleaner ui, prior view check, audio element, rendering fixes
This commit is contained in:
parent
f215d038b3
commit
cacd992f53
7 changed files with 320 additions and 57 deletions
|
@ -147,6 +147,19 @@ globalThis.tab_editor = () => {
|
|||
}
|
||||
};
|
||||
|
||||
globalThis.get_preview = async () => {
|
||||
return await (
|
||||
await fetch("/api/v1/render", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
content: globalThis.editor.getValue(),
|
||||
metadata: globalThis.metadata_editor.getValue(),
|
||||
}),
|
||||
})
|
||||
).text();
|
||||
};
|
||||
|
||||
globalThis.tab_preview = async () => {
|
||||
if (
|
||||
!document
|
||||
|
@ -157,16 +170,7 @@ globalThis.tab_preview = async () => {
|
|||
}
|
||||
|
||||
// render
|
||||
const res = await (
|
||||
await fetch("/api/v1/render", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
content: globalThis.editor.getValue(),
|
||||
metadata: globalThis.metadata_editor.getValue(),
|
||||
}),
|
||||
})
|
||||
).text();
|
||||
const res = await get_preview();
|
||||
|
||||
document.getElementById("preview_tab").innerHTML = res;
|
||||
hljs.highlightAll();
|
||||
|
@ -366,3 +370,24 @@ setTimeout(() => {
|
|||
// run initial hash check
|
||||
hash_check(window.location.hash);
|
||||
}, 150);
|
||||
|
||||
globalThis.submitter_load = (submitter) => {
|
||||
return {
|
||||
load() {
|
||||
submitter.querySelector("[ui_ident=text]").classList.add("hidden");
|
||||
submitter
|
||||
.querySelector("[ui_ident=loader]")
|
||||
.classList.remove("hidden");
|
||||
submitter.setAttribute("disabled", "true");
|
||||
},
|
||||
failed() {
|
||||
submitter
|
||||
.querySelector("[ui_ident=text]")
|
||||
.classList.remove("hidden");
|
||||
submitter
|
||||
.querySelector("[ui_ident=loader]")
|
||||
.classList.add("hidden");
|
||||
submitter.removeAttribute("disabled");
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
--color-green: hsl(100, 84%, 20%);
|
||||
--color-yellow: oklch(47% 0.157 37.304);
|
||||
--color-purple: hsl(284, 84%, 20%);
|
||||
--color-green-lowered: hsl(100, 84%, 15%);
|
||||
--color-red-lowered: hsl(0, 84%, 35%);
|
||||
|
||||
--shadow-x-offset: 0;
|
||||
--shadow-y-offset: 0.125rem;
|
||||
|
@ -207,6 +209,11 @@ video {
|
|||
appearance: none;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
opacity: 50%;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.button.small {
|
||||
--h: 28px;
|
||||
}
|
||||
|
@ -243,6 +250,24 @@ video {
|
|||
background: var(--color-super-raised);
|
||||
}
|
||||
|
||||
.button.green:not(.dark *) {
|
||||
background: var(--color-green);
|
||||
color: white !important;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-green-lowered) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.button.red:not(.dark *) {
|
||||
background: var(--color-red);
|
||||
color: white !important;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-red-lowered) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* dropdown */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
|
@ -272,9 +297,8 @@ video {
|
|||
}
|
||||
|
||||
.dropdown .inner .title {
|
||||
font-weight: 500;
|
||||
border-top: solid 1px var(--color-super-lowered);
|
||||
margin-top: var(--pad-2);
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown:has(.inner.open) .button:nth-child(1):not(.inner *) {
|
||||
|
@ -710,6 +734,23 @@ span {
|
|||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
animation: spin linear infinite 2s forwards running;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
@ -753,3 +794,32 @@ details .content {
|
|||
padding: var(--pad-4);
|
||||
background: var(--color-surface);
|
||||
}
|
||||
|
||||
/* dialog */
|
||||
dialog {
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
box-shadow: var(--shadow-x-offset) var(--shadow-y-offset) var(--shadow-size)
|
||||
var(--color-shadow);
|
||||
animation: fadein ease-in-out 1 0.25s forwards running;
|
||||
max-width: 95%;
|
||||
width: 30rem;
|
||||
margin: auto;
|
||||
padding: var(--pad-4);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
dialog.inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--pad-2);
|
||||
}
|
||||
|
||||
dialog::backdrop {
|
||||
background: hsla(0, 0%, 0%, 25%);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
dialog:is(.dark *)::backdrop {
|
||||
background: hsla(0, 0%, 100%, 15%);
|
||||
}
|
||||
|
|
|
@ -27,17 +27,19 @@
|
|||
("title" "Info")
|
||||
(text "i"))))
|
||||
(div
|
||||
("class" "card tab tabs container")
|
||||
("class" "flex justify_center tab")
|
||||
(div
|
||||
("class" "card tab tabs container w_full")
|
||||
("id" "tabs_group")
|
||||
(div
|
||||
("id" "editor_tab")
|
||||
("class" "tab fadein"))
|
||||
("class" "tab fadein w_full"))
|
||||
(div
|
||||
("id" "preview_tab")
|
||||
("class" "tab fadein hidden"))
|
||||
("class" "tab fadein hidden w_full"))
|
||||
(div
|
||||
("id" "metadata_tab")
|
||||
("class" "tab fadein hidden")))
|
||||
("class" "tab fadein hidden w_full"))))
|
||||
(form
|
||||
("class" "w_full flex flex_col gap_2")
|
||||
("style" "margin-top: var(--pad-2)")
|
||||
|
@ -80,20 +82,46 @@
|
|||
("class" "flex gap_2")
|
||||
(button
|
||||
("class" "button green")
|
||||
(text "Save"))
|
||||
(span ("ui_ident" "text") (text "Save"))
|
||||
(span ("class" "hidden loader no_fill") ("ui_ident" "loader") (text "{{ icon \"loader-circle\" }}")))
|
||||
(a
|
||||
("href" "/{{ entry.slug }}")
|
||||
("class" "button")
|
||||
(text "Back")))
|
||||
|
||||
(button
|
||||
("class" "button red")
|
||||
("type" "button")
|
||||
("onclick" "document.getElementById('delete_modal').showModal()")
|
||||
("id" "fake_delete_button")
|
||||
(span ("ui_ident" "text") (text "Delete"))
|
||||
(span ("class" "hidden loader no_fill") ("ui_ident" "loader") (text "{{ icon \"loader-circle\" }}")))
|
||||
|
||||
(dialog
|
||||
("id" "delete_modal")
|
||||
(div
|
||||
("class" "inner")
|
||||
(h2 ("class" "text_center w_full") (text "Delete {{ entry.slug }}?"))
|
||||
(p (text "Deleting this entry will make its custom slug claimable by anyone."))
|
||||
(p (text "Please ensure that you understand the consequences of deleting this entry before continuing."))
|
||||
(hr ("class" "margin"))
|
||||
(div
|
||||
("class" "w_full flex gap_2 justify_between")
|
||||
(button
|
||||
("class" "button")
|
||||
("type" "button")
|
||||
("onclick" "document.getElementById('delete_modal').close()")
|
||||
(text "Cancel"))
|
||||
(button
|
||||
("class" "button red")
|
||||
("ui_ident" "delete")
|
||||
(text "Delete"))))
|
||||
("onclick" "document.getElementById('delete_modal').close()")
|
||||
(text "Delete")))))))
|
||||
|
||||
; editor
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/lib/codemirror.js"))
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/markdown/markdown.js"))
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/toml/toml.js"))
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/display/placeholder.js"))
|
||||
(link ("rel" "stylesheet") ("href" "https://unpkg.com/codemirror@5.39.2/lib/codemirror.css"))
|
||||
(script ("src" "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"))
|
||||
|
@ -105,16 +133,15 @@
|
|||
(script
|
||||
(text "setTimeout(() => {
|
||||
globalThis.init_editor();
|
||||
globalThis.init_editor(\"metadata_editor\", \"plain\", \"metadata_tab\", \"editor_metadata_content\");
|
||||
globalThis.init_editor(\"metadata_editor\", \"toml\", \"metadata_tab\", \"editor_metadata_content\");
|
||||
}, 150);
|
||||
|
||||
globalThis.edit_entry = (e) => {
|
||||
e.preventDefault();
|
||||
const rm = e.submitter.getAttribute(\"ui_ident\") === \"delete\";
|
||||
|
||||
if (rm && !confirm(\"Are you sure you want to do this?\")) {
|
||||
return;
|
||||
}
|
||||
const { load, failed } = submitter_load(rm ? document.getElementById(\"fake_delete_button\") : e.submitter);
|
||||
load();
|
||||
|
||||
fetch(\"/api/v1/entries/{{ entry.slug }}\", {
|
||||
method: \"POST\",
|
||||
|
@ -147,7 +174,36 @@
|
|||
}
|
||||
} else {
|
||||
show_message(res.message, false);
|
||||
failed();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
globalThis.download = (content, type, name) => {
|
||||
const blob = new Blob([content], { type });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement(\"a\");
|
||||
|
||||
anchor.setAttribute(\"download\", name);
|
||||
anchor.href = url;
|
||||
anchor.click();
|
||||
anchor.remove();
|
||||
}"))
|
||||
(text "{% endblock %}")
|
||||
|
||||
(text "{% block dropdown %}")
|
||||
(hr)
|
||||
(span ("class" "title") (text "export"))
|
||||
(button
|
||||
("class" "button")
|
||||
("onclick" "download(globalThis.editor.getValue(), 'text/markdown', '{{ entry.slug }}.md')")
|
||||
(text "markdown"))
|
||||
(button
|
||||
("class" "button")
|
||||
("onclick" "download(globalThis.metadata_editor.getValue(), 'application/toml', '{{ entry.slug }}.toml')")
|
||||
(text "metadata"))
|
||||
(button
|
||||
("class" "button")
|
||||
("onclick" "(async () => { download(await get_preview(), 'text/html', '{{ entry.slug }}.html') })();")
|
||||
(text "html"))
|
||||
(text "{%- endblock %}")
|
||||
|
|
|
@ -30,24 +30,27 @@
|
|||
("title" "Info")
|
||||
(text "i"))))
|
||||
(div
|
||||
("class" "card tab tabs container")
|
||||
("class" "flex justify_center tab")
|
||||
(div
|
||||
("class" "card tab tabs container w_full")
|
||||
("id" "tabs_group")
|
||||
(div
|
||||
("id" "editor_tab")
|
||||
("class" "tab fadein"))
|
||||
("class" "tab fadein w_full"))
|
||||
(div
|
||||
("id" "preview_tab")
|
||||
("class" "tab fadein hidden"))
|
||||
("class" "tab fadein hidden w_full"))
|
||||
(div
|
||||
("id" "metadata_tab")
|
||||
("class" "tab fadein hidden")))
|
||||
("class" "tab fadein hidden w_full"))))
|
||||
(form
|
||||
("class" "w_full flex justify_between gap_2 flex_collapse_rev")
|
||||
("style" "margin-top: var(--pad-2)")
|
||||
("onsubmit" "create_entry(event)")
|
||||
(button
|
||||
("class" "button")
|
||||
(text "Go"))
|
||||
(span ("ui_ident" "text") (text "Go"))
|
||||
(span ("class" "hidden loader no_fill") ("ui_ident" "loader") (text "{{ icon \"loader-circle\" }}")))
|
||||
(div
|
||||
("class" "flex gap_2")
|
||||
(input
|
||||
|
@ -68,6 +71,7 @@
|
|||
; editor
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/lib/codemirror.js"))
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/markdown/markdown.js"))
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/toml/toml.js"))
|
||||
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/display/placeholder.js"))
|
||||
(link ("rel" "stylesheet") ("href" "https://unpkg.com/codemirror@5.39.2/lib/codemirror.css"))
|
||||
(script ("src" "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"))
|
||||
|
@ -76,11 +80,15 @@
|
|||
(script
|
||||
(text "setTimeout(() => {
|
||||
globalThis.init_editor();
|
||||
globalThis.init_editor(\"metadata_editor\", \"plain\", \"metadata_tab\");
|
||||
globalThis.init_editor(\"metadata_editor\", \"toml\", \"metadata_tab\");
|
||||
}, 150);
|
||||
|
||||
globalThis.create_entry = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { load, failed } = submitter_load(e.submitter);
|
||||
load();
|
||||
|
||||
fetch(\"/api/v1/entries\", {
|
||||
method: \"POST\",
|
||||
headers: {
|
||||
|
@ -102,6 +110,7 @@
|
|||
window.location.href = `/${res.payload[0]}`;
|
||||
} else {
|
||||
show_message(res.message, false);
|
||||
failed();
|
||||
}
|
||||
})
|
||||
}"))
|
||||
|
|
|
@ -20,14 +20,21 @@ pub fn render_markdown(input: &str) -> String {
|
|||
allowed_attributes.insert("align");
|
||||
allowed_attributes.insert("src");
|
||||
allowed_attributes.insert("style");
|
||||
allowed_attributes.insert("controls");
|
||||
allowed_attributes.insert("autoplay");
|
||||
allowed_attributes.insert("loop");
|
||||
|
||||
tetratto_shared::markdown::clean_html(
|
||||
html.replace("<style>", "<span>:temp_style")
|
||||
.replace("</style>", "</span>:temp_style"),
|
||||
.replace("</style>", "</span>:temp_style")
|
||||
.replace("<audio", ":temp_audio<span")
|
||||
.replace("</audio>", "</span>:temp_audio"),
|
||||
allowed_attributes,
|
||||
)
|
||||
.replace("<span>:temp_style", "<style>")
|
||||
.replace("</span>:temp_style", "</style>")
|
||||
.replace(":temp_audio<span", "<audio")
|
||||
.replace("</span>:temp_audio", "</audio>")
|
||||
}
|
||||
|
||||
pub(crate) fn is_numeric(value: &str) -> bool {
|
||||
|
@ -138,6 +145,12 @@ fn parse_highlight_line(output: &mut String, buffer: &mut String, line: &str) {
|
|||
close_1 = false;
|
||||
}
|
||||
|
||||
if open_1 && char != '=' {
|
||||
buffer.push('=');
|
||||
open_1 = false;
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
match char {
|
||||
'=' => {
|
||||
if !is_open {
|
||||
|
@ -195,7 +208,13 @@ fn parse_underline_line(output: &mut String, buffer: &mut String, line: &str) {
|
|||
if open_1 && char != '~' {
|
||||
is_open = false;
|
||||
open_1 = false;
|
||||
|
||||
if char == '[' {
|
||||
// image
|
||||
buffer.push('!');
|
||||
} else {
|
||||
buffer.push_str("!");
|
||||
}
|
||||
}
|
||||
|
||||
if close_1 && char != '!' {
|
||||
|
@ -897,6 +916,7 @@ pub fn get_toc_list(input: &str) -> (String, String) {
|
|||
let mut output = String::new();
|
||||
let mut toc = String::new();
|
||||
let mut in_pre = false;
|
||||
let mut hc_offset: Option<usize> = None;
|
||||
|
||||
for line in input.split("\n") {
|
||||
if line.starts_with("```") || line.starts_with("<style>") || line.starts_with("</style>") {
|
||||
|
@ -914,6 +934,7 @@ pub fn get_toc_list(input: &str) -> (String, String) {
|
|||
if line.starts_with("#") {
|
||||
// get heading count
|
||||
let mut hc = 0;
|
||||
let real_hc;
|
||||
|
||||
for x in line.chars() {
|
||||
if x != '#' {
|
||||
|
@ -923,8 +944,21 @@ pub fn get_toc_list(input: &str) -> (String, String) {
|
|||
hc += 1;
|
||||
}
|
||||
|
||||
real_hc = hc.clone();
|
||||
if hc_offset.is_none() {
|
||||
if hc > 1 {
|
||||
// offset this count to 1 so the list renders properly
|
||||
hc_offset = Some(hc - 1);
|
||||
hc = 1;
|
||||
} else {
|
||||
hc_offset = Some(0);
|
||||
}
|
||||
} else if let Some(offset) = hc_offset {
|
||||
hc -= offset;
|
||||
}
|
||||
|
||||
// add heading with id
|
||||
let x = line.replacen(&"#".repeat(hc), "", 1);
|
||||
let x = line.replacen(&"#".repeat(real_hc), "", 1);
|
||||
let htext = x.trim();
|
||||
|
||||
let id = underscore_chars(
|
||||
|
@ -933,7 +967,7 @@ pub fn get_toc_list(input: &str) -> (String, String) {
|
|||
);
|
||||
|
||||
output.push_str(&format!(
|
||||
"<h{hc} id=\"{id}\">{}</h{hc}>\n",
|
||||
"<h{real_hc} id=\"{id}\">{}</h{real_hc}>\n\n",
|
||||
render_markdown(&htext)
|
||||
));
|
||||
|
||||
|
|
42
src/model.rs
42
src/model.rs
|
@ -122,12 +122,24 @@ pub struct EntryMetadata {
|
|||
#[serde(default, alias = "CONTAINER_PADDING")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_padding: String,
|
||||
/// The padding of the container on mobile devices.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/padding>
|
||||
#[serde(default, alias = "CONTAINER_MOBILE_PADDING")]
|
||||
#[validate(max_length = 32)]
|
||||
pub container_mobile_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 maximum width of the container on mobile devices.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/max-width>
|
||||
#[serde(default, alias = "CONTAINER_MOBILE_MAX_WIDTH")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_mobile_max_width: String,
|
||||
/// The padding of the container.
|
||||
/// The color of the text in the inner container.
|
||||
#[serde(default, alias = "CONTAINER_INNER_FOREGROUND_COLOR")]
|
||||
|
@ -257,6 +269,12 @@ pub struct EntryMetadata {
|
|||
#[serde(default, alias = "CONTAINER_BORDER_WIDTH")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_width: String,
|
||||
/// The border around the container on mobile devices.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-width>
|
||||
#[serde(default, alias = "CONTAINER_MOBILE_BORDER_WIDTH")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_mobile_border_width: String,
|
||||
/// The border around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-style>
|
||||
|
@ -269,6 +287,12 @@ pub struct EntryMetadata {
|
|||
#[serde(default, alias = "CONTAINER_BORDER_RADIUS")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_border_radius: String,
|
||||
/// The border around the container on mobile devices.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius>
|
||||
#[serde(default, alias = "CONTAINER_MOBILE_BORDER_RADIUS")]
|
||||
#[validate(max_length = 16)]
|
||||
pub container_mobile_border_radius: String,
|
||||
/// The shadow around the container.
|
||||
///
|
||||
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow>
|
||||
|
@ -392,6 +416,18 @@ macro_rules! metadata_css {
|
|||
}
|
||||
};
|
||||
|
||||
($selector:expr, $property:literal, $self:ident.$field:ident->$output:ident, $media_query:literal) => {
|
||||
if !$self.$field.is_empty() {
|
||||
$output.push_str(&format!(
|
||||
"@media screen and ({}) {{ {} {{ {}: {}; }} }}\n",
|
||||
$media_query,
|
||||
$selector,
|
||||
$property,
|
||||
EntryMetadata::css_escape(&$self.$field)
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
($selector:expr, $property:literal, $field:ident->$output:ident) => {
|
||||
if !$field.is_empty() {
|
||||
$output.push_str(&format!(
|
||||
|
@ -526,7 +562,9 @@ impl EntryMetadata {
|
|||
let mut output = "<style>".to_string();
|
||||
|
||||
metadata_css!(".container", "padding", self.container_padding->output);
|
||||
metadata_css!(".container", "padding", self.container_mobile_padding->output, "max-width: 900px");
|
||||
metadata_css!(".container", "max-width", self.container_max_width->output);
|
||||
metadata_css!(".container", "max-width", self.container_mobile_max_width->output, "max-width: 900px");
|
||||
metadata_css!(".container", "color", self.container_inner_foreground_color->output);
|
||||
metadata_css!(".container", "background", self.container_inner_background->output);
|
||||
metadata_css!(".container", "background-color", self.container_inner_background_color->output);
|
||||
|
@ -550,7 +588,9 @@ impl EntryMetadata {
|
|||
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-width", self.container_mobile_border_width->output, "max-width: 900px");
|
||||
metadata_css!(".container", "border-radius", self.container_border_radius->output);
|
||||
metadata_css!(".container", "border-radius", self.container_mobile_border_radius->output, "max-width: 900px");
|
||||
metadata_css!(".container", "box-shadow", self.container_shadow->output);
|
||||
metadata_css!(".container", "text-shadow", self.content_text_shadow->output);
|
||||
metadata_css!("*, html *", "--color-link" !important, self.content_link_color->output);
|
||||
|
@ -641,7 +681,7 @@ impl EntryMetadata {
|
|||
}
|
||||
|
||||
if self.content_disable_paragraph_margin {
|
||||
output.push_str(".container p { margin: 0; }");
|
||||
output.push_str(".container p { margin: 0 !important; }");
|
||||
}
|
||||
|
||||
output + "</style>"
|
||||
|
|
|
@ -146,6 +146,10 @@ async fn view_request(
|
|||
};
|
||||
|
||||
slug = slug.to_lowercase();
|
||||
let viewed_header = (
|
||||
"Set-Cookie".to_string(),
|
||||
format!("Atto-Viewed=true; Path=/{slug}; Max-Age=86400"),
|
||||
);
|
||||
|
||||
let entry = match data
|
||||
.query(&SimplifiedQuery {
|
||||
|
@ -165,7 +169,10 @@ async fn view_request(
|
|||
&Error::GeneralNotFound("entry".to_string()).to_string(),
|
||||
);
|
||||
|
||||
return Html(tera.render("error.lisp", &ctx).unwrap());
|
||||
return (
|
||||
[viewed_header],
|
||||
Html(tera.render("error.lisp", &ctx).unwrap()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -179,7 +186,10 @@ async fn view_request(
|
|||
if let Err(e) = metadata.validate() {
|
||||
let mut ctx = default_context(&data, &build_code);
|
||||
ctx.insert("error", &e.to_string());
|
||||
return Html(tera.render("error.lisp", &ctx).unwrap());
|
||||
return (
|
||||
[viewed_header],
|
||||
Html(tera.render("error.lisp", &ctx).unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
@ -188,7 +198,10 @@ async fn view_request(
|
|||
{
|
||||
let mut ctx = default_context(&data, &build_code);
|
||||
ctx.insert("entry", &entry);
|
||||
return Html(tera.render("password.lisp", &ctx).unwrap());
|
||||
return (
|
||||
[viewed_header],
|
||||
Html(tera.render("password.lisp", &ctx).unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
// pull views
|
||||
|
@ -205,11 +218,18 @@ async fn view_request(
|
|||
// count view
|
||||
let views = r.value.parse::<usize>().unwrap();
|
||||
|
||||
if jar.get("Atto-Viewed").is_none() {
|
||||
// the Atto-Viewed cookie tells us if we've already viewed this
|
||||
// entry recently (at all in the past week)
|
||||
if let Err(e) = data.update(r.id, (views + 1).to_string()).await {
|
||||
let mut ctx = default_context(&data, &build_code);
|
||||
ctx.insert("error", &e.to_string());
|
||||
|
||||
return Html(tera.render("error.lisp", &ctx).unwrap());
|
||||
return (
|
||||
[viewed_header],
|
||||
Html(tera.render("error.lisp", &ctx).unwrap()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
views
|
||||
|
@ -220,7 +240,10 @@ async fn view_request(
|
|||
let mut ctx = default_context(&data, &build_code);
|
||||
ctx.insert("error", &e.to_string());
|
||||
|
||||
return Html(tera.render("error.lisp", &ctx).unwrap());
|
||||
return (
|
||||
[viewed_header],
|
||||
Html(tera.render("error.lisp", &ctx).unwrap()),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -240,10 +263,16 @@ async fn view_request(
|
|||
|
||||
if metadata.safety_content_warning.is_empty() | qflags.contains(&QuickFlag::AcceptWarning) {
|
||||
// regular view
|
||||
Html(tera.render("view.lisp", &ctx).unwrap())
|
||||
(
|
||||
[viewed_header],
|
||||
Html(tera.render("view.lisp", &ctx).unwrap()),
|
||||
)
|
||||
} else {
|
||||
// warning
|
||||
Html(tera.render("warning.lisp", &ctx).unwrap())
|
||||
(
|
||||
[viewed_header],
|
||||
Html(tera.render("warning.lisp", &ctx).unwrap()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue