From 09b68b68bedebb152d3426d74e716815b2c62b86 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 25 Apr 2025 23:50:25 -0600 Subject: [PATCH] Replace SVGs with custom icon font --- assets/icons.woff2 | Bin 0 -> 5888 bytes assets/platforms/n64.svg | 7 +--- crates/images/src/lib.rs | 28 -------------- crates/web/src/handlers/auth.rs | 4 +- crates/web/src/handlers/common.rs | 6 +-- crates/web/src/handlers/manage.rs | 12 +++--- crates/web/src/handlers/mod.rs | 1 - crates/web/src/handlers/project.rs | 7 ++-- crates/web/src/handlers/report.rs | 34 ++++++++++++----- css/main.scss | 57 ++++++++++++++++++++++++++--- 10 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 assets/icons.woff2 diff --git a/assets/icons.woff2 b/assets/icons.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..34311ea6040681f817245d63aea17611fa6241aa GIT binary patch literal 5888 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 - - - - + + diff --git a/crates/images/src/lib.rs b/crates/images/src/lib.rs index 1d7c8d2..75befb8 100644 --- a/crates/images/src/lib.rs +++ b/crates/images/src/lib.rs @@ -5,7 +5,6 @@ pub mod treemap; use std::str::FromStr; use axum::{ - extract::Path, http::{StatusCode, header}, response::{IntoResponse, Response}, }; @@ -17,33 +16,6 @@ pub fn image_mime_from_ext(ext: &str) -> Option { .map(|format| Mime::from_str(format.to_mime_type()).unwrap()) } -pub async fn get_asset(Path(filename): Path) -> Result { - let path = join_normalized("assets", &filename); - let Some(ext) = path.extension() else { - return Err(AppError::Status(StatusCode::NOT_FOUND)); - }; - let content_type = if let Some(format) = image::ImageFormat::from_extension(ext) { - format.to_mime_type() - } else { - match ext.to_str() { - Some("svg") => mime::IMAGE_SVG.as_ref(), - _ => return Err(AppError::Status(StatusCode::NOT_FOUND)), - } - }; - let output = tokio::fs::read(path).await?; - Ok(( - [ - (header::CONTENT_TYPE, content_type), - #[cfg(not(debug_assertions))] - (header::CACHE_CONTROL, "public, max-age=3600"), - #[cfg(debug_assertions)] - (header::CACHE_CONTROL, "no-cache"), - ], - output, - ) - .into_response()) -} - pub async fn get_og() -> Result { let path = join_normalized("assets", "og.svg"); let svg_src = tokio::fs::read_to_string(&path) diff --git a/crates/web/src/handlers/auth.rs b/crates/web/src/handlers/auth.rs index b548ca0..af2ea04 100644 --- a/crates/web/src/handlers/auth.rs +++ b/crates/web/src/handlers/auth.rs @@ -44,8 +44,8 @@ pub async fn login( } Ok(html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { "Logging in... • decomp.dev" } meta http-equiv="refresh" content=(format!("0;URL={redirect_url}")); diff --git a/crates/web/src/handlers/common.rs b/crates/web/src/handlers/common.rs index da9f37b..1f5a855 100644 --- a/crates/web/src/handlers/common.rs +++ b/crates/web/src/handlers/common.rs @@ -133,11 +133,7 @@ pub fn nav_links() -> Markup { a href="https://wiki.decomp.dev" { "Wiki" } } li { - a #theme-toggle href="#" title="Toggle theme" { - (PreEscaped(r#" - -"#)) - } + a #theme-toggle .icon-theme-light-dark href="#" title="Toggle theme" {} } } } diff --git a/crates/web/src/handlers/manage.rs b/crates/web/src/handlers/manage.rs index dfa0683..499c717 100644 --- a/crates/web/src/handlers/manage.rs +++ b/crates/web/src/handlers/manage.rs @@ -39,8 +39,8 @@ pub async fn manage( Ok(html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { "Manage • decomp.dev" } (header()) @@ -138,8 +138,8 @@ async fn render_new( Ok(html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { "New Project • decomp.dev" } (header()) @@ -351,8 +351,8 @@ async fn render_manage_project( html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { (project_short_name) " • Manage" } (header()) diff --git a/crates/web/src/handlers/mod.rs b/crates/web/src/handlers/mod.rs index 4f4b597..db04177 100644 --- a/crates/web/src/handlers/mod.rs +++ b/crates/web/src/handlers/mod.rs @@ -32,7 +32,6 @@ pub fn build_router() -> Router { .route("/manage/{owner}/{repo}", get(manage::manage_project)) .route("/manage/{owner}/{repo}", post(manage::manage_project_save)) .route("/manage/{owner}/{repo}/refresh", post(manage::manage_project_refresh)) - .route("/assets/{*filename}", get(decomp_dev_images::get_asset)) .route("/og.png", get(decomp_dev_images::get_og)) .route("/", get(project::get_projects)) .route("/{owner}/{repo}", get(report::get_report)) diff --git a/crates/web/src/handlers/project.rs b/crates/web/src/handlers/project.rs index a1a37ae..3abda6c 100644 --- a/crates/web/src/handlers/project.rs +++ b/crates/web/src/handlers/project.rs @@ -157,8 +157,8 @@ pub async fn get_projects( let rendered = html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { "Projects • decomp.dev" } (header()) @@ -251,8 +251,7 @@ fn project_fragment( } @if let Some(platform) = &info.project.platform { @let platform_name = Platform::from_str(platform).map(|p| p.name()).unwrap_or(platform); - img class="platform-icon" src=(format!("/assets/platforms/{}.svg", platform)) - alt=(platform_name) title=(platform_name) width="24" height="24"; + span class=(format!("platform-icon icon-{platform}")) title=(platform_name) {} } } h6 { diff --git a/crates/web/src/handlers/report.rs b/crates/web/src/handlers/report.rs index 7f795b5..ef03e22 100644 --- a/crates/web/src/handlers/report.rs +++ b/crates/web/src/handlers/report.rs @@ -664,8 +664,8 @@ async fn render_report( Ok(html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { (project_short_name) " • Progress Report" } (header()) @@ -710,9 +710,19 @@ async fn render_report( details class="dropdown" { summary {} ul dir="rtl" { - li { a href=(project_history_path) { "History" } } + li { + a href=(project_history_path) { + "History " + span .icon-chart-line { " " } + } + } @if can_manage { - li { a href=(project_manage_path) { "Manage" } } + li { + a href=(project_manage_path) { + "Manage " + span .icon-cog { " " } + } + } } } } @@ -764,20 +774,24 @@ async fn render_report( div role="group" { @if let Some(prev_commit_path) = prev_commit_path { a role="button" class="outline secondary" href=(prev_commit_path) { - "Previous" + span .icon-left-open {} + " Previous" } } @else { button disabled class="outline secondary" { - "Previous" + span .icon-left-open {} + " Previous" } } @if let Some(next_commit_path) = next_commit_path { a role="button" class="outline secondary" href=(next_commit_path) { - "Next" + "Next " + span .icon-right-open {} } } @else { button disabled class="outline secondary" { - "Next" + "Next " + span .icon-right-open {} } } @if let Some(latest_commit_path) = latest_commit_path { @@ -899,8 +913,8 @@ async fn render_history( Ok(html! { (DOCTYPE) - html { - head lang="en" { + html lang="en" { + head { meta charset="utf-8"; title { (project_short_name) " • Progress History" } (header()) diff --git a/css/main.scss b/css/main.scss index cf180e4..3e77a47 100644 --- a/css/main.scss +++ b/css/main.scss @@ -109,10 +109,6 @@ $breakpoints: ( --pico-code-kbd-background-color: #333; --progress-background-color: #{$slate-800}; --progress-stripe-color: rgba(255, 255, 255, 0.15); - - .platform-icon { - filter: invert(1); - } } [data-theme=dark] { @@ -125,6 +121,50 @@ $breakpoints: ( } } +@font-face { + font-family: 'icons'; + src: url('../assets/icons.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + font-size: 0.9em; + line-height: 1em; + + &:before { + display: inline-block; + width: 1em; + font-family: "icons"; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-decoration: inherit; + text-align: center; + text-transform: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +} + +.icon-history:before { content: '\e800'; } +.icon-cog:before { content: '\e801'; } +.icon-theme-light-dark:before { content: '\e802'; } +.icon-chart-line:before { content: '\e803'; } +.icon-github-circled:before { content: '\e804'; } +.icon-left-open:before { content: '\e805'; } +.icon-right-open:before { content: '\e806'; } + +.icon-gc:before { content: '\ea00'; } +.icon-wii:before { content: '\ea01'; } +.icon-n64:before { content: '\ea02'; } +.icon-gba:before { content: '\ea03'; } +.icon-nds:before { content: '\ea04'; } +.icon-switch:before { content: '\ea05'; } +.icon-ps:before { content: '\ea06'; } +.icon-ps2:before { content: '\ea07'; } + // Override the tooltip component [data-tooltip]:not(a, button, input) { border-bottom: none; @@ -157,6 +197,12 @@ nav { } } +#theme-toggle { + font-size: 1.33rem; + vertical-align: middle; + text-decoration: none; +} + @keyframes progress-stripes { 0% { background-position: 0 0 @@ -273,8 +319,7 @@ $unit-size: 0.75rem; } .platform-icon { - width: 1.5rem; - height: 1.5rem; + font-size: 1.5rem; } }