diff --git a/.gitattributes b/.gitattributes index 58b8264..71cf752 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ *.png filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text diff --git a/Cargo.lock b/Cargo.lock index 84be85a..c90e435 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,6 +256,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -476,15 +482,16 @@ dependencies = [ [[package]] name = "cached" -version = "0.45.1" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eb5776f28a149524d1d8623035760b4454ec881e8cf3838fa8d7e1b11254b3" +checksum = "8cead8ece0da6b744b2ad8ef9c58a4cdc7ef2921e60a6ddfb9eaaa86839b5fc5" dependencies = [ + "ahash", "async-trait", "cached_proc_macro", "cached_proc_macro_types", "futures", - "hashbrown 0.13.2", + "hashbrown 0.14.1", "instant", "once_cell", "thiserror", @@ -611,11 +618,12 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comrak" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "482aa5695bca086022be453c700a40c02893f1ba7098a2c88351de55341ae894" +checksum = "82c995deda3bfdebd07d0e2af79e9da13e4b1be652b21a746f3f5b24bf0a49ef" dependencies = [ "clap", + "derive_builder", "entities", "memchr", "once_cell", @@ -696,7 +704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd" dependencies = [ "derive_more", - "indexmap", + "indexmap 1.9.3", "nom", ] @@ -819,6 +827,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -1020,7 +1034,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -1043,6 +1057,16 @@ dependencies = [ "bumpalo", ] +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +dependencies = [ + "ahash", + "allocator-api2", +] + [[package]] name = "heck" version = "0.3.3" @@ -1208,6 +1232,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown 0.14.1", +] + [[package]] name = "instant" version = "0.1.12" @@ -1708,7 +1742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" dependencies = [ "base64", - "indexmap", + "indexmap 1.9.3", "line-wrap", "quick-xml", "serde", @@ -2058,9 +2092,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -2083,7 +2117,7 @@ version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itoa", "ryu", "serde", @@ -2358,9 +2392,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", "serde_spanned", @@ -2370,20 +2404,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.9" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d964908cec0d030b812013af25a0e57fddfadb1e066ecc6681d86253129d4f" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap", + "indexmap 2.0.2", "serde", "serde_spanned", "toml_datetime", @@ -2803,9 +2837,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index b32cda4..950b999 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,15 +12,15 @@ license = "AGPL-3.0-or-later" [dependencies] actix-web = "4.4" actix-files = "0.6" -cached = { version = "0.45", features = ["async"] } +cached = { version = "0.46", features = ["async"] } ramhorns = "0.14" -toml = "0.7" +toml = "0.8" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" minify-html = "0.11" minify-js = "0.5" glob = "0.3" -comrak = "0.18" +comrak = "0.19" reqwest = { version = "0.11", features = ["json"] } chrono = "0.4.30" chrono-tz = "0.8" diff --git a/README.md b/README.md index 71cf1d2..8604739 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ onion = "http://youraddress.onion/" ## Example of [`config.toml`](./config/config.toml) ```toml -mail = your.mail at host.com" +mail = "your.mail at host.com" lang = "lang" onion = "http://youraddress.onion/" app_name = "Nickname" diff --git a/src/config.rs b/src/config.rs index d6601f6..e3d6b1e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -124,6 +124,7 @@ fn init(dist_dir: String, static_dir: String, templates_dir: String) -> String { keep_closing_tags: true, minify_css: true, minify_js: true, + remove_bangs: false, ..minify_html::Cfg::spec_compliant() }; diff --git a/src/main.rs b/src/main.rs index d21a16e..5aafe56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use actix_web::{middleware::DefaultHeaders, web, App, HttpServer}; use std::io::Result; use crate::routes::{ - agreements, api_v1, blog, contrib, cours, cv, gaming, index, memorial, networks, not_found, + agreements, api_v1, blog, contact, contrib, cours, cv, gaming, index, memorial, not_found, portfolio, setup, web3, }; @@ -29,10 +29,15 @@ async fn main() -> Result<()> { HttpServer::new(move || { App::new() .app_data(web::Data::new(config.to_owned())) - .wrap(DefaultHeaders::new().add(( - "Onion-Location", - config.fc.onion.as_deref().unwrap_or_default(), - ))) + .wrap( + DefaultHeaders::new() + .add(( + "Onion-Location", + config.fc.onion.as_deref().unwrap_or_default(), + )) + .add(("Server", format!("ewp/{}", env!("CARGO_PKG_VERSION")))) + .add(("Permissions-Policy", "interest-cohort=()")), + ) .service(web::scope("/api").service(web::scope("v1").service(api_v1::love))) .service(index::page) .service(agreements::security) @@ -47,7 +52,8 @@ async fn main() -> Result<()> { .service(cv::page) .service(gaming::page) .service(memorial::page) - .service(networks::page) + .service(contact::page) + .service(contact::service_redirection) .service(portfolio::page) .service(setup::page) .service(web3::page) diff --git a/src/misc/markdown.rs b/src/misc/markdown.rs index aa670f1..1a18509 100644 --- a/src/misc/markdown.rs +++ b/src/misc/markdown.rs @@ -1,9 +1,6 @@ use crate::misc::date::Date; use comrak::nodes::{AstNode, NodeValue}; -use comrak::{ - format_html, parse_document, Arena, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions, - ComrakRenderOptions, ListStyleType, -}; +use comrak::{format_html, parse_document, Arena, ComrakOptions, ListStyleType}; use ramhorns::Content; use serde::{Deserialize, Deserializer}; use std::fs; @@ -17,6 +14,7 @@ pub struct FileMetadata { pub publish: Option, pub tags: Option>, pub toc: Option, + pub language: Option, } #[derive(Content, Debug, Clone)] @@ -55,39 +53,41 @@ pub struct File { /// Options used for parser and compiler MD --> HTML pub fn get_options() -> ComrakOptions { - ComrakOptions { - extension: ComrakExtensionOptions { - strikethrough: true, - tagfilter: true, - table: true, - autolink: true, - tasklist: true, - superscript: true, - header_ids: Some(String::new()), - footnotes: true, - description_lists: true, - front_matter_delimiter: Some("---".into()), - }, - parse: ComrakParseOptions { - smart: true, // could be boring - default_info_string: Some("plaintext".into()), - relaxed_tasklist_matching: true, - }, - render: ComrakRenderOptions { - hardbreaks: false, // could be true? change by metadata could be good for compatibility - github_pre_lang: false, - full_info_string: true, - width: 0, // 0 mean disabled? - unsafe_: true, - escape: false, - list_style: ListStyleType::Dash, - sourcepos: false, - }, - } + let mut options = comrak::Options::default(); + + // Extension + options.extension.strikethrough = true; + options.extension.tagfilter = true; + options.extension.table = true; + options.extension.autolink = true; + options.extension.tasklist = true; + options.extension.superscript = true; + options.extension.header_ids = Some(String::new()); + options.extension.footnotes = true; + options.extension.description_lists = true; + options.extension.front_matter_delimiter = Some("---".into()); + + // Parser + options.parse.smart = true; // could be boring + options.parse.default_info_string = Some("plaintext".into()); + options.parse.relaxed_tasklist_matching = true; + options.parse.relaxed_autolinks = true; + + // Renderer + options.render.hardbreaks = false; // could be true? change by metadata could be good for compatibility + options.render.github_pre_lang = false; + options.render.full_info_string = true; + options.render.width = 0; // 0 mean disabled? + options.render.unsafe_ = true; + options.render.escape = false; + options.render.list_style = ListStyleType::Dash; + options.render.sourcepos = false; + + options } /// Transform markdown string to File structure -pub fn read(raw_text: &str) -> File { +fn read(raw_text: &str) -> File { let arena = Arena::new(); let options = get_options(); diff --git a/src/routes/agreements.rs b/src/routes/agreements.rs index 91a87a7..1aaca6d 100644 --- a/src/routes/agreements.rs +++ b/src/routes/agreements.rs @@ -6,7 +6,7 @@ use ramhorns::Content; #[routes] #[get("/.well-known/security.txt")] #[get("/security.txt")] -pub async fn security(req: HttpRequest, config: web::Data) -> impl Responder { +async fn security(req: HttpRequest, config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_securitytxt( config.get_ref().to_owned(), get_url(req.connection_info()), @@ -34,7 +34,7 @@ fn build_securitytxt(config: Config, url: String) -> String { } #[get("/humans.txt")] -pub async fn humans(config: web::Data) -> impl Responder { +async fn humans(config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_humanstxt(config.get_ref().to_owned())) } @@ -59,7 +59,7 @@ fn build_humanstxt(config: Config) -> String { } #[get("/robots.txt")] -pub async fn robots() -> impl Responder { +async fn robots() -> impl Responder { HttpResponse::Ok().body(build_robotstxt()) } @@ -69,7 +69,7 @@ fn build_robotstxt() -> String { } #[get("/sitemap.xml")] -pub async fn sitemap() -> impl Responder { +async fn sitemap() -> impl Responder { // TODO actix_web::web::Redirect::to("/") } diff --git a/src/routes/api_v1.rs b/src/routes/api_v1.rs index e56c0ef..5068f02 100644 --- a/src/routes/api_v1.rs +++ b/src/routes/api_v1.rs @@ -7,7 +7,7 @@ struct Info { } #[get("/love")] -pub async fn love() -> impl Responder { +async fn love() -> impl Responder { HttpResponse::Ok().json(Info { unix_epoch: 1605576600, }) diff --git a/src/routes/blog.rs b/src/routes/blog.rs index 053989b..66482d1 100644 --- a/src/routes/blog.rs +++ b/src/routes/blog.rs @@ -21,13 +21,13 @@ use crate::{ markdown::{get_metadata, get_options, read_file, File, FileMetadata}, utils::get_url, }, - template::Infos, + template::{Infos, NavBar}, }; const MIME_TYPE_RSS: &str = "application/rss+xml"; #[get("/blog")] -pub async fn index(req: HttpRequest, config: web::Data) -> impl Responder { +async fn index(req: HttpRequest, config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_index( config.get_ref().to_owned(), get_url(req.connection_info()), @@ -36,12 +36,13 @@ pub async fn index(req: HttpRequest, config: web::Data) -> impl Responde #[derive(Content, Debug)] struct BlogIndexTemplate { + navbar: NavBar, posts: Vec, no_posts: bool, } #[once(time = 120)] -pub fn build_index(config: Config, url: String) -> String { +fn build_index(config: Config, url: String) -> String { let mut posts = get_posts("data/blog"); // Sort from newest to oldest @@ -51,6 +52,10 @@ pub fn build_index(config: Config, url: String) -> String { config.tmpl.render( "blog/index.html", BlogIndexTemplate { + navbar: NavBar { + blog: true, + ..NavBar::default() + }, no_posts: posts.is_empty(), posts, }, @@ -172,12 +177,13 @@ fn get_posts(location: &str) -> Vec { #[derive(Content, Debug)] struct BlogPostTemplate { + navbar: NavBar, post: Option, toc: String, } #[get("/blog/p/{id}")] -pub async fn page( +async fn page( req: HttpRequest, path: web::Path<(String,)>, config: web::Data, @@ -193,9 +199,18 @@ fn build_post(file: String, config: Config, url: String) -> String { let mut post = None; let (infos, toc) = get_post(&mut post, file, config.fc.name.unwrap_or_default(), url); - config - .tmpl - .render("blog/post.html", BlogPostTemplate { post, toc }, infos) + config.tmpl.render( + "blog/post.html", + BlogPostTemplate { + navbar: NavBar { + blog: true, + ..NavBar::default() + }, + post, + toc, + }, + infos, + ) } fn get_post( @@ -234,7 +249,7 @@ fn get_post( page_title: Some(format!("Post: {}", title)), page_desc: Some(format!("Blog d'{name}")), page_kw: Some( - vec!["blog", "blogging", "write", "writing"] + ["blog", "blogging", "write", "writing"] .iter() .map(|&tag| tag.to_owned()) .chain(tags.into_iter().map(|t| t.name)) @@ -248,7 +263,7 @@ fn get_post( } #[get("/blog/rss")] -pub async fn rss(req: HttpRequest, config: web::Data) -> impl Responder { +async fn rss(req: HttpRequest, config: web::Data) -> impl Responder { HttpResponse::Ok() .append_header(("content-type", MIME_TYPE_RSS)) .body(build_rss( diff --git a/src/routes/contact.rs b/src/routes/contact.rs new file mode 100644 index 0000000..20187ff --- /dev/null +++ b/src/routes/contact.rs @@ -0,0 +1,84 @@ +use actix_web::{get, routes, web, HttpRequest, HttpResponse, Responder}; +use cached::proc_macro::once; +use ramhorns::Content; + +use crate::{ + config::Config, + misc::utils::get_url, + template::{Infos, NavBar}, +}; + +#[get("/contact")] +async fn page(req: HttpRequest, config: web::Data) -> impl Responder { + HttpResponse::Ok().body(build_page( + config.get_ref().to_owned(), + get_url(req.connection_info()), + )) +} + +#[routes] +#[get("/contact/{service}")] +#[get("/contact/{service}/{scope}")] +async fn service_redirection(req: HttpRequest) -> impl Responder { + let info = req.match_info(); + let find_redirection = match info.query("service") { + /* Socials links */ + "twitter" => Some("https://twitter.com/Mylloon".to_owned()), + "mastodon" => Some("https://piaille.fr/@mylloon".to_owned()), + "discord" => match info.get("scope") { + Some("user") => Some("https://discord.com/users/158260864623968257/".to_owned()), + Some("guild") => Some("https://discord.gg/Z5ePxH4".to_owned()), + _ => None, + }, + "reddit" => Some("https://www.reddit.com/user/mylloon".to_owned()), + "instagram" => Some("https://www.instagram.com/mylloon/".to_owned()), + "kitsu" => Some("https://kitsu.io/users/Mylloon/library?status=completed".to_owned()), + "steam" => Some("https://steamcommunity.com/id/mylloon/".to_owned()), + "youtube" => Some("https://www.youtube.com/c/Mylloon".to_owned()), + "twitch" => Some("https://www.twitch.tv/mylloon".to_owned()), + + /* Forges */ + "github" => Some("https://github.com/Mylloon".to_owned()), + "gitlab" => Some("https://gitlab.com/Mylloon".to_owned()), + "codeberg" => Some("https://codeberg.org/Mylloon".to_owned()), + "forgejo" => Some("https://git.mylloon.fr/Anri".to_owned()), + + /* Others */ + "keyoxide" => { + Some("https://keyoxide.org/27024A99057E58B8087A5022A82D63DFF8D1317F".to_owned()) + } + _ => None, + }; + + if let Some(redirection) = find_redirection { + // Redirect to the desired service + actix_web::web::Redirect::to(redirection) + } else { + // By default, returns to the contact page + actix_web::web::Redirect::to("/contact") + } +} + +#[derive(Content, Debug)] +struct NetworksTemplate { + navbar: NavBar, +} + +#[once(time = 60)] +fn build_page(config: Config, url: String) -> String { + config.tmpl.render( + "contact.html", + NetworksTemplate { + navbar: NavBar { + contact: true, + ..NavBar::default() + }, + }, + Infos { + page_title: Some("Contacts".into()), + page_desc: Some(format!("Réseaux d'{}", config.fc.name.unwrap_or_default())), + page_kw: None, + url, + }, + ) +} diff --git a/src/routes/contrib.rs b/src/routes/contrib.rs index 32b37f9..4d134fc 100644 --- a/src/routes/contrib.rs +++ b/src/routes/contrib.rs @@ -6,20 +6,21 @@ use crate::{ github::{fetch_pr, ProjectState}, utils::get_url, }, - template::Infos, + template::{Infos, NavBar}, }; use actix_web::{get, web, HttpRequest, HttpResponse, Responder}; use cached::proc_macro::once; use ramhorns::Content; #[get("/contrib")] -pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder { +async fn page(req: HttpRequest, config: web::Data) -> impl Responder { let url = get_url(req.connection_info()); HttpResponse::Ok().body(build_page(config.get_ref().to_owned(), url).await) } #[derive(Content, Debug)] struct PortfolioTemplate { + navbar: NavBar, error: bool, projects: Option>, waiting: Option>, @@ -45,7 +46,12 @@ struct Pull { } #[once(time = 120)] -pub async fn build_page(config: Config, url: String) -> String { +async fn build_page(config: Config, url: String) -> String { + let navbar = NavBar { + contrib: true, + ..NavBar::default() + }; + // Fetch latest data from github let data = match fetch_pr().await { Ok(projects) => { @@ -107,6 +113,7 @@ pub async fn build_page(config: Config, url: String) -> String { }); PortfolioTemplate { + navbar, error: false, projects: Some( data.iter() @@ -132,6 +139,7 @@ pub async fn build_page(config: Config, url: String) -> String { eprintln!("{}", e); PortfolioTemplate { + navbar, error: true, projects: None, waiting: None, diff --git a/src/routes/cours.rs b/src/routes/cours.rs index f43ea62..b86662c 100644 --- a/src/routes/cours.rs +++ b/src/routes/cours.rs @@ -1,7 +1,7 @@ use actix_web::{get, Responder}; #[get("/cours")] -pub async fn page() -> impl Responder { +async fn page() -> impl Responder { // TODO actix_web::web::Redirect::to("/") } diff --git a/src/routes/cv.rs b/src/routes/cv.rs index c9e4cdd..de687ca 100644 --- a/src/routes/cv.rs +++ b/src/routes/cv.rs @@ -1,7 +1,7 @@ use actix_web::{get, Responder}; #[get("/cv")] -pub async fn page() -> impl Responder { +async fn page() -> impl Responder { // TODO actix_web::web::Redirect::to("/") } diff --git a/src/routes/gaming.rs b/src/routes/gaming.rs index 5885bcc..c8ab433 100644 --- a/src/routes/gaming.rs +++ b/src/routes/gaming.rs @@ -1,7 +1,7 @@ use actix_web::{get, Responder}; #[get("/gaming")] -pub async fn page() -> impl Responder { +async fn page() -> impl Responder { // TODO actix_web::web::Redirect::to("/") } diff --git a/src/routes/index.rs b/src/routes/index.rs index e09e85b..c3f3dce 100644 --- a/src/routes/index.rs +++ b/src/routes/index.rs @@ -1,21 +1,42 @@ use actix_web::{get, web, HttpRequest, HttpResponse, Responder}; use cached::proc_macro::once; +use ramhorns::Content; -use crate::{config::Config, misc::utils::get_url, template::Infos}; +use crate::{ + config::Config, + misc::utils::get_url, + template::{Infos, NavBar}, +}; #[get("/")] -pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder { +async fn page(req: HttpRequest, config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_page( config.get_ref().to_owned(), get_url(req.connection_info()), )) } +#[derive(Content, Debug)] +struct IndexTemplate { + navbar: NavBar, + fullname: String, +} + #[once(time = 60)] -pub fn build_page(config: Config, url: String) -> String { +fn build_page(config: Config, url: String) -> String { config.tmpl.render( "index.html", - (), + IndexTemplate { + navbar: NavBar { + index: true, + ..NavBar::default() + }, + fullname: config + .fc + .fullname + .to_owned() + .unwrap_or("Fullname".to_owned()), + }, Infos { page_title: config.fc.fullname, page_desc: Some("Page principale".into()), diff --git a/src/routes/memorial.rs b/src/routes/memorial.rs index 2ade9da..595f411 100644 --- a/src/routes/memorial.rs +++ b/src/routes/memorial.rs @@ -1,7 +1,7 @@ use actix_web::{get, Responder}; #[get("/memorial")] -pub async fn page() -> impl Responder { +async fn page() -> impl Responder { // Memorial? J'espere ne jamais faire cette page lol actix_web::web::Redirect::to("/") } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index da3bd5d..137a5c4 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,13 +1,13 @@ pub mod agreements; pub mod api_v1; pub mod blog; +pub mod contact; pub mod contrib; pub mod cours; pub mod cv; pub mod gaming; pub mod index; pub mod memorial; -pub mod networks; pub mod not_found; pub mod portfolio; pub mod setup; diff --git a/src/routes/networks.rs b/src/routes/networks.rs deleted file mode 100644 index 7608a2b..0000000 --- a/src/routes/networks.rs +++ /dev/null @@ -1,26 +0,0 @@ -use actix_web::{get, web, HttpRequest, HttpResponse, Responder}; -use cached::proc_macro::once; - -use crate::{config::Config, misc::utils::get_url, template::Infos}; - -#[get("/networks")] -pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder { - HttpResponse::Ok().body(build_page( - config.get_ref().to_owned(), - get_url(req.connection_info()), - )) -} - -#[once(time = 60)] -pub fn build_page(config: Config, url: String) -> String { - config.tmpl.render( - "networks.html", - (), - Infos { - page_title: Some("Mes réseaux".into()), - page_desc: Some(format!("Réseaux d'{}", config.fc.name.unwrap_or_default())), - page_kw: None, - url, - }, - ) -} diff --git a/src/routes/not_found.rs b/src/routes/not_found.rs index 7665ff3..51c7f69 100644 --- a/src/routes/not_found.rs +++ b/src/routes/not_found.rs @@ -1,13 +1,39 @@ -use actix_web::{web, HttpResponse, Responder}; +use actix_web::{web, HttpRequest, HttpResponse, Responder}; use cached::proc_macro::once; +use ramhorns::Content; -use crate::{config::Config, template::Infos}; +use crate::{ + config::Config, + misc::utils::get_url, + template::{Infos, NavBar}, +}; -pub async fn page(config: web::Data) -> impl Responder { - HttpResponse::NotFound().body(build_page(config.get_ref().to_owned())) +pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder { + HttpResponse::NotFound().body(build_page( + config.get_ref().to_owned(), + get_url(req.connection_info()), + )) +} + +#[derive(Content, Debug)] +struct NotFoundTemplate { + navbar: NavBar, + www: String, + onion: Option, } #[once(time = 60)] -pub fn build_page(config: Config) -> String { - config.tmpl.render("404.html", (), Infos::default()) +fn build_page(config: Config, url: String) -> String { + config.tmpl.render( + "404.html", + NotFoundTemplate { + navbar: NavBar::default(), + www: url, + onion: config.fc.onion, + }, + Infos { + page_desc: Some("Une page perdu du web".into()), + ..Infos::default() + }, + ) } diff --git a/src/routes/portfolio.rs b/src/routes/portfolio.rs index 23563e2..ce17d3a 100644 --- a/src/routes/portfolio.rs +++ b/src/routes/portfolio.rs @@ -9,11 +9,11 @@ use crate::{ markdown::{read_file, File}, utils::get_url, }, - template::Infos, + template::{Infos, NavBar}, }; #[get("/portfolio")] -pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder { +async fn page(req: HttpRequest, config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_page( config.get_ref().to_owned(), get_url(req.connection_info()), @@ -22,62 +22,38 @@ pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder #[derive(Content, Debug)] struct PortfolioTemplate<'a> { - bots_app: Option>, - bots_loc: Option, - persos_app: Option>, - persos_loc: Option, - univ_content: Option, - univ_loc: Option, + navbar: NavBar, + location_apps: Option<&'a str>, + apps: Option>, err_msg: &'a str, } #[once(time = 60)] -pub fn build_page(config: Config, url: String) -> String { +fn build_page(config: Config, url: String) -> String { let projects_dir = "data/projects"; let ext = ".md"; - // Get bots apps - let bots_apps_loc = format!("{projects_dir}/bots"); - let bots_apps = glob(&format!("{bots_apps_loc}/*{ext}")) + // Get apps + let apps = glob(&format!("{projects_dir}/*{ext}")) .unwrap() .map(|e| read_file(&e.unwrap().to_string_lossy()).unwrap()) .collect::>(); - // Get perso apps - let perso_apps_loc = format!("{projects_dir}/perso"); - let perso_apps = glob(&format!("{perso_apps_loc}/*{ext}")) - .unwrap() - .map(|e| read_file(&e.unwrap().to_string_lossy()).unwrap()) - .collect::>(); - - let univ_loc = format!("{projects_dir}/univ{ext}"); - - let (bots_app, bots_loc) = if bots_apps.is_empty() { - (None, Some(bots_apps_loc)) + let bots_app = if apps.is_empty() { + (None, Some(projects_dir)) } else { - (Some(bots_apps), None) - }; - - let (persos_app, persos_loc) = if perso_apps.is_empty() { - (None, Some(perso_apps_loc)) - } else { - (Some(perso_apps), None) - }; - - let (univ_content, univ_loc) = match read_file(&univ_loc) { - Some(data) => (Some(data.content), None), - _ => (None, Some(univ_loc)), + (Some(apps), None) }; config.tmpl.render( "portfolio.html", PortfolioTemplate { - bots_app, - bots_loc, - persos_app, - persos_loc, - univ_content, - univ_loc, + navbar: NavBar { + portfolio: true, + ..NavBar::default() + }, + apps: bots_app.0, + location_apps: bots_app.1, err_msg: "is empty", }, Infos { diff --git a/src/routes/setup.rs b/src/routes/setup.rs index 4a8d1f2..dc6bf2a 100644 --- a/src/routes/setup.rs +++ b/src/routes/setup.rs @@ -1,7 +1,7 @@ use actix_web::{get, Responder}; #[get("/setup")] -pub async fn page() -> impl Responder { +async fn page() -> impl Responder { // Explication de l'histoire de par exemple wiki/cat et le follow up // avec les futures video youtube probablement un shortcut // vers un billet de blog diff --git a/src/routes/web3.rs b/src/routes/web3.rs index 93e2c9e..0c8ccc8 100644 --- a/src/routes/web3.rs +++ b/src/routes/web3.rs @@ -4,7 +4,7 @@ use cached::proc_macro::once; use crate::{config::Config, misc::utils::get_url, template::Infos}; #[get("/web3")] -pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder { +async fn page(req: HttpRequest, config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_page( config.get_ref().to_owned(), get_url(req.connection_info()), @@ -12,7 +12,7 @@ pub async fn page(req: HttpRequest, config: web::Data) -> impl Responder } #[once(time = 60)] -pub fn build_page(config: Config, url: String) -> String { +fn build_page(config: Config, url: String) -> String { config.tmpl.render( "web3.html", (), diff --git a/src/template.rs b/src/template.rs index c0bc404..e01279a 100644 --- a/src/template.rs +++ b/src/template.rs @@ -22,6 +22,16 @@ pub struct Infos { pub url: String, } +#[derive(Content, Debug, Default)] +pub struct NavBar { + pub index: bool, + pub blog: bool, + pub portfolio: bool, + pub contact: bool, + pub contrib: bool, + pub cours: bool, +} + /// Final structure given to template #[derive(Content, Debug)] struct Data { diff --git a/static/badges/friends/21_12.webp b/static/badges/friends/21_12.webp new file mode 100644 index 0000000..9b1cca3 Binary files /dev/null and b/static/badges/friends/21_12.webp differ diff --git a/static/badges/friends/azazouille.webp b/static/badges/friends/azazouille.webp new file mode 100644 index 0000000..5ea992c Binary files /dev/null and b/static/badges/friends/azazouille.webp differ diff --git a/static/css/blog/index.css b/static/css/blog/index.css index eb310ac..2f51588 100644 --- a/static/css/blog/index.css +++ b/static/css/blog/index.css @@ -1,127 +1,54 @@ @media (prefers-color-scheme: light) { :root { - --selection: #36837db3; - --bg: #ffffff; --line: #aebed0; --date: #d2e0f0; --point: #8c9daf; --bg-hover: #cedce2; --point-hover: #ff00ff; - --font-color: #18181b; --title-color: #a14cb3; + --rss-inverse: 0%; } } @media (prefers-color-scheme: dark) { :root { - --selection: #4bad9480; - --bg: #171e26; --line: #374351; --date: #242e38; --point: #515f70; --bg-hover: #1f2730; --point-hover: #ff00ff; - --font-color: #a1a1aa; --title-color: #a25add; + --rss-inverse: 80%; } } -:root { - --font-size: 20px; -} - -::selection { - color: rgb(255, 255, 255); - background: var(--selection); -} - -html { - background-color: var(--bg); - font-family: "Segoe UI", Arial, sans-serif, "Segoe UI Emoji", - "Segoe UI Symbol"; -} - -/* Scrollbar - Firefox */ -* { - scrollbar-color: var(--font-color) var(--bg); -} - -/* Scrollbar - Chrome */ -*::-webkit-scrollbar { - width: 7px; - height: 9px; - background: var(--bg); -} - -*::-webkit-scrollbar-thumb { - background-color: var(--font-color); - border-radius: 10px; -} - -/* Title of page */ +/* Title */ h1 { - color: var(--font-color); - text-transform: uppercase; - letter-spacing: 0.3cap; margin-bottom: 0; } -h2, -p { - margin: 0px; -} - -h1, -footer { - text-align: center; - font-size: calc(var(--font-size) * 2); -} - -footer > * { - color: var(--font-color); - font-weight: bold; - text-decoration: none; - font-size: calc(var(--font-size) / 1.5); - text-transform: uppercase; - opacity: 0.7; - letter-spacing: 0.15rem; -} - -footer > *::before { - content: "> "; -} - -a:hover { - color: var(--title-color); -} - -.timeline { - box-sizing: border-box; - color: var(--font-color); - padding: 30px 20px; - display: flex; - width: 100%; - justify-content: center; -} - -/* Barre */ -.timeline > ul { - list-style-type: none; - border-left: 2px solid var(--line); - padding: 0px 5px; +/* RSS link */ +#rss::before { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='20' width='20' viewBox='0 0 24 24' %3E%3Cpath d='M6.18 15.64a2.18 2.18 0 0 1 2.18 2.18C8.36 19 7.38 20 6.18 20 5 20 4 19 4 17.82a2.18 2.18 0 0 1 2.18-2.18M4 4.44A15.56 15.56 0 0 1 19.56 20h-2.83A12.73 12.73 0 0 0 4 7.27V4.44m0 5.66a9.9 9.9 0 0 1 9.9 9.9h-2.83A7.07 7.07 0 0 0 4 12.93V10.1Z' %3E%3C/path%3E%3C/svg%3E"); + padding-right: 2px; + vertical-align: middle; + filter: invert(var(--rss-inverse)); } /* Card */ -.timeline > ul > li { +main li { padding: 20px 20px; position: relative; cursor: pointer; border-radius: 5px; } -/* Dates */ -.timeline > ul > li > span { - display: inline-block; +main li:hover { + background: var(--bg-hover); +} + +/* Card dates */ +main span { background-color: var(--date); border-radius: 5px; padding: 2px 5px; @@ -129,82 +56,48 @@ a:hover { font-family: monospace; } -/* Titles */ -.timeline > ul > li > .content > h2 { +/* Card text */ +li h2, +li p { + margin: 0px; + padding-top: 5px; +} + +/* Card titles */ +li h2 { color: var(--title-color); font-size: var(--font-size); - padding-top: 5px; - text-decoration: none; } -/* Descriptions */ -.timeline > ul > li > .content > p { - padding-top: 5px; +/* Card descriptions */ +li p { font-size: calc(var(--font-size) - 2px); - max-width: 25em; - - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; } -/* Points côté */ -.timeline > ul > li::before { +/* Timeline bar */ +main ul { + list-style-type: none; + border-left: 2px solid var(--line); + padding: 0px 5px; +} + +/* Timeline dot */ +main li::before { position: absolute; + content: ""; width: 10px; height: 10px; background-color: var(--point); border-radius: 50%; + left: -11px; top: 59px; + transition: 0.2s; } -/* Card hover */ -.timeline > ul > li:hover { - background-color: var(--bg-hover); -} - -/* Point côté hover */ -.timeline > ul > li:hover::before { +main li:hover::before { background-color: var(--point-hover); box-shadow: 0px 0px 10px 2px var(--point-hover); } - -nav { - display: flex; - justify-content: center; -} - -#rss { - text-transform: lowercase; - color: var(--font-color); - text-decoration: none; -} - -hr { - border: 0; - margin: 0; - height: 1px; - background: var(--line); - margin: 2rem auto; - width: 20%; -} - -#rss:hover { - color: var(--title-color); - text-decoration: underline; -} - -@media only screen and (max-width: 300px) { - .timeline { - padding: 30px 5px 30px 10px; - } - - .timeline > ul > li > .content > h2 > a { - color: var(--title-color); - font-size: calc(var(--font-size) - 2px); - } -} diff --git a/static/css/blog/post.css b/static/css/blog/post.css index 47d411c..a1e3723 100644 --- a/static/css/blog/post.css +++ b/static/css/blog/post.css @@ -1,13 +1,9 @@ @media (prefers-color-scheme: light) { :root { - --selection: rgba(92, 54, 131, 0.7); - --bg: #ffffff; - --font-color: #18181b; --code-font-color: #333333; --code-bg-color: #eeeeee; --quote-border-color: #9852fa; --quote-bg-color: #d8d6d6; - --link-hover-color: #fd62af; --separator-color: #cccccc; --tag-bg-color: #d2e0f0; } @@ -15,103 +11,68 @@ @media (prefers-color-scheme: dark) { :root { - --selection: rgba(124, 75, 173, 0.5); - --bg: #171e26; - --font-color: #bcbcc5; --code-font-color: #eeeeee; --code-bg-color: #333333; --quote-border-color: #bd93f9; --quote-bg-color: #273341; - --link-hover-color: #ff80bf; --separator-color: #414558; --tag-bg-color: #242e38; } } :root { - --font-size: 17px; --max-width: 750px; } +/* Page */ html { - font-family: "Segoe UI", Arial, sans-serif, "Segoe UI Emoji", - "Segoe UI Symbol"; - font-size: var(--font-size); scroll-behavior: smooth; - background-color: var(--bg); - line-height: 1.5; } -/* Scrollbar - Firefox */ -* { - scrollbar-color: var(--font-color) var(--bg); +body { + max-width: var(--max-width); + margin: auto; } -/* Scrollbar - Chrome */ -*::-webkit-scrollbar { - width: 7px; - height: 9px; - background: var(--bg); +/* Post title */ +header h1 { + font-size: calc(var(--font-size) * 2); } -*::-webkit-scrollbar-thumb { - background-color: var(--font-color); - border-radius: 10px; -} - -/* Post's title */ -header > h1 { - font-size: 2.5rem; -} - -/* Date's title */ -header > span { +/* Post date */ +header span { font-style: italic; } -/* Tags */ -header > ul { +/* Post tags */ +header > ul:last-of-type { display: inline; margin-left: 1em; padding: 0; } -header li::before { +header > ul:last-of-type li::before { content: "#"; } -header li { +header > ul:last-of-type li { display: inline; - background-color: var(--tag-bg-color); + background: var(--tag-bg-color); border-radius: 5px; padding: 2px 8px; - font-size: calc(var(--font-size) / 1.1); + font-size: calc(var(--font-size) * 0.9); } -::selection { - color: rgb(255, 255, 255); - background: var(--selection); -} - -* { - color: var(--font-color); - outline: none; -} - -p { - color: var(--font-color); - font-size: var(--font-size); +/* Post */ +main { + margin: 0; + padding: 0; + max-width: 100%; } /* Anchors */ -h1:hover > a.anchor::before, -h2:hover > a.anchor::before, -h3:hover > a.anchor::before, -h4:hover > a.anchor::before, -h5:hover > a.anchor::before, -h6:hover > a.anchor::before { +:is(h1, h2, h3, h4, h5, h6):hover a.anchor::before { visibility: visible; - color: var(--font-color); } a.anchor::before { @@ -122,30 +83,15 @@ a.anchor::before { a.anchor { text-decoration: none; + vertical-align: baseline; } -body { - margin: 0 auto; - max-width: var(--max-width); -} - -footer { - text-align: center; -} - -footer > * { - text-transform: uppercase; - font-size: calc(var(--font-size) / 1.2); - letter-spacing: 0.15rem; - opacity: 0.6; - text-decoration: none; - padding-bottom: 1em; -} - -a:hover { - color: var(--link-hover-color); +/* Links in headers */ +:is(h1, h2, h3, h4, h5, h6) a { + font-size: inherit; } +/* Separators */ hr { border: 0; height: 1px; @@ -158,7 +104,7 @@ blockquote { padding: 0.1em 10px; border-left: 6px solid; border-color: var(--quote-border-color); - background-color: var(--quote-bg-color); + background: var(--quote-bg-color); border-top-right-radius: 5px; border-bottom-right-radius: 5px; } @@ -173,17 +119,17 @@ img { /* Code */ kbd, code { - font-family: Consolas, monospace; + font-family: monospace; } /* Little snippet of code (not blocks) */ kbd, code:not(.hljs):not(:has(svg)) { - background-color: var(--code-bg-color); + background: var(--code-bg-color); border-radius: 3px; color: var(--code-font-color); box-shadow: 0 1px 1px black; - font-size: calc(var(--font-size) / 1.2); + font-size: calc(var(--font-size) * 0.8); padding: 2px 4px; vertical-align: 1.5px; } @@ -193,6 +139,17 @@ code:not(.hljs):not(:has(svg)) { border-radius: 5px; } +.hljs::-webkit-scrollbar { + width: 7px; + height: 9px; + background: var(--background); +} + +.hljs::-webkit-scrollbar-thumb { + background-color: var(--font-color); + border-radius: 10px; +} + /* Marge for numbers */ .hljs-ln-n { margin-right: 0.4em; @@ -201,6 +158,7 @@ code:not(.hljs):not(:has(svg)) { /* Numbers in codeblocks */ .hljs-ln-numbers { text-align: right; + color: var(--font-color); } /* Fix scroll in codeblocks with line numbering */ @@ -210,7 +168,7 @@ table.hljs-ln { /* Background for copy code button */ .hljs-copy-button { - background-color: var(--bg) !important; + background-color: var(--background) !important; } /* Light theme for the copy code button */ @@ -234,28 +192,23 @@ table.hljs-ln { visibility: collapse; } +/* Reference to footnotes */ +.footnote-ref a { + text-decoration: underline dotted; + font-size: calc(var(--font-size) * 0.8); +} + /* Footnotes */ .footnotes a { font-family: "Segoe UI"; text-decoration: underline dotted; } -/* Reference to footnotes */ -.footnote-ref > a { - text-decoration: underline dotted; -} - /* Mermaid diagrams */ pre:has(code.language-mermaid) { text-align: center; } -@media only screen and (max-width: 750px) { - body { - margin: 20px; - } -} - /* Table of content */ nav#toc { position: fixed; @@ -264,6 +217,7 @@ nav#toc { margin-left: 50px; } +/* breakpoint */ @media only screen and (max-width: 1500px) { /* Visible only on large screens */ nav#toc { diff --git a/static/css/constants.css b/static/css/constants.css new file mode 100644 index 0000000..97e1a78 --- /dev/null +++ b/static/css/constants.css @@ -0,0 +1,23 @@ +/* Parameters light */ +@media (prefers-color-scheme: light) { + :root { + --background: #f1f1f1; + --font-color: #18181b; + --link-color: #df5a9c; + } +} + +/* Parameters dark */ +@media (prefers-color-scheme: dark) { + :root { + --background: #171e26; + --font-color: #bcbcc5; + --link-color: #ff80bf; + } +} + +/* Global parameters */ +:root { + --font-size: 1.15rem; + --font-family: "Segoe UI", "Segoe UI Emoji", "Segoe UI Symbol"; +} diff --git a/static/css/contact.css b/static/css/contact.css new file mode 100644 index 0000000..3d6c9a0 --- /dev/null +++ b/static/css/contact.css @@ -0,0 +1,53 @@ +/* https://stackoverflow.com/a/40244401/15436737 */ + +/* Title */ +h1 { + margin: 0; +} + +h1 + p { + margin: 8px; +} + +/* List */ +main ul { + column-count: 2; + column-gap: 5em; +} + +main li > p { + margin: 0; + padding: 3%; +} + +/* breakpoint */ +@media only screen and (max-width: 640px) { + main ul { + column-count: 1; + } +} + +/* Links */ +main a { + text-decoration: none; + display: inline-block; + color: color-mix(in srgb, var(--font-color) 30%, var(--link-color)); +} + +main a:after { + display: block; + content: ""; + border-bottom: solid 3px; + transform: scaleX(0); + transition: transform 250ms ease-in-out; + transform-origin: 100% 50%; +} + +main a:hover:after { + transform: scaleX(1); + transform-origin: 0 50%; +} + +main a:hover { + text-decoration: none; +} diff --git a/static/css/contrib.css b/static/css/contrib.css new file mode 100644 index 0000000..c93caf3 --- /dev/null +++ b/static/css/contrib.css @@ -0,0 +1,35 @@ +h2 { + padding-left: 1rem; +} + +/* Element of lists */ +main li { + display: inline-block; +} + +main h1, +h2 { + font-weight: 800; +} + +/* Links */ +main a { + text-decoration: none; + font-weight: 600; +} + +main a:hover { + text-decoration: underline; +} + +p { + margin: 0; +} + +main p::after { + content: ", "; +} + +main li:last-child p::after { + content: ""; +} diff --git a/static/css/index.css b/static/css/index.css new file mode 100644 index 0000000..8f5475e --- /dev/null +++ b/static/css/index.css @@ -0,0 +1,69 @@ +@media (prefers-color-scheme: light) { + :root { + --name-color: #d30f39; + --pronouns-color: #6c6f85; + --font-color: #4c4f69; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --name-color: #f38ba8; + --pronouns-color: #a6adc8; + --font-color: #c8d0ee; + } +} + +:root { + --title-weight: 600; +} + +/* Name header */ +#name { + font-size: calc(var(--font-size) * 2.5); + font-weight: var(--title-weight); + color: var(--name-color); +} + +#subname { + margin-top: 0; + font-size: calc(var(--font-size) * 1.07); + margin-bottom: 1.5em; +} + +#pronouns { + font-size: calc(var(--font-size) * 0.8); + color: var(--pronouns-color); +} + +#avatar { + width: calc(var(--font-size) * 5); + border-radius: 50%; + float: right; +} + +/* breakpoint */ +@media only screen and (max-width: 640px) { + #avatar { + position: absolute; + width: calc(var(--font-size) * 4); + left: 10px; + top: -30px; + } +} + +/* Description */ +h1 { + font-weight: var(--title-weight); + font-size: calc(var(--font-size) * 1.6); + margin-bottom: 0; +} + +/* Friends */ +#friends a:hover { + opacity: 1; +} + +#friends a { + padding-right: 10px; +} diff --git a/static/css/languages.css b/static/css/languages.css new file mode 100644 index 0000000..b1f2d06 --- /dev/null +++ b/static/css/languages.css @@ -0,0 +1,71 @@ +:root { + --lang-size-dot: 0.8em; + --lang-margin-text: 3px; + --lang-font-size: calc(var(--font-size) * 0.65); +} + +/* Dot */ +p[data-lang]::before { + content: ""; + + height: var(--lang-size-dot); + width: var(--lang-size-dot); + + border-radius: 50%; + display: inline-block; +} + +/* Text */ +p[data-lang]::after { + margin-left: var(--lang-margin-text); + font-size: var(--lang-font-size); +} + +/* Definitions */ +p[data-lang="ts"]::before { + background-color: #3178c6; +} + +p[data-lang="ts"]::after { + content: "TypeScript"; +} + +p[data-lang="rust"]::before { + background-color: #dea584; +} + +p[data-lang="rust"]::after { + content: "Rust"; +} + +p[data-lang="py"]::before { + background-color: #3572a5; +} + +p[data-lang="py"]::after { + content: "Python"; +} + +p[data-lang="ocaml"]::before { + background-color: #ef7a08; +} + +p[data-lang="ocaml"]::after { + content: "OCaml"; +} + +p[data-lang="js"]::before { + background-color: #e3d357; +} + +p[data-lang="js"]::after { + content: "JavaScript"; +} + +p[data-lang="c"]::before { + background-color: #555555; +} + +p[data-lang="c"]::after { + content: "C"; +} diff --git a/static/css/portfolio.css b/static/css/portfolio.css new file mode 100644 index 0000000..1ef0677 --- /dev/null +++ b/static/css/portfolio.css @@ -0,0 +1,86 @@ +@media (prefers-color-scheme: light) { + :root { + --extreme: white; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --extreme: black; + } +} + +:root { + --border-color: #6e6e6f; + --font-size-card: calc(var(--font-size) * 0.8); +} + +main { + max-width: 840px; /* breakpoint */ +} + +/* List */ +main ul { + padding: 0; + display: grid; + grid-template-columns: repeat(2, 1fr); +} + +/* breakpoint */ +@media only screen and (max-width: 740px) { + main ul { + grid-template-columns: none; + } +} + +/* Card */ +main li { + display: flex; + + border-radius: 8px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); + + background: color-mix(in srgb, var(--background) 90%, var(--extreme)); + border: 1px solid + color-mix(in srgb, var(--background) 50%, var(--border-color)); + transition: all 0.3s; + + padding: 10px; + margin-block: 10px; + margin-inline: 5px; +} + +main li:hover { + background: color-mix(in srgb, var(--background) 40%, var(--extreme)); + cursor: pointer; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.3); +} + +/* Element */ +div { + display: flex; + flex-direction: column; + width: 100%; +} + +/* Element title */ +div h3 { + margin-block: 0; +} + +/* Element description */ +span { + margin: auto; + margin-left: 0; +} + +/* Element text */ +div p, +div a { + font-size: var(--font-size-card); +} + +/* Element language */ +p[data-lang] { + margin: 0; +} diff --git a/static/css/style.css b/static/css/style.css index 45f0a20..3a67435 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,189 +1,67 @@ -/* Normal font */ -@font-face { - font-family: "Luciole"; - font-style: normal; - font-weight: normal; - src: url(/css/fonts/Luciole-Regular.ttf); -} - -/* Italic font */ -@font-face { - font-family: "Luciole"; - font-style: italic; - font-weight: normal; - src: url(/css/fonts/Luciole-Regular-Italic.ttf); -} - -/* Bold fond */ -@font-face { - font-family: "Luciole"; - font-style: normal; - font-weight: bold; - src: url(/css/fonts/Luciole-Bold.ttf); -} - -/* Bold italic font */ -@font-face { - font-family: "Luciole"; - font-style: italic; - font-weight: bold; - src: url(/css/fonts/Luciole-Bold-Italic.ttf); -} - -/* Page bottom */ -footer { - margin-top: 1em; - height: 3rem; -} - -/* Regular tags */ +/* Theme of the pages */ html { - position: relative; - min-height: 100vh; - height: 100%; - scroll-behavior: smooth; + background-color: var(--background); + font-family: "Segoe UI", "Segoe UI Emoji", "Segoe UI Symbol", system-ui; } -body { - position: relative; - padding-bottom: 3em; - min-height: 90%; - background-color: rgb(24, 24, 24); -} - -p, -h1, -h2, -h3, -li, +body, a { - font-family: "Luciole", sans-serif; + color: var(--font-color); + font-size: var(--font-size); } -::selection { - color: rgb(255, 255, 255); - background: rgba(124, 75, 173, 0.486); +a { + transition: opacity 0.2s; } -/* Frames */ -h1.subtitle { - text-decoration: none; - color: rgb(28, 118, 236); +a:hover { + opacity: 0.6; } -h1#title { - text-align: center; - color: rgb(28, 236, 174); -} - -h2.subtitle, -h2.subtitle a { - color: rgb(28, 118, 236) !important; -} - -h3.subsubtitle, -#content h3.subsubtitle a { - text-align: left; - color: rgb(149, 87, 201); -} - -#content h3.subsubtitle a:hover { - color: rgb(0, 181, 236); - transition: color 0.1s; -} - -div#content { - margin-left: auto; - margin-right: auto; - margin-top: 2%; - padding: 0.9% 0.9% 0.9% 0.9%; - width: 42%; - border-radius: 6px; - border: 1px solid rgb(170, 170, 170); - text-align: center; -} - -@media only screen and (max-width: 850px) { - /* Mobile display */ - div#content { - width: 90%; - } -} - -#content a, -#content a:visited, -#content ul { - text-decoration: none; - color: rgb(201, 201, 201); - text-align: center; -} - -#content p { - text-decoration: none; - color: rgb(201, 201, 201); - text-align: center; - padding: 0.7em; -} - -#content a:hover { - color: rgb(0, 181, 236); - transition: color 0.1s; -} - -/* https://stackoverflow.com/a/40244401/15436737 */ - -#content a { +main { position: relative; - text-decoration: none; + font-size: var(--font-size); + padding: 2em 1em; + font-family: var(--font-family); + margin: 0 auto; + max-width: 640px; /* breakpoint */ +} + +/* Navigation bar across all of the pages */ +header nav { + text-align: center; +} + +header nav li { display: inline-block; } -#content a:after { - display: block; +/* Maybe do this only with 'large' screens */ +header nav p::after { + content: "·"; + padding: 10px; +} + +/* breakpoint */ +@media only screen and (max-width: 640px) { + header nav p::after { + padding: 6px; + } +} + +header nav li:last-child p::after { content: ""; - border-bottom: solid 3px; - transform: scaleX(0); - transition: transform 250ms ease-in-out; - transform-origin: 100% 50%; } -#content a:hover:after { - transform: scaleX(1); - transform-origin: 0 50%; -} - -/* ------------------------------------------- */ - -div.subcontent { - margin-left: auto; - margin-right: auto; - margin-top: 5%; - padding: 1.2% 1.2% 1.2% 1.2%; - width: 78%; - border-radius: 6px; - border: 1px solid rgb(170, 170, 170); - text-align: center; -} - -#listecontent li { - margin-bottom: 0.5em; -} - -/* Back links going to the index */ -footer.backToIndexPage { - text-align: center; -} - -footer.backToIndexPage a { - color: rgb(114, 180, 76); +header nav a { text-decoration: none; + color: var(--link-color); } -footer.backToIndexPage a:hover { - color: rgb(152, 187, 132); +header nav a:hover { + text-decoration: underline; } -/* Hide content */ -.hide { - display: none; +.bold { + font-weight: bold; } diff --git a/static/js/index.js b/static/js/index.js new file mode 100644 index 0000000..e2a8306 --- /dev/null +++ b/static/js/index.js @@ -0,0 +1,32 @@ +class Tag { + constructor(variant, style = "") { + this.variant = variant; + this.style = style; + } +} + +window.addEventListener("load", () => { + const tags = [ + new Tag("Comment ça marche un PC 😵‍💫"), + new Tag("Idiot certifié"), + new Tag("undefined", "font-family: monospace"), + new Tag("/api/v1/love", "font-family: monospace"), + new Tag("Étudiant qui va rater son master"), + new Tag("Peak D2 sur Valo 🤡"), + new Tag( + "1312", + ` + display: inline-block; + background: linear-gradient(to bottom right, red 0%, red 50%, black 50%); + background-clip: text; + color: transparent; + ` + ), + new Tag("Nul en CSS", "font-family: 'Comic Sans MS', cursive"), + ]; + + const random = Math.round(Math.random() * (tags.length - 1)); + const element = document.getElementById("subname"); + element.textContent = tags[random].variant; + element.style = tags[random].style; +}); diff --git a/templates/404.html b/templates/404.html index 7e8b1df..afdba64 100644 --- a/templates/404.html +++ b/templates/404.html @@ -1,10 +1,37 @@ - + {{>head.html}} - -

404 :/

- {{>footer.html}} + +
{{>navbar.html}}
+
+ {{#data}} + +

Perdu ?

+ +

Vous pouvez revenir sur le site via :

+
    +
  • La barre de navigation

  • + +
  • +

    + Le site internet (disponible grâce au projet + W3). +

    +
  • + + {{#onion}} +
  • +

    + Le site onion (disponible grâce au réseau + Tor). +

    +
  • + {{/onion}} +
+ + {{/data}} +
diff --git a/templates/blog/footer.html b/templates/blog/footer.html deleted file mode 100644 index 42b0c3d..0000000 --- a/templates/blog/footer.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/templates/blog/head.html b/templates/blog/head.html deleted file mode 100644 index b7f5c8b..0000000 --- a/templates/blog/head.html +++ /dev/null @@ -1,8 +0,0 @@ -{{page_title}}{{#page_title}} - {{/page_title}}{{app_name}} - - - - - - -{{>icons.html}} {{>metadata.html}} diff --git a/templates/blog/index.html b/templates/blog/index.html index 844288b..cfd44b4 100644 --- a/templates/blog/index.html +++ b/templates/blog/index.html @@ -1,34 +1,40 @@ - + - {{>blog/head.html}} + {{>head.html}} + - - {{#data}} + +
{{>navbar.html}}
+
+ {{#data}} -

Blog

- -
+

Blog

+

Blog perso, je dis peut-être n'importe quoi 🫶

+ Lien vers le flux RSS - {{#no_posts}} -

Aucun posts

- {{/no_posts}} {{^no_posts}} -
+ {{#no_posts}} +

Aucun posts

+ + {{/no_posts}} {{^no_posts}}
    {{#posts}} -
  • +
  • {{>blog/date.html}} -
    -

    {{title}}

    - {{#desc}} -

    {{desc}}

    - {{/desc}} -
    +

    {{title}}

    + {{#desc}} +

    {{desc}}

    + {{/desc}}
  • {{/posts}}
-
- {{/no_posts}} {{/data}} {{>footer.html}} + {{/no_posts}} {{/data}} +
diff --git a/templates/blog/post.html b/templates/blog/post.html index fb4723d..2182d9d 100644 --- a/templates/blog/post.html +++ b/templates/blog/post.html @@ -1,15 +1,21 @@ - + - {{>blog/head.html}} + {{>head.html}} + {{#data}} {{#post}} {{#metadata}} {{#math}}{{>libs/katex_head.html}}{{/math}} {{#syntax_highlight}}{{>libs/hljs_head.html}}{{/syntax_highlight}} - +
- {{#info}} + {{>navbar.html}} {{#info}}

{{title}}

{{#date}} {{>blog/date.html}} {{/date}}
    @@ -26,7 +32,7 @@
    {{&content}}
    {{/post}} - {{>blog/footer.html}} {{#post}} {{#metadata}} + {{#post}} {{#metadata}} {{#mermaid}}{{>libs/mermaid_footer.html}}{{/mermaid}} {{#math}}{{>libs/katex_footer.html}}{{/math}} {{#syntax_highlight}}{{>libs/hljs_footer.html}}{{/syntax_highlight}} diff --git a/templates/contact.html b/templates/contact.html new file mode 100644 index 0000000..eb26d80 --- /dev/null +++ b/templates/contact.html @@ -0,0 +1,211 @@ + + + + {{>head.html}} + + + +
    {{>navbar.html}}
    +
    +

    Contact

    +

    Je suis présent relativement partout sur internet 😸

    +

    Réseaux sociaux

    + + +

    Forges

    +
      +
    • +

      + Github : + Mylloon +

      +
    • +
    • +

      + Gitlab : + Mylloon +

      +
    • +
    • +

      + Codeberg : + Mylloon +

      +
    • +
    • +

      + Forgejo (mon instance) : + Anri +

      +
    • +
    + +

    Autre

    + +
    + + diff --git a/templates/contrib.html b/templates/contrib.html index 1c15051..a3f3f8c 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -1,42 +1,72 @@ - + {{>head.html}} + - {{#data}} {{#error}} -
    -

    Github ne veut pas que tu vois ces informations...

    -
    - {{/error}} {{^error}} -
    -

    Mes contributions

    - +
    {{>navbar.html}}
    + {{#data}} +
    +

    Mes contributions GitHub

    + {{#error}} +

    GitHub ne veut pas que tu vois ces informations...

    + {{/error}} {{^error}} + + {{#projects}} -

    {{name}}

    -

    +

    {{name}}

    +
      {{#pulls_merged}} - #{{id}} +
    • + +

      + #{{id}}

      +
    • + {{/pulls_merged}} -

      +
    {{/projects}} -

    En attente

    -

    + +

    En attente

    + + +

    Non mergées

    +
    - {{/error}} {{/data}} {{>footer.html}} +
+ + {{/error}} {{/data}} diff --git a/templates/footer.html b/templates/footer.html deleted file mode 100644 index 2e63873..0000000 --- a/templates/footer.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/templates/head.html b/templates/head.html index 683bbc5..36237f9 100644 --- a/templates/head.html +++ b/templates/head.html @@ -6,4 +6,5 @@ {{>icons.html}} {{>metadata.html}} + diff --git a/templates/index.html b/templates/index.html index b86b402..9e67a08 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,14 +1,77 @@ - + {{>head.html}} + - -
-

Blog

-

Portfolio

-

Réseaux

-

Contributions

-

Cours (external)

+ +
{{>navbar.html}}
+
+ {{#data}} + +
+ {{fullname}} + (il/lui, he/him) + Avatar +
+

+ +
+

Bienvenue !

+

Content de vous voir ici !

+
+ +
+

Qui suis-je ?

+

Je m'appelle Anri, mon pseudo est Mylloon.

+

+ J'aime beaucoup l'informatique depuis très petit, ce site est écrit de + A à Z par moi-même (modulo la quantité astronomique de librairie + utilisé) en Rust. J'adore le monde de l'open source, l'immense + majorité de mes projets sont sous licence + AGPLv3. +

+

+ Vous pouvez voir mon portfolio ici, ainsi que + mes contributions sur GitHub ici. Il m'arrive + aussi, parfois, d'écrire sur mon blog. +

+
+ +
+

Personnes incroyables

+ 21_12 + Azazouille +
+ + {{/data}} +
+ diff --git a/templates/navbar.html b/templates/navbar.html new file mode 100644 index 0000000..70c5903 --- /dev/null +++ b/templates/navbar.html @@ -0,0 +1,57 @@ +{{#data}}{{#navbar}} + +{{/navbar}}{{/data}} diff --git a/templates/networks.html b/templates/networks.html deleted file mode 100644 index 4565554..0000000 --- a/templates/networks.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - {{>head.html}} - - -
-

Contacts et réseaux sociaux

-

- Email -

-

- Twitter / - Mastodon -

-

- Compte et - Serveur Discord / - Serveur Revolt -

-

- Reddit -

-

- Instagram / - Pixelfed -

-

- Github / - Gitlab / - Codeberg / - Mon Git -

-

- Kitsu -

-

- Steam -

-

- Youtube / - Twitch -

-

- Keyoxide -

-
- {{>footer.html}} - - diff --git a/templates/portfolio.html b/templates/portfolio.html index 82f2017..23376b6 100644 --- a/templates/portfolio.html +++ b/templates/portfolio.html @@ -1,50 +1,40 @@ - + {{>head.html}} + + - {{#data}} -

Projets qui me tiennent à coeur

-
-

Bots

-
- {{#bots_app}} {{#metadata}} {{#info}} -

- {{title}} -

- {{/info}} {{/metadata}} -
{{&content}}
- {{/bots_app}} {{^bots_app}} -

{{bots_loc}} {{err_msg}}

- {{/bots_app}} -
-
-

- Projet de l'université -

-
- {{&univ_content}} {{^univ_content}} -

{{univ_loc}} {{err_msg}}

- {{/univ_content}} -
+
{{>navbar.html}}
+
+ {{#data}} +

Portfolio

+

+ Je programme depuis 2018 et j'ai appris une multitude de langages + depuis. Étant passionné de logiciels libres depuis ma licence + d'informatique à Paris 8, je publie tout sur des forges publiques. +

-

Projets perso

-
- {{#persos_app}} {{#metadata}} {{#info}} -

- {{title}} -

- {{/info}} {{/metadata}} -
{{&content}}
- {{/persos_app}} {{^persos_app}} -

{{persos_loc}} {{err_msg}}

- {{/persos_app}} -
-
-
- {{/data}} {{>footer.html}} + {{#location_apps}} + +

{{location_apps}} {{err_msg}}

+ {{/location_apps}} {{^location_apps}} + +

Projets

+
    + {{#apps}} {{#metadata}} {{#info}} +
  • +
    +

    {{title}}

    + {{&content}} {{#language}} +

    + {{/language}} +
    +
  • + {{/info}} {{/metadata}} {{/apps}} +
+ {{/location_apps}} {{/data}} +