C+
Packages · View as Markdown
v0.0.27 is a macOS / AppKit release. That is the supported path. Other platforms are not recommended; wait for a later version.

facet

facet describes user interfaces. It does not draw them: a backend does that. On macOS the backend is facet_appkit; on Linux, facet_gtk. The tree itself is a flex_layout Node. There is no VDOM and no re-render: a handler finds the keyed node that changed and writes it.

A first tree

Bare element names resolve through facet/elements. key is an address: find, the agent surface, and the platform accessibility identifier all use it.

import "facet/facet" as core;
import "facet/elements" as ui;

fn body() -> core::Node {
    return @ui {
        vstack(key: "body") {
            label("hello", key: "greeting")
            button("click me", key: "go")
        }
    };
}

The { after vstack(key: "body") must be on the same line. See Builder blocks.

Component, lifecycle, screen

State lives in a struct. Component supplies the tree. Lifecycle hooks fire for the component (never by it): on_attach after mount, on_detach before teardown. Screen names the window.

import "facet/component" as component;
import "facet/screen" as screen;
import "stdlib/vec" as vec;

struct Counter { clicks: i64 }

impl Counter: component::Component {
    fn build(ref this) -> core::Node { return body(); }
}

impl Counter: component::Lifecycle {
    fn on_attach(ref this) { return; }
    fn on_detach(ref this) { return; }
}

impl Counter: screen::Screen {
    fn chrome(this) -> screen::Chrome {
        return screen::Chrome::new(title: "hello", width: 360.0f64, height: 200.0f64);
    }
    fn menu_items(this) -> vec::Vec[screen::MenuItem] {
        return vec::new::[screen::MenuItem]();
    }
}

fn counter_screen() -> screen::ScreenBox {
    return screen::screen_box(Counter { clicks: 0 as i64 });
}

A handler is fn(sender: *u8, ctx: *u8). Pass a bound method (on_click: this.bump) or a free function plus on_click_ctx. The handler finds the one label that changed and writes it. Nothing else in the tree is visited.

Run it

import "facet/runtime" as runtime;
import "stdlib/status" as status;
import "facet_appkit/facet_appkit" as backend;

fn main() -> i32 {
    backend::install();
    var app: runtime::App = runtime::App::new("hello");
    app.screen("counter", counter_screen);
    match app.run("counter") {
        status::Status::Ok => { return 0; }
        _other => { return 1; }
    }
}

App is the process tier: named screen routes, an optional menu-bar builder, on_launch / on_quit, and app.run(initial). nav::go / push / pop / quit replace or stack screens. mount::set_content fills a keyed outlet with a component's tree. mount::switch_to parks siblings (Display::None) so view state survives a tab switch.

A target with no backend still compiles: facet's verbs become no-ops that say so once on stderr.

Theme

Colours resolve through roles. Tier 1 is the platform's semantic colours (Color::text(), accent(), window_background(), …). Tier 2 is app-retintable (primary / on_primary, surface, ink(a), success / warning / danger, …). Color::adaptive(light:, dark:) is a light/dark pair resolved at paint time. set_theme re-themes the live app.

import "facet/theme" as theme;
import "facet/vocabulary" as vocab;

theme::set_theme(theme::Theme::new(
    primary: vocab::Color::rgba(0.30f64, 0.42f64, 0.85f64, 1.0f64),
));

Color::text() / text_secondary() / text_tertiary() are the label tiers. primary is the brand role.

Services, jobs, resources, agent

after / Cancellable, run_on_main, spawn_ui, run_on_worker, and run_job cover timers, main-thread dispatch, and off-thread work. block_on in a handler blocks the loop. Resources are REST verbs (get / post / put / delete) plus watch over a shared store.

To serve the tree to an external agent: facet/agent::enable() before app.run, then app.agent_mcp(path). For an in-process assistant, facet/agent::in_app(). See Agent surface.

The declared verb list lives in the package's docs/contract.md. A verb absent there does not exist.