Skip to content

Commit 0c0751a

Browse files
author
GCWing
committed
fix(macos): bundle native icons with verified legacy fallback
1 parent 3957ef6 commit 0c0751a

25 files changed

Lines changed: 635 additions & 188 deletions

‎.github/workflows/desktop-package.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,19 @@ jobs:
337337
- name: Download and verify target Flashgrep binary
338338
run: node scripts/prepare-flashgrep-resource.mjs --target "${{ matrix.platform.target }}"
339339

340+
- name: Select Xcode for macOS Icon Composer assets
341+
if: runner.os == 'macOS'
342+
shell: bash
343+
run: |
344+
test -d /Applications/Xcode_26.2.app/Contents/Developer
345+
echo "DEVELOPER_DIR=/Applications/Xcode_26.2.app/Contents/Developer" >> "$GITHUB_ENV"
346+
347+
- name: Verify native macOS icon bundling
348+
if: runner.os == 'macOS'
349+
env:
350+
OPENBITFUN_TEST_MACOS_BUNDLE: '1'
351+
run: node --test scripts/macos-icon-bundle.test.mjs
352+
340353
- name: Build desktop app
341354
run: ${{ matrix.platform.build_command }}
342355

‎.github/workflows/nightly-artifacts.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,19 @@ jobs:
219219
CARGO_PROFILE_TEST_DEBUG: "0"
220220
run: cargo test --locked -p openbitfun-cli --test terminal_process_contracts -- --test-threads=1
221221

222+
- name: Select Xcode for macOS Icon Composer assets
223+
if: runner.os == 'macOS'
224+
shell: bash
225+
run: |
226+
test -d /Applications/Xcode_26.2.app/Contents/Developer
227+
echo "DEVELOPER_DIR=/Applications/Xcode_26.2.app/Contents/Developer" >> "$GITHUB_ENV"
228+
229+
- name: Verify native macOS icon bundling
230+
if: runner.os == 'macOS'
231+
env:
232+
OPENBITFUN_TEST_MACOS_BUNDLE: '1'
233+
run: node --test scripts/macos-icon-bundle.test.mjs
234+
222235
- name: Build desktop app
223236
shell: bash
224237
run: |
521 KB
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://schema.tauri.app/config/2",
3+
"build": {
4+
"beforeBundleCommand": "pnpm --dir .. run check:macos-icons"
5+
},
6+
"bundle": {
7+
"icon": [
8+
"icons/openbitfun-app-icon.icns",
9+
"icons/openbitfun-app-icon.ico",
10+
"icons/openbitfun-app-icon.png",
11+
"../../assets/brand/macos/openbitfun.icon"
12+
]
13+
}
14+
}

‎assets/brand/README.md‎

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,90 @@ The iOS App Store icon uses an opaque black square; iOS applies the corner mask.
2323
mark, a light transparent mark, and the application icon. Fine-line mark exports
2424
are rendered directly from the SVG with size-specific optical treatments at
2525
small sizes. Application icons are resized from the preserved application artwork.
26-
Windows ICO frames, Linux icons, and macOS PNG representations use the matching
27-
size-specific application exports. Tauri encodes the legacy 16/32 px ICNS
28-
representations from the same artwork.
26+
Windows ICO frames and Linux icons use the matching size-specific application
27+
exports. macOS owns a separate native resource pipeline described below; the
28+
shared PNGs, Windows ICO, Linux, web, and mobile artwork are unchanged.
29+
30+
## macOS icons
31+
32+
`macos/openbitfun.icon` is the Icon Composer document for macOS 26 and newer,
33+
including macOS 27. Its background is an opaque, full-bleed black fill and its
34+
foreground is the transparent silver mark projected from the same brand master.
35+
It has no baked outer mask, shadow, or Dock padding. macOS applies the enclosure
36+
and appearance treatment through `Assets.car`. The source uses Icon Composer 1.2
37+
features supported by Xcode 26.2; it does not require an Xcode 27-only document.
38+
No language or region-specific icon variants are needed.
39+
40+
The committed ICNS is the static dev and older-macOS fallback. It is rendered
41+
from that Composer document by Apple's `ictool`, on the separate authored
42+
`macos/legacy-canvas.json` layout, and encoded with Apple's `iconutil`. That
43+
legacy layout has an 824 px body on a 1024 px transparent canvas. This is our
44+
legacy artwork geometry, **not a universal Apple padding requirement**. It never
45+
alters the modern Composer source. Ten 1x/2x representations cover 16–1024 px.
46+
There is no runtime OS-version scaling and no modification of ICNS chunks by
47+
the shared brand generator; canonicalization only orders the native chunks.
48+
49+
Tauri CLI is pinned to **2.11.4** in both workspace apps. The macOS config overlay
50+
adds `.icon` alongside ICNS; base and Windows/Linux icon selections stay intact.
51+
Tauri dev reads ICNS through `NSApplication.applicationIconImage`. Bundled builds
52+
use `CFBundleIconName` with `Assets.car`, plus `CFBundleIconFile` for the legacy
53+
resource. `tauri.dev.conf.json` and the generated release config inherit the
54+
platform icon list instead of overwriting it. The minimum application OS version
55+
is unchanged; compiling the modern catalog for 26 does not raise that minimum.
56+
57+
Normal installs, dev builds, and non-Apple builds use the committed ICNS and need
58+
no Icon Composer installation. After changing the application mark, Composer
59+
document, or legacy layout, regenerate on macOS with **Icon Composer 1.2**:
60+
61+
```bash
62+
# ICON_COMPOSER_APP can point to an alternate location for Icon Composer.app.
63+
pnpm run generate:macos-icons
64+
pnpm run generate-brand-assets
65+
```
66+
67+
The native export manifest records the renderer version and source/output
68+
hashes. Normal asset generation refuses stale native exports instead of silently
69+
shipping a mismatched dev icon. Commit the Composer layer, manifest, and all
70+
three generated ICNS copies together.
71+
72+
Packaging requires **full Xcode 26 or newer**, with `actool` and `assetutil`
73+
selected by `DEVELOPER_DIR` or `xcode-select`. Command Line Tools alone are not
74+
enough. Stable/beta and nightly macOS runners select **Xcode 26.2** on both Intel
75+
and Apple Silicon. The pre-bundle check fails when modern tools are unavailable;
76+
the build wrapper verifies the resulting catalog, both Info.plist icon keys,
77+
and the exact fallback bytes before reporting success. No signed bundle is
78+
modified by verification.
79+
80+
```bash
81+
node --test scripts/generate-brand-assets.test.mjs scripts/macos-icon-bundle.test.mjs scripts/desktop-tauri-build.test.mjs
82+
# Full Xcode: exercise actual Tauri + actool packaging in a disposable fixture.
83+
OPENBITFUN_TEST_MACOS_BUNDLE=1 node --test scripts/macos-icon-bundle.test.mjs
84+
# Inspect an existing bundle without launching it.
85+
node scripts/macos-icon-bundle.mjs verify /path/to/OpenBitFun.app
86+
```
87+
88+
These checks validate resources, native decoding, and package metadata. They do
89+
not establish visual Dock equivalence on every supported OS or display. Remote
90+
workspace, remote control, Peer Device Mode, and detached dispatch are not
91+
exercised by these local packaging checks; their protocols and host logic are
92+
unchanged.
93+
94+
References: [Apple app icon guidance](https://developer.apple.com/design/human-interface-guidelines/app-icons),
95+
[Icon Composer](https://developer.apple.com/documentation/xcode/creating-your-app-icon-using-icon-composer),
96+
[NSApplication.applicationIconImage](https://developer.apple.com/documentation/appkit/nsapplication/applicationiconimage),
97+
and [Tauri 2.11.4 macOS icon bundler](https://github.com/tauri-apps/tauri/blob/tauri-cli-v2.11.4/crates/tauri-bundler/src/bundle/macos/icon.rs).
2998

3099
Browser entry points select explicit 16/32 px favicons. The system tray reuses
31-
the configured application/window icon on every desktop platform.
100+
the macOS template mark on macOS and the configured application icon elsewhere.
32101

33102
Run `pnpm run generate-brand-assets` after changing either source master. The
34-
generator is the single owner of the derived desktop, web, installer, Android,
35-
iOS, and HarmonyOS files.
103+
generator owns the shared desktop, web, installer, Android, iOS, and HarmonyOS
104+
projections. Native macOS exports have the separate owner described above.
36105

37-
Verify generated dimensions, small-size rim contrast, favicon references, and
38-
icon containers with `node --test scripts/generate-brand-assets.test.mjs`.
106+
Verify generated dimensions, small-size rim contrast, favicon references,
107+
platform selection, and icon containers with
108+
`node --test scripts/generate-brand-assets.test.mjs`. macOS export and native
109+
bundling checks also run `scripts/macos-icon-bundle.test.mjs`.
39110

40111
`source/release-letter-mascot.svg` preserves the authored character from
41112
`openbitfun-letter.html`. Its named body, rigid head/rod, and eye parts are used
521 KB
Binary file not shown.

‎assets/brand/macos/generated.json‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"iconComposerVersion": "1.2",
3+
"iconComposerBuild": "76",
4+
"sourceSha256": "9b600c6605029a2b23d480ec5595b7634ffb94dd9c6a2cc91a98d645601f65f0",
5+
"icnsSha256": "04ca39fd01030f7dcdf787670c3e56f006e33e8384eba14e793db75b6765af3e"
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"canvasSize": 1024,
3+
"artworkBounds": { "x": 100, "y": 100, "width": 824, "height": 824 }
4+
}
85.7 KB
Loading
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"fill": {
3+
"solid": "srgb:0.00000,0.00000,0.00000,1.00000"
4+
},
5+
"groups": [
6+
{
7+
"name": "Application mark",
8+
"layers": [
9+
{
10+
"image-name": "Mark.png",
11+
"name": "Silver mark",
12+
"glass": false
13+
}
14+
],
15+
"shadow": {
16+
"kind": "none",
17+
"opacity": 0
18+
},
19+
"specular": false,
20+
"translucency": {
21+
"enabled": false,
22+
"value": 0
23+
}
24+
}
25+
],
26+
"supported-platforms": {
27+
"squares": [
28+
"macOS"
29+
]
30+
}
31+
}

0 commit comments

Comments
 (0)