diff --git a/app/public/app.js b/app/public/app.js
index ebe8480..1689f5d 100644
--- a/app/public/app.js
+++ b/app/public/app.js
@@ -349,3 +349,20 @@ globalThis.toggle_metadata_css = (e) => {
window.location.reload();
}
};
+
+globalThis.hash_check = (hash) => {
+ if (hash.startsWith("#/")) {
+ for (const x of Array.from(document.querySelectorAll(".subpage"))) {
+ x.classList.add("hidden");
+ }
+
+ document.getElementById(hash).classList.remove("hidden");
+ }
+};
+
+window.addEventListener("hashchange", (_) => hash_check(window.location.hash));
+
+setTimeout(() => {
+ // run initial hash check
+ hash_check(window.location.hash);
+}, 150);
diff --git a/app/public/style.css b/app/public/style.css
index 1ff752a..bc1829a 100644
--- a/app/public/style.css
+++ b/app/public/style.css
@@ -709,3 +709,20 @@ table tr:not(thead *):nth-child(odd) {
table thead th {
text-align: left;
}
+
+/* details */
+details {
+ width: 100%;
+ margin: var(--pad-4) 0;
+}
+
+details summary {
+ background: var(--color-super-raised);
+ padding: var(--pad-2) var(--pad-4);
+ cursor: pointer;
+}
+
+details .content {
+ padding: var(--pad-4);
+ background: var(--color-surface);
+}
diff --git a/src/markdown.rs b/src/markdown.rs
index a5ac837..aba7642 100644
--- a/src/markdown.rs
+++ b/src/markdown.rs
@@ -1,11 +1,13 @@
use std::collections::HashSet;
pub fn render_markdown(input: &str) -> String {
- let html = tetratto_shared::markdown::render_markdown_dirty(&parse_text_color(
- &parse_highlight(&parse_link(&parse_image(&parse_image_size(
- &parse_underline(&parse_comment(&input.replace("[/]", "
"))),
+ let html = tetratto_shared::markdown::render_markdown_dirty(&parse_page(&parse_details(
+ &parse_text_color(&parse_highlight(&parse_link(&parse_image(
+ &parse_image_size(&parse_underline(&parse_comment(
+ &input.replace("[/]", "
"),
+ ))),
)))),
- ))
+ )))
.replace("$per", "%");
let mut allowed_attributes = HashSet::new();
@@ -704,3 +706,180 @@ pub fn parse_image(input: &str) -> String {
pub fn parse_link(input: &str) -> String {
parser_ignores_pre!(parse_link_line, input)
}
+
+/// Match page definitions.
+///
+/// Each page is denoted with two at symbols, followed by the name of the page.
+/// The page can also have an optional second argument (separated by a semicolon)
+/// which accepts the "visible" value; marking the page as visible by default.
+///
+/// To close a page (after you're done with the page's content), just put two
+/// at symbols with nothing else on the line.
+///
+/// You're able to put content AFTER the page closing line. This allows you to have
+/// persistant content which is shared between every page. Only content within pages
+/// is hidden when navigating to another page. This means everything in the entry
+/// that isn't part of a page will remian throughout navigations.
+///
+/// # Example
+/// ```md
+/// @@ home; visible
+/// this is the homepage which is shown by default!
+/// @@
+///
+/// @@ about
+/// this is the about page which is NOT shown by default! a link with an href of "#/about" will open this page
+/// @@
+/// ```
+pub fn parse_page(input: &str) -> String {
+ let mut output = String::new();
+ let mut buffer = String::new();
+ let mut page_id = String::new();
+
+ let mut start_shown = false;
+ let mut in_page = false;
+ let mut in_pre = false;
+
+ for line in input.split("\n") {
+ if line.starts_with("```") {
+ in_pre = !in_pre;
+
+ if in_page {
+ buffer.push_str(&format!("{line}\n"));
+ } else {
+ output.push_str(&format!("{line}\n"));
+ }
+
+ continue;
+ }
+
+ if in_pre {
+ if in_page {
+ buffer.push_str(&format!("{line}\n"));
+ } else {
+ output.push_str(&format!("{line}\n"));
+ }
+
+ continue;
+ }
+
+ // not in pre
+ if line == "@@" {
+ // ending block
+ if in_page {
+ output.push_str(&format!(
+ "