Desktop platform APIs
Capability modules under src/desktop/. All follow the same pattern: explicit Error sets with error.Unsupported where a platform can't deliver, and no silent fallbacks.
Tray
var tray = try desktop.tray.init(allocator, &window, .{
.tooltip = "My app",
.icon_symbol = "bolt.fill", // macOS SF Symbol; icon_path elsewhere
.menu = &.{
.{ .label = "Open", .id = 1 },
.{}, // separator
.{ .label = "Quit", .id = 2 },
},
.on_menu_item = onMenuItem, .on_menu_item_ctx = ctx,
});macOS NSStatusItem · Windows Shell_NotifyIcon · Linux libayatana-appindicator3 (dlopen'd; Unsupported without it; GTK4 unsupported).
Notifications
try desktop.notifications.show(allocator, .{ .title = "Done", .body = "Export finished." });macOS UNUserNotificationCenter (needs a signed bundle) · Windows toast (balloon fallback) · Linux libnotify.
Global hotkeys
var hk = try desktop.hotkeys.init(allocator, onHotkey, ctx);
try hk.register(1, .{ .cmd = true, .shift = true }, keycode_k);Carbon (macOS) · RegisterHotKey (Windows) · X11 XGrabKey (Linux; Wayland → Unsupported; fires from a worker thread).
Deep links & single instance
var lock = desktop.single_instance.acquire(allocator, "com.me.app") catch |err| switch (err) {
error.AlreadyRunning => {
try desktop.deep_link.forwardToRunningInstance(allocator, "com.me.app", url);
return;
},
else => return err,
};
defer lock.release();
try desktop.deep_link.startListener(&window, "com.me.app");
// registerScheme(alloc, "myapp", bundle_id) registers the myapp:// handlerThe second launch forwards its URL to the first and exits; the running window receives it via on_url_open. (macOS warm-launches are handled by the system; the forwarders matter on Windows/Linux.)
Clipboard & cookies
try window.clipboard().writeText("copied");
const text = try window.clipboard().readText(allocator); // also Html/Image (PNG)
var cookies = window.cookies();
try cookies.set(.{ .name = "session", .value = token, .http_only = true });Linux clipboard is text-only (Unsupported for HTML/image).
File watching
var watcher = try desktop.fswatch.init(allocator, path, onChange, ctx);FSEvents (macOS, recursive, ~1s batches) · ReadDirectoryChangesW (Windows, recursive, worker thread) · inotify (Linux, single directory, non-recursive).
Autostart, updates, power, displays
try desktop.autostart.enable(allocator, io, environ, .{ .name = "com.me.app", .exe_path = exe });
if (try desktop.updates.checkForUpdate(allocator, feed_url, current_version)) |info| {
try desktop.updates.applyUpdate(allocator, io, info); // sha256-verified
}
// feed: {"version":"1.2.3","download_url":"…","sha256":"…","notes":"…"}
const pct = desktop.power.batteryPercent(); // ?u32
const charging = desktop.power.isCharging();
const screens = try desktop.displays.list(allocator); // x/y/w/h, scale, primaryUpdates: macOS atomic bundle swap, Windows swap.cmd handoff, Linux Unsupported.
Dialogs, print, snapshot
FileDialogOptions (open/save, multiple, directories), AlertOptions (buttons + informational/warning/critical), window.printWithOptions(.{ .kind = .system, .copies = 2 }), window.snapshot(...) for window captures (GTK4 Unsupported).
Next: Build options.