crates/cli/ents-web/src/assets.rs
assets.rshistorycomment on this file
| 1 | //! Static assets embedded at compile time so the built binary stays |
| 2 | //! self-contained -- no runtime fetch, no separate asset bundle to ship |
| 3 | //! alongside `git-ents`. `ents.css` is the hand-rolled workbench |
| 4 | //! stylesheet, keyed to the design handoff's tokens and component specs; |
| 5 | //! its `@font-face` rules load the [`FONTS`] IBM Plex faces this crate |
| 6 | //! serves itself (`GET /fonts/{name}`) rather than fetching them from |
| 7 | //! Google Fonts, so the design's exact type ships without a network |
| 8 | //! dependency. `ents.js` is new to this crate |
| 9 | //! (pre-redo had no client-side script at all): a vanilla, |
| 10 | //! dependency-free progressive enhancement over `crate::pages::files`'s |
| 11 | //! raw-source blob view -- click-to-select a line or range and an inline |
| 12 | //! comment composer -- served alongside `ents.css` the same way, via |
| 13 | //! `crate::router`'s own `GET /ents.js` route. |
| 14 | //! |
| 15 | //! The icon functions below are vendored Octicons (`.gitvendors`, MIT; see |
| 16 | //! `assets/icons/LICENSE`), re-homed here from |
| 17 | //! `pre-redo:crates/git-ents-server/src/web/icons/` for |
| 18 | //! [`crate::pages::files`]'s directory listing and breadcrumbs -- the same |
| 19 | //! `include_str!`-and-tag pattern |
| 20 | //! `pre-redo:crates/git-ents-server/src/web/icons.rs` used. The workbench |
| 21 | //! shell's own chrome (the `.rail` page-family icons, the `.palette` |
| 22 | //! search glass, the `.branch` pill, the `.editor-open` pill's `↗` mark) |
| 23 | //! draws from [`sprite`] instead: one hand-rolled `<symbol>` sprite |
| 24 | //! embedded per page by `crate::pages::layout`, each use site a tiny |
| 25 | //! [`icon_use`] reference rather than a repeated inline SVG. Every sprite |
| 26 | //! symbol is an original drawing in the design handoff's 24×24 stroke style |
| 27 | //! (`stroke-width: 1.7`, round caps), never a vendored asset, so no |
| 28 | //! third-party icon license applies to the sprite; the Octicons under |
| 29 | //! `assets/icons/` remain this module's only third-party assets. |
| 30 | |
| 31 | use std::sync::LazyLock; |
| 32 | |
| 33 | use maud::{Markup, PreEscaped}; |
| 34 | |
| 35 | pub(crate) const OVERRIDES: &str = include_str!("assets/ents.css"); |
| 36 | |
| 37 | /// The client-side line-selection/comment-composer script |
| 38 | /// [`crate::router`]'s `GET /ents.js` serves -- see this module's own doc. |
| 39 | pub(crate) const SCRIPT: &str = include_str!("assets/ents.js"); |
| 40 | |
| 41 | /// The self-hosted IBM Plex webfonts the workbench renders in |
| 42 | /// (`assets/fonts/`, SIL OFL, see `assets/fonts/LICENSE`), embedded so the |
| 43 | /// design's exact type is served without a runtime Google Fonts fetch -- |
| 44 | /// the same "self-contained binary, no network dependency" rule |
| 45 | /// [`OVERRIDES`] and the vendored Octicons already hold. `ents.css`'s |
| 46 | /// `@font-face` rules name each by its `GET /fonts/{name}` URL; [`font`] |
| 47 | /// resolves that name back to these bytes. |
| 48 | pub(crate) const FONTS: &[(&str, &[u8])] = &[ |
| 49 | ( |
| 50 | "plex-sans-400.woff2", |
| 51 | include_bytes!("assets/fonts/plex-sans-400.woff2"), |
| 52 | ), |
| 53 | ( |
| 54 | "plex-sans-500.woff2", |
| 55 | include_bytes!("assets/fonts/plex-sans-500.woff2"), |
| 56 | ), |
| 57 | ( |
| 58 | "plex-sans-600.woff2", |
| 59 | include_bytes!("assets/fonts/plex-sans-600.woff2"), |
| 60 | ), |
| 61 | ( |
| 62 | "plex-sans-700.woff2", |
| 63 | include_bytes!("assets/fonts/plex-sans-700.woff2"), |
| 64 | ), |
| 65 | ( |
| 66 | "plex-mono-400.woff2", |
| 67 | include_bytes!("assets/fonts/plex-mono-400.woff2"), |
| 68 | ), |
| 69 | ( |
| 70 | "plex-mono-500.woff2", |
| 71 | include_bytes!("assets/fonts/plex-mono-500.woff2"), |
| 72 | ), |
| 73 | ( |
| 74 | "plex-mono-600.woff2", |
| 75 | include_bytes!("assets/fonts/plex-mono-600.woff2"), |
| 76 | ), |
| 77 | ]; |
| 78 | |
| 79 | /// The embedded woff2 bytes for `name`, or `None` for a name no |
| 80 | /// `@font-face` rule references -- [`crate::router`]'s `GET /fonts/{name}` |
| 81 | /// handler serves the hit and 404s the miss, so an unknown path can never |
| 82 | /// read outside this fixed table. |
| 83 | pub(crate) fn font(name: &str) -> Option<&'static [u8]> { |
| 84 | FONTS |
| 85 | .iter() |
| 86 | .find(|(file, _)| *file == name) |
| 87 | .map(|(_, bytes)| *bytes) |
| 88 | } |
| 89 | |
| 90 | /// Adapt a vendored Octicon to this UI: tag it with the `.icon` class the |
| 91 | /// stylesheet targets and mark it decorative for assistive tech. Every |
| 92 | /// vendored file opens with a bare `<svg …>` element, so a single prefix |
| 93 | /// swap suffices (mirrors |
| 94 | /// `pre-redo:crates/git-ents-server/src/web/icons.rs`'s own `inline`). |
| 95 | fn inline(svg: &str) -> String { |
| 96 | svg.replacen("<svg ", "<svg class=\"icon\" aria-hidden=\"true\" ", 1) |
| 97 | } |
| 98 | |
| 99 | /// Define an icon accessor per vendored Octicon file. Each prepares its |
| 100 | /// inline markup once and hands out a cheap clone on use. |
| 101 | macro_rules! icons { |
| 102 | ($($name:ident => $file:literal),* $(,)?) => { |
| 103 | $( |
| 104 | pub(crate) fn $name() -> Markup { |
| 105 | static HTML: LazyLock<String> = |
| 106 | LazyLock::new(|| inline(include_str!(concat!("assets/icons/", $file, ".svg")))); |
| 107 | PreEscaped(HTML.clone()) |
| 108 | } |
| 109 | )* |
| 110 | }; |
| 111 | } |
| 112 | |
| 113 | icons! { |
| 114 | icon_folder => "file-directory-fill", |
| 115 | icon_file => "file", |
| 116 | icon_chevron => "chevron-right", |
| 117 | } |
| 118 | |
| 119 | /// The workbench shell's inline `<symbol>` sprite (see this module's own |
| 120 | /// doc) -- embedded once per page, right after `<body>`, so every |
| 121 | /// [`icon_use`] reference on the page resolves against it. |
| 122 | pub(crate) fn sprite() -> Markup { |
| 123 | PreEscaped(include_str!("assets/sprite.svg").to_owned()) |
| 124 | } |
| 125 | |
| 126 | /// An `.icon`-classed, decorative `<use>` reference into [`sprite`] -- |
| 127 | /// `id` names one of its `<symbol>`s (`i-home`, `i-files`, ...). Sized |
| 128 | /// entirely by the use site's own CSS rule (`.rail a .icon`, |
| 129 | /// `.palette .icon`, `.branch .icon`), since a symbol carries only a |
| 130 | /// viewBox. |
| 131 | pub(crate) fn icon_use(id: &str) -> Markup { |
| 132 | PreEscaped(format!( |
| 133 | "<svg class=\"icon\" aria-hidden=\"true\"><use href=\"#{id}\"/></svg>" |
| 134 | )) |
| 135 | } |