Skip to content

Commit d33d8be

Browse files
OptimusLinuxclaude
andcommitted
Install Linux app icon into hicolor theme
The launcher entry previously referenced the app icon by absolute path (Icon=/.../assets/icon.png). When that PNG is replaced in-place between builds, GNOME and several other launchers keep showing the *original* cached image because their thumbnail cache doesn't reliably invalidate on path+mtime alone — which is what just happened with the v1.2.2 icon swap on the dev machine. Fix: install the icon into the user's hicolor icon theme at ~/.local/share/icons/hicolor/256x256/apps/redalert.png and reference it by themed name (Icon=redalert) in the .desktop file. Themed icons go through the icon-theme cache, which we also nudge with a best-effort gtk-update-icon-cache call. Falls back to the absolute-path form if the theme install fails for any reason. Also fires update-desktop-database after writing the launcher entry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d59d44d commit d33d8be

1 file changed

Lines changed: 62 additions & 3 deletions

File tree

‎src/autoStart.js‎

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { writeFileSync, unlinkSync, existsSync, mkdirSync, readFileSync } from 'fs';
1+
import { writeFileSync, unlinkSync, existsSync, mkdirSync, readFileSync, copyFileSync } from 'fs';
2+
import { execFile } from 'child_process';
23
import { join, dirname } from 'path';
34
import { fileURLToPath } from 'url';
45
import { homedir } from 'os';
@@ -72,27 +73,81 @@ function setAutoStartWindows(enabled) {
7273
log.info(`Windows auto-start ${enabled ? 'enabled' : 'disabled'} (managed by installer)`);
7374
}
7475

76+
/**
77+
* Installs the app icon into the user's hicolor icon theme so launchers can
78+
* find it by name. We install at every standard size we have available
79+
* (just the 256x256 source — desktop environments will downscale as needed).
80+
*
81+
* Using a themed icon (referenced as `Icon=redalert` in the .desktop file)
82+
* avoids a nasty cache trap: when the .desktop file references an absolute
83+
* PNG path and that PNG is later replaced in-place, GNOME / many launchers
84+
* keep showing the *original* image because their thumbnail cache is keyed
85+
* by path+mtime in a way that doesn't always invalidate. Themed icons go
86+
* through the icon-theme cache, which is rebuilt on demand.
87+
*/
88+
function installLinuxAppIcon() {
89+
const sourceIcon = join(appDir, 'assets', 'icon.png');
90+
if (!existsSync(sourceIcon)) {
91+
log.warn('App icon source not found, skipping theme install:', sourceIcon);
92+
return false;
93+
}
94+
95+
const iconsRoot = join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'icons', 'hicolor');
96+
const targetDir = join(iconsRoot, '256x256', 'apps');
97+
const targetIcon = join(targetDir, 'redalert.png');
98+
99+
try {
100+
mkdirSync(targetDir, { recursive: true });
101+
// Only copy if missing or contents differ, to avoid disk churn on every start.
102+
let needsCopy = true;
103+
if (existsSync(targetIcon)) {
104+
try {
105+
const a = readFileSync(sourceIcon);
106+
const b = readFileSync(targetIcon);
107+
if (a.equals(b)) needsCopy = false;
108+
} catch {}
109+
}
110+
if (needsCopy) {
111+
copyFileSync(sourceIcon, targetIcon);
112+
log.info('App icon installed into hicolor theme:', targetIcon);
113+
// Best-effort icon-cache refresh; failure is fine (not all desktops have it).
114+
execFile('gtk-update-icon-cache', ['-f', '-t', iconsRoot], (err) => {
115+
if (err) log.info('gtk-update-icon-cache not available or failed (non-critical)');
116+
});
117+
}
118+
return true;
119+
} catch (e) {
120+
log.warn('Failed to install app icon into hicolor theme:', e.message);
121+
return false;
122+
}
123+
}
124+
75125
/**
76126
* Installs a desktop launcher entry on Linux so RedAlert appears in the
77127
* applications menu / launcher. Idempotent — safe to call on every startup.
78128
*/
79129
export function installLinuxLauncher() {
80130
if (!isLinux) return;
81131

132+
// Install the icon into the hicolor theme first so we can reference it by name.
133+
const iconInstalled = installLinuxAppIcon();
134+
82135
const appsDir = join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'applications');
83136
const desktopPath = join(appsDir, 'redalert.desktop');
84137

85138
const execPath = process.pkg
86139
? process.execPath
87140
: `${process.execPath} ${join(appDir, 'src', 'main.js')}`;
88-
const iconPath = join(appDir, 'assets', 'icon.png');
141+
// Prefer themed icon name if we successfully installed it, otherwise fall back
142+
// to the absolute path so the entry still has *some* icon.
143+
const iconRef = iconInstalled ? 'redalert' : join(appDir, 'assets', 'icon.png');
89144

90145
const desktopEntry = `[Desktop Entry]
91146
Type=Application
92147
Name=RedAlert
93148
Comment=Pikud HaOref alert monitor
94149
Exec=${execPath}
95-
Icon=${iconPath}
150+
Icon=${iconRef}
96151
Terminal=false
97152
Categories=Utility;Network;
98153
StartupNotify=false
@@ -111,6 +166,10 @@ StartupNotify=false
111166
if (needsWrite) {
112167
writeFileSync(desktopPath, desktopEntry);
113168
log.info('Launcher entry installed:', desktopPath);
169+
// Best-effort desktop database refresh.
170+
execFile('update-desktop-database', [appsDir], (err) => {
171+
if (err) log.info('update-desktop-database not available or failed (non-critical)');
172+
});
114173
}
115174
} catch (e) {
116175
log.warn('Failed to install Linux launcher entry:', e.message);

0 commit comments

Comments
 (0)