commit bfd3bc610f0f12566b8cb88fd6fdb34bb6b2774d
parent e17d62cf8c2c507166cf231ec2490bd19957cb14
Author: alex wennerberg <alex@alexwennerberg.com>
Date: Thu, 16 Dec 2021 00:10:16 -0800
Begin horrorshow rewrite
build is broken / buggy at this point
Diffstat:
8 files changed, 208 insertions(+), 374 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -24,64 +24,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
-name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
-[[package]]
-name = "askama"
-version = "0.10.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134"
-dependencies = [
- "askama_derive",
- "askama_escape",
- "askama_shared",
-]
-
-[[package]]
-name = "askama_derive"
-version = "0.10.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522"
-dependencies = [
- "askama_shared",
- "proc-macro2",
- "syn",
-]
-
-[[package]]
-name = "askama_escape"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb"
-
-[[package]]
-name = "askama_shared"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2582b77e0f3c506ec4838a25fa8a5f97b9bed72bb6d3d272ea1c031d8bd373bc"
-dependencies = [
- "askama_escape",
- "humansize",
- "nom",
- "num-traits",
- "percent-encoding",
- "proc-macro2",
- "quote",
- "serde",
- "syn",
- "toml",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -97,24 +39,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
-name = "bitflags"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-
-[[package]]
-name = "bitvec"
-version = "0.19.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
name = "block-buffer"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -151,7 +75,7 @@ version = "0.1.0"
dependencies = [
"ammonia",
"anyhow",
- "askama",
+ "horrorshow",
"linkify",
"mailparse",
"mbox-reader",
@@ -202,12 +126,6 @@ dependencies = [
]
[[package]]
-name = "funty"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
-
-[[package]]
name = "futf"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -239,6 +157,12 @@ dependencies = [
]
[[package]]
+name = "horrorshow"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8371fb981840150b1a54f7cb117bf6699f7466a1d4861daac33bc6fe2b5abea0"
+
+[[package]]
name = "html5ever"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -253,12 +177,6 @@ dependencies = [
]
[[package]]
-name = "humansize"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
-
-[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -282,19 +200,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
-name = "lexical-core"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
-dependencies = [
- "arrayvec",
- "bitflags",
- "cfg-if",
- "ryu",
- "static_assertions",
-]
-
-[[package]]
name = "libc"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -405,28 +310,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
-name = "nom"
-version = "6.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
-dependencies = [
- "bitvec",
- "funty",
- "lexical-core",
- "memchr",
- "version_check",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -528,12 +411,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5"
[[package]]
-name = "radium"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
-
-[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -585,30 +462,10 @@ dependencies = [
]
[[package]]
-name = "ryu"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
-
-[[package]]
name = "serde"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.126"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
[[package]]
name = "sha3"
@@ -627,12 +484,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1"
[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
name = "string_cache"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -669,12 +520,6 @@ dependencies = [
]
[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
name = "tendril"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -712,15 +557,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
-name = "toml"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
-dependencies = [
- "serde",
-]
-
-[[package]]
name = "typenum"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -815,12 +651,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
-name = "wyz"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
-
-[[package]]
name = "xml5ever"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
@@ -13,7 +13,7 @@ html = ["ammonia"]
[dependencies]
ammonia = {version = "3", optional = true}
anyhow = "1.0"
-askama = {version = "0.10", default_feature = false}
+horrorshow = "0.8.4"
linkify = "0.8.0"
mailparse = "0.13"
mbox-reader = "0.2.0" #unamaintained, should remove dep
diff --git a/src/filters.rs b/src/filters.rs
@@ -1,105 +0,0 @@
-use linkify::{LinkFinder, LinkKind};
-use std::time::{SystemTime, UNIX_EPOCH};
-
-pub fn time_ago(amount: &u64) -> askama::Result<String> {
- Ok(timeago(*amount))
-}
-
-const SOLAR_YEAR_SECS: u64 = 31556926;
-// TODO filter body:
-// add <span> for lines starting with > to make them grey
-// parse hyperlinks for you
-
-// stolen from
-// https://github.com/robinst/linkify/blob/demo/src/lib.rs#L5
-
-pub fn email_body(body: &str) -> askama::Result<String> {
- let mut bytes = Vec::new();
- let mut in_reply: bool = false;
- for line in body.lines() {
- if line.starts_with(">") || (line.starts_with("On ") && line.ends_with("wrote:")) {
- if !in_reply {
- in_reply = true;
- bytes.extend_from_slice(b"<span class='reply-text'>");
- }
- } else if in_reply {
- bytes.extend_from_slice(b"</span>");
- in_reply = false
- }
-
- let mut finder = LinkFinder::new();
- for span in finder.spans(line) {
- match span.kind() {
- Some(LinkKind::Url) => {
- bytes.extend_from_slice(b"<a href=\"");
- escape(span.as_str(), &mut bytes);
- bytes.extend_from_slice(b"\">");
- escape(span.as_str(), &mut bytes);
- bytes.extend_from_slice(b"</a>");
- }
- Some(LinkKind::Email) => {
- bytes.extend_from_slice(b"<a href=\"mailto:");
- escape(span.as_str(), &mut bytes);
- bytes.extend_from_slice(b"\">");
- escape(span.as_str(), &mut bytes);
- bytes.extend_from_slice(b"</a>");
- }
- _ => {
- escape(span.as_str(), &mut bytes);
- }
- }
- }
- bytes.extend(b"\n");
- }
- if in_reply {
- bytes.extend_from_slice(b"</span>");
- }
- // TODO err conversion
- Ok(String::from_utf8(bytes).expect("not utf8"))
-}
-
-fn escape(text: &str, dest: &mut Vec<u8>) {
- for c in text.bytes() {
- match c {
- b'&' => dest.extend_from_slice(b"&"),
- b'<' => dest.extend_from_slice(b"<"),
- b'>' => dest.extend_from_slice(b">"),
- b'"' => dest.extend_from_slice(b"""),
- b'\'' => dest.extend_from_slice(b"'"),
- _ => dest.push(c),
- }
- }
-}
-
-fn timeago(unixtime: u64) -> String {
- let current_time = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .unwrap()
- .as_secs();
- if unixtime > current_time {
- return "in the future".to_owned();
- }
- let diff = current_time - unixtime;
- let amount: u64;
- let metric: &str;
- if diff < 60 {
- amount = diff;
- metric = "second";
- } else if diff < 60 * 60 {
- amount = diff / 60;
- metric = "minute";
- } else if diff < 60 * 60 * 24 {
- amount = diff / (60 * 60);
- metric = "hour";
- } else if diff < SOLAR_YEAR_SECS * 2 {
- amount = diff / (60 * 60 * 24);
- metric = "day";
- } else {
- amount = diff / SOLAR_YEAR_SECS * 2;
- metric = "year";
- }
- match amount {
- 1 => format!("{} {} ago", amount, metric),
- _ => format!("{} {}s ago", amount, metric),
- }
-}
diff --git a/src/main.rs b/src/main.rs
@@ -1,5 +1,15 @@
use anyhow::{anyhow, Context, Result};
-use askama::Template;
+use horrorshow::helper::doctype;
+use horrorshow::owned_html;
+use horrorshow::prelude::*;
+use horrorshow::Template;
+use std::io;
+use std::io::BufWriter;
+use std::path::Path;
+
+#[macro_use]
+extern crate horrorshow;
+
use mailparse::*;
use mbox_reader::MboxFile;
use sha3::{
@@ -13,7 +23,7 @@ use urlencoding;
use config::{Config, INSTANCE};
mod config;
-mod filters;
+mod utils;
// TODO be more clear about the expected input types
// maildi
@@ -39,6 +49,52 @@ struct MailThread<'a> {
last_reply: u64,
}
+fn layout(page_title: impl Render, content: impl Render) -> impl Render {
+ // owned_html _moves_ the arguments into the template. Useful for returning
+ // owned (movable) templates.
+ owned_html! {
+ : doctype::HTML;
+ html {
+ head {
+ title : &page_title;
+ : Raw("<meta http-equiv='Permissions-Policy' content='interest-cohort=()'/>
+ <link rel='stylesheet' type='text/css' href='style.css' />
+ <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0' />
+ <link rel='icon' href='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📧</text></svg>'>");
+ meta(name="description", content=&page_title);
+ }
+ body {
+ main {
+ :&content
+ }
+ hr;
+ div(class="footer") {
+ : Raw("Archive generated with <a href='https://git.alexwennerberg.com/crabmail/'>crabmail</a>")
+ }
+ }
+ }
+ }
+}
+
+struct ThreadList<'a> {
+ threads: Vec<MailThread<'a>>,
+}
+
+impl<'a> ThreadList<'a> {
+ pub fn write_to_file(&self) -> Result<()> {
+ let tmp = html! {
+ h1(class="page-title"): &Config::global().list_name;
+ a(href=format!("mailto:{}", &Config::global().list_email)) {
+ : &Config::global().list_email
+ }
+ hr;
+ @ for thread in &self.threads {
+ }
+ };
+ Ok(())
+ }
+}
+
impl<'a> MailThread<'a> {
pub fn last_reply(&self) -> u64 {
return self.messages[self.messages.len() - 1].date;
@@ -47,6 +103,49 @@ impl<'a> MailThread<'a> {
fn build_atom_feed() -> String {
String::new()
}
+
+ fn write_to_file(&self, out_dir: &Path) -> Result<()> {
+ let root = self.messages[0];
+ let tmp = html! {
+ h1(class="page-title"): &root.subject;
+ div {
+ @ for message in &self.messages {
+ hr;
+ div(id=&message.id, class="message") {
+ a(href=format!("mailto:{}", &message.from.addr), class="addr") {
+ : &message.from.to_string();
+ }
+ }
+ span(class="timeago") {
+ : utils::timeago(message.date)
+ }
+ a(title="permalink", href=format!("#{}", &message.id)) {
+ : "🔗"
+ }
+ @ if message.in_reply_to.is_some() { // TODO figure out match
+ a(title="replies-to", href=format!("#{}", message.in_reply_to.clone().unwrap())){
+ : "Re:"
+ }
+ }
+ div(class="email-body") {
+ : Raw(utils::email_body(&message.body))
+ }
+ div(class="right"){
+ a (href=message.mailto()) {
+ :"✉️ reply"
+ }
+ }
+ }
+ }
+ };
+ let thread_dir = out_dir.join("threads");
+ std::fs::create_dir(&thread_dir).ok();
+
+ let mut file = File::create(&thread_dir.join(format!("{}.html", &self.hash)))?;
+ let mut br = BufWriter::new(file);
+ layout(root.subject.as_str(), tmp).write_to_io(&mut br)?;
+ Ok(())
+ }
}
impl Email {
@@ -219,9 +318,6 @@ fn main() -> Result<()> {
})
.collect();
std::fs::create_dir(&out_dir).ok();
- let thread_dir = &out_dir.join("threads");
- std::fs::create_dir(thread_dir).ok();
-
let mut threads = vec![];
for root in &mut thread_roots {
let mut thread_ids = vec![];
@@ -251,37 +347,18 @@ fn main() -> Result<()> {
thread.last_reply = thread.last_reply();
- let mut file = File::create(thread_dir.join(format!("{}.html", thread.hash)))?;
- file.write(
- Thread {
- thread: &thread,
- config: Config::global(),
- }
- .render()?
- .as_bytes(),
- )
- .ok();
-
+ thread.write_to_file(&out_dir);
threads.push(thread);
}
threads.sort_by_key(|a| a.last_reply);
threads.reverse();
let mut file = File::create(out_dir.join("index.html"))?;
- file.write(
- ThreadList {
- threads: threads,
- config: Config::global(),
- }
- .render()?
- .as_bytes(),
- )
- .ok();
// kinda clunky
let css = include_bytes!("style.css");
let mut css_root = File::create(out_dir.join("style.css"))?;
css_root.write(css);
- let mut css_sub = File::create(thread_dir.join("style.css"))?;
+ let mut css_sub = File::create(out_dir.join("threads").join("style.css"))?;
css_sub.write(css);
Ok(())
}
@@ -293,18 +370,3 @@ fn remove_missing() {}
fn parse_path(s: &std::ffi::OsStr) -> Result<std::path::PathBuf, &'static str> {
Ok(s.into())
}
-
-#[derive(Template)]
-#[template(path = "thread.html")]
-struct Thread<'a> {
- thread: &'a MailThread<'a>,
- config: &'a Config,
-}
-
-#[derive(Template)]
-#[template(path = "threadlist.html")]
-struct ThreadList<'a> {
- // message root
- threads: Vec<MailThread<'a>>,
- config: &'a Config, // Not ideal repetition
-}
diff --git a/src/style.css b/src/style.css
@@ -59,7 +59,7 @@ table { border-spacing: 0.5em 0.1em; }
font-size: .9rem;
}
-hr.thin {
+hr {
border: 0;
height: 0;
color: lightgrey;
diff --git a/src/utils.rs b/src/utils.rs
@@ -0,0 +1,96 @@
+use linkify::{LinkFinder, LinkKind};
+use std::time::{SystemTime, UNIX_EPOCH};
+
+const SOLAR_YEAR_SECS: u64 = 31556926;
+// partly stolen from
+// https://github.com/robinst/linkify/blob/demo/src/lib.rs#L5
+
+pub fn email_body(body: &str) -> String {
+ let mut bytes = Vec::new();
+ let mut in_reply: bool = false;
+ for line in body.lines() {
+ if line.starts_with(">") || (line.starts_with("On ") && line.ends_with("wrote:")) {
+ if !in_reply {
+ in_reply = true;
+ bytes.extend_from_slice(b"<span class='reply-text'>");
+ }
+ } else if in_reply {
+ bytes.extend_from_slice(b"</span>");
+ in_reply = false
+ }
+
+ let mut finder = LinkFinder::new();
+ for span in finder.spans(line) {
+ match span.kind() {
+ Some(LinkKind::Url) => {
+ bytes.extend_from_slice(b"<a href=\"");
+ escape(span.as_str(), &mut bytes);
+ bytes.extend_from_slice(b"\">");
+ escape(span.as_str(), &mut bytes);
+ bytes.extend_from_slice(b"</a>");
+ }
+ Some(LinkKind::Email) => {
+ bytes.extend_from_slice(b"<a href=\"mailto:");
+ escape(span.as_str(), &mut bytes);
+ bytes.extend_from_slice(b"\">");
+ escape(span.as_str(), &mut bytes);
+ bytes.extend_from_slice(b"</a>");
+ }
+ _ => {
+ escape(span.as_str(), &mut bytes);
+ }
+ }
+ }
+ bytes.extend(b"\n");
+ }
+ if in_reply {
+ bytes.extend_from_slice(b"</span>");
+ }
+ // TODO err conversion
+ String::from_utf8(bytes).unwrap()
+}
+
+fn escape(text: &str, dest: &mut Vec<u8>) {
+ for c in text.bytes() {
+ match c {
+ b'&' => dest.extend_from_slice(b"&"),
+ b'<' => dest.extend_from_slice(b"<"),
+ b'>' => dest.extend_from_slice(b">"),
+ b'"' => dest.extend_from_slice(b"""),
+ b'\'' => dest.extend_from_slice(b"'"),
+ _ => dest.push(c),
+ }
+ }
+}
+pub fn timeago(unixtime: u64) -> String {
+ let current_time = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap()
+ .as_secs();
+ if unixtime > current_time {
+ return "in the future".to_owned();
+ }
+ let diff = current_time - unixtime;
+ let amount: u64;
+ let metric: &str;
+ if diff < 60 {
+ amount = diff;
+ metric = "second";
+ } else if diff < 60 * 60 {
+ amount = diff / 60;
+ metric = "minute";
+ } else if diff < 60 * 60 * 24 {
+ amount = diff / (60 * 60);
+ metric = "hour";
+ } else if diff < SOLAR_YEAR_SECS * 2 {
+ amount = diff / (60 * 60 * 24);
+ metric = "day";
+ } else {
+ amount = diff / SOLAR_YEAR_SECS * 2;
+ metric = "year";
+ }
+ match amount {
+ 1 => format!("{} {} ago", amount, metric),
+ _ => format!("{} {}s ago", amount, metric),
+ }
+}
diff --git a/templates/base.html b/templates/base.html
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="Permissions-Policy" content="interest-cohort=()"/>
- <link rel="stylesheet" type="text/css" href="style.css" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0" />
- <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📧</text></svg>">
- <meta name="description" content="{{config.list_name}}">
- <title>{{config.list_name}}</title>
- {% block head %}{% endblock %}
- </head>
- <body>
- <main>
- <div id="content">
- {% block content %}{% endblock %}
- </div>
- <hr class="thin">
- <div class="footer">
- Archive generated with <a href="https://git.alexwennerberg.com/crabmail/">crabmail</a>
- </div>
- </main>
- </body>
-</html>
diff --git a/templates/thread.html b/templates/thread.html
@@ -1,25 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
- {% let root = thread.messages[0] %}
-<div class="page-title"><h1>{{root.subject}}</h1></div>
- <div>
- {% for message in thread.messages %}
- <hr class="thin">
- <div id="{{message.id}}" class="message">
- <b>
- <a href="mailto:{{message.from.addr}}" class="addr">{{message.from}}</a></b>
- <span class="timeago">{{message.date | time_ago}}</span>
- <a title="permalink" href="#{{message.id}}">🔗</a>
- {% match message.in_reply_to %}{% when Some with (replies_to) %}
- <a title="replies-to" href="#{{replies_to}}">Re:</a>{%when none %}{% endmatch %}
- <div class="email-body">
- {{message.body | email_body | safe }}
- </div>
- <div class="right">
- <a href="{{message.mailto()}}">✉️ reply</a>
- </div>
- {% endfor %}
- </div>
- </div>
-{% endblock %}