Fix rounded macOS icon on older Macs (#8018)

Older macOS versions display Buzz’s opaque icon as an oversized square.
Bake rounded corners and transparent padding into the macOS `.icns`,
preserving the original bee artwork and leaving other platforms
unchanged.

Generation uses Hermit-pinned uv, Python 3.14.3, and Pillow 12.2.0.
`scripts/macos-icon.sh --check` regenerates temporarily and compares all
decoded representations; macOS CI runs it before packaging. Pixel
reproducibility is verified independently of Apple’s ICNS container
encoding.

Validated native AppKit preview, all 10 representations, fresh-cache
generation, one-pixel/missing-representation drift rejection, workflow
lint, and pre-push checks. Full local `just ci` is running. Packaged
Dock checks on older/current macOS remain outstanding.

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
klopez4212
2026-10-01 17:00:57 +01:00
committed by GitHub
parent 514a9a86d3
commit 0ee2ad7fbf
9 changed files with 121 additions and 2 deletions

View File

@@ -31,6 +31,8 @@ jobs:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Verify macOS icon representations
run: scripts/macos-icon.sh --check
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
with:
workspaces: desktop/src-tauri

View File

@@ -66,6 +66,10 @@ jobs:
- 'scripts/normative-corpus.json'
- 'justfile'
desktop:
- 'scripts/generate-macos-icon.py'
- 'scripts/macos-icon.sh'
- 'bin/uv'
- 'bin/.uv-*.pkg'
- 'scripts/model-capabilities.json'
- 'scripts/normative-corpus.json'
- 'desktop/**'

1
bin/.uv-0.12.21.pkg Symbolic link
View File

@@ -0,0 +1 @@
hermit

1
bin/uv Symbolic link
View File

@@ -0,0 +1 @@
.uv-0.12.21.pkg

1
bin/uvx Symbolic link
View File

@@ -0,0 +1 @@
.uv-0.12.21.pkg

Binary file not shown.

View File

@@ -710,7 +710,10 @@ test("hidden spoiler images are excluded from gallery navigation until revealed"
.last();
await expect(row).toBeVisible();
await row.locator(`img[src*="${SPOILER_VISIBLE_SHA}"]`).click();
const visibleImage = row.locator(`img[src*="${SPOILER_VISIBLE_SHA}"]`);
await visibleImage.scrollIntoViewIfNeeded();
await waitForAnimations(page);
await visibleImage.click();
const dialog = page.getByRole("dialog");
await expect(dialog).toBeVisible();
await expect(page.getByRole("button", { name: "Next image" })).toHaveCount(0);
@@ -723,7 +726,13 @@ test("hidden spoiler images are excluded from gallery navigation until revealed"
await spoiler.click();
await expect(spoiler).toHaveAttribute("data-revealed", "true");
await row.locator(`img[src*="${SPOILER_VISIBLE_SHA}"]`).click();
// The gallery snapshots visible images when opened; the state attribute
// changes before the spoiler image finishes its opacity transition.
await expect(spoiler.locator(`img[src*="${SPOILER_HIDDEN_SHA}"]`)).toHaveCSS(
"opacity",
"1",
);
await visibleImage.click();
await expect(dialog).toBeVisible();
await expect(page.getByRole("button", { name: "Next image" })).toBeVisible();
});

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Build the legacy macOS icon without relying on system corner masking.
Run: scripts/macos-icon.sh [--check]
The wrapper pins uv, Python and Pillow. Verification compares the complete set
of decoded RGBA representations, not PNG encoding or ICNS container bytes:
Apple's system iconutil may encode the same pixels differently across macOS.
The shared artwork and other platforms' icons are intentionally untouched.
"""
# /// script
# requires-python = "==3.14.3"
# dependencies = ["Pillow==12.2.0"]
# ///
import argparse
from pathlib import Path
import subprocess
import tempfile
from PIL import Image, ImageDraw
ROOT = Path(__file__).resolve().parent.parent
ICONS = ROOT / "desktop/src-tauri/icons"
def generate(output):
# An 824px tile on a 1024px canvas leaves the macOS optical inset.
# Work at 4x resolution to antialias the rounded edge at every icon size.
scale = 4
canvas = Image.new("RGBA", (1024 * scale, 1024 * scale))
with Image.open(ICONS / "buzz-source.png") as source:
tile = source.convert("RGBA").resize(
(824 * scale, 824 * scale), Image.Resampling.LANCZOS
)
mask = Image.new("L", tile.size)
ImageDraw.Draw(mask).rounded_rectangle(
(0, 0, tile.width - 1, tile.height - 1), radius=185 * scale, fill=255
)
canvas.paste(tile, (100 * scale, 100 * scale), mask)
with tempfile.TemporaryDirectory(prefix="buzz-macos-icon-") as temporary:
iconset = Path(temporary) / "Buzz.iconset"
iconset.mkdir()
for size in (16, 32, 128, 256, 512):
for density in (1, 2):
suffix = "@2x" if density == 2 else ""
canvas.resize(
(size * density, size * density), Image.Resampling.LANCZOS
).save(iconset / f"icon_{size}x{size}{suffix}.png")
subprocess.run(
["/usr/bin/iconutil", "-c", "icns", str(iconset), "-o", str(output)],
check=True,
)
def verify(candidate, reference):
"""Reject missing/extra representations and any decoded pixel drift."""
with tempfile.TemporaryDirectory(prefix="buzz-icon-check-") as temporary:
sets = []
for name, icon in (("candidate", candidate), ("reference", reference)):
iconset = Path(temporary) / f"{name}.iconset"
subprocess.run(
["/usr/bin/iconutil", "-c", "iconset", str(icon), "-o", str(iconset)],
check=True,
)
sets.append({p.name: p for p in iconset.glob("*.png")})
if sets[0].keys() != sets[1].keys():
raise ValueError("macOS icon representation set drifted")
for name in sorted(sets[0]):
with Image.open(sets[0][name]) as a, Image.open(sets[1][name]) as b:
if a.size != b.size or a.convert("RGBA").tobytes() != b.convert("RGBA").tobytes():
raise ValueError(f"macOS icon pixel drift: {name}")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true", help="verify without modifying the icon")
parser.add_argument("--output", type=Path, default=ICONS / "icon.icns")
args = parser.parse_args()
if args.check:
with tempfile.TemporaryDirectory(prefix="buzz-icon-regenerate-") as temporary:
generated = Path(temporary) / "icon.icns"
generate(generated)
verify(args.output, generated)
print("macOS icon: all decoded representations match")
else:
generate(args.output)
if __name__ == "__main__":
main()

7
scripts/macos-icon.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
# Hermit pins uv; inline script metadata pins Python and Pillow exactly.
# uv script environments are isolated from the caller's Python packages.
exec "$ROOT/bin/uv" run --managed-python --python 3.14.3 \
--script "$ROOT/scripts/generate-macos-icon.py" "$@"