mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 08:02:24 +00:00
feat(agents): give Grok its brand avatar
Grok was drawing the generic robot glyph on a slate disc — the fallback picked back when no usable mark was bundled. xAI publishes its symbol only as a ~2:1 landscape lockup that bleeds off its own canvas; traced and fitted to a 24x24 box it is unreadable as a 16px silhouette, which is what the tab chip and sidebar render. So the bundled mark is lobehub/lobe-icons' square transcription (MIT), drawn for exactly this avatar use. Its notice rides in the SVG. The slate accent goes with it. That mid-tone exists for vendors whose monochrome mark is grey or a gradient (Cursor), because a white field vanishes on a light theme; a black field has no such problem — it stays darker than even the darkest theme background and the white mark carries the badge. Grok brands in black, like Codex, so it keeps that. Adds a guard test: every CLIAgent::icon_path must resolve through the asset source. A brand mark means touching two files, and forgetting the registration costs the agent its avatar silently.
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- Grok brand mark, from lobehub/lobe-icons (MIT, Copyright (c) 2023 LobeHub).
|
||||||
|
Normalized to tty7's 24x24 icon format; geometry unchanged. -->
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815" fill="#000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 874 B |
+11
-5
@@ -338,8 +338,11 @@ impl CLIAgent {
|
|||||||
|
|
||||||
/// Brand accent (0xRRGGBB) for the tab chip's agent dot. Chosen for legibility
|
/// Brand accent (0xRRGGBB) for the tab chip's agent dot. Chosen for legibility
|
||||||
/// on both light and dark themes rather than exact brand black/white. A pure
|
/// on both light and dark themes rather than exact brand black/white. A pure
|
||||||
/// black or white dot vanishes against one theme, so monochrome vendors get
|
/// *white* field vanishes against a light theme, so vendors whose mark is a
|
||||||
/// a recognizable mid-tone hue instead; Codex keeps its black field.
|
/// grey or gradient monochrome (Cursor) get a recognizable mid-tone hue
|
||||||
|
/// instead. A black field is a different case: it stays darker than even the
|
||||||
|
/// darkest theme background and the white mark on it carries the badge, so
|
||||||
|
/// vendors who actually brand in black (Codex, Grok) keep it.
|
||||||
pub fn accent_rgb(self) -> u32 {
|
pub fn accent_rgb(self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
CLIAgent::Claude => 0xD97757, // Claude terracotta
|
CLIAgent::Claude => 0xD97757, // Claude terracotta
|
||||||
@@ -357,7 +360,7 @@ impl CLIAgent {
|
|||||||
CLIAgent::Hermes => 0x8B5CF6, // violet
|
CLIAgent::Hermes => 0x8B5CF6, // violet
|
||||||
CLIAgent::Vibe => 0xFF7000, // Mistral orange
|
CLIAgent::Vibe => 0xFF7000, // Mistral orange
|
||||||
CLIAgent::Antigravity => 0x2563EB, // Google blue (darker than Gemini's)
|
CLIAgent::Antigravity => 0x2563EB, // Google blue (darker than Gemini's)
|
||||||
CLIAgent::Grok => 0x64748B, // xAI is monochrome → slate
|
CLIAgent::Grok => 0x000000, // xAI brands in black
|
||||||
CLIAgent::Qwen => 0x7C3AED, // Qwen purple
|
CLIAgent::Qwen => 0x7C3AED, // Qwen purple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,6 +382,7 @@ impl CLIAgent {
|
|||||||
CLIAgent::Cursor => "icons/agents/cursor.svg",
|
CLIAgent::Cursor => "icons/agents/cursor.svg",
|
||||||
CLIAgent::Goose => "icons/agents/goose.svg",
|
CLIAgent::Goose => "icons/agents/goose.svg",
|
||||||
CLIAgent::Droid => "icons/agents/droid.svg",
|
CLIAgent::Droid => "icons/agents/droid.svg",
|
||||||
|
CLIAgent::Grok => "icons/agents/grok.svg",
|
||||||
// No brand mark bundled → generic robot glyph.
|
// No brand mark bundled → generic robot glyph.
|
||||||
CLIAgent::Aider
|
CLIAgent::Aider
|
||||||
| CLIAgent::Pi
|
| CLIAgent::Pi
|
||||||
@@ -386,7 +390,6 @@ impl CLIAgent {
|
|||||||
| CLIAgent::Hermes
|
| CLIAgent::Hermes
|
||||||
| CLIAgent::Vibe
|
| CLIAgent::Vibe
|
||||||
| CLIAgent::Antigravity
|
| CLIAgent::Antigravity
|
||||||
| CLIAgent::Grok
|
|
||||||
| CLIAgent::Qwen => "icons/bot.svg",
|
| CLIAgent::Qwen => "icons/bot.svg",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -940,9 +943,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The two vendors who actually brand in black keep the black field rather
|
||||||
|
/// than the mid-tone substitute monochrome marks otherwise get.
|
||||||
#[test]
|
#[test]
|
||||||
fn codex_avatar_uses_its_black_brand_field() {
|
fn black_branded_avatars_keep_their_brand_field() {
|
||||||
assert_eq!(CLIAgent::Codex.accent_rgb(), 0x000000);
|
assert_eq!(CLIAgent::Codex.accent_rgb(), 0x000000);
|
||||||
|
assert_eq!(CLIAgent::Grok.accent_rgb(), 0x000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -151,6 +151,11 @@ fn agent_icon(path: &str) -> Option<&'static [u8]> {
|
|||||||
"icons/agents/cursor.svg" => include_bytes!("../../assets/icons/agents/cursor.svg"),
|
"icons/agents/cursor.svg" => include_bytes!("../../assets/icons/agents/cursor.svg"),
|
||||||
"icons/agents/goose.svg" => include_bytes!("../../assets/icons/agents/goose.svg"),
|
"icons/agents/goose.svg" => include_bytes!("../../assets/icons/agents/goose.svg"),
|
||||||
"icons/agents/droid.svg" => include_bytes!("../../assets/icons/agents/droid.svg"),
|
"icons/agents/droid.svg" => include_bytes!("../../assets/icons/agents/droid.svg"),
|
||||||
|
// The one mark not taken from the vendor directly: xAI publishes its
|
||||||
|
// symbol only as a ~2:1 landscape lockup that turns to mush as a 16px
|
||||||
|
// silhouette, so this is lobehub/lobe-icons' square transcription (MIT),
|
||||||
|
// drawn for exactly this avatar use. Its notice rides in the SVG.
|
||||||
|
"icons/agents/grok.svg" => include_bytes!("../../assets/icons/agents/grok.svg"),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(bytes)
|
Some(bytes)
|
||||||
@@ -181,6 +186,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Every agent avatar must resolve to real bytes. Adding a brand mark means
|
||||||
|
/// touching two files — the SVG and the arm above — and forgetting the
|
||||||
|
/// second one costs the agent its avatar with nothing to show for it.
|
||||||
|
#[test]
|
||||||
|
fn every_agent_icon_resolves() {
|
||||||
|
for agent in crate::core::cli_agent::CLIAgent::ALL {
|
||||||
|
let path = agent.icon_path();
|
||||||
|
assert!(
|
||||||
|
Assets.load(path).unwrap().is_some(),
|
||||||
|
"{} points at {path}, which nothing serves",
|
||||||
|
agent.display_name()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A `stock/` path for a glyph tty7 never overrode still has to resolve —
|
/// A `stock/` path for a glyph tty7 never overrode still has to resolve —
|
||||||
/// the prefix is a bypass, not a separate asset set.
|
/// the prefix is a bypass, not a separate asset set.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user