Compare commits

...

2 Commits

Author SHA1 Message Date
J (Multica Agent)
4043e4ad06 fix(desktop): pin StartupWMClass and resolve bundled icon outside asar
Follow-up to the prior commit, addressing two review points:

1. Pin `linux.desktop.entry.StartupWMClass` to `Multica`. Without an
   explicit value electron-builder uses `productName` (`Multica`) — same
   string Electron emits as the WM_CLASS class on X11 — so this is a
   no-op at build time today. The point is to make the matching contract
   visible in the config: anyone changing `productName` or adding
   `app.setName()` at boot now sees a diff against this file rather than
   silently breaking the GNOME WM_CLASS ↔ StartupWMClass association
   that controls which icon the launcher / taskbar renders.

2. `BUNDLED_ICON_PATH` now points into `app.asar.unpacked/` in packaged
   builds. The icon is extracted there by `asarUnpack: resources/**`,
   but `__dirname` resolves inside `app.asar/`. Electron's
   `BrowserWindow({ icon })` on Linux passes the path to the native
   window-icon code, which expects a real filesystem path — same caveat
   handled by `bundledCliPath()` in daemon-manager.ts. Dev builds have
   no `app.asar` in `__dirname`, so the replace is a no-op there.

Co-authored-by: multica-agent <github@multica.ai>
2026-05-11 23:37:43 +08:00
J (Multica Agent)
3f92550ce5 fix(desktop): restore Multica app icon on Linux
Two independent regressions caused Ubuntu to render the generic system
Settings (gear) icon instead of the Multica asterisk:

1. **Scoped npm name leaked into Linux desktop identity.**
   electron-builder defaults `executableName` to the package `name`. With
   `@multica/desktop` the slash is stripped but the `@` is kept, so the
   installed deb shipped:
     /usr/share/applications/@multicadesktop.desktop
     Icon=@multicadesktop
     /usr/share/icons/hicolor/*/apps/@multicadesktop.png
   The leading `@` violates the freedesktop desktop-entry naming guidance,
   so GNOME on Ubuntu fails to associate the running window with the
   `.desktop` entry and falls back to the theme default app icon. The
   artifact-filename side of this same leak was already patched in
   10618b1f via hardcoded `artifactName`; this commit closes the gap for
   the desktop/icon identity by forcing `executableName: multica`, which
   also lines up with `StartupWMClass=Multica` (productName-derived).

2. **No runtime icon fallback for Linux production.**
   `BrowserWindow({ icon })` was only set under `is.dev`. macOS/Windows
   production can rely on the .app bundle / .exe-embedded icon, but Linux
   AppImage direct-launches never install a `.desktop` entry, so the WM
   has no other path to the bundled icon and falls back to the theme
   default. Always pass the bundled icon on Linux (and keep the existing
   dev behavior on every platform).

Verification plan (Ubuntu VM, must be done against a freshly built .deb /
AppImage from this branch):
  - dpkg -i multica-desktop-*-linux-*.deb produces
      /usr/share/applications/multica.desktop
      Icon=multica
      /usr/share/icons/hicolor/*/apps/multica.png
  - AppImage launched directly: window/taskbar icon = Multica asterisk
  - GNOME Activities launcher entry: icon = Multica asterisk

Fixes the Ubuntu icon regression reported in
https://github.com/multica-ai/multica/issues/2424.

Co-authored-by: multica-agent <github@multica.ai>
2026-05-11 23:27:41 +08:00
2 changed files with 56 additions and 8 deletions

View File

@@ -32,6 +32,34 @@ mac:
dmg:
artifactName: multica-desktop-${version}-mac-${arch}.${ext}
linux:
# Override the Linux executable name to avoid leaking the scoped npm
# package name (`@multica/desktop`) into the installed binary, the
# `.desktop` file, and the hicolor icon filename. Without this override
# electron-builder defaults `executableName` to the package `name`,
# which after slash-stripping becomes `@multicadesktop` — producing
# `/usr/share/applications/@multicadesktop.desktop`,
# `Icon=@multicadesktop`, and
# `/usr/share/icons/hicolor/*/apps/@multicadesktop.png`. The leading `@`
# violates the freedesktop desktop-entry naming guidance, so GNOME /
# Ubuntu fail to associate the running window with the `.desktop` entry
# and fall back to the theme's default app icon (the Settings gear on
# Yaru). Forcing `multica` makes every Linux identity slot agree and
# matches `StartupWMClass=Multica` (productName-derived).
executableName: multica
# Pin StartupWMClass explicitly to the WM_CLASS that Electron emits on
# X11. Electron derives WM_CLASS from `app.getName()`, which in packaged
# builds resolves to `productName` (`Multica`). Without an explicit
# `StartupWMClass`, electron-builder writes `productName` as the default
# — making this declaration redundant with current settings — but
# pinning the value here turns a silent future drift (e.g. if anyone
# renames productName or sets app.setName at boot) into a visible diff
# against this file. The WM_CLASS ↔ StartupWMClass match is what lets
# GNOME associate the running window with the `.desktop` entry and
# therefore render the right icon. The post-build verification step in
# PR #2437 is `xprop WM_CLASS` on a real Ubuntu install.
desktop:
entry:
StartupWMClass: Multica
target:
- AppImage
- deb

View File

@@ -11,10 +11,25 @@ import { getAppVersion } from "./app-version";
import { loadRuntimeConfig } from "./runtime-config-loader";
import type { RuntimeConfigResult } from "../shared/runtime-config";
// Bundled icon used for dev-mode dock/taskbar branding. In production the
// app bundle icon (from electron-builder) wins; this path is only consumed
// by the `is.dev` branch below.
const DEV_ICON_PATH = join(__dirname, "../../resources/icon.png");
// Bundled icon used for dock/taskbar branding. macOS/Windows production
// builds let the OS pick up the icon from the .app bundle / .exe resources,
// but Linux production needs an explicit BrowserWindow `icon` — AppImage
// direct-launch doesn't register the .desktop entry, so GNOME has no path
// from the running window to the hicolor icon and falls back to the
// theme default. Consumed in createWindow() (all platforms in dev, Linux
// in prod) and the macOS dev dock branch.
//
// `asarUnpack: resources/**` in electron-builder.yml extracts the icon to
// `app.asar.unpacked/`, but `__dirname` resolves into `app.asar/`. The
// Linux native window-icon code path expects a real filesystem path
// (unlike Electron's nativeImage loader which transparently reads from
// asar), so swap the segment — same pattern as bundledCliPath() in
// daemon-manager.ts. In dev `__dirname` has no `app.asar`, so the replace
// is a no-op.
const BUNDLED_ICON_PATH = join(__dirname, "../../resources/icon.png").replace(
"app.asar",
"app.asar.unpacked",
);
// macOS/Linux GUI launches inherit a minimal PATH from launchd that omits
// the user's shell config (~/.zshrc, Homebrew, nvm, ~/.local/bin, etc.).
@@ -106,9 +121,14 @@ function createWindow(): void {
trafficLightPosition: { x: 16, y: 13 },
show: false,
autoHideMenuBar: true,
// Windows/Linux pick up the window/taskbar icon from this option in
// dev — on macOS it's ignored (dock comes from app.dock.setIcon below).
...(is.dev ? { icon: DEV_ICON_PATH } : {}),
// Windows/Linux pick up the window/taskbar icon from this option.
// On macOS it's ignored (dock comes from app.dock.setIcon below).
// Linux production needs this explicitly because AppImage direct-launch
// does not install a .desktop entry, so the WM has no other path to
// the bundled icon; without it Ubuntu falls back to the theme default.
...(is.dev || process.platform === "linux"
? { icon: BUNDLED_ICON_PATH }
: {}),
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
sandbox: false,
@@ -251,7 +271,7 @@ if (!gotTheLock) {
// so the Canary dev build is visually distinct from a stock Electron
// run. `app.dock` is macOS-only — guard the call.
if (is.dev && process.platform === "darwin" && app.dock) {
const icon = nativeImage.createFromPath(DEV_ICON_PATH);
const icon = nativeImage.createFromPath(BUNDLED_ICON_PATH);
if (!icon.isEmpty()) app.dock.setIcon(icon);
}