From 379ac5869d8b9211a1814d4ac8caf34e7c108e93 Mon Sep 17 00:00:00 2001 From: charles Date: Wed, 17 Dec 2025 23:36:41 -0800 Subject: [PATCH] add: basic obsidian mode --- Cargo.lock | 39 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 11 +++++++++++ src/main.rs | 27 ++++++++++++++++++++++----- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a59bc2b..4e131b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -851,6 +860,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + [[package]] name = "rikidown" version = "0.1.0" @@ -859,6 +897,7 @@ dependencies = [ "git2", "markdown", "minijinja", + "regex", "rouille", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 566a23d..c76513d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ markdown = "1.0.0" rouille = "3.6.2" minijinja = { version = "2.1.0", features = ["loader"] } serde = { version = "1.0.203", features = ["derive"] } +regex = "1" diff --git a/README.md b/README.md index d658220..9633f39 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,17 @@ The arguments supported include: - --git-repo the URL of the git repo to load - --listen address to listen on, in the form of 0.0.0.0:8080 +- --obsidian-mode pre-processes markdown files to make them more standard compliant Additionally, if there is a './template.html' file in the repo, is will be used to wrap the generated HTML. Specifically, the 'body' block will be replaced by the markdown content. + +## Obsidian mode + +Intended to create compatibility with Obsidian, because it has a fancy UI. +Specifically, this replaces `[[thing]]` with `[thing](thing.md)` +None of the other options in https://help.obsidian.md/obsidian-flavored-markdown are included. + +## Future work + +Replace the Obisdian markdown with https://crates.io/crates/obsidian-export. diff --git a/src/main.rs b/src/main.rs index e545546..9541101 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use clap::Parser; use git2::Repository; use minijinja::Environment; +use regex::Regex; use rouille::Response; use serde::Serialize; @@ -19,12 +20,21 @@ struct Args { /// Address to listen on, in the form of 0.0.0.0:8080 #[arg(long, default_value = "0.0.0.0:8080")] listen: String, + + /// pre-processes markdown files to make them more standard compliant + #[arg(long)] + obsidian_mode: bool, } #[derive(Serialize)] struct Context {} -fn render_page(repo_path: &PathBuf, path: &Path) -> Response { +fn preprocess_obsidian(md: &str) -> String { + let re = Regex::new(r"\[\[(.*?)\]\]").unwrap(); + re.replace_all(md, "[$1]($1.md)").to_string() +} + +fn render_page(repo_path: &PathBuf, path: &Path, obsidian_mode: bool) -> Response { let md = match fs::read_to_string(path) { Ok(md) => md, Err(e) => { @@ -33,6 +43,12 @@ fn render_page(repo_path: &PathBuf, path: &Path) -> Response { } }; + let md = if obsidian_mode { + preprocess_obsidian(&md) + } else { + md + }; + let body_html = markdown::to_html(&md); let template_path = repo_path.join("template.html"); @@ -95,6 +111,7 @@ fn main() { println!("Listening on: {}", args.listen); + let obsidian_mode = args.obsidian_mode; rouille::start_server(args.listen, move |request| { let url = request.url(); let requested_path = repo_path.join(url.trim_start_matches('/')); @@ -103,18 +120,18 @@ fn main() { if requested_path.is_dir() { let index_path = requested_path.join("index.md"); if index_path.is_file() { - return render_page(&repo_path, &index_path); + return render_page(&repo_path, &index_path, obsidian_mode); } let readme_path = requested_path.join("README.md"); if readme_path.is_file() { - return render_page(&repo_path, &readme_path); + return render_page(&repo_path, &readme_path, obsidian_mode); } } // Check if the path as-is is a file. e.g. /README.md if requested_path.is_file() { if requested_path.extension().and_then(std::ffi::OsStr::to_str) == Some("md") { - return render_page(&repo_path, &requested_path); + return render_page(&repo_path, &requested_path, obsidian_mode); } // For now, 404 on other file types. A real implementation might serve static files. return Response::empty_404(); @@ -124,7 +141,7 @@ fn main() { let mut md_path = requested_path; md_path.set_extension("md"); if md_path.is_file() { - return render_page(&repo_path, &md_path); + return render_page(&repo_path, &md_path, obsidian_mode); } Response::empty_404()