Tray adds an icon to the system status bar (macOS menu bar), notification area (Linux), or notification area / system tray (Windows). In Bunmaska the native status item is created eagerly in the constructor and reconfigured through forwarding methods, and the class extends Node’s EventEmitter so the listener API (on/once/…) matches Electron’s contract.
Platform support is uneven and honest about it:
- macOS - fully wired to a real
NSStatusItem. Works un-bundled (bun main.ts). Icon, tooltip, title, context menu, and theclickevent all function. - Linux - a
StatusNotifierItemexported over D-Bus (KDE, the GNOME AppIndicator extension, Waybar, swaybar, etc. draw the icon). It is gated behind theBUNMASKA_ENABLE_LINUX_TRAY=1environment variable. With the gate off, or when no session bus is reachable, the tray is an inert no-op rather than a throw - so cross-platform code can construct aTraysafely everywhere. Even when live,setContextMenuis not yet shown on Linux (thecom.canonical.dbusmenuservice is deferred). - Windows - wired to the notification area via
Shell_NotifyIcon. Icon, tooltip, and the left-clickclickevent all function. Caveats:setContextMenuis deferred (a no-op - no menu is shown),setTitleis a no-op (Windows tray icons have no inline text), and right-click / double-click are not surfaced.
import { app, Menu, Tray } from 'bunmaska';
let tray: Tray | null = null;
app.whenReady().then(() => {
tray = new Tray('/path/to/iconTemplate.png');
const contextMenu = Menu.buildFromTemplate([
{ label: 'Item1', type: 'radio' },
{ label: 'Item2', type: 'radio' },
]);
tray.setToolTip('This is my application.');
tray.setContextMenu(contextMenu);
});
Constructor
new Tray(image)
imagestring | NativeImage - a filesystem path, or a NativeImage.
Creates a status item and shows the icon immediately. image is a filesystem path or a NativeImage (a NativeImage is written to a temp PNG the native backends load by path); there is no guid parameter. A bad or unreadable path does not crash; the icon is simply not set. On macOS, pass a Template Image (a filename ending in Template) so the menu bar can invert it for light/dark mode.
import { Tray } from 'bunmaska';
const tray = new Tray('/path/to/iconTemplate.png');
Methods
tray.setToolTip(toolTip)
toolTipstring
Sets the hover text for the tray icon. No-op after destroy().
tray.setToolTip('Syncing - 3 items left');
tray.setTitle(title) macOS
titlestring
Sets the text shown next to the icon in the macOS status bar. On Linux this maps to the SNI Title (when the live tray is enabled) and is otherwise a no-op. On Windows it is a no-op - tray icons there have no inline text. Note the simplified signature: Bunmaska does not accept Electron’s options.fontType argument. No-op after destroy().
tray.setTitle('42');
tray.setImage(image)
imagestring | NativeImage - a filesystem path, or a NativeImage.
Replaces the icon. Accepts a path string or a NativeImage, as the constructor does. No-op after destroy().
tray.setImage('/path/to/active-iconTemplate.png');
tray.setContextMenu(menu)
menuMenu | null
Attaches a context menu (shown on click) or clears it with null. No-op after destroy().
On macOS this installs a real NSMenu and works as expected. On Linux this is accepted but currently a soft no-op - the menu is not shown, because the dbusmenu service is deferred. On Windows it is likewise a deferred no-op - the menu is accepted but not yet shown. It never throws, so the same code runs on every platform.
import { Menu } from 'bunmaska';
const menu = Menu.buildFromTemplate([
{ label: 'Open', click: () => openWindow() },
{ type: 'separator' },
{ label: 'Quit', role: 'quit' },
]);
tray.setContextMenu(menu);
tray.destroy()
Removes the status item. Idempotent - calling it more than once is safe, and every other method becomes a no-op afterward.
tray.destroy();
tray.isDestroyed()
Returns boolean - whether destroy() has been called.
if (!tray.isDestroyed()) {
tray.setToolTip('still here');
}
Events
Event: ‘click’
Emitted when the tray icon is activated. Unlike Electron, the listener receives no arguments - there is no event, bounds, or position payload.
Platform nuance: on macOS, when a context menu is set, AppKit consumes the click to present the menu, so click fires only when no menu is set. On Linux, the host’s Activate action drives click (and only when the live tray is enabled). On Windows, a left-click on the tray icon drives click (right-click and double-click are not surfaced).
tray.on('click', () => {
console.log('tray activated');
});
Not in Bunmaska (yet)
Compared with Electron’s Tray, the following are not implemented:
guidconstructor parameter - no UUID-based icon identity / position persistence.clickevent payload - Bunmaska’sclickcarries noevent/bounds/position. Electron’sboundsandpositiondata are unavailable.right-click/double-click/middle-clickevents - deferred until a real event source is wired.- All mouse and drag events -
mouse-up,mouse-down,mouse-enter,mouse-leave,mouse-move,drop,drop-files,drop-text,drag-enter,drag-leave,drag-endare not emitted. setPressedImage(image)(macOS) - no pressed-state icon.getTitle()- title is write-only; there is no getter.setIgnoreDoubleClickEvents()/getIgnoreDoubleClickEvents()- not implemented.popUpContextMenu()/closeContextMenu()- no programmatic menu pop-up/dismiss.getBounds()- the icon’s screen rectangle is not exposed.getGUID()- no GUID support, so nothing to return.- Linux and Windows context menus -
setContextMenuis accepted but the menu is not yet drawn on Linux (dbusmenu deferred) or Windows (deferred no-op). Only macOS shows the menu today. - Windows balloon / focus members -
displayBalloon,removeBalloon,focus,balloon-*events, etc. are not implemented, even though the Windows tray icon itself (viaShell_NotifyIcon) is now supported.