From 989fd5b04a8ae0447d4a52344f38748edb680a00 Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:41:42 -0400 Subject: [PATCH] goblin: name avatars get a thin yellow outline, not a conic gradient The username-seeded conic-gradient ring is replaced by a single thin, light-yellow outline that hugs the avatar circle (image or grinmark orb). It only appears for a claimed name (never a bare npub) and simply signifies 'this identity has a name' - no per-name color, no gap, no gradient. Drops the now-dead ring_params seed. --- src/gui/views/goblin/identicon.rs | 10 --- src/gui/views/goblin/widgets.rs | 105 ++++++++++-------------------- 2 files changed, 36 insertions(+), 79 deletions(-) diff --git a/src/gui/views/goblin/identicon.rs b/src/gui/views/goblin/identicon.rs index 794a1e04..aba46978 100644 --- a/src/gui/views/goblin/identicon.rs +++ b/src/gui/views/goblin/identicon.rs @@ -60,16 +60,6 @@ fn hsl_to_rgb(h: f64, s: f64, l: f64) -> String { format!("#{r:02x}{g:02x}{b:02x}") } -/// Conic-ring hue path for a custom-image avatar, seeded by the USERNAME (not -/// the pubkey, per the design): base hue from the first two hash bytes, sweep -/// width (60°–180°) from the third. Deterministic, like the gradients. -pub(super) fn ring_params(name: &str) -> (f64, f64) { - let hash = Sha256::digest(name.as_bytes()); - let base = ((u16::from(hash[0]) << 8 | u16::from(hash[1])) as f64 / 65_535.0) * 360.0; - let sweep = 60.0 + (hash[2] as f64 / 255.0) * 120.0; - (base, sweep) -} - /// Normalise any caller-supplied id (npub bech32 OR raw hex) to the canonical /// lowercase hex pubkey used as the seed everywhere. pub fn to_hex_seed(id: &str) -> String { diff --git a/src/gui/views/goblin/widgets.rs b/src/gui/views/goblin/widgets.rs index 79a3a79e..b02750eb 100644 --- a/src/gui/views/goblin/widgets.rs +++ b/src/gui/views/goblin/widgets.rs @@ -27,67 +27,42 @@ pub fn amount_str(atomic: u64) -> String { grin_core::core::amount_to_hr_string(atomic, true) } -/// A custom-picture avatar: the texture drawn in a circle, wrapped by a thin -/// conic-gradient ring derived deterministically from the username (the name, -/// not the npub). The image is inset so the ring sits at the perimeter. +/// A custom-picture avatar: the texture drawn to fill the circle, with a thin +/// light-yellow outline hugging its edge when the identity is a claimed name. pub fn avatar_tex(ui: &mut Ui, tex: &egui::TextureHandle, name: &str, size: f32) -> Response { let (rect, resp) = ui.allocate_exact_size(Vec2::splat(size), Sense::click()); - let thickness = (size * 0.045).max(1.0); - let gap = (size * 0.07).max(2.0); - let img_rect = rect.shrink(thickness + gap); - let rounding = eframe::epaint::CornerRadius::same((img_rect.width() / 2.0) as u8); + let rounding = eframe::epaint::CornerRadius::same((rect.width() / 2.0) as u8); egui::Image::new(tex) .corner_radius(rounding) - .fit_to_exact_size(img_rect.size()) - .paint_at(ui, img_rect); - conic_ring( - ui, - rect.center(), - size / 2.0 - thickness / 2.0, - thickness, - name, - ); + .fit_to_exact_size(rect.size()) + .paint_at(ui, rect); + if is_named(name) { + name_ring(ui, rect.center(), size); + } resp } -/// Thin conic-gradient ring at the avatar perimeter, hue path seeded by the -/// username (see `identicon::ring_params`). Drawn as a feathered triangle mesh -/// (~64 segments, per-vertex color) so edges stay smooth; a triangle-wave hue -/// sweep keeps the gradient seamless where the circle closes. No new deps. -fn conic_ring(ui: &Ui, center: egui::Pos2, r_mid: f32, thickness: f32, name: &str) { - use eframe::epaint::{Mesh, Shape, Vertex, WHITE_UV}; - let (base_hue, sweep) = super::identicon::ring_params(name); - const SEGS: u32 = 64; - const FEATHER: f32 = 0.75; - let r_in = r_mid - thickness / 2.0; - let r_out = r_mid + thickness / 2.0; - let radii = [r_out + FEATHER, r_out, r_in, (r_in - FEATHER).max(0.0)]; - let alphas = [0u8, 255, 255, 0]; - let mut mesh = Mesh::default(); - for i in 0..=SEGS { - let frac = i as f32 / SEGS as f32; - let theta = frac * std::f32::consts::TAU - std::f32::consts::FRAC_PI_2; - let wave = 1.0 - (2.0 * frac - 1.0).abs(); - let hue = (base_hue + sweep * f64::from(wave)) % 360.0; - let (r, g, b) = super::identicon::hsl_rgb8(hue, 0.62, 0.55); - let dir = Vec2::new(theta.cos(), theta.sin()); - for (radius, alpha) in radii.iter().zip(alphas) { - mesh.vertices.push(Vertex { - pos: center + dir * *radius, - uv: WHITE_UV, - color: Color32::from_rgba_unmultiplied(r, g, b, alpha), - }); - } - } - for i in 0..SEGS { - let a = i * 4; - let b = a + 4; - for ring in 0..3 { - let (p0, p1, p2, p3) = (a + ring, a + ring + 1, b + ring, b + ring + 1); - mesh.indices.extend_from_slice(&[p0, p1, p2, p1, p3, p2]); - } - } - ui.painter().add(Shape::mesh(mesh)); +/// A thin light-yellow outline on the avatar's edge — the marker of a claimed +/// name. Just an outline that touches the circle (no gap, no gradient); the +/// yellow reads as "this identity has a name" and nothing more. +fn name_ring(ui: &Ui, center: egui::Pos2, size: f32) { + let thickness = (size * 0.03).max(1.0); + // Goblin accent yellow, lightened toward white so the outline stays soft. + let c = super::theme::tokens().accent; + let light = Color32::from_rgb( + c.r().saturating_add((255 - c.r()) / 3), + c.g().saturating_add((255 - c.g()) / 3), + c.b().saturating_add((255 - c.b()) / 3), + ); + // Sit the stroke just inside the perimeter so it stays fully on-canvas. + let radius = size / 2.0 - thickness / 2.0; + ui.painter() + .circle_stroke(center, radius, egui::Stroke::new(thickness, light)); +} + +/// Whether a display name is a real claimed name (not empty, not a bare npub). +fn is_named(name: &str) -> bool { + !name.is_empty() && !name.starts_with("npub") } /// Deterministic gradient avatar (a pubkey-seeded two-tone tile with the Grin @@ -100,23 +75,15 @@ pub fn gradient_avatar(ui: &mut Ui, id: &str, size: f32) -> Response { resp } -/// The UNCHANGED npub-seeded grinmark gradient with a thin conic ring seeded by -/// the USERNAME simply added around its edge — the gradient stays exactly as a -/// ring-less avatar draws it; the ring is the only thing the username adds. +/// The UNCHANGED npub-seeded grinmark gradient orb with a thin light-yellow +/// outline hugging its edge to mark a claimed name — the orb fills the circle +/// exactly as a ring-less avatar draws it; the outline is all the name adds. pub fn gradient_avatar_ringed(ui: &mut Ui, id: &str, name: &str, size: f32) -> Response { let (rect, resp) = ui.allocate_exact_size(Vec2::splat(size), Sense::click()); - let thickness = (size * 0.045).max(1.0); - let gap = (size * 0.07).max(2.0); - // Inset the orb so the username ring sits AROUND it with a clear gap, - // rather than overlapping its edge. - paint_gradient(ui, id, rect.shrink(thickness + gap)); - conic_ring( - ui, - rect.center(), - size / 2.0 - thickness / 2.0, - thickness, - name, - ); + paint_gradient(ui, id, rect); + if is_named(name) { + name_ring(ui, rect.center(), size); + } resp }