feat(brand): "Duo" logo refresh — mint panes mark across all icon assets

Replace the orange window-and-cursor identity with the "Duo" mark: two
offset session panes (mint #3FDD8C behind, ink #17171A in front) with a
prompt chevron, on a cream tile following the Big Sur icon grid.

- app-icon.svg is the master; app-icon.png, tty7.icns and favicon.ico
  are re-rendered from it (rsvg-convert + iconutil + PIL)
- logo.svg/logo.png/logo@256.png carry the tile-less transparent mark
- tray.svg redrawn as the same mark in template form: solid front pane
  with the chevron masked out, back pane at partial alpha
- bare (non-bundled) macOS binaries now set the Dock icon at runtime
  via NSApplication.setApplicationIconImage, so cargo dev/run shows the
  logo instead of the generic executable icon
- README version badge and the social preview switch to the new brand
  green; social preview copy re-aligned with the current README slogan
This commit is contained in:
l0ng-ai
2026-07-17 16:31:41 +08:00
parent 55b5d0151f
commit 6af47dfb53
14 changed files with 72 additions and 16 deletions
Generated
+1
View File
@@ -9042,6 +9042,7 @@ dependencies = [
"notify-rust",
"objc2 0.6.4",
"objc2-app-kit 0.3.2",
"objc2-foundation 0.3.2",
"plist",
"portable-pty",
"reqwest_client",
+4 -1
View File
@@ -172,7 +172,10 @@ winresource = "0.1"
# so the native traffic-light buttons render in the right light/dark style.
[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6"
objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSResponder", "NSAppearance", "NSGraphics"] }
objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSResponder", "NSAppearance", "NSGraphics", "NSImage"] }
# NSData feeds the runtime Dock icon for bare (non-bundled) binaries — see
# `set_dock_icon_for_bare_binary` in main.rs.
objc2-foundation = { version = "0.3", features = ["NSData"] }
# x11/wayland are the Linux windowing backends; only pull them on Linux. The
# Windows backend (`gpui_windows`) and macOS backend are selected by gpui_platform
+1 -1
View File
@@ -11,7 +11,7 @@
<br />
[![CI](https://github.com/l0ng-ai/tty7/actions/workflows/ci.yml/badge.svg)](https://github.com/l0ng-ai/tty7/actions/workflows/ci.yml)
[![Version](https://img.shields.io/github/v/tag/l0ng-ai/tty7?label=version&color=ff8a5c)](https://github.com/l0ng-ai/tty7/releases)
[![Version](https://img.shields.io/github/v/tag/l0ng-ai/tty7?label=version&color=3FDD8C)](https://github.com/l0ng-ai/tty7/releases)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
[![Discord](https://img.shields.io/badge/Discord-join%20chat-5865F2?logo=discord&logoColor=white)](https://discord.gg/s3dethqz2V)
+1 -1
View File
@@ -11,7 +11,7 @@
<br />
[![CI](https://github.com/l0ng-ai/tty7/actions/workflows/ci.yml/badge.svg)](https://github.com/l0ng-ai/tty7/actions/workflows/ci.yml)
[![Version](https://img.shields.io/github/v/tag/l0ng-ai/tty7?label=version&color=ff8a5c)](https://github.com/l0ng-ai/tty7/releases)
[![Version](https://img.shields.io/github/v/tag/l0ng-ai/tty7?label=version&color=3FDD8C)](https://github.com/l0ng-ai/tty7/releases)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
[![Discord](https://img.shields.io/badge/Discord-%E5%8A%A0%E5%85%A5%E7%BE%A4%E7%BB%84-5865F2?logo=discord&logoColor=white)](https://discord.gg/s3dethqz2V)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

+10 -5
View File
@@ -1,6 +1,11 @@
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="8" y="8" width="104" height="104" rx="24" fill="#ff8a5c"/>
<rect x="26" y="32" width="68" height="56" rx="13" fill="none" stroke="#0d1117" stroke-width="6"/>
<line x1="26" y1="49" x2="94" y2="49" stroke="#0d1117" stroke-width="4" opacity="0.7"/>
<rect x="60" y="60" width="12" height="20" rx="2.5" fill="#0d1117"/>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<!-- "Duo" mark (design 2a): two offset session panes, prompt chevron in the
front one. Tile inset + radius follow the macOS Big Sur icon grid
(824pt tile @1024). -->
<rect x="100" y="100" width="824" height="824" rx="185" fill="#ECEAE4"/>
<g transform="translate(108.8 125.6) scale(8.4)">
<rect x="28" y="16" width="58" height="46" rx="12" fill="#3FDD8C" opacity="0.8"/>
<rect x="10" y="30" width="58" height="46" rx="12" fill="#17171A"/>
<path d="M24 42 L35 53 L24 64" fill="none" stroke="#ECEAE4" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 24 KiB

+5 -4
View File
@@ -1,5 +1,6 @@
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="12" y="22" width="96" height="76" rx="15" fill="none" stroke="#3F3F46" stroke-width="6"/>
<line x1="12" y1="44" x2="108" y2="44" stroke="#3F3F46" stroke-width="4" opacity="0.7"/>
<rect x="52" y="58" width="15" height="26" rx="3" fill="#FF6B35"/>
<svg width="120" height="120" viewBox="8 6 80 80" xmlns="http://www.w3.org/2000/svg">
<!-- "Duo" mark on transparent — same geometry as app-icon.svg, no tile. -->
<rect x="28" y="16" width="58" height="46" rx="12" fill="#3FDD8C" opacity="0.8"/>
<rect x="10" y="30" width="58" height="46" rx="12" fill="#17171A"/>
<path d="M24 42 L35 53 L24 64" fill="none" stroke="#ECEAE4" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

After

Width:  |  Height:  |  Size: 137 KiB

+15 -4
View File
@@ -1,5 +1,16 @@
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="11" y="21" width="98" height="78" rx="18" fill="none" stroke="#000" stroke-width="9"/>
<path d="M34 48 L50 61 L34 74" fill="none" stroke="#000" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/>
<line x1="60" y1="76" x2="82" y2="76" stroke="#000" stroke-width="9" stroke-linecap="round"/>
<svg width="96" height="96" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg">
<!-- Template (alpha-only) tray glyph: the "Duo" mark, same geometry as
app-icon.svg. Solid front pane with the chevron knocked out; the back
pane reads through partial alpha (template images keep alpha, drop
color). The mask spans both panes so the chevron cuts clean through. -->
<defs>
<mask id="chevron-cut">
<rect width="96" height="96" fill="#fff"/>
<path d="M24 42 L35 53 L24 64" fill="none" stroke="#000" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
</mask>
</defs>
<g mask="url(#chevron-cut)">
<rect x="28" y="16" width="58" height="46" rx="12" fill="#000" opacity="0.35"/>
<rect x="10" y="30" width="58" height="46" rx="12" fill="#000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 824 B

BIN
View File
Binary file not shown.
+35
View File
@@ -246,6 +246,39 @@ fn enrich_path_from_login_shell() {
unsafe { std::env::set_var("PATH", merged) };
}
/// A bare (non-bundled) binary — `cargo dev` / `cargo run` — has no
/// `Info.plist` pointing the Dock at `tty7.icns`, so macOS shows the generic
/// executable icon. Feed the Dock the bundled logo at runtime in that case;
/// launches from the real `.app` keep the `.icns` and skip this.
#[cfg(target_os = "macos")]
fn set_dock_icon_for_bare_binary() {
use objc2::{AnyThread, MainThreadMarker};
use objc2_app_kit::{NSApplication, NSImage};
use objc2_foundation::NSData;
let bundled = std::env::current_exe().is_ok_and(|p| {
p.components()
.any(|c| c.as_os_str().to_string_lossy().ends_with(".app"))
});
if bundled {
return;
}
// gpui's `run` closure executes on the main thread; bail defensively
// rather than panic if that ever stops holding.
let Some(mtm) = MainThreadMarker::new() else {
return;
};
static ICON_PNG: &[u8] = include_bytes!("../assets/app-icon.png");
let data = NSData::with_bytes(ICON_PNG);
if let Some(image) = NSImage::initWithData(NSImage::alloc(), &data) {
// SAFETY: passing a valid NSImage on the main thread; AppKit copies the
// reference, no ownership transferred.
unsafe {
NSApplication::sharedApplication(mtm).setApplicationIconImage(Some(&image));
}
}
}
fn main() {
// Agent-hook mode: `tty7 agent-hook <agent> <event>` is the tiny emitter
// Claude Code's hooks invoke (see `core::agent_hooks`). It reads the hook
@@ -323,6 +356,8 @@ fn main() {
gpui_component::init(cx);
register_bundled_fonts(cx);
cx.activate(true);
#[cfg(target_os = "macos")]
set_dock_icon_for_bare_binary();
// Load user config once and stash it as a global for views to read.
cx.set_global(Config::load());
// Build the theme registry (built-ins + user theme files) before the