feat: squircle dock icon for macOS dev mode

macOS only applies the squircle mask to packaged .app bundles. For dev
mode, generate a pre-masked icon-dock.png via Swift/AppKit at launch
and set it with app.dock.setIcon(). Silently skipped on non-macOS.
This commit is contained in:
Jannis Braun
2026-03-16 04:15:09 +01:00
parent da05eb3262
commit c176ea801a
3 changed files with 32 additions and 1 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"main": "dist/main.js",
"scripts": {
"build:ts": "tsc",
"dev": "tsc && electron .",
"dev": "mkdir -p build && cp ../../icon.png build/icon.png && node -e \"try{require('child_process').execSync('swift scripts/gen-dock-icon.swift',{stdio:'inherit'})}catch(e){}\" && tsc && electron .",
"prebuild": "mkdir -p build && cp ../../icon.png build/icon.png",
"build": "tsc && electron-builder",
"clean": "rm -rf dist dist-electron"
@@ -0,0 +1,21 @@
// Generates a squircle-masked dock icon for macOS dev mode.
// macOS only applies the squircle mask to packaged .app bundles,
// so app.dock.setIcon() needs a pre-masked PNG.
import AppKit
let src = NSImage(contentsOfFile: "build/icon.png")!
let size = src.size
let out = NSImage(size: size)
out.lockFocus()
let radius = size.width * 0.2237
let path = NSBezierPath(roundedRect: NSRect(origin: .zero, size: size), xRadius: radius, yRadius: radius)
path.addClip()
src.draw(in: NSRect(origin: .zero, size: size), from: NSRect(origin: .zero, size: size), operation: .sourceOver, fraction: 1.0)
out.unlockFocus()
let tiff = out.tiffRepresentation!
let rep = NSBitmapImageRep(data: tiff)!
let png = rep.representation(using: .png, properties: [:])!
try! png.write(to: URL(fileURLWithPath: "build/icon-dock.png"))
+10
View File
@@ -513,6 +513,16 @@ if (!gotTheLock) {
// ─── App Lifecycle ──────────────────────────────────────────────────────────
app.whenReady().then(async () => {
// Set dock icon on macOS (overrides default Electron icon in dev mode)
if (process.platform === 'darwin' && app.dock) {
const dockIcon = path.join(__dirname, '..', 'build', 'icon-dock.png');
const fallbackIcon = path.join(__dirname, '..', 'build', 'icon.png');
const iconPath = fs.existsSync(dockIcon) ? dockIcon : fallbackIcon;
if (fs.existsSync(iconPath)) {
app.dock.setIcon(iconPath);
}
}
// macOS application menu with "Change Instance"
if (process.platform === 'darwin') {
const appMenu = Menu.buildFromTemplate([