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