feat(client): refresh brand to Heicode H logo + align macOS build script

- Replace logo source with new glassy gradient H (heicode-logo.jpg)
- Regen all icon sizes via scripts/rebuild-icons.py (PIL + ICO + ICNS):
  32/64/128/128@2x/icon.png + Square{30,44,71,89,107,142,150,284,310}+StoreLogo
  + multi-res icon.ico + icon.icns
- Update public/app-icon.png to 1024×1024 derived
- Align cc-haha/desktop/scripts/build-macos-arm64.sh to Heicode branding:
  APP_BUNDLE_NAME=HeiCode.app, APP_BUNDLE_ID=com.heicode.desktop,
  DMG volume=Heicode, default DMG=Heicode_0.1.0_aarch64.dmg,
  cleanup paths use heicode-desktop crate name (mirrors Cargo.toml rename)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 17:38:36 +08:00
co-authored by Claude Opus 4.7
parent a268079751
commit 9ffe261374
21 changed files with 97 additions and 12 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

After

Width:  |  Height:  |  Size: 414 KiB

+14 -12
View File
@@ -9,12 +9,14 @@ REPO_ROOT="$(cd "${DESKTOP_DIR}/.." && pwd)"
TARGET_TRIPLE="aarch64-apple-darwin"
TAURI_TARGET_DIR="${DESKTOP_DIR}/src-tauri/target"
CANONICAL_OUTPUT_DIR="${DESKTOP_DIR}/build-artifacts/macos-arm64"
APP_BUNDLE_NAME="Claude Code Haha.app"
APP_BUNDLE_ID="com.claude-code-haha.desktop"
APP_BUNDLE_NAME="HeiCode.app"
APP_BUNDLE_ID="com.heicode.desktop"
DMG_VOLUME_NAME="Heicode"
DEFAULT_DMG_BASENAME="Heicode_0.1.0_aarch64.dmg"
usage() {
cat <<'EOF'
Build Claude Code Haha desktop for macOS Apple Silicon and output a DMG.
Build Heicode desktop for macOS Apple Silicon and output a DMG.
Usage:
./desktop/scripts/build-macos-arm64.sh [extra tauri build args...]
@@ -79,10 +81,10 @@ rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/bundle"
rm -rf "${DESKTOP_DIR}/src-tauri/target/release/bundle"
rm -rf "${DESKTOP_DIR}/dist"
rm -f "${DESKTOP_DIR}/tsconfig.tsbuildinfo"
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/build/claude-code-desktop-"*
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/.fingerprint/claude-code-desktop-"*
rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/claude_code_desktop-"*
rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/libclaude_code_desktop-"*
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/build/heicode-desktop-"*
rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/.fingerprint/heicode-desktop-"*
rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/heicode_desktop-"*
rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/libheicode_desktop-"*
echo "[build-macos-arm64] Rebuilding frontend (tsc + vite)..."
(cd "${DESKTOP_DIR}" && bun run build)
@@ -159,15 +161,15 @@ build_canonical_dmg() {
local staging_dir
local rw_dmg
staging_dir="$(mktemp -d "${TMPDIR:-/tmp}/cc-haha-dmg.XXXXXX")"
rw_dmg="$(mktemp "${TMPDIR:-/tmp}/cc-haha-rw.XXXXXX").dmg"
staging_dir="$(mktemp -d "${TMPDIR:-/tmp}/heicode-dmg.XXXXXX")"
rw_dmg="$(mktemp "${TMPDIR:-/tmp}/heicode-rw.XXXXXX").dmg"
cp -R "${app_bundle}" "${staging_dir}/"
ln -s /Applications "${staging_dir}/Applications"
# Create a read-write DMG first so we can customize the Finder layout
hdiutil create \
-volname "Claude Code Haha" \
-volname "${DMG_VOLUME_NAME}" \
-srcfolder "${staging_dir}" \
-ov \
-format UDRW \
@@ -188,7 +190,7 @@ build_canonical_dmg() {
# 所以这里允许 osascript 非零退出,只 warn,不让 set -e 炸掉整个脚本。
if ! osascript <<APPLESCRIPT
tell application "Finder"
tell disk "Claude Code Haha"
tell disk "${DMG_VOLUME_NAME}"
open
set current view of container window to icon view
set toolbar visible of container window to false
@@ -274,7 +276,7 @@ if [[ -n "${LATEST_APP}" ]]; then
rm -f "${CANONICAL_OUTPUT_DIR}/"*.dmg
build_canonical_dmg \
"${CANONICAL_OUTPUT_DIR}/${APP_BUNDLE_NAME}" \
"${CANONICAL_OUTPUT_DIR}/$(basename "${LATEST_DMG:-Claude Code Haha_0.1.0_aarch64.dmg}")"
"${CANONICAL_OUTPUT_DIR}/$(basename "${LATEST_DMG:-${DEFAULT_DMG_BASENAME}}")"
fi
cat > "${CANONICAL_OUTPUT_DIR}/BUILD_INFO.txt" <<EOF
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 414 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+83
View File
@@ -0,0 +1,83 @@
"""Regenerate Tauri/desktop icons from heicode-logo.jpg."""
from pathlib import Path
from PIL import Image
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "heicode-logo.jpg"
TAURI_ICONS = ROOT / "cc-haha" / "desktop" / "src-tauri" / "icons"
PUBLIC = ROOT / "cc-haha" / "desktop" / "public"
def load_trimmed_square():
img = Image.open(SRC).convert("RGB")
# Find bbox of non-white pixels (treat >=250 RGB as white).
gray = img.convert("L")
mask = gray.point(lambda v: 0 if v >= 250 else 255)
bbox = mask.getbbox()
if bbox:
img = img.crop(bbox)
# Pad to square on white.
w, h = img.size
side = max(w, h)
canvas = Image.new("RGB", (side, side), (255, 255, 255))
canvas.paste(img, ((side - w) // 2, (side - h) // 2))
return canvas
def to_rgba_with_transparent_white(img: Image.Image, threshold: int = 248) -> Image.Image:
rgba = img.convert("RGBA")
pixels = rgba.load()
w, h = rgba.size
for y in range(h):
for x in range(w):
r, g, b, _ = pixels[x, y]
if r >= threshold and g >= threshold and b >= threshold:
pixels[x, y] = (r, g, b, 0)
return rgba
def main():
base = load_trimmed_square()
print(f"Trimmed source: {base.size}")
# PNG outputs (RGB on white → looks correct on any background, matches glass edges).
sizes = {
TAURI_ICONS / "32x32.png": 32,
TAURI_ICONS / "64x64.png": 64,
TAURI_ICONS / "128x128.png": 128,
TAURI_ICONS / "128x128@2x.png": 256,
TAURI_ICONS / "icon.png": 1024,
PUBLIC / "app-icon.png": 1024,
}
for path, size in sizes.items():
path.parent.mkdir(parents=True, exist_ok=True)
base.resize((size, size), Image.LANCZOS).save(path, "PNG")
print(f"wrote {path} ({size})")
# Windows Square*Logo / StoreLogo
win_sizes = {
"Square30x30Logo.png": 30,
"Square44x44Logo.png": 44,
"Square71x71Logo.png": 71,
"Square89x89Logo.png": 89,
"Square107x107Logo.png": 107,
"Square142x142Logo.png": 142,
"Square150x150Logo.png": 150,
"Square284x284Logo.png": 284,
"Square310x310Logo.png": 310,
"StoreLogo.png": 50,
}
for name, size in win_sizes.items():
path = TAURI_ICONS / name
base.resize((size, size), Image.LANCZOS).save(path, "PNG")
print(f"wrote {path} ({size})")
# ICO with multi-resolution.
ico_path = TAURI_ICONS / "icon.ico"
base.save(ico_path, format="ICO", sizes=[(16,16),(24,24),(32,32),(48,48),(64,64),(128,128),(256,256)])
print(f"wrote {ico_path}")
# ICNS (PIL supports it). Required sizes: 16,32,64,128,256,512,1024.
icns_path = TAURI_ICONS / "icon.icns"
base.save(icns_path, format="ICNS", sizes=[(16,16),(32,32),(64,64),(128,128),(256,256),(512,512),(1024,1024)])
print(f"wrote {icns_path}")
if __name__ == "__main__":
main()