fix(icons): паритет иконки окна с pack-иконкой и sync lockfile

- Копировать build/icon.png в dist/renderer/app-pack-icon.png после Vite
- Приоритет pack PNG для BrowserWindow; на win32/linux без SVG в nativeImage
- macOS: app.dock.setIcon из того же набора PNG
- package-lock.json в соответствии с package.json

Made-with: Cursor
This commit is contained in:
     Фонтош Иван Сергеевич
2026-04-19 15:00:33 +08:00
parent d14a674e22
commit 5e7dc5ea19
5 changed files with 108 additions and 36 deletions
@@ -20,9 +20,10 @@ void test('createWindows: закрытие редактора завершает
assert.ok(src.includes('markAppQuitting'));
});
void test('createWindows: иконка окна (PNG приоритетно, затем SVG)', () => {
void test('createWindows: иконка окна (pack PNG, затем window PNG; SVG только вне win32)', () => {
const src = readCreateWindows();
assert.ok(src.includes('resolveWindowIconPath'));
assert.ok(src.includes('app-pack-icon.png'));
assert.ok(src.includes('app-window-icon.png'));
assert.ok(src.includes('app-logo.svg'));
});
+56 -11
View File
@@ -40,18 +40,34 @@ function getPreloadPath(): string {
}
/**
* Иконка окна. На Windows `nativeImage` из SVG часто пустой — сначала ищем PNG
* (`app-window-icon.png`), затем SVG из public / dist.
* PNG для иконки окна / дока: тот же растр, что electron-builder берёт из `build/icon.png`
* (копия в dist после сборки), затем окно 256px, затем dev-пути. SVG не используем для
* nativeImage на Windows — иначе пустая картинка и дефолтная иконка Electron вместо exe.
*/
function resolveWindowIconPath(): string | undefined {
function resolveBrandingPngPaths(): string[] {
const root = app.getAppPath();
const candidates = [
return [
path.join(root, 'dist', 'renderer', 'app-pack-icon.png'),
path.join(root, 'dist', 'renderer', 'app-window-icon.png'),
path.join(root, 'build', 'icon.png'),
path.join(root, 'app', 'renderer', 'public', 'app-window-icon.png'),
];
}
function resolveWindowIconPath(): string | undefined {
for (const p of resolveBrandingPngPaths()) {
try {
if (fs.existsSync(p)) return p;
} catch {
/* ignore */
}
}
const root = app.getAppPath();
const svgFallback = [
path.join(root, 'dist', 'renderer', 'app-logo.svg'),
path.join(root, 'app', 'renderer', 'public', 'app-logo.svg'),
];
for (const p of candidates) {
for (const p of svgFallback) {
try {
if (fs.existsSync(p)) return p;
} catch {
@@ -62,15 +78,44 @@ function resolveWindowIconPath(): string | undefined {
}
function resolveWindowIcon(): Electron.NativeImage | undefined {
const tryPath = (filePath: string): Electron.NativeImage | undefined => {
try {
const img = nativeImage.createFromPath(filePath);
if (!img.isEmpty()) return img;
} catch {
/* ignore */
}
return undefined;
};
if (process.platform === 'win32' || process.platform === 'linux') {
for (const p of resolveBrandingPngPaths()) {
if (!fs.existsSync(p)) continue;
const img = tryPath(p);
if (img) return img;
}
return undefined;
}
const p = resolveWindowIconPath();
if (!p) return undefined;
try {
const img = nativeImage.createFromPath(p);
if (!img.isEmpty()) return img;
} catch {
/* ignore */
return tryPath(p);
}
/** macOS: в Dock показываем тот же PNG, что и у упакованного приложения на Windows (иконка exe). */
export function applyDockIconIfNeeded(): void {
if (process.platform !== 'darwin' || !app.dock) return;
for (const p of resolveBrandingPngPaths()) {
if (!fs.existsSync(p)) continue;
try {
const img = nativeImage.createFromPath(p);
if (img.isEmpty()) continue;
app.dock.setIcon(img);
return;
} catch {
/* try next */
}
}
return undefined;
}
function createWindow(kind: WindowKind): BrowserWindow {