oriel

Modules

What an Oriel module is, how build flags turn modules on, and every module and plugin with its flag, platforms and page.

A module is a piece of Oriel an app switches on in build.zig: the tray, SQLite, notifications, a local LLM. Each one is a Zig namespace on oriel (oriel.tray, oriel.sql) with one public API, and a backend per platform underneath. A module the app leaves off is neither compiled nor linked, so an app pays only for what it uses. Plugins (global shortcuts, input injection, the clipboard) work the same way; they live in src/plugins/ instead of src/modules/ because they reach outside the app’s own window.

The core is always there and has no flag: windows, commands and events, the lifecycle, paths, logging, permissions, platform hooks and app extensions. This page is the index; each module’s API, examples and platform notes are on its own page, linked from the table.

Turning modules on

Flags are options of the oriel dependency in the app’s build.zig:

const dep = b.dependency("oriel", .{
    .target = target,
    .optimize = optimize,
    .sql = true,          // on by default; written out for clarity
    .tray = true,
    .llama = true,        // off by default: a native dependency
    .media_server = false, // on by default; this app doesn't use it
});
  • Defaults. Every module and plugin is on unless the app says otherwise, except the native dependencies and a few opt-ins: deep_link, sqlite_vec, llama, llama_mtmd, whisper, kokoro, speakers, audio_capture, camera, bluetooth, system_ai, layer_shell and native_ui are off. updater is also off on Android (the store updates apps there).
  • oriel init writes the list out. The generated build.zig sets the desktop modules to false, so a new app starts small; change a line to true to use that module.
  • Declaring a capability turns its module on. .permissions.camera turns on camera, .permissions.bluetooth turns on bluetooth, .permissions.local_network turns on network, and .share_target turns on share, so there is no flag to keep in sync (unless the app set that flag to false itself: then the module stays off and the build prints a warning). The other way round, a module that needs a permission declares it: audio_capture declares microphone and system_audio.
  • Dependencies between flags are checked when the build starts: sqlite_vec needs sql, llama_mtmd needs llama, and kokoro and speakers need llama or whisper (they share their ggml build). Some namespaces follow another flag instead of having one: oriel.chat is on with llama, oriel.tts and oriel.audio_play with kokoro, oriel.dictation with whisper and audio_capture together, oriel.ggml_gpu with llama or whisper, and oriel.canvas with native_ui.
  • On Oriel’s own command line (building Oriel itself, its examples or tests) the same flags are -D options: zig build -Dllama, -Dtray=false. Apps pass them through too: oriel build -Dnative_ui.

Availability

The flags say what the app uses, once, for every target. Where a module can’t exist (a tray on a phone, system-wide hotkeys on iOS), its flag resolves to off for that target and oriel.<module>.available is false. Guard the calls:

if (oriel.tray.available) {
    tray = try oriel.tray.Tray.create(gpa, .{ ... });
}
  • available is a comptime constant. Code under a false guard is never analyzed: it costs nothing where the module is missing, and needs no stub.
  • An unguarded call is a build error that names the module and the fix: oriel.tray.Tray is not available on ios: guard the call with if (oriel.tray.available), or, for a module the app left off, ... is disabled: enable it with .tray = true in b.dependency("oriel", ...), or guard the call with if (oriel.tray.available).
  • Runtime state stays runtime. A desktop with no tray host, a phone without Gemini Nano, a denied permission: these are the module’s own status calls or errors, documented on its page.
  • The page sees the same table as window.oriel.features, frozen, in camelCase: { tray: false, fsWatch: true, mediaServer: false, ... }, typed as Features in the generated oriel.ts. A UI hides what this build can’t do.
  • Asking for the impossible on the command line fails. zig build -Dtarget=aarch64-ios -Dtray=true stops with -Dtray is not available on ios. From an app’s b.dependency the same request quietly resolves to off (zig build --verbose lists what was turned off), because the app’s flags are meant for every target.

How modules work

flowchart LR
  B["App build.zig<br/>.tray = true"] -->|flags + target| F["Features.fromOptions<br/>(Oriel's build.zig)"]
  T["module_table.zig<br/>missingOn(platform)"] -->|supported?| F
  F -->|build_options| O["src/oriel.zig"]
  O -->|on| M["src/modules/tray.zig<br/>available = true"]
  O -->|off or missing| S["generated shim<br/>available = false"]
  M -->|switch on target.os| L["tray/linux.zig"]
  M --> W["tray/windows.zig"]
  M --> C["tray/macos.zig"]
  F -->|feature_names| J["features.zig"]
  J -->|window.oriel.features| P["Page"]
  M -->|check| K["oriel.checkAll<br/>(examples/smoke)"]
  1. Resolving the flags. Oriel’s build.zig has one Features struct with a field per flag. Features.fromOptions reads each option from the app’s b.dependency("oriel", ...) (or the command line), applies the defaults, and asks src/core/module_table.zig whether the module exists on the target’s platform (missingOn, with Android split out of Linux). The result goes into the build_options module, along with feature_names.
  2. Choosing the namespace. src/oriel.zig declares each module as pub const tray = if (options.tray) @import("modules/tray.zig") else unavailable("tray"). Off, the namespace is a generated shim (tools/module_shims.zig) with the same public declarations as the real module, each a build error carrying module_table.reason, and available = false. A test keeps the shim’s declarations equal to the module’s.
  3. Choosing the backend. The module’s facade (src/modules/tray.zig) re-exports the documented API, holds the shared types (tray/common.zig) and picks its implementation with a switch on target.os (kept private: oriel.tray. shows only the API): tray/linux.zig, tray/windows.zig, tray/macos.zig. Some backends serve several platforms (fs_watch uses inotify on Linux and Android; store shares its Apple backend between macOS and iOS); a module that doesn’t exist on a platform has a @compileError there, which the shim keeps out of reach.
  4. Linking. The same flags decide what Oriel’s build.zig links: libpulse for audio_capture on Linux, xkbcommon and XTest for input, llama.cpp and whisper.cpp for llama and whisper, PhotosUI for camera on iOS.
  5. Telling the page. src/core/features.zig turns the resolved flags into the window.oriel.features object the bridge script (src/core/bridge_js.zig) defines, the same JSON for the native renderer, and the Features TypeScript interface.
  6. Checking it works. Most modules have a check(gpa, ctx) smoke test in each backend. oriel.checkAll(gpa, ctx) runs the permission check and then the check of every enabled module, returning []oriel.Check ({ module, ok, detail }); examples/smoke shows the results in a window or prints them with --check. oriel doctor is the build machine’s side: on Linux it reports the libraries the plugins link (wayland-client, xkbcommon, XTest, X11), whether a tray host and the GlobalShortcuts portal are present.

Every module

✓ works; — not on this platform (available is false); a note where it works differently. Default is the flag’s value when the app doesn’t set it.

ModuleWhat it’s forFlag (default)LinuxWindowsmacOSAndroidiOSPage
oriel.traySystem tray icon with a menutray (on)✓✓✓——Tray
oriel.menuThe window’s menu barmenu (on)✓✓✓——Menu
oriel.dialogOpen, save and folder dialogsdialog (on)✓✓✓✓ SAF✓ document pickerDialogs
oriel.notificationSystem notificationsnotification (on)✓✓✓✓✓Notifications
oriel.storePersistent key-value settingsstore (on)✓✓✓✓✓Store
oriel.sqlEmbedded SQLitesql (on)✓✓✓✓✓SQL
oriel.sqlite_vecVector search in SQLite (sqlite-vec)sqlite_vec (off; needs sql)✓✓✓✓✓SQL
oriel.fs_watchWatching directories for changesfs_watch (on)✓✓✓✓—File watching
oriel.deep_linkOpening the app from its own URL schemesdeep_link (off)✓✓✓✓✓Deep links
oriel.networkMulticast lock and mDNS/DNS-SDnetwork (on; also on with .permissions.local_network)multicast; no mDNS yet✓ (mDNS: Windows 10 1809+)multicast; no mDNS yet✓multicast; no mDNS yetNetwork
oriel.bluetoothBluetooth LE advertisingbluetooth (off; on with .permissions.bluetooth)✓ BlueZno device nameno service data✓no service dataBluetooth
oriel.shareThe share sheet, and receiving sharesshare (on; also on with .share_target)not yet (error.Unsupported)✓✓✓✓Share
oriel.cameraPhotos from the camera or librarycamera (off; on with .permissions.camera)library onlylibrary onlylibrary only✓✓Camera
oriel.media_serverStreaming large local media with range requestsmedia_server (on)✓✓✓——Media server
oriel.updaterSigned self-updatesupdater (on; off on Android)✓✓✓check only—Updater
oriel.global_shortcutSystem-wide hotkeysglobal_shortcut (on)✓✓✓in-app shortcuts—Global shortcuts
oriel.inputTyping and pasting into other appsinput (on)✓✓✓——Input
oriel.clipboardText and image clipboardclipboard (on)✓✓✓✓✓Clipboard
oriel.chatChat with a local LLM (llama.cpp)llama (off)✓✓✓✓✓Chat
oriel.llamallama.cpp’s C API, JSON-schema grammarsllama (off); images with llama_mtmd (off)✓✓✓✓✓Local AI
oriel.dictationVoice to text, live and from fileswhisper + audio_capture (off)✓✓✓✓ (also the system recognizer)✓ (also the system recognizer)Dictation
oriel.whisperwhisper.cppwhisper (off)✓✓✓✓✓Local AI
oriel.audio_captureMicrophone and system audio as samplesaudio_capture (off)✓✓✓microphonesmicrophonesLocal AI
oriel.ttsOffline text to speech (Kokoro-82M)kokoro (off; needs llama or whisper)✓✓✓✓✓Text to speech
oriel.kokoro, oriel.audio_playSynthesis and playback under oriel.ttskokoro (off)✓✓✓✓✓Text to speech
oriel.speakersSpeaker embeddings, who spoke when (voice-detect.cpp)speakers (off; needs llama or whisper)✓✓✓——Speakers
oriel.ggml_gpuGPU backends for llama and whisper: CUDA, Vulkan; OpenCL and Hexagon on Androidllama or whisper✓✓✓✓✓Local AI
oriel.system_aiThe phone’s own model (Gemini Nano, Foundation Models)system_ai (off)———✓✓ iOS 26System AI
oriel.canvasDrawing a page’s <canvas> from Zignative_ui (off)✓✓✓✓✓Native renderer
(renderer)Drawing the page with native views instead of a WebView, experimentalnative_ui (off)✓✓✓✓✓Native renderer
(windows)Wayland overlay windows as layer surfaces (gtk4-layer-shell)layer_shell (off)✓ Wayland————Windows

The always-on core, for completeness (no flag; oriel.hooks.available is the one with platform limits):

NamespaceWhat it’s forPage
oriel.AppWindows, commands, events, the app’s run loopCommands, Windows
oriel.lifecycleBackground, foreground, memory pressure, sleep, terminatingLifecycle
oriel.permissionsDeclared OS permissions, queried and requestedPermissions
oriel.pathsThe app’s data and cache directoriesPaths
oriel.logLogs to stderr and a fileLogging
oriel.security, oriel.isolationCSP, allowed origins, capabilities, the isolation hookSecurity
oriel.hooksPlatform hooks in Zig (Linux, Windows, macOS)Platform hooks
oriel.extensionsThe app’s own native code, called by nameApp extensions

Related

Search documentation

Type to search the documentation.