templates and styles, copying from main site

This commit is contained in:
Butter 2026-04-07 21:03:38 -04:00
parent 2ee0dfc685
commit 8461e542de
6 changed files with 561 additions and 1 deletions

180
Cargo.lock generated
View File

@ -8,6 +8,61 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "askama"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
dependencies = [
"askama_derive",
"askama_escape",
"humansize",
"num-traits",
"percent-encoding",
]
[[package]]
name = "askama_axum"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41603f7cdbf5ac4af60760f17253eb6adf6ec5b6f14a7ed830cf687d375f163"
dependencies = [
"askama",
"axum-core",
"http",
]
[[package]]
name = "askama_derive"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
dependencies = [
"askama_parser",
"basic-toml",
"mime",
"mime_guess",
"proc-macro2",
"quote",
"serde",
"syn",
]
[[package]]
name = "askama_escape"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "askama_parser"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.89" version = "0.1.89"
@ -25,6 +80,12 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "axum" name = "axum"
version = "0.7.9" version = "0.7.9"
@ -80,6 +141,15 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "basic-toml"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.11.0" version = "2.11.0"
@ -132,6 +202,12 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-sink"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.32" version = "0.3.32"
@ -183,6 +259,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "http-range-header"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.1" version = "1.10.1"
@ -195,6 +277,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humansize"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
dependencies = [
"libm",
]
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.9.0" version = "1.9.0"
@ -242,6 +333,12 @@ version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
[[package]]
name = "libm"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.14" version = "0.4.14"
@ -275,6 +372,22 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.2.0" version = "1.2.0"
@ -286,6 +399,25 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.4" version = "1.21.4"
@ -371,8 +503,11 @@ name = "sarmentine"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"askama",
"askama_axum",
"axum", "axum",
"tokio", "tokio",
"tower-http",
] ]
[[package]] [[package]]
@ -388,6 +523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive",
] ]
[[package]] [[package]]
@ -523,6 +659,19 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tokio-util"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.5.3" version = "0.5.3"
@ -539,6 +688,31 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower-http"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
dependencies = [
"bitflags",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@ -571,6 +745,12 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.24" version = "1.0.24"

View File

@ -6,6 +6,9 @@ edition = "2024"
[dependencies] [dependencies]
axum = "0.7" axum = "0.7"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
askama = { version = "0.12", features = ["with-axum"] }
askama_axum = "0.4"
tower-http = { version = "0.5", features = ["fs"] }
# Error handling # Error handling
anyhow = "1" anyhow = "1"

View File

@ -1,9 +1,33 @@
use askama::Template;
use askama_axum::IntoResponse;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use tower_http::services::ServeDir;
// Represents the currently logged-in user, passed into every template.
// None = logged out.
pub struct CurrentUser {
pub username: String,
}
#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {
title: String,
current_user: Option<CurrentUser>,
}
async fn index() -> impl IntoResponse {
IndexTemplate {
title: "home".into(),
current_user: None, // no auth yet
}
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let app = Router::new() let app = Router::new()
.route("/", get(|| async { "sarmentine" })); .route("/", get(index))
.nest_service("/static", ServeDir::new("static"));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await .await

295
static/css/style.css Normal file
View File

@ -0,0 +1,295 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: #4a4060;
}
pre {
overflow-x: auto;
}
.site {
background: #fdf6e3;
color: #4a4060;
font-family: 'VT323', monospace;
font-size: 18px;
min-height: 100vh;
border: 3px ridge #d4879c;
position: relative;
overflow: hidden;
max-width: 960px;
margin: 0 auto;
}
.tiled-bg {
position: absolute;
inset: 0;
background-image: radial-gradient(circle, #d4879c22 1px, transparent 1px);
background-size: 20px 20px;
pointer-events: none;
}
.nav {
background: #4a4060;
display: flex;
border-bottom: 3px solid #d4879c;
font-family: 'Silkscreen', monospace;
font-size: 11px;
position: relative;
z-index: 1;
}
.nav a {
color: #fdf6e3;
padding: 6px 14px;
text-decoration: none;
border-right: 1px solid #6b5b8a;
display: block;
}
.nav a:hover {
background: #d4879c;
}
.layout {
display: flex;
position: relative;
z-index: 1;
}
.sidebar {
width: 120px;
flex-shrink: 0;
border-right: 2px solid #d4879c;
padding: 12px 8px;
background: #fdf6e3cc;
display: flex;
flex-direction: column;
gap: 12px;
}
.sidebar-box {
border: 2px solid #b48ead;
background: #fff8f0;
padding: 8px;
font-size: 14px;
line-height: 1.4;
}
.sidebar-box-title {
font-family: 'Silkscreen', monospace;
font-size: 10px;
color: #d4879c;
border-bottom: 1px solid #d4879c;
margin-bottom: 6px;
padding-bottom: 3px;
text-transform: uppercase;
}
.sidebar-text {
font-size: 14px;
color: #4a4060;
line-height: 1.5;
}
.sidebar-meta {
font-size: 12px;
color: #b48ead;
margin-bottom: 3px;
}
.visitor-counter {
font-family: 'Silkscreen', monospace;
font-size: 10px;
color: #4a4060;
background: #ebcb8b;
border: 2px inset #4a4060;
padding: 2px 6px;
display: inline-block;
letter-spacing: 2px;
margin-top: 4px;
}
.now-playing {
font-size: 13px;
color: #4a4060;
line-height: 1.5;
}
.marquee-wrap {
overflow: hidden;
border: 1px solid #d4879c;
background: #4a4060;
padding: 2px 0;
margin-top: 4px;
}
.marquee-inner {
display: inline-block;
color: #ebcb8b;
font-family: 'Silkscreen', monospace;
font-size: 9px;
white-space: nowrap;
animation: marquee 12s linear infinite;
}
@keyframes marquee {
from { transform: translateX(160px); }
to { transform: translateX(-100%); }
}
.blinkie {
display: inline-block;
background: #d4879c;
color: #fdf6e3;
font-family: 'Silkscreen', monospace;
font-size: 9px;
padding: 2px 4px;
animation: blink 1s step-end infinite;
width: 100%;
text-align: center;
}
@keyframes blink {
50% { background: #b48ead; }
}
.main {
flex: 1;
max-width: 100%;
min-width: 0;
height: auto;
padding: 16px;
background: #fdf6e3ee;
}
.main img {
max-width: 100%;
height: auto;
}
.site-title {
font-family: 'VT323', monospace;
font-size: 42px;
color: #d4879c;
text-shadow: 2px 2px 0 #b48ead, 4px 4px 0 #ebcb8b;
line-height: 1;
margin-bottom: 4px;
}
.site-subtitle {
font-family: 'Silkscreen', monospace;
font-size: 10px;
color: #b48ead;
margin-bottom: 16px;
letter-spacing: 1px;
}
.divider {
border: none;
border-top: 2px dashed #d4879c;
margin: 12px 0;
}
.post {
border: 2px solid #b48ead;
background: #fff8f0;
padding: 10px 12px;
margin-bottom: 10px;
}
.post-title {
font-family: 'Silkscreen', monospace;
font-size: 12px;
color: #d4879c;
margin-bottom: 4px;
text-decoration: none;
display: block;
}
.post-title:hover {
color: #b48ead;
}
.post-meta {
font-size: 13px;
color: #b48ead;
margin-bottom: 6px;
}
.post-body {
font-size: 16px;
color: #4a4060;
line-height: 1.3;
}
.tag {
display: inline-block;
background: #b48ead;
color: #fdf6e3;
font-family: 'Silkscreen', monospace;
font-size: 9px;
padding: 2px 5px;
margin-right: 3px;
}
.status-bar {
background: #4a4060;
color: #ebcb8b;
font-family: 'Silkscreen', monospace;
font-size: 9px;
padding: 3px 8px;
display: flex;
justify-content: space-between;
border-top: 2px solid #d4879c;
position: relative;
z-index: 1;
}
.button-large {
image-rendering: pixelated;
width: calc(88px * 2);
height: calc(31px * 2);
}
/* nav right (login/logout) */
.nav {
justify-content: flex-start;
}
.nav-right {
margin-left: auto;
display: flex;
}
.nav-right a,
.nav-logout {
color: #fdf6e3;
padding: 6px 14px;
text-decoration: none;
border-left: 1px solid #6b5b8a;
display: block;
font-family: 'Silkscreen', monospace;
font-size: 11px;
background: none;
border-top: none;
border-right: none;
border-bottom: none;
cursor: pointer;
}
.nav-right a:hover,
.nav-logout:hover {
background: #d4879c;
}
/* layout: no sidebar */
.layout {
display: block;
}
.main {
max-width: 100%;
}

43
templates/base.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }} :: sarmentine</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=VT323&family=Silkscreen&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<div class="site">
<div class="tiled-bg"></div>
<nav class="nav">
<a href="/">home</a>
<a href="/threads">threads</a>
<div class="nav-right">
{% if let Some(user) = current_user %}
<a href="/profile/{{ user.username }}">{{ user.username }}</a>
<form method="post" action="/auth/logout" style="display:inline;">
<button type="submit" class="nav-logout">logout</button>
</form>
{% else %}
<a href="/auth/login">login</a>
<a href="/auth/register">register</a>
{% endif %}
</div>
</nav>
<div class="layout">
<div class="main">
{% block content %}{% endblock %}
</div>
</div>
<div class="status-bar">
<span>rhizomatic queer ascendance</span>
<span>toasterdragon.com</span>
</div>
</div>
</body>
</html>

15
templates/index.html Normal file
View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block content %}
<div class="site-title">sarmentine</div>
<div class="site-subtitle">:: who was that? ::</div>
<hr class="divider">
<div style="font-family:'Silkscreen',monospace; font-size:11px; color:#d4879c; margin-bottom:8px; text-transform:uppercase;">recent threads</div>
<div class="post">
<div class="post-title">nothing here yet...</div>
<div class="post-body">be the first to post :3</div>
</div>
{% endblock %}