Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Augani/kael/llms.txt

Use this file to discover all available pages before exploring further.

Kael exposes a unified platform API through the App context so your application can integrate deeply with the host operating system without writing platform-specific code yourself. From a system tray icon with a full context menu to Touch ID / Windows Hello authentication, every capability follows the same pattern: configure a struct, call a method on cx, and register a callback.

System tray

1

Set the tray icon

Pass raw PNG bytes to cx.set_tray_icon. Pass None to remove the icon.
use kael::prelude::*;

cx.set_tray_icon(Some(include_bytes!("../assets/tray.png")));
2

Build the tray menu

Construct a Vec<TrayMenuItem> using the four variants below and hand it to cx.set_tray_menu.
use kael::{TrayMenuItem};

cx.set_tray_menu(vec![
    TrayMenuItem::Action {
        label: "Open Window".into(),
        id:    "open".into(),
    },
    TrayMenuItem::Toggle {
        label:   "Enable Notifications".into(),
        checked: true,
        id:      "notifications".into(),
    },
    TrayMenuItem::Submenu {
        label: "Theme".into(),
        items: vec![
            TrayMenuItem::Action { label: "Light".into(), id: "theme-light".into() },
            TrayMenuItem::Action { label: "Dark".into(),  id: "theme-dark".into()  },
        ],
    },
    TrayMenuItem::Separator,
    TrayMenuItem::Action {
        label: "Quit".into(),
        id:    "quit".into(),
    },
]);
VariantFieldsDescription
Actionlabel, idA clickable item. id is returned in the action callback.
SeparatorA visual divider.
Submenulabel, itemsA nested menu with its own Vec<TrayMenuItem>.
Togglelabel, checked, idA checkmark item whose state you manage.
3

Handle menu actions

Register cx.on_tray_menu_action to receive the id of whichever item the user clicks.
cx.on_tray_menu_action(|id, cx| match id.as_ref() {
    "open"  => { /* open your main window */ }
    "quit"  => cx.quit(),
    _       => {}
});
4

React to tray icon clicks

cx.on_tray_icon_event fires on left-click, right-click, and double-click.
use kael::TrayIconEvent;

cx.on_tray_icon_event(|event, cx| {
    if event == TrayIconEvent::LeftClick {
        // show a panel centered on the tray icon
        if let Some(bounds) = cx.tray_icon_bounds() {
            // use bounds to position your window
            let _ = bounds;
        }
    }
});
Call cx.set_tray_panel_mode(true) to receive TrayIconEvent::LeftClick instead of showing the native menu on left-click. This is useful if you want to display a custom panel rather than a system context menu.

Global hotkeys

Register a hotkey with an integer ID and a Keystroke, then listen for presses with cx.on_global_hotkey.
use kael::Keystroke;

// Register Cmd/Ctrl+Shift+Space as hotkey ID 1
let keystroke = Keystroke::parse("cmd-shift-space").unwrap();
cx.register_global_hotkey(1, &keystroke).unwrap();

cx.on_global_hotkey(|id| {
    if id == 1 {
        // toggle your command palette
    }
});

// Release events are available separately
cx.on_global_hotkey_up(|id| {
    if id == 1 {
        // handle key release
    }
});
To unregister a hotkey — for example, when the user reconfigures it:
cx.unregister_global_hotkey(1);
Global hotkeys are system-wide and will intercept keystrokes even when your app is in the background. Always provide a way for the user to disable or reconfigure them.

Native file and save dialogs

cx.prompt_for_paths and cx.prompt_for_new_path open the native file picker. Both are async and return via a oneshot::Receiver.
use kael::PathPromptOptions;

// Open dialog — pick one or more files
cx.prompt_for_paths(
    PathPromptOptions {
        files:       true,
        directories: false,
        multiple:    true,
        prompt:      Some("Choose files to import".into()),
    },
    cx.to_async(),
    |paths, _cx| {
        if let Some(paths) = paths {
            for path in paths {
                // handle each selected path
                let _ = path;
            }
        }
    },
);

// Save dialog — choose a destination path
cx.prompt_for_new_path(
    &std::path::PathBuf::from("."),
    |path, _cx| {
        if let Some(path) = path {
            // write to `path`
            let _ = path;
        }
    },
);
PathPromptOptions fields:
FieldTypeDescription
filesboolAllow file selection.
directoriesboolAllow directory selection.
multipleboolAllow selecting more than one item.
promptOption<SharedString>Label shown in the dialog title bar.

Desktop notifications

Send a simple notification:
cx.show_notification("Build complete", "Your project compiled successfully.").unwrap();
Add action buttons with NotificationAction:
use kael::NotificationAction;

cx.show_notification_with_actions(
    "New message",
    "You have an unread message from Alice.",
    &[
        NotificationAction { id: "reply".into(),   label: "Reply".into()   },
        NotificationAction { id: "dismiss".into(), label: "Dismiss".into() },
    ],
    |action_id| match action_id.as_str() {
        "reply"   => { /* open reply view */ }
        "dismiss" => {}
        _         => {}
    },
)
.unwrap();
NotificationAction has two fields:
FieldTypeDescription
idStringReturned in the callback when the user clicks this button.
labelStringButton text displayed in the notification.
Showing notifications requires the Notification capability. The call returns Err if the capability has been denied.

Printing

Build a PrintJob, attach one or more PrintPage values, then either print silently or show the native print dialog.
use kael::{PrintJob, PrintPage, PrintOrientation, px};

let job = PrintJob::new("Invoice #1042")
    .orientation(PrintOrientation::Portrait)
    .page(PrintPage::new(size(px(612.), px(792.)), |ctx, _cx| {
        // use ctx to issue drawing commands for this page
        let _ = ctx;
    }));

// Show the native print dialog
window.show_print_dialog(job, cx).unwrap();

// Or print directly without a dialog
// window.print(job, cx).unwrap();
All pages in a single PrintJob must use the same page size. PrintJob::new returns an error if you try to mix sizes.

Biometric authentication

Check whether Touch ID, Windows Hello, or a fingerprint reader is available before attempting authentication:
use kael::{BiometricStatus, BiometricKind};

match cx.biometric_status() {
    BiometricStatus::Available(BiometricKind::TouchId) => {
        cx.authenticate_biometric("Authenticate to unlock secrets", |success| {
            if success {
                // grant access
            }
        });
    }
    BiometricStatus::Available(BiometricKind::WindowsHello) => {
        cx.authenticate_biometric("Verify your identity", |success| {
            let _ = success;
        });
    }
    BiometricStatus::Available(BiometricKind::Fingerprint) => {
        cx.authenticate_biometric("Use your fingerprint to continue", |success| {
            let _ = success;
        });
    }
    BiometricStatus::Unavailable => {
        // fall back to a password prompt
    }
}
BiometricStatusMeaning
Available(BiometricKind::TouchId)macOS Touch ID is enrolled and available.
Available(BiometricKind::WindowsHello)Windows Hello is configured.
Available(BiometricKind::Fingerprint)A generic fingerprint reader is available.
UnavailableNo biometric hardware or it is not enrolled.

Auto-launch at login

// Enable launch at login
cx.set_auto_launch("com.example.myapp", true).unwrap();

// Disable launch at login
cx.set_auto_launch("com.example.myapp", false).unwrap();

// Query the current state
let enabled = cx.is_auto_launch_enabled("com.example.myapp");
Pass your app’s bundle identifier on macOS or the app name on Windows and Linux.

Single instance enforcement

Kael’s platform::single_instance module uses a Unix domain socket (macOS / Linux) or a named mutex (Windows) to detect whether another instance is already running.
use kael::platform::single_instance;

match single_instance::acquire("com.example.myapp") {
    Ok(guard) => {
        // We are the primary instance. `guard` must be kept alive for
        // the lifetime of the process.
        Application::new().run(move |cx| {
            let _guard = guard;
            // start your app
        });
    }
    Err(_) => {
        // Another instance is running. Notify it and exit.
        eprintln!("Another instance is already running.");
        std::process::exit(0);
    }
}

Dock and taskbar control

// Set the badge label on the macOS dock icon (or taskbar overlay on Windows)
cx.set_dock_badge(Some("3"));
cx.set_dock_badge(None); // clear the badge

// Request user attention (bounces the dock icon on macOS)
use kael::AttentionType;
cx.request_user_attention(AttentionType::Informational);
cx.cancel_user_attention();

// Set a taskbar/dock progress bar
use kael::ProgressBarState;
window.set_progress_bar(ProgressBarState::Normal(0.75));
window.set_progress_bar(ProgressBarState::None);

Build docs developers (and LLMs) love