Skip to content

Rust API

Everything an app needs comes from the sabine crate. It re-exports the bridge, platform and runtime types it uses, so apps depend on sabine alone, plus serde and specta for typed commands.

The crates aren’t published on crates.io. Depend on a release tag of the repository:

[dependencies]
sabine = { git = "https://github.com/Lantharos/Sabine", tag = "v0.68" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
specta = { version = "=2.0.0-rc.27", features = ["derive"] }

Projects created with sabine new already have these. To browse the full API, with every type and method, build the documentation locally from your app’s project:

Terminal window
cargo doc -p sabine --open

sabine::prelude brings in the types most apps use:

use sabine::prelude::*;
// AppChrome, AppEnvironment, BridgeCommand, BridgeError, BridgeResponse, BridgeResult,
// SabineColor, SabineError, SabineLifecyclePolicy, SabineProcess, SabineProcessHandle,
// SabineResult, SabineWindow, SabineWindowChrome, TrayIcon, WindowBackgroundEffect,
// WindowRegion, WindowRegionRect
impl SabineWindow {
pub fn main(build: impl FnOnce(Self) -> SabineResult<Self>) -> !;
pub fn main_with_process(
build: impl FnOnce(Self) -> SabineResult<Self>,
launched: impl FnOnce(&SabineProcess),
) -> !;
pub fn main_with_process_mut(
build: impl FnOnce(Self) -> SabineResult<Self>,
launched: impl FnOnce(&mut SabineProcess),
) -> !;
pub fn launch(self) -> SabineResult<SabineProcess>;
}
pub fn dispatch_host_mode_from_args(args: &[String]) -> bool;

SabineWindow::main is the recommended entry point. It runs Sabine’s internal modes when the executable was started as a window host, setup window or notice, loads Sabine.toml, hands a window configured from it to build, launches it, waits, and exits with the window’s exit status. When build returns an error, or the launch fails, it shows a native notice and exits with status 1. A launch handed to an already running instance exits with status 0.

#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
use sabine::prelude::*;
use serde::{Deserialize, Serialize};
use specta::Type;
#[derive(Deserialize, Type)]
struct NewNote {
title: String,
}
#[derive(Serialize, Type)]
struct Note {
id: String,
title: String,
}
fn main() {
SabineWindow::main(|window| {
Ok(window
.app()
.glass()
.app_chrome(AppChrome::default())
.min_size(720, 480)
.bridge_typed("notes.create", create_note))
});
}
fn create_note(note: NewNote) -> Result<Note, BridgeError> {
let title = note.title.trim();
if title.is_empty() {
return Err(BridgeError::new("A note needs a title"));
}
Ok(Note {
id: format!("note-{}", title.len()),
title: title.to_string(),
})
}

main_with_process and main_with_process_mut also hand you the launched SabineProcess before waiting, for example to keep its event emitter in app state or to open more windows:

use sabine::prelude::*;
fn main() {
SabineWindow::main_with_process_mut(
|window| Ok(window.app().title("Notes")),
|process| {
let inspector = SabineWindow::for_current_app()
.map(|window| window.title("Inspector").size(420, 640));
if let Ok(window) = inspector {
let _ = process.open_window(window);
}
},
);
}

launch starts the app and returns its SabineProcess without waiting, for apps that run their own loop. When a Windows setup program runs the executable, launch prepares Sabine and exits instead. Call process.wait() when you’re done; on macOS, call it on the main thread, or run an AppKit loop there, since the tray and global shortcuts live on the main thread.

A custom main must let Sabine run its internal modes before parsing arguments, starting a logger or doing anything else, and return when it did:

fn main() {
let args = std::env::args().collect::<Vec<_>>();
if sabine::dispatch_host_mode_from_args(&args) {
return;
}
// Parse arguments and set up the app.
}
impl SabineWindow {
pub fn new() -> Self;
pub fn with_manifest(self, path: impl AsRef<Path>) -> SabineResult<Self>;
pub fn for_current_app() -> SabineResult<Self>;
}

main finds Sabine.toml through SABINE_MANIFEST_PATH, next to a bundled executable, in the working directory, or in the Cargo package folder under cargo run. with_manifest applies another one explicitly: identity, version, update source, URL schemes and document types, the production web entry or URL, allowed origins, local file access, the loading screen and, on Linux, the sandbox and shared_runtime. Settings you make in Rust afterwards take precedence.

for_current_app builds a window for another open_window call, with the running app’s identity and web entry, and its development URL during sabine dev.

Recipes set a window up for its role. Later builder calls refine them.

Recipe What it sets
.app() System chrome, opaque, the browser-tab lifecycle: the page freezes after five minutes out of sight
.palette() Frameless, glass, hidden when it loses focus, out of the taskbar, the hidden-window lifecycle
.tray_app() Starts hidden and inactive, out of the taskbar, the hidden-window lifecycle. Pair it with .tray_icon(...) and single-instance behavior
.background() Launches without its window: no window host or browser runs until the app is activated or SabineProcessHandle::present is called. Closing the window ends both while the app keeps running

See Creating windows.

Every builder method takes self and returns Self, so calls chain. A window that isn’t configured otherwise is 900 by 640 logical pixels, at least 420 by 280, titled Sabine, opaque, with system chrome and a #111113 background.

Method Does
app_id(impl Into<String>) The app id: lowercase letters, digits, dots and hyphens. Required; usually from Sabine.toml
app_version(impl Into<String>) The app’s version
title(impl Into<String>) The window title; must not be empty
entry(impl Into<String>) A local HTML file, served from its folder at sabine://app/
url(impl Into<String>) A production URL; its exact origin may use the bridge. Exclusive with entry
content_suffix(impl AsRef<str>) Appends a query string or fragment to the page the window opens: the entry, the URL, or the development server’s page in a development run
allowed_origin(impl Into<String>) Lets another origin’s pages use the bridge
security(ContentSecurity) Replaces the allowed origins wholesale
local_files(bool) Lets pages load any readable file through sabine://file/<absolute path>
runtime(RuntimeConfig) Where the app looks for a Chromium runtime
shared_runtime(bool) Linux only. Whether the app’s pages may run in the shared Chromium; true by default
vaapi_hardware_decode(bool) Linux only. VA-API video decoding for WebCodecs and media; off by default
debug(u16) Opens Chromium’s remote debugging on this port, also in production
Method Does
size(u32, u32) The initial size in logical pixels
min_size(u32, u32) The minimum size
fixed_size(u32, u32) Sets size and minimum size, and makes the window not resizable
maximized(bool) Opens maximized
remember_size() Opens at the size and maximized state the window closed with; size and maximized apply the first time
resizable(bool) Whether people can resize the window
Method Does
visible(bool) Whether the window starts shown. false also applies the hidden-window lifecycle defaults
hidden() visible(false) and active(false)
active(bool) Whether the window takes focus when shown
hide_on_blur(bool) Hides the window when it loses focus, with the hidden-window lifecycle defaults
hide_on_close(bool) Hides the window instead of closing it
skip_taskbar(bool) Off the Windows taskbar, out of the macOS Dock, and out of the taskbar on Wayland compositors with kestrel_window_v1
always_on_top(bool) Above other windows on Windows, macOS, and Wayland compositors with kestrel_window_v1
Method Does
transparent(bool) A transparent window. false also removes the background effect and blur region
background_color(SabineColor) The color before the first frame and behind Sabine’s own surfaces
splash(bool) Shows Sabine’s loading screen when the page takes longer than 1.5 seconds to appear
splash_after(Duration) The same, after another delay
glass() background_effect(WindowBackgroundEffect::Glass)
background_effect(WindowBackgroundEffect) A desktop material behind the window; any effect makes the window transparent
app_chrome(AppChrome) An app-drawn titlebar with window controls, and glass sidebar regions
system_chrome() The desktop’s own decorations, the default
frameless() No system decorations; the page draws everything
chrome(SabineWindowChrome) Picks a chrome style directly, including Sabine, a titlebar Sabine draws itself

See Window chrome and Glass and effects.

Method Does
regions(WindowRegions) Sets the blur, opaque and input regions together
blur_region(WindowRegion) Where the background effect shows
opaque_region(WindowRegion) What the page always covers
input_region(WindowRegion) Where the window takes pointer input
drag_region(WindowRegionRect) An area that moves the window
drag_exclusion_region(WindowRegionRect) An area inside a drag region that doesn’t
titlebar_drag_region(i32) A drag strip of this height across the top
control_region(SabineWindowControlAction, WindowRegionRect) An area that minimizes, maximizes or closes the window; a negative x counts from the right edge

Pages can also mark draggable areas with -webkit-app-region: drag, and change regions while the window runs.

Method Does
lifecycle_policy(SabineLifecyclePolicy) Replaces the whole lifecycle policy
active_frame_rate(u32) The frame rate while active; 0 follows the display
background_frame_rate(u32) The frame rate while suspended; at least 1
suspend_on_occluded(bool), suspend_on_blur(bool) When the page drops to its background frame rate: out of sight, including minimized, or without focus
freeze_after(Duration) Freezes the page after it has been out of sight this long
disable_freezing() Never freezes the page
retain_hidden_frame(bool) Keeps the last frame while hidden, for a quick return

See Lifecycle.

Method Does
tray_icon(TrayIcon) The app’s tray icon and menu
autostart(AutostartEntry) Starts the app at login; its id must be the app id
global_shortcut(GlobalShortcutRegistration) A system-wide shortcut; can be called more than once
native_messaging_host(NativeMessagingHost) A browser extension host
single_instance(SingleInstancePolicy) What a second launch does
single_instance_id(impl Into<String>) Names the instance, instead of the app id
pub fn on_launched(self, listener: impl Fn(SabineProcessHandle) + Send + Sync + 'static) -> Self;
pub fn on_visibility_changed(self, listener: impl Fn(WindowVisibility) + Send + Sync + 'static) -> Self;
pub fn on_appearance_changed(self, listener: impl Fn(WindowId, Appearance) + Send + Sync + 'static) -> Self;
pub fn on_desktop_event(self, listener: impl Fn(&SabineProcessHandle, &PlatformEvent) + Send + Sync + 'static) -> Self;

on_launched hands app code that only builds the window a handle to the process. on_desktop_event hears every tray click, global shortcut, later launch and link before the pages do, whether or not a window is open, so a background app can open its window from Rust. on_visibility_changed reports each change of visibility or suspension, starting with the state the window opens in and ending with a hidden report when it closes. on_appearance_changed reports the desktop’s light or dark preference and accent color, starting with the appearance the window opens with.

pub struct WindowVisibility {
pub window: WindowId,
pub visible: bool,
pub suspended: bool,
}
pub fn bridge_typed<Req, Res, F>(self, name: impl Into<String>, handler: F) -> Self
where
Req: serde::de::DeserializeOwned + specta::Type,
Res: serde::Serialize + specta::Type,
F: Fn(Req) -> Result<Res, BridgeError> + Send + Sync + 'static;
pub fn bridge_typed_async<Req, Res, F, Fut>(self, name: impl Into<String>, handler: F) -> Self
where
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Res, BridgeError>> + Send + 'static;
pub fn bridge_handler<F>(self, name: impl Into<String>, handler: F) -> Self
where
F: Fn(BridgeCommand) -> BridgeResult + Send + Sync + 'static;
pub fn bridge_handler_async<F, Fut>(self, name: impl Into<String>, handler: F) -> Self
where
F: Fn(BridgeCommand) -> Fut + Send + Sync + 'static,
Fut: Future<Output = BridgeResult> + Send + 'static;
pub fn bridge_descriptor_handler<F>(self, descriptor: BridgeCommandDescriptor, handler: F) -> Self;
pub fn bridge_descriptor_handler_async<F, Fut>(self, descriptor: BridgeCommandDescriptor, handler: F) -> Self;
pub fn bridge_event<T: specta::Type>(self, event: &BridgeEvent<T>) -> Self;
pub fn bridge_type<T: specta::Type>(self) -> Self;

Typed commands deserialize their parameters into Req and answer with Res as JSON, and they appear in the app’s generated TypeScript bindings, as do events declared with bridge_event and extra types added with bridge_type. Raw handlers receive the whole BridgeCommand, including binary bodies. Synchronous handlers run on four worker threads per window and shouldn’t wait on slow work; async handlers are awaited on sabine::async_runtime(). Descriptor handlers let extra origins call one command. Register a command for one desktop under #[cfg(target_os = "...")].

See Commands, Typed bindings and Events.

pub struct AppChrome {
pub titlebar: i32, // 38
pub sidebar: i32, // 260; 0 for no sidebar
pub radius: i32, // 14
pub control_width: i32, // 46
}
impl AppChrome {
pub fn new(titlebar: i32, sidebar: i32) -> Self;
}

app_chrome gives the window a drag strip as tall as titlebar. On Linux and Windows the window turns frameless and gets minimize, maximize and close regions, each control_width wide, at the titlebar’s right end, for the page to draw buttons over. On macOS the window keeps its traffic lights over a transparent titlebar, and the page extends under it. With a sidebar, the window also gets the blur, opaque and input regions of a glass sidebar layout, rounded by radius except on macOS, where the system rounds the window.

pub enum SabineWindowChrome { System, Sabine, Frameless }
pub enum SabineWindowControlAction { Minimize, Maximize, Close }
pub struct SabineWindowControlRegion {
pub action: SabineWindowControlAction,
pub rect: WindowRegionRect,
}
impl SabineColor {
pub const fn rgb8(r: u8, g: u8, b: u8) -> Self;
pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self;
pub fn opacity(self, alpha: f32) -> Self;
}

SabineColor is an sRGB color with the same 8-bit channels as CSS.

pub struct SabineLifecyclePolicy {
pub active_frame_rate: u32, // 0: follow the display
pub background_frame_rate: u32, // 5
pub suspend_on_occluded: bool, // true
pub suspend_on_blur: bool, // false
pub freeze_after: Option<Duration>, // None
pub retain_hidden_frame: bool, // false
}
impl SabineLifecyclePolicy {
pub fn browser_tab() -> Self; // default, freezing after 5 minutes
pub fn hidden_window() -> Self; // 1 fps in the background, suspend on blur,
// freeze after 1 minute, keep the hidden frame
}
impl SabineProcess {
pub fn id(&self) -> Option<WindowId>;
pub fn handle(&self) -> SabineProcessHandle;
pub fn open_window(&mut self, window: SabineWindow) -> SabineResult<WindowId>;
pub fn close_window(&mut self, window: WindowId) -> bool;
pub fn wait(self) -> std::io::Result<ExitStatus>;
pub fn bridge_event_emitter(&self) -> BridgeEventEmitter;
pub fn take_open_urls(&self) -> Vec<String>;
pub fn tray(&self) -> Option<TrayHandle>;
pub fn notify(&self, notification: &Notification) -> bool;
pub fn guest_control(&self, control: &GuestHostControl) -> bool;
pub fn show(&self) -> bool;
pub fn hide(&self) -> bool;
pub fn set_visible(&self, visible: bool) -> bool;
pub fn focus_window(&self) -> bool;
pub fn begin_activity(&self, name: impl Into<String>) -> SabineActivityLease;
pub fn begin_activity_with(&self, options: ActivityOptions) -> SabineActivityLease;
pub fn activities(&self) -> Vec<ActivityRecord>;
pub fn metrics(&self) -> SabineLaunchMetricsSnapshot;
}
  • id is the first window’s id while it is open. WindowId is a u32, the id of the window host’s process.
  • open_window opens another window with the app’s id and bridge handlers; handlers registered on the window you pass are ignored. It waits up to 20 seconds for the first window to be ready, since the others join its browser. close_window returns false for an unknown id.
  • wait blocks until every window has closed, then returns the first window’s exit status. A background app keeps waiting for its next activation, until SabineProcessHandle::quit ends it.
  • take_open_urls drains the queue of URLs and documents the app was asked to open, by its arguments, a second launch, or the system.
  • notify shows a desktop notification from the first window; its pages hear clicks as notification.click events.
  • Activities keep every window’s page running while it is out of sight, until the lease is ended or dropped. ActivityOptions::new(name).keep_running(false) records an activity without keeping pages awake.
  • Dropping the SabineProcess closes its windows.

Methods that return bool report whether at least one window took the message.

#[derive(Clone)]
pub struct SabineProcessHandle { /* … */ }
impl SabineProcessHandle {
pub fn open_window(&self, window: SabineWindow) -> SabineResult<WindowId>;
pub fn present(&self, activation_token: Option<String>) -> bool;
pub fn quit(&self) -> bool;
pub fn set_autostart(&self, enabled: bool) -> Result<(), String>;
}

A handle can be sent to other threads and kept in app state while wait runs. present shows and focuses the app’s window, opening it first while a background app runs without one. Pass the activation token from a notification click or single-instance activation on Wayland, so the window may take focus. open_window from a handle waits in line until the first window is ready, without blocking the wait loop. quit closes every window and ends the app, also a background app or one whose window hides on close, and wait then returns a success status. set_autostart turns the app’s autostart entry on or off; see Starting at login.

impl BridgeEventEmitter {
pub fn emit(&self, name: impl Into<String>, payload: serde_json::Value) -> bool;
pub fn emit_to(&self, window: WindowId, name: impl Into<String>, payload: serde_json::Value) -> bool;
pub fn send<T: Serialize>(&self, event: &BridgeEvent<T>, payload: &T) -> bool;
pub fn send_bytes(&self, event: &BridgeEvent<Bytes>, bytes: &[u8]) -> bool;
pub fn emit_bytes(&self, name: impl Into<String>, bytes: &[u8]) -> bool;
pub fn emit_bytes_to(&self, window: WindowId, name: impl Into<String>, bytes: &[u8]) -> bool;
pub fn set_regions(&self, regions: &WindowRegions) -> bool;
pub fn set_regions_of(&self, window: WindowId, regions: &WindowRegions) -> bool;
pub fn set_square_corners(&self, square: bool) -> bool;
pub fn notify(&self, window: WindowId, notification: &Notification) -> bool;
pub fn print_files(&self, window: WindowId, paths: &[PathBuf], title: Option<&str>) -> bool;
pub fn guest_control(&self, window: WindowId, control: &GuestHostControl) -> bool;
pub fn show(&self) -> bool;
pub fn hide(&self) -> bool;
pub fn set_visible(&self, visible: bool) -> bool;
pub fn focus_window(&self) -> bool;
pub fn focus_window_with_activation_token(&self, token: Option<&str>) -> bool;
}

The emitter is Clone, Send and Sync. Events go to every window unless sent with a _to variant. Sending waits while a window’s pages haven’t taken earlier events, so producers run at the pace pages keep up with. Pages receive bytes as a Uint8Array.

impl TrayHandle {
pub fn set_icon(&self, path: Option<PathBuf>, template: bool);
pub fn set_tooltip(&self, tooltip: Option<String>);
pub fn set_menu(&self, items: Vec<TrayMenuItem>);
}

Returned by SabineProcess::tray() when the window has a tray icon. See Tray.

pub struct SabineLaunchMetricsSnapshot {
pub label: String,
pub elapsed: Duration,
pub stages: Vec<SabineLaunchMetric>,
}
pub struct SabineLaunchMetric {
pub stage: String,
pub elapsed: Duration,
}

See Architecture for the stages.

pub struct BridgeCommand {
pub name: String,
pub params: serde_json::Value,
pub origin: Option<String>,
pub body: Option<Vec<u8>>,
pub window: Option<u32>,
}
pub struct BridgeResponse {
pub result: serde_json::Value,
pub body: Option<Vec<u8>>,
}
impl BridgeResponse {
pub fn json(result: serde_json::Value) -> Self;
pub fn bytes(body: impl Into<Vec<u8>>) -> Self;
}
pub struct BridgeError {
pub message: String,
}
impl BridgeError {
pub fn new(message: impl Into<String>) -> Self;
}
pub type BridgeResult = Result<BridgeResponse, BridgeError>;

window names the window whose page made the call, for answering it alone with emit_to. BridgeError implements Display and std::error::Error; its message is what the page sees when the call fails.

impl BridgeCommandDescriptor {
pub fn new(name: impl Into<String>) -> Self;
pub fn allowed_origin(self, origin: impl Into<String>) -> Self;
}
impl<T> BridgeEvent<T> {
pub const fn new(name: &'static str) -> Self;
pub const fn name(&self) -> &'static str;
}
pub struct Bytes;
pub struct ContentSecurity {
pub remote_content: bool,
pub allowed_origins: Vec<String>,
}

Declare events as constants and send them through the emitter:

use sabine::{BridgeEvent, Bytes};
const NOTE_SAVED: BridgeEvent<String> = BridgeEvent::new("notes.saved");
const TERMINAL_OUTPUT: BridgeEvent<Bytes> = BridgeEvent::new("terminal.output");

GuestHostControl drives guests from Rust: SabineProcess::guest_control drives the first window’s guests, and the emitter’s guest_control those of the window it names. Controls take effect without an answer; pages observe the results through guest events.

pub enum GuestHostControl {
Create(GuestCreateOptions),
Destroy { id: String },
Navigate { id: String, url: String },
SetBounds { id: String, bounds: GuestBounds },
SetVisible { id: String, visible: bool },
SetCovered { covered: bool },
Focus { id: String },
Reload { id: String, ignore_cache: bool },
GoBack { id: String },
GoForward { id: String },
SetZoom { id: String, factor: f64 },
ExecuteJavaScript { id: String, code: String },
DownloadAction {
download_id: String,
action: GuestDownloadAction,
save_path: Option<String>,
show_dialog: bool,
},
}
pub enum GuestDownloadAction { Accept, Cancel, Pause, Resume }
pub enum GuestPopupPolicy { Deny, NavigateSame, OpenGuest }

GuestCreateOptions holds id, url, html, bounds, partition, allow_bridge (false), intercepted_shortcuts, intercept_horizontal_wheel, visible (true), popup_policy (Deny), allow_downloads (true) and background_color, and implements Default. GuestBounds::new(x, y, width, height) takes CSS pixels relative to the window. See Guests.

pub fn async_runtime() -> &'static tokio::runtime::Handle;

The process’s Tokio runtime, with two worker threads, started on first use. Sabine awaits async bridge handlers and runs its own D-Bus work on it, and apps can spawn their background tasks there too. Synchronous code waits on it with async_runtime().block_on(...); async code never does.

pub enum AppEnvironment { Development, Production }
impl AppEnvironment {
pub fn current() -> Self;
pub fn production_build() -> bool;
pub fn of(app_id: &str) -> Self;
pub fn as_str(self) -> &'static str;
pub fn app_id(self, id: &str) -> String;
pub fn app_data_dir(self, app_id: &str) -> PathBuf;
pub fn data_dir(self, production: impl AsRef<Path>) -> PathBuf;
}
  • current reads SABINE_ENV, and treats a set SABINE_DEV_URL as development. A build made for production, with SABINE_ENV=production while it compiles as sabine bundle does, is always Production.
  • production_build says whether this binary was built for production. Such builds ignore the variables that point a development run at its project.
  • app_id appends .dev in development.
  • app_data_dir is the folder an app keeps its files in: the app id below %APPDATA%, ~/Library/Application Support or $XDG_DATA_HOME, with the .dev id in development. It is the folder Sabine’s app sandbox lets the app write.
  • data_dir keeps an existing production location and puts development data in a development folder below it. Neither creates folders.
use sabine::AppEnvironment;
let notes = AppEnvironment::current()
.app_data_dir("com.example.notes")
.join("notes.json");
pub fn resource_path(name: &str) -> Option<PathBuf>;

Finds a file or folder listed under [resources] in Sabine.toml: in the source project during sabine dev and cargo run, and in the bundle once the app is packaged. It returns None when the name isn’t listed or the path doesn’t exist.

[resources]
server = "../build/server"
let server = sabine::resource_path("server").expect("the server ships with the app");
pub struct RuntimeConfig {
pub mode: RuntimeMode, // SharedPreferred
pub index_url: Option<String>, // the CEF build index to download from
pub allow_user_install: bool, // true
pub allow_bundled: bool, // true
pub bundled_dir: Option<PathBuf>,
}
pub enum RuntimeMode {
SystemRequired, // only a system-wide runtime
SystemPreferred, // system-wide, then the user's, then bundled
SharedPreferred, // the user's, then system-wide, then bundled
Bundled, // only the runtime bundled with the app
}

Within each location, the newest validated runtime wins. Apps rarely need to change this; offline bundles set bundled_dir from the manifest’s folder. See Shared runtime.

pub enum SabineError {
Runtime(RuntimeError),
ManifestRead { path: PathBuf, source: std::io::Error },
ManifestParse { path: PathBuf, source: toml::de::Error },
CreationFailed { message: String },
SetupCancelled,
SetupFailed,
InstanceAlreadyRunning,
}
pub type SabineResult<T> = Result<T, SabineError>;
Variant Means
Runtime No usable Chromium runtime, or a runtime operation failed
ManifestRead, ManifestParse Sabine.toml couldn’t be read or parsed
CreationFailed The configuration is invalid, or a window, host or service step failed; the message says which
SetupCancelled The person closed the setup window
SetupFailed Setup failed; the setup window already showed why
InstanceAlreadyRunning The launch was handed to the running instance

SabineWindow::main handles all of these itself: it exits quietly for SetupCancelled and InstanceAlreadyRunning and shows a notice for the others.

These come from Sabine’s platform layer and are re-exported from sabine.

impl TrayIcon {
pub fn new(id: impl Into<String>, title: impl Into<String>) -> Self;
pub fn icon(self, path: impl Into<PathBuf>) -> Self;
pub fn template(self, template: bool) -> Self;
pub fn tooltip(self, tooltip: impl Into<String>) -> Self;
pub fn menu(self, menu: Vec<TrayMenuItem>) -> Self;
}
impl TrayMenuItem {
pub fn new(id: impl Into<String>, label: impl Into<String>) -> Self;
pub fn separator() -> Self;
pub fn checkbox(id: impl Into<String>, label: impl Into<String>, checked: bool) -> Self;
pub fn submenu(id: impl Into<String>, label: impl Into<String>, items: Vec<TrayMenuItem>) -> Self;
pub fn action(self, action: impl Into<String>) -> Self;
pub fn enabled(self, enabled: bool) -> Self;
}
pub enum TrayMenuItemKind { Normal, Separator, Checkbox { checked: bool }, Submenu(Vec<TrayMenuItem>) }
pub struct TrayActivation {
pub tray_id: String,
pub item_id: Option<String>,
pub action: Option<String>,
pub checked: Option<bool>,
}
pub struct GlobalShortcutRegistration {
pub id: String,
pub shortcut: Shortcut,
pub action: String,
pub description: Option<String>,
}
pub struct Shortcut {
pub modifiers: ShortcutModifiers,
pub key: String,
}
pub struct ShortcutModifiers { pub ctrl: bool, pub alt: bool, pub shift: bool, pub meta: bool }
pub struct GlobalShortcutFailure { pub id: String, pub action: String, pub message: String }

Shortcut::new(key) starts with no modifiers. Keys are a single character, a common name such as Space or F13, or a KeyboardEvent.code value such as MediaPlayPause. See Shortcuts.

impl Notification {
pub fn new(id: impl Into<String>, title: impl Into<String>) -> Self;
pub fn body(self, body: impl Into<String>) -> Self;
pub fn silent(self, silent: bool) -> Self;
pub fn urgency(self, urgency: Urgency) -> Self;
pub fn category(self, category: impl Into<String>) -> Self;
}
pub enum Urgency { Low, Normal, Critical }
pub struct Appearance {
pub dark: bool,
pub accent: Option<[u8; 3]>,
}

Showing a notification with the id of one already shown replaces it. Appearance::accent_hex() returns the accent as a CSS hex color. See Notifications and Appearance and shell.

pub struct AutostartEntry {
pub id: String,
pub name: String,
pub command: String,
pub enabled: bool,
}
pub enum SingleInstancePolicy { AllowMultiple, ReuseExisting, FocusExisting }
pub struct NativeMessagingHost {
pub id: String,
pub name: String,
pub executable: PathBuf,
pub allowed_origins: Vec<String>,
pub allowed_extensions: Vec<String>,
}
pub enum PlatformEvent {
OpenUrls(Vec<String>),
Tray(TrayActivation),
GlobalShortcut(GlobalShortcutActivation),
GlobalShortcutFailed(GlobalShortcutFailure),
SingleInstance(SingleInstanceActivation),
}
pub struct GlobalShortcutActivation {
pub id: String,
pub action: String,
pub activation_token: Option<String>,
}
pub struct SingleInstanceActivation {
pub policy: SingleInstancePolicy,
pub arguments: Vec<String>,
pub working_directory: Option<PathBuf>,
pub activation_token: Option<String>,
}

.on_desktop_event hands app code each PlatformEvent. SingleInstancePolicy::AllowMultiple is the default; .single_instance_id alone doesn’t turn single-instance behavior on. See Autostart, Single instance and Native messaging.

pub enum WindowBackgroundEffect {
None,
Glass,
#[cfg(target_os = "windows")] Blur,
#[cfg(target_os = "windows")] Mica,
#[cfg(target_os = "windows")] MicaAlt,
#[cfg(target_os = "macos")] HudWindow,
#[cfg(target_os = "macos")] Sidebar,
}
impl WindowRegionRect {
pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self;
}
impl WindowRegion {
pub fn empty() -> Self;
pub fn rect(x: i32, y: i32, width: i32, height: i32) -> Self;
pub fn add_rect(self, x: i32, y: i32, width: i32, height: i32) -> Self;
pub fn full() -> Self;
pub fn rounded_rect(radius: i32) -> Self;
pub fn rounded_left(width: i32, radius: i32) -> Self;
pub fn titlebar_and_sidebar(sidebar_width: i32, titlebar_height: i32, radius: i32) -> Self;
pub fn content_after_sidebar(sidebar_width: i32, titlebar_height: i32) -> Self;
pub fn content_after_sidebar_rounded_right(sidebar_width: i32, titlebar_height: i32, radius: i32) -> Self;
}
impl WindowRegions {
pub fn new() -> Self;
pub fn blur(self, region: WindowRegion) -> Self;
pub fn opaque(self, region: WindowRegion) -> Self;
pub fn input(self, region: WindowRegion) -> Self;
}

Every shape except rect follows the window’s size, and their rounded corners turn square while the window is maximized, fullscreen or tiled. The platform-only effects exist only when compiling for that platform, so use them under #[cfg(target_os = "...")].