Skip to content

fix(desktop): give the dev app an icon on linux - #216

Closed
quickbeard wants to merge 1 commit into
mainfrom
linux-dev-icon
Closed

quickbeard wants to merge 1 commit into
mainfrom
linux-dev-icon

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Problem

On GNOME/Wayland, bun dev:desktop showed the generic executable icon instead of the CoDev C. macOS and Windows were fine.

BrowserWindow's icon (src/main/windows.ts:182) only reaches the shell through X11's _NET_WM_ICON, and Electron 42 runs as a native Wayland client on GNOME — where the shell resolves a window's icon by matching its app_id to an installed .desktop file. macOS sets its dock icon at runtime (app.dock.setIcon) and Windows uses the HWND icon, so only Linux was affected.

Root cause

Electron slugifies the app's package.json productName ?? name into the Wayland app_id before main runs. Measured with WAYLAND_DEBUG=1, reading xdg_toplevel.set_app_id:

manifest app_id
@opencode-ai/desktop (the dev app) opencode-ai-desktop
productName: "CoDev Code Dev" codev-code-dev
ai.opencode.desktop.dev ai-opencode-desktop-dev

app.setName(), --class and CHROME_DESKTOP were each tested and none of them override it (setName only changed the window title). Nothing named opencode-ai-desktop.desktop is installed, so the shell had no icon to show.

Fix

scripts/install-dev-desktop-entry.ts (new) derives that app_id from the package manifest and writes ~/.local/share/applications/<app_id>.desktop pointing Icon= at the channel icon copy-icons already stages, with StartupWMClass for the X11 path and NoDisplay=true so it never shows in the launcher. predev runs it after copy-icons; it is Linux-only and idempotent.

Packaged builds need no change

Verified by building a deb and running it: the packaged app announces set_app_id("ai.opencode.desktop.dev"), which already agrees with everything electron-builder installs — /usr/share/applications/ai.opencode.desktop.dev.desktop, its Icon=/StartupWMClass, and the hicolor icon basename. Packaged Electron takes the app_id from the executable name; only the unpackaged run, where the executable is plain electron, falls back to the package name.

Test plan

  • bun test scripts/install-dev-desktop-entry.test.ts — 3/3 pass (the slug cases are the ones measured against Electron 42)
  • bun typecheck clean
  • desktop-file-validate accepts the generated entry
  • bun dev:desktop on GNOME/Wayland now shows the CoDev icon in the dash — confirmed visually
  • electron-builder --linux deb built and the packaged binary's app_id read back as above

DIVERGENCES.md: the existing packages/desktop/scripts/predev.ts row covers the added line and its reasoning; the new script and its test are fork-only files.

🤖 Generated with Claude Code

BrowserWindow's `icon` only reaches the shell through X11's _NET_WM_ICON,
and Electron 42 runs as a native Wayland client on GNOME, where the icon
comes from the .desktop file matching the window's app_id. An unpackaged
`bun dev:desktop` therefore showed the generic executable icon, while
macOS (app.dock.setIcon) and Windows (the HWND icon) were unaffected.

Electron slugifies package.json `productName ?? name` into that app_id
before main runs — `@opencode-ai/desktop` becomes `opencode-ai-desktop`,
which matches nothing installed. app.setName(), --class and CHROME_DESKTOP
were all measured (WAYLAND_DEBUG=1, reading xdg_toplevel.set_app_id) not
to override it, so predev writes the matching entry instead.

Packaged builds need nothing: a linux deb announces
set_app_id("ai.opencode.desktop.dev"), which already agrees with the
desktop file, StartupWMClass and hicolor icon electron-builder installs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@quickbeard quickbeard closed this Aug 14, 2026
@quickbeard
quickbeard deleted the linux-dev-icon branch August 14, 2026 01:52
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