fix(desktop): ship the Icon Composer app icon on macOS (#60020)

Co-authored-by: iamdavidhill <iamdavidhill@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot]
2026-10-30 14:40:47 +10:00
committed by GitHub
Unverified
parent 825167351e
commit 1bf82105f0
4 changed files with 52 additions and 1 deletions
@@ -88,6 +88,22 @@ test("legacy Linux launcher stays hidden", async () => {
expect(desktop).toContain("NoDisplay=true")
})
test.each(channels)("macOS icon format for $channel", ({ channel }) => {
const config = load(channel)
expect(config.mac?.icon).toBe(channel === "prod" ? "resources/icons/icon.icon" : "resources/icons/icon.icns")
expect(config.dmg?.icon).toBe(channel === "prod" ? "resources/icons/icon.icns" : undefined)
// The prod override must keep the rest of the shared macOS settings.
expect(config.mac?.target).toEqual(["dmg", "zip"])
expect(config.mac?.extendInfo?.NSMicrophoneUsageDescription).toBeTruthy()
})
test("the production Icon Composer package references its bundled artwork", async () => {
const icon = await Bun.file(path.join(import.meta.dirname, "icons/prod/icon.icon/icon.json")).json()
expect(icon["supported-platforms"]).toEqual({ squares: ["macOS"] })
const image = icon.groups[0].layers[0]["image-name"]
expect(await Bun.file(path.join(import.meta.dirname, "icons/prod/icon.icon/Assets", image)).exists()).toBe(true)
})
test("shared packaging defaults", async () => {
const config = load("dev")
// Packaging runs in secretless build jobs; the release workflow signs and notarizes afterwards.
+11 -1
View File
@@ -1,4 +1,4 @@
import { readdir, stat } from "node:fs/promises"
import { copyFile, readdir, stat } from "node:fs/promises"
import path from "node:path"
import { fileURLToPath } from "node:url"
@@ -126,6 +126,11 @@ const getBase = (appId: string): Configuration => {
if ((await stat(version)).size === 0) throw new Error(`Bundled CLI version must be a non-empty file: ${version}`)
// With an Icon Composer mac.icon, electron-builder bundles the ICNS that actool generates as the pre-macOS 26
// fallback. That one tops out at 256px and draws a light ring around the squircle, so ship ours instead.
if (context.electronPlatformName === "darwin")
await copyFile(path.join(packageDir, "resources/icons/icon.icns"), path.join(resources, "icon.icns"))
if (context.electronPlatformName !== "win32") return
const prebuilds = path.join(
@@ -245,6 +250,11 @@ export function getConfig() {
appId,
productName: "OpenCode",
protocols: { name: "OpenCode", schemes: ["opencode"] },
// An Icon Composer package compiles to Assets.car, which macOS 26 and later draw the app icon from
// (Dock, Finder, system dialogs and the system icon styles). Older macOS keeps using the ICNS.
mac: { ...base.mac, icon: "resources/icons/icon.icon" },
// The DMG volume icon would otherwise use the same actool-generated ICNS.
dmg: { icon: "resources/icons/icon.icns" },
publish: {
provider: "generic",
url: "https://opencode.ai/update/api/latest/desktop/opencode/",
Binary file not shown.

After

Width:  |  Height:  |  Size: 699 KiB

@@ -0,0 +1,25 @@
{
"groups": [
{
"name": "OpenCode",
"layers": [
{
"name": "OpenCode",
"image-name": "icon.png",
"glass": false
}
],
"shadow": {
"kind": "none",
"opacity": 0
},
"translucency": {
"enabled": false,
"value": 0.5
}
}
],
"supported-platforms": {
"squares": ["macOS"]
}
}