Tray

Add an icon, tooltip, title, and context menu to the system status bar / notification area (main process).

Tray adds an icon to the system status bar (macOS menu bar) or notification area (Linux). 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 the click event all function.
  • Linux - a StatusNotifierItem exported over D-Bus (KDE, the GNOME AppIndicator extension, Waybar, swaybar, etc. draw the icon). It is gated behind the BUNMASKA_ENABLE_LINUX_TRAY=1 environment 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 a Tray safely everywhere. Even when live, setContextMenu is not yet shown on Linux (the com.canonical.dbusmenu service is deferred).
  • Windows - not supported (Bunmaska is macOS + Linux only). Constructing a Tray throws UnsupportedPlatformError.
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)

  • image string - a filesystem path to the icon file.

Creates a status item and shows the icon immediately. Unlike Electron, the image argument is a path string only - a NativeImage is not accepted, and 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)

  • toolTip string

Sets the hover text for the tray icon. No-op after destroy().

tray.setToolTip('Syncing - 3 items left');

tray.setTitle(title) macOS

  • title string

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. Note the simplified signature: Bunmaska does not accept Electron’s options.fontType argument. No-op after destroy().

tray.setTitle('42');

tray.setImage(image)

  • image string - a filesystem path to the new icon.

Replaces the icon. As with the constructor, this takes a path string, not a NativeImage. No-op after destroy().

tray.setImage('/path/to/active-iconTemplate.png');

tray.setContextMenu(menu)

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. It never throws, so the same code runs on both platforms.

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).

tray.on('click', () => {
  console.log('tray activated');
});

Not in Bunmaska (yet)

Compared with Electron’s Tray, the following are not implemented:

  • guid constructor parameter - no UUID-based icon identity / position persistence.
  • NativeImage arguments - the constructor, setImage, etc. take a path string only.
  • click event payload - Bunmaska’s click carries no event / bounds / position. Electron’s bounds and position data are unavailable.
  • right-click / double-click / middle-click events - 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-end are 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 context menus - setContextMenu is accepted but the menu is not yet drawn on Linux (dbusmenu deferred).
  • All Windows members - displayBalloon, removeBalloon, focus, balloon-* events, etc. Bunmaska has no Windows backend at all.