Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/desktop/electron-builder.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ export default {
from: 'bundled-tools.json',
to: 'bundled-tools.json',
},
{
// The app icon is read at runtime by the BrowserWindow `icon` option, and
// `files` above does not carry `assets/`. Electron reports the missing
// file as an empty image rather than an error, so without this the
// packaged app just draws no window icon.
from: 'assets',
to: 'assets',
},
{
// Menu bar status item art. Without this the packaged app resolves an
// empty NativeImage and Electron silently shows no icon at all.
Expand Down
18 changes: 18 additions & 0 deletions apps/desktop/src/main/__tests__/desktop-assets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import assert from 'node:assert/strict';
import { join } from 'node:path';
import { test } from 'node:test';
import { desktopAssetPath, desktopAssetRoot } from '../desktop-assets.js';

test('a packaged build reads assets from the copy beside the app', () => {
const resourcesPath = join('/Applications', 'Maka.app', 'Contents', 'Resources');
assert.equal(desktopAssetRoot({ isPackaged: true, resourcesPath }), resourcesPath);
assert.equal(
desktopAssetPath({ isPackaged: true, resourcesPath }, 'assets', 'icon.png'),
join(resourcesPath, 'assets', 'icon.png'),
);
});

test('a dev run keeps resolving the repo layout, not the resources path', () => {
const root = desktopAssetRoot({ isPackaged: false, resourcesPath: '/unused' });
assert.ok(root.endsWith(join('apps', 'desktop')), `${root} should point at apps/desktop`);
});
28 changes: 28 additions & 0 deletions apps/desktop/src/main/desktop-assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { join } from 'node:path';

/**
* Root that `apps/desktop/assets` hangs off at runtime.
*
* Dev resolves the repo layout: two levels up from the built main bundle in
* `dist/main/` lands on `apps/desktop`. A packaged app has no such tree —
* `files` in the builder config carries `dist/`, `dist-renderer/` and
* `package.json`, and nothing else — so the assets ride along as an extra
* resource and the same segments hang off `process.resourcesPath` instead.
*
* Resolving the dev path in a packaged build fails silently: Electron reports
* an unreadable file as an EMPTY NativeImage rather than as an error, and the
* BrowserWindow `icon` option simply draws nothing.
*/
export function desktopAssetRoot(runtime: {
readonly isPackaged: boolean;
readonly resourcesPath: string;
}): string {
return runtime.isPackaged ? runtime.resourcesPath : join(import.meta.dirname, '..', '..');
}

export function desktopAssetPath(
runtime: { readonly isPackaged: boolean; readonly resourcesPath: string },
...segments: readonly string[]
): string {
return join(desktopAssetRoot(runtime), ...segments);
}
3 changes: 2 additions & 1 deletion apps/desktop/src/main/main-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
import type { AppSettings } from '@maka/core/settings';
import { desktopAssetPath } from './desktop-assets.js';
import { isExternalUrl } from './external-link-guard.js';
import { readSavedBounds, writeSavedBounds, SAFE_MIN_HEIGHT, SAFE_MIN_WIDTH, type SavedBounds } from './window-state.js';
import { BrowserViewController } from './browser/controller.js';
Expand Down Expand Up @@ -268,7 +269,7 @@ export function createMainWindowController(deps: MainWindowControllerDeps): Main
// / window title bar; .icns / .ico packaging will come with the
// installer build pass. The asset path resolves from the built
// dist/main/main.js (two levels up to apps/desktop, then assets).
icon: join(import.meta.dirname, '..', '..', 'assets', 'icon.png'),
icon: desktopAssetPath({ isPackaged: app.isPackaged, resourcesPath: process.resourcesPath }, 'assets', 'icon.png'),
// PR-WINDOW-TITLEBAR-0: hide the native title bar so the renderer
// chrome can extend to the top edge on every platform. macOS keeps
// `hiddenInset` + traffic-light buttons (top-left); Windows uses
Expand Down