Skip to content

feat(brand): macOS-spec app icon and reproducible build pipeline#177

Open
hughesyadaddy wants to merge 2 commits into
TomBadash:masterfrom
hughesyadaddy:feat/app-icon-refresh
Open

feat(brand): macOS-spec app icon and reproducible build pipeline#177
hughesyadaddy wants to merge 2 commits into
TomBadash:masterfrom
hughesyadaddy:feat/app-icon-refresh

Conversation

@hughesyadaddy
Copy link
Copy Markdown
Contributor

@hughesyadaddy hughesyadaddy commented May 14, 2026

Summary

  • Replace the committed 256x256 raster (images/logo_icon.png, images/AppIcon.icns, images/logo.ico) with assets that follow each platform's icon-grid contract: an Apple HIG 824 px squircle centred on a 1024 canvas with a 100 px transparent gutter for macOS, and a 16 / 24 / 32 / 48 / 64 / 128 / 256 Windows multi-size .ico with the squircle re-fit to fill the canvas.
  • Add scripts/build_app_icon.py to regenerate AppIcon.icns (via iconutil + sips) and logo.ico (via Pillow) deterministically from the single 1024x1024 RGBA master.
  • Replace the sidebar brand mark (literal "M" Text node) with a 44 px navy squircle hosting the Phosphor mouse-simple glyph that the menu-bar tray icon and the new Dock tile already draw.
  • Add brandMarkBg / brandMarkFg to ui/qml/Theme.js so the sidebar brand mark renders identically under light and dark appearances.
  • Annotate the sidebar brand mark with Accessible.role: StaticText, Accessible.name: "Mouser", and Accessible.description: <version> for VoiceOver.

What Changed

  • images/logo_icon.png: new 1024 x 1024 RGBA master, navy gradient squircle (#2b416e#16213e#0b1220), Phosphor mouse-simple glyph in white centred at ~56 % of the squircle width, 100 px transparent gutter on every side.
  • images/AppIcon.icns: regenerated with the full Retina ladder (16 / 32 / 128 / 256 / 512 + their @2x variants), gutter preserved per Apple HIG so macOS can composite the system drop shadow off the alpha boundary.
  • images/logo.ico: regenerated as a 7-frame multi-size icon (16 / 24 / 32 / 48 / 64 / 128 / 256), squircle re-fit to ~96 % of canvas per Microsoft Learn so the 16 px representation stays legible on the Windows 11 taskbar.
  • scripts/build_app_icon.py: validates the master is 1024 x 1024 RGBA, drives iconutil + sips to assemble the .icns, drives Pillow to write the .ico, exits non-zero on missing tools, wrong master mode, or any subprocess failure.
  • ui/qml/Main.qml: replaces the literal "M" sidebar brand mark with the navy squircle + Phosphor glyph and adds the Accessible annotations above.
  • ui/qml/Theme.js: adds the two theme-independent brandMarkBg / brandMarkFg tokens.

Validation

  • python scripts/build_app_icon.py regenerates images/AppIcon.icns and images/logo.ico from the master deterministically; validation surfaces missing tools, wrong master mode, or wrong master size.
  • Rebuilt macOS bundle via build_macos_app.sh; Dock / Cmd-Tab / About tile renders at Retina resolution with the standard system drop shadow off the 100 px gutter.
  • Source-checkout launch (python main_qml.py --show-window); _install_macos_dock_icon() decodes the new 1024 PNG and the runtime Dock tile matches the bundle tile.
  • Sidebar brand mark renders identically under appearance_mode: system / light / dark.
  • python -m pytest tests/
  • git diff --check

Notes

  • Mouser-mac.spec already consumes AppIcon.icns via CFBundleIconFile and Mouser.spec already consumes logo.ico on the Windows path, so no spec changes were needed.
  • Tray-icon SVG rendering at runtime is unchanged; the same Phosphor mouse-simple glyph now feeds all three surfaces (Dock tile, sidebar mark, tray icon).
  • images/logo.png (the 600 x 313 README marketing render) is intentionally out of scope; repo-level branding stays a separate decision.
  • The activation-policy reconcile in feat(macos): launcher identity, Dock tile, hide-to-tray, native menu-bar item #168 is what makes the Dock tile visible on a --show-window launch from a source checkout. This PR is about the art the Dock then renders; the two land cleanly in either order.

Screenshots

1024 master itself — 824 squircle centred on 1024 with a 100 px transparent gutter, navy gradient, Phosphor mouse-simple glyph in white:

App icon (1024 master)

In-app sidebar with the refreshed brand mark (top-left), a pocket-sized navy squircle echoing the Dock tile:

Sidebar brand mark

@hughesyadaddy hughesyadaddy force-pushed the feat/app-icon-refresh branch from dd82c9b to 6ca146d Compare May 14, 2026 19:19
hughesyadaddy added a commit to hughesyadaddy/Mouser that referenced this pull request May 14, 2026
Regenerate every screenshot from working-branch state so the
sidebar brand mark visible in each shot reflects the latest icon
work (Phosphor mouse-simple glyph on navy squircle). Add a direct
preview of the 1024 master so TomBadash#177 can show the icon itself
instead of just describing it.

The About-dialog launch path stays sanitised to /Applications/...
in the harness so it doesn't leak the dev's local checkout layout.
hughesyadaddy added a commit to hughesyadaddy/Mouser that referenced this pull request May 14, 2026
The previous screenshots/app-icon-1024.png on this branch was a copy
of the OLD upstream MX Control Suite raster (77132 bytes, 256x256
upscaled to 1024 in display), not the new icon. The "Screenshots"
section in PR TomBadash#177 therefore embedded the icon the PR claims to be
replacing, which is the opposite of useful.

Replace it with the actual new master from
images/logo_icon.png at HEAD of feat/app-icon-refresh: 1024x1024
RGBA, navy gradient squircle on a 100 px transparent gutter with
the Phosphor mouse-simple glyph in white.

No other screenshot on this branch needed updating -- the sidebar
shot (app-icon-refresh-sidebar.png) already reflects the new
sidebar brand mark.
@hughesyadaddy hughesyadaddy force-pushed the feat/app-icon-refresh branch from 6ca146d to 3789a4c Compare May 14, 2026 22:51
The committed 256x256 raster was upscaled to 1024 for the macOS Dock
and downscaled to 16 px for the Windows taskbar from the same source,
satisfying neither platform's icon-grid contract. Re-base all three
derived assets on a single 1024x1024 RGBA master that follows the
Apple HIG (824 squircle on a 100 px transparent gutter) and add
scripts/build_app_icon.py to regenerate AppIcon.icns (iconutil + sips
Retina ladder) and logo.ico (Pillow 7-frame multi-size, ~96 % fill)
deterministically from that master.

Replace the sidebar brand mark "M" Text node with a 44 px navy
squircle hosting the same Phosphor mouse-simple glyph the menu-bar
tray icon and the new Dock tile draw, so all three surfaces share
one metaphor. Add brandMarkBg / brandMarkFg to Theme.js so the
sidebar mark renders identically under light and dark, and annotate
it with Accessible.role / name / description for VoiceOver.
- Introduce BuildIconError so callers (and tests) can distinguish recoverable
  validation failures from unrelated SystemExits raised elsewhere.
- Route every subprocess invocation through a single _run_tool() helper that
  captures stderr and surfaces it on non-zero exit -- no more bare
  CalledProcessError tracebacks for missing input files or denied permissions.
- Validate the master PNG with explicit Pillow open + error wrapping so a
  corrupt file is reported by path, not by traceback.
- Allow build_icns / build_ico to write to caller-supplied paths so tests
  exercise the writers without mutating the checked-in icon assets.
- Sort ICNS_SIZES iteration so the iconset directory walk is deterministic
  across host filesystems (belt-and-braces; iconutil indexes by filename).

Cover the new surface area in tests/test_build_app_icon.py (14 tests):
* exhaustive master-validation boundary cases (missing/wrong mode/wrong size),
* _require_tool path resolution + actionable error,
* _run_tool stderr surfacing,
* build_ico happy path + reproducibility (two runs yield identical bytes) +
  empty-master rejection,
* main() platform gating (refuses non-darwin) and stubbed darwin happy path.
@hughesyadaddy hughesyadaddy force-pushed the feat/app-icon-refresh branch from 24390be to c140053 Compare May 18, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant