add: basic obsidian mode

This commit is contained in:
2025-12-17 23:36:41 -08:00
parent a01cda4566
commit 379ac5869d
4 changed files with 73 additions and 5 deletions

39
Cargo.lock generated
View File

@@ -8,6 +8,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 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]] [[package]]
name = "alloc-no-stdlib" name = "alloc-no-stdlib"
version = "2.0.4" version = "2.0.4"
@@ -851,6 +860,35 @@ dependencies = [
"bitflags", "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]] [[package]]
name = "rikidown" name = "rikidown"
version = "0.1.0" version = "0.1.0"
@@ -859,6 +897,7 @@ dependencies = [
"git2", "git2",
"markdown", "markdown",
"minijinja", "minijinja",
"regex",
"rouille", "rouille",
"serde", "serde",
] ]

View File

@@ -10,3 +10,4 @@ markdown = "1.0.0"
rouille = "3.6.2" rouille = "3.6.2"
minijinja = { version = "2.1.0", features = ["loader"] } minijinja = { version = "2.1.0", features = ["loader"] }
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.203", features = ["derive"] }
regex = "1"

View File

@@ -16,6 +16,17 @@ The arguments supported include:
- --git-repo the URL of the git repo to load - --git-repo the URL of the git repo to load
- --listen address to listen on, in the form of 0.0.0.0:8080 - --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 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. 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.

View File

@@ -1,6 +1,7 @@
use clap::Parser; use clap::Parser;
use git2::Repository; use git2::Repository;
use minijinja::Environment; use minijinja::Environment;
use regex::Regex;
use rouille::Response; use rouille::Response;
use serde::Serialize; use serde::Serialize;
@@ -19,12 +20,21 @@ struct Args {
/// Address to listen on, in the form of 0.0.0.0:8080 /// Address to listen on, in the form of 0.0.0.0:8080
#[arg(long, default_value = "0.0.0.0:8080")] #[arg(long, default_value = "0.0.0.0:8080")]
listen: String, listen: String,
/// pre-processes markdown files to make them more standard compliant
#[arg(long)]
obsidian_mode: bool,
} }
#[derive(Serialize)] #[derive(Serialize)]
struct Context {} 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) { let md = match fs::read_to_string(path) {
Ok(md) => md, Ok(md) => md,
Err(e) => { 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 body_html = markdown::to_html(&md);
let template_path = repo_path.join("template.html"); let template_path = repo_path.join("template.html");
@@ -95,6 +111,7 @@ fn main() {
println!("Listening on: {}", args.listen); println!("Listening on: {}", args.listen);
let obsidian_mode = args.obsidian_mode;
rouille::start_server(args.listen, move |request| { rouille::start_server(args.listen, move |request| {
let url = request.url(); let url = request.url();
let requested_path = repo_path.join(url.trim_start_matches('/')); let requested_path = repo_path.join(url.trim_start_matches('/'));
@@ -103,18 +120,18 @@ fn main() {
if requested_path.is_dir() { if requested_path.is_dir() {
let index_path = requested_path.join("index.md"); let index_path = requested_path.join("index.md");
if index_path.is_file() { 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"); let readme_path = requested_path.join("README.md");
if readme_path.is_file() { 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 // Check if the path as-is is a file. e.g. /README.md
if requested_path.is_file() { if requested_path.is_file() {
if requested_path.extension().and_then(std::ffi::OsStr::to_str) == Some("md") { 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. // For now, 404 on other file types. A real implementation might serve static files.
return Response::empty_404(); return Response::empty_404();
@@ -124,7 +141,7 @@ fn main() {
let mut md_path = requested_path; let mut md_path = requested_path;
md_path.set_extension("md"); md_path.set_extension("md");
if md_path.is_file() { if md_path.is_file() {
return render_page(&repo_path, &md_path); return render_page(&repo_path, &md_path, obsidian_mode);
} }
Response::empty_404() Response::empty_404()