Files
gridline/scripts/regenerate-icons.sh
T
adrianbonpin ce4a73b0bb fix(icons): stop macOS drawing the app icon on a legacy plate (#40)
On macOS 26+ the Dock and any launcher that resolves app icons through
NSWorkspace.icon(forFile:) (Spotlight, Vicinae, Raycast, ...) drew a grey
rounded-square "legacy plate" behind Gridline's icon, i.e. a white border
around the app icon at every render size below ~72px.

Reproduced outside any launcher: NSWorkspace.icon(forFile:) on
Gridline.app returns the plated icon at 16-64px and the clean icon at
>=72px. The trigger lives in icon.icns, not in the app -- moving that file
into Zed.app reproduces the border, moving Zed's icns into Gridline.app
removes it.

Two things in the file cause it:

  - ic12 (64x64)                     -> plates the 40-64px range
  - is32/s8mk (16x16 RGB+mask)
    il32/l8mk (32x32 RGB+mask)       -> plates the 16-32px range

macOS treats padded small representations as legacy, and this icns carries
them in both places.

Fix, in two parts.

1. Strip those chunks. scripts/fix-icns.mjs does it; `tauri icon` re-emits
   them on every run, so it has to be a committed, repeatable step.
   scripts/verify-app-icon.mjs is the regression check: it builds a
   throwaway .app around the committed icon.icns, asks AppKit to render it
   at 16..512px and fails if any size comes back plated. It fails (6/11
   plated) on the previous icns and passes on this one.

2. Regenerate the raster set from icon.svg via
   scripts/regenerate-icons.sh, so the icons directory matches its source
   of truth again. The SVG has been full-bleed since the initial commit
   (md5 unchanged at d872e42/3dab09f/6854d88/HEAD) while the rasters were
   replaced with a hand-tuned padded version at v0.2.0 and had drifted
   from it ever since. The script generates into a temp dir because
   `tauri icon` only takes one output directory and would otherwise drop
   generated android/ and ios/ trees into the desktop-only icons dir.

No visual change on macOS: IconServices normalizes app-icon artwork when
rendering, so padded and full-bleed sets come out pixel-identical there
(same bbox and art fraction at 48/128/512px).

Windows and Linux do consume the PNG/ICO directly, so the icon there does
change from padded to full-bleed -- consistent with the SVG, but a
visible difference worth eyeballing before release.

Verified: verify-app-icon passes at 16, 24, 32, 40, 48, 64, 72, 96, 128,
256, 512px; iconutil and `file` still decode the new .icns and .ico
(6 sizes); frontend tests pass (131 files / 1134 tests).
2026-09-28 18:03:22 +08:00

87 lines
2.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Regenerate desktop/src-tauri/icons/* from icons/icon.svg.
#
# Usage:
# scripts/regenerate-icons.sh
#
# `icon.svg` is the source of truth for the app icon; the PNG/ICNS/ICO set next
# to it is generated output and must be regenerated whenever the SVG changes.
#
# Always regenerate through this script rather than calling `tauri icon`
# directly, for two reasons:
#
# 1. `tauri icon` only supports a single output directory for all platforms, so
# running it in place would also drop generated `android/` and `ios/` icon
# trees into the (desktop-only) icons directory. This generates into a temp
# dir and copies back just the desktop set.
#
# 2. `tauri icon` emits the legacy `is32`/`s8mk`/`il32`/`l8mk` and `ic12`
# representations, which make macOS 26+ draw the app icon on a grey
# "legacy plate" at small sizes (a white border in the Dock and in any
# launcher that uses NSWorkspace). fix-icns.mjs strips them — see that file
# for the details and the measurements.
#
# The committed icon set was last regenerated because those stale rasters meant
# the shipped icon no longer matched icon.svg at all.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
DESKTOP_DIR="$REPO_ROOT/desktop"
ICONS_DIR="$DESKTOP_DIR/src-tauri/icons"
SOURCE_SVG="$ICONS_DIR/icon.svg"
if ! command -v bun >/dev/null 2>&1; then
echo "error: bun is required (https://bun.sh)" >&2
exit 1
fi
if [ ! -f "$SOURCE_SVG" ]; then
echo "error: missing icon source $SOURCE_SVG" >&2
exit 1
fi
# Files `tauri icon` produces for the desktop/Windows/macOS targets. The
# android/ and ios/ output is intentionally ignored.
DESKTOP_ICON_FILES=(
32x32.png
64x64.png
128x128.png
128x128@2x.png
icon.png
icon.icns
icon.ico
Square30x30Logo.png
Square44x44Logo.png
Square71x71Logo.png
Square89x89Logo.png
Square107x107Logo.png
Square142x142Logo.png
Square150x150Logo.png
Square284x284Logo.png
Square310x310Logo.png
StoreLogo.png
)
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
echo "==> rendering $SOURCE_SVG with tauri icon"
( cd "$DESKTOP_DIR" && bunx tauri icon "src-tauri/icons/icon.svg" -o "$TMP_DIR" )
for file in "${DESKTOP_ICON_FILES[@]}"; do
if [ ! -f "$TMP_DIR/$file" ]; then
echo "error: tauri icon did not produce $file" >&2
exit 1
fi
cp "$TMP_DIR/$file" "$ICONS_DIR/$file"
echo " updated icons/$file"
done
echo "==> stripping legacy ICNS representations"
bun "$REPO_ROOT/scripts/fix-icns.mjs" "$ICONS_DIR/icon.icns"
echo
echo "icons regenerated. Verify with:"
echo " bun $REPO_ROOT/scripts/verify-app-icon.mjs"