fix(electron): paint the dock unread dot into the icon bitmap (#27)
`getDockUnreadIcon()` composited the dot with an SVG data URL, but
`nativeImage.createFromDataURL` only decodes the raster formats Chromium knows
(PNG/JPEG/WebP/GIF). An SVG data URL silently yields an EMPTY image — no error,
no warning.
So `getDockUnreadIcon()` returned a 0x0 image, `setDockUnreadDot`'s
`if (!img.isEmpty())` guard skipped `app.dock.setIcon` every time, and the
unconditional `app.dock.setBadge('')` cleared any badge that might otherwise
have shown. Net effect on macOS: App.tsx faithfully calls `setUnreadDot(true)`
when an unmuted conversation has unread, and nothing whatsoever appears on the
dock icon. Because the tray mirror already uses baked bitmaps and works, the
symptom reads as "tray shows unread, dock doesn't".
This file already documents the trap twice — the tray's own comment records that
this is "why the old SVG-based tray icon never appeared", which is exactly why
the tray moved to raw BGRA bitmaps. The dock path was left behind.
Use the same technique: rasterise the dot straight into the app icon's bitmap
and hand that to `nativeImage.createFromBitmap`. Geometry stays in the existing
1024-unit design space and scales to the icon's real pixel size, so
DOCK_UNREAD_DOT keeps its meaning. Blending `c*a + dst*(1-a)` on all four
channels preserves Chromium's premultiplied-alpha invariant. A missing or
undecodable icon still returns an empty image so the existing isEmpty() guard
behaves as before.
Verified against this repo's own bundled Electron, running the real function:
base icon: 1024x1024 isEmpty: false
dot centre before: {B:253,G:248,R:239,A:255}
dot centre after : {B:48,G:59,R:255,A:255} // #ff3b30
result isEmpty : false size: 1024x1024 // was isEmpty: true at 0x0
far pixel unchanged: true
premultiplied violations: 0 X
Xialie Zhuang committed
97f77ce0a4f8e3de3ff96c65c7e600eec5cf27c0
Parent: a9d6f7c
Committed by GitHub <noreply@github.com>
on 8/19/2026, 8:00:08 PM