Skip to content
roverflowPublic

About

A GNOME Shell extension that shows StatusNotifierItem (AppIndicator) icons tightly grouped in the top panel. Written from scratch for GNOME 45 and later,

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

Traylet

A GNOME Shell extension that shows StatusNotifierItem (AppIndicator) icons tightly grouped in the top panel. Written from scratch for GNOME 45 and later, after auditing the AppIndicator extension this repo forked originally (see ../legacy). About a tenth of the code, because it drops a decade of compatibility shims and supports exactly one protocol.

What it does

  • Owns org.kde.StatusNotifierWatcher and shows every registered item as a panel icon. Both registration conventions work: Ayatana apps that pass an object path and KDE apps that pass a bus name.
  • Left click activates the app (or opens the menu when the item says it is a menu), right click opens the menu, middle click sends SecondaryActivate, scrolling forwards Scroll events.
  • Renders icons from theme names, absolute paths, app-shipped theme directories, and raw ARGB pixmaps.
  • Mirrors each item's dbusmenu into a native shell popup menu, including submenus, separators, checkmarks, radios, and item icons.
  • Preferences: icon size, spacing, opacity, desaturation, brightness, contrast. All bound directly to GSettings.
  • Optional RAM usage readout left of the icons: one async read of /proc/meminfo per interval (MemTotal minus MemAvailable, the free(1) definition). Toggle, refresh interval, format (percent, used, used/total), and the gap to the icons are settings; when off, no timer runs at all.
  • Hover and pressed styling comes from the shell theme (panel-button), so light and dark themes both look right.

What it deliberately does not do

  • No legacy XEmbed (X11 systray) icons. That protocol is nearly extinct; use the legacy extension if you still need it.
  • No overlay icons, tooltips, or the Ayatana label extension. Rarely visible at panel sizes, and each one costs protocol code.
  • No custom icon replacement table.
  • No brute-force rediscovery of items that registered before the extension enabled. Well-behaved apps watch the watcher name and re-register when it changes owner; the old approach spawned a helper process to walk the bus and was broken in the fork anyway.

Install

./install.sh            # install or update
./install.sh --uninstall

Then restart the shell (log out and in on Wayland) and run gnome-extensions enable traylet@roverflow. Disable the legacy extension first if it is running; both queue for the same bus name and only one acts.

Code layout

The seam runs between protocol and presentation. Files under sni/ never import St or Clutter; files under ui/ never speak raw D-Bus except through their own proxy in menuClient.js.

extension.js       entry point; everything created in enable(), torn down in disable()
util.js            Debouncer (owns GSource cleanup) and cancelled-error check
system/
  memory.js        polls /proc/meminfo, emits used/total samples; costs nothing while stopped
sni/
  interfaces.js    D-Bus interface XML as constants; no file reads at runtime
  watcher.js       owns the watcher name, emits item-added / item-removed with ready StatusItems
  item.js          one StatusNotifierItem: property cache, change batching, actions
  icons.js         icon facts (name, theme path, pixmaps) to a single Gio.Icon
ui/
  tray.js          the panel container; owns TrayIcon widgets and menu registration
  trayIcon.js      per-item widget: icon, effects, click and scroll routing
  ramIndicator.js  RAM label; wires the memory monitor to settings and the panel
  menuClient.js    dbusmenu tree to shell PopupMenu; full-layout rebuild, in-place property patches

Design rules the code follows:

  • Nothing happens in the Extension constructor. enable() builds, disable() destroys, and nothing global gets patched.
  • Every async call runs against a Gio.Cancellable owned by the object that started it, cancelled in that object's destroy().
  • Signal connections on widgets use connectObject, so they disconnect when the widget goes away.
  • The SNI spec's refusal to use PropertiesChanged is handled in one place: item.js maps change signals to property names and batches the re-fetches behind a 50ms debounce.

License

GPL-2.0-or-later, same as the extension this replaces.

About

A GNOME Shell extension that shows StatusNotifierItem (AppIndicator) icons tightly grouped in the top panel. Written from scratch for GNOME 45 and later,

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages