The SDK is not public yet: it will be published with the system’s source. These pages show how applets are written, so that you know what to expect.
Every built-in application of etyOS — Settings, Files, the Terminal, the dock itself — is a Nirvana applet. Writing one takes a Rust file and an entry in a registry. There is no CSS to write, no markup, no toolkit to learn beyond a set of components.
The idea
An applet does two things:
render: returns a tree of components (a page with groups of rows, a sheet, a text field) that describes what the window shows now.event: is told what the person did (clicked this, typed that) and changes its own state.
After every event Nirvana calls render again and patches the window to
match: what stayed stays (with its focus, its scroll position, the text being
typed), what changed moves.
use nirvana_protocol::prelude::*;
struct Demo {
enabled: bool,
}
impl Applet for Demo {
fn render(&mut self, _window: &Window) -> Node {
Page::new()
.title("Demo")
.child(Group::new().child(
Row::new("Enabled").trailing(Switch::new("enabled", self.enabled).label("Enabled")),
))
.into()
}
fn event(&mut self, event: &Event, _commands: &mut Commands) {
if event.node_id == "enabled" {
self.enabled = event.flag().unwrap_or(self.enabled);
}
}
}
fn main() {
run(|_handle| Demo { enabled: false });
}
That is a whole applet. How the switch looks, how the row is spaced, what the window does in dark appearance, how it animates: none of it is the applet’s to say. It says what; Nirvana owns how (see The design code).
The parts
┌────────────── one applet, running ──────────────┐
│ │
│ your applet (Rust) the runtime │
│ ┌──────────────┐ JSON ┌────────────────┐ │
│ │ render() │ ───────▶ │ main process │ │
│ │ event() │ ◀─────── │ (validates) │ │
│ └──────────────┘ stdio └───────┬────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ renderer: UI │ │
│ │ kit, patching │ │
│ └────────────────┘ │
└─────────────────────────────────────────────────┘
| Part | What it does |
|---|---|
| The SDK | The Rust crate applets depend on, nirvana-protocol: the components, the event loop, translation, and helpers for preferences, installed applications, the machine and the compositor. |
| The runtime | Starts the applet’s program, passes messages between it and the window, checks every one of them, opens the windows. |
| The UI kit | Turns a scene into the window: components, icons, design tokens, and the patcher that keeps a window alive between scenes. |
| The registry | The list of applets that may run, and how the window of each is made. |
Each running applet is its own Nirvana process, under the Wayland app id
etyde-<id>. Starting an applet that is already open brings its window
forward.
What an applet cannot do, on purpose
- No styling. A scene carries no CSS, no colors, no sizes in pixels.
Spacing, color and type are chosen from named tokens (
Gap::Sm,Tone::Secondary,TextStyle::Headline). - No markup. A scene is data. Text is always text.
- No unknown fields. The runtime checks each scene against a list of what every component accepts. A field it does not know is refused, so that the Rust crate and the runtime cannot drift apart quietly.
In exchange an applet gets what is hard to do by hand: one look across the system, light and dark, keyboard access, focus rings, motion, translations, glass that the compositor blurs.
The applet’s process is an ordinary program of the user: it reads files, runs tools and talks to D-Bus as any program does. The window is only what shows its scene: nothing in a scene can make it run anything.
What it is not for
Nirvana is for the system’s own applications: settings, tools, shell surfaces. It is not a general application toolkit, and an applet has to be in the registry of the system it runs on. Other applications are ordinary Wayland (or X11) programs, and run as on any Linux.
Next
- Your first applet: from an empty folder to a running window.
- Render and events: the loop in detail.
- Components: everything a scene can hold.