OXYGENHUB

feat(web): the Diffusion mark as the diffusion account's picture (closes #154) 08e221b77658

Ripples from the Oxygen ring out to three consumers, in the mark's
gradient on the deep blue of the band; served for the account the hub runs
until someone uploads another picture for it.
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-13 13:50 UTC
Signature
✔ signed by Andy McCall
Hash
08e221b776581dd09db3b37c9d3360be6bf420dc197b878a3581dde4d661b63d
Depends on
101740454617 a160b98fb922 b5668108487f c9aacc0e7dc1 d23464c0ac60 eb2a3502601d
Depended on by
ac689cd21adf 4fa0da126b70
In bookmarks
api-unknown-fields cancel-landed-runs diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
In tags
v0.5.0 v0.5.1 v0.5.2 v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
7 atoms, 1 tree operation, 3 files

Browse the tree at this change

added assets/icons/diffusion.svg +17 −0
1 +
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
2 +
  <title>Diffusion</title>
3 +
  <defs>
4 +
    <linearGradient id="dfGrad" x1="0%" y1="0%" x2="100%" y2="100%">
5 +
      <stop offset="0%" stop-color="#00F0FF"/>
6 +
      <stop offset="100%" stop-color="#0072FF"/>
7 +
    </linearGradient>
8 +
  </defs>
9 +
  <rect width="64" height="64" fill="#0b1a3a"/>
10 +
  <circle cx="17" cy="32" r="7.5" fill="none" stroke="url(#dfGrad)" stroke-width="3"/>
11 +
  <circle cx="17" cy="32" r="3" fill="#00F0FF"/>
12 +
  <path d="M25 18.14 A16 16 0 0 1 25 45.86" fill="none" stroke="url(#dfGrad)" stroke-width="3" stroke-linecap="round" opacity=".9"/>
13 +
  <path d="M30.5 8.62 A27 27 0 0 1 30.5 55.38" fill="none" stroke="url(#dfGrad)" stroke-width="3" stroke-linecap="round" opacity=".6"/>
14 +
  <circle cx="45.7" cy="11.9" r="4" fill="#00F0FF"/>
15 +
  <circle cx="52" cy="32" r="4" fill="#FFFFFF"/>
16 +
  <circle cx="45.7" cy="52.1" r="4" fill="#0072FF"/>
17 +
</svg>
modified CHANGELOG.md +1 −0
…
4 unchanged lines
5 5
## Unreleased
6 6
7 7
- **Diffusion (#148, docs/diffusion.md).** When a repository that others pin as a subrepo moves on, each consumer that follows it gets a proposal moving the pin, signed by the hub, tested by the consumer's own pipeline, with the upstream's changes listed. One proposal per consumer and subrepo, kept up to date; never landed by itself. Turned on per repository (Settings › Diffusion, `PUT /repos/{owner}/{name}/diffusion`, or `follow` in `.oxymodules`), off by default; `green` waits for the upstream's run to pass. A repository's page shows who uses it and how far behind each is (Diffusion › Diffuses to, `GET /repos/{owner}/{name}/consumers`). Consumers are bumped on the bookmark their work lands on, learned from where their proposals land.
8 +
- **Diffusion has a face (#154):** the `diffusion` account's picture is the Diffusion mark (`assets/icons/diffusion.svg`), ripples from a source out to its consumers, until someone uploads another.
8 9
- **Diffusion in the API (#153):** `GET /repos/{owner}/{name}/diffusion` gives what a repository takes from and what it diffuses to, each with its pill, for the VS Code extension.
9 10
- **Tidier pages (#152).** The repository sidebar is a column of cards with shaded headings: Diffusion in one card (Takes from and Diffuses to, each with its pill), the ten most recently moved bookmarks with a link to the rest, and the hub's delegate key shown with the OxygenHub mark. The code tree has a shaded head with the latest change and the history, and folder and file icons; the site bar has icons; the Diffusion tables share one column grid; Help › Coming from GitHub covers submodules and Renovate.
10 11
- **Admin › Runners › Images (#146):** every runner image, where it is and what uses it.
…
164 unchanged lines
modified crates/hub/src/web/avatar.rs +17 −2
…
189 unchanged lines
190 190
                .into_response();
191 191
        }
192 192
    }
193 -
    ([(header::CONTENT_TYPE, "image/svg+xml"), (header::CACHE_CONTROL, "public, max-age=300")], identicon(name)).into_response()
193 +
    // The hub's own accounts have their marks until someone uploads another.
194 +
    let svg = system_mark(name).map(String::from).unwrap_or_else(|| identicon(name));
195 +
    ([(header::CONTENT_TYPE, "image/svg+xml"), (header::CACHE_CONTROL, "public, max-age=300")], svg).into_response()
194 196
}
195 197
198 +
/// The mark of an account the hub runs itself: Diffusion's ripples from a
199 +
/// source out to its consumers.
200 +
pub fn system_mark(name: &str) -> Option<&'static str> {
201 +
    (name == crate::diffusion::ACCOUNT).then_some(include_str!("../../../../assets/icons/diffusion.svg"))
202 +
}
203 +
196 204
#[cfg(test)]
197 205
mod tests {
198 -
    use super::{identicon, process, SIZE};
206 +
    use super::{identicon, process, system_mark, SIZE};
199 207
200 208
    fn encoded(img: image::DynamicImage, format: image::ImageFormat) -> Vec<u8> {
201 209
        let mut out = Vec::new();
…
30 unchanged lines
232 240
        // Larger than the side limit: refused before allocating it.
233 241
        let big = encoded(image::DynamicImage::ImageLuma8(image::GrayImage::new(8001, 1)), image::ImageFormat::Png);
234 242
        assert!(process(&big).is_err(), "8001 pixels wide");
243 +
    }
244 +
245 +
    #[test]
246 +
    fn the_diffusion_account_has_its_mark() {
247 +
        let m = system_mark("diffusion").expect("a mark");
248 +
        assert!(m.starts_with("<svg") && m.contains("<title>Diffusion</title>"));
249 +
        assert!(system_mark("andy").is_none() && system_mark("Diffusion").is_none(), "only the hub's own account, by its exact name");
235 250
    }
236 251
237 252
    #[test]
…
21 unchanged lines