WolOS

Blog · · WolOS

Cryswol and Wolkit: WolOS apps in Crystal

WolOS now has a language for its apps: Cryswol reaches every system verb from Crystal, and Wolkit draws windows from views and signals. What works today, what is in review, and what is not done yet.

WolOS has had a stable app interface for a while, libwol, but no language of its own to write apps in. That is what Cryswol is for. This post says what it is, what you can do with it in WolOS 0.12, what is in review for the next build, and what is not done yet. The Cryswol page has the tour; the manual has the reference.

What it is

Cryswol is a Crystal library that gives an app every WolOS system verb: windows, notifications, the clipboard, preferences, file panels, power, network, sound, Search, messages between apps, devices, sharing and background services. An app writes require "cryswol" and gets Crystal objects, blocks and exceptions instead of pointers and error codes. A failed call raises an error that carries the system's own sentence, written for a person. Cryswol adds no privilege: the system still checks every permission.

Wolkit is the UI kit on top. You describe a window once, keep state in signals, and when a signal changes only the parts that read it are sent to the engine. Here is a whole to-do app, step 5 of the tutorial:

require "wolkit"

class Todos < Wolkit::App
  @items = list(String)
  @draft = signal("")

  def view
    window "Todos", width: 420, height: 560 do
      column gap: :md, pad: :lg do
        row gap: :sm do
          text_field @draft, placeholder: "What needs doing?", grow: 1, on_submit: -> { add }
          button "Add", primary: true, disabled: @draft.blank?, on_click: -> { add }
        end
        each @items, key: ->(t : String) { t } do |todo|
          row { text todo, grow: 1; icon_button :trash, label: "Delete #{todo}", on_click: -> { @items.delete(todo) } }
        end
        text "#{@items.size} left", role: :caption
      end
    end
  end

  def add
    @items << @draft.value.strip unless @draft.blank?
    @draft.value = ""
  end
end

Wolkit.run Todos

There is no colour, pixel or markup in it. Spacing and type are names from the design scale, so a size off the scale is a compile error, and colours are theme tokens, so dark mode, high contrast and your accent colour follow with no code. Leave the label off an icon button and the compiler stops at your line, because screen readers need it. Every program in the tutorial is type-checked against the kit by a script in the WolOS repository, so the tutorial cannot quietly go out of date.

Under the kit is libWolkit, an engine written in Rust that owns the scene, style, layout, text, paint and the accessibility tree. The app and the engine talk through two binary streams, so a click that changes one label costs one small patch. On the build machine an interaction allocated 285 bytes and an idle app allocated nothing at all; the method and every number are in Wolkit benchmarks.

What works today, in 0.12

  • Cryswol and libwol ship as packages. The base image does not carry a compiler: install the Developer module (wol modules install dev, or tick Developer Tools in the installer) and crystal, libwol and the Cryswol shard are there. See Cryswol.
  • Wolkit, as a preview. The engine, the Crystal shard, the style language and the headless test harness. A test starts the real engine with no display, clicks and types, and takes a screenshot that is the same on every machine.
  • The tutorial, Your first Wolkit app: a to-do list in eight short steps.

In review for the next build

These are written and tested, and waiting to be merged. Their manual pages are already up, so you can read what is coming.

  • wol shard and one signed list of Crystal libraries, each pinned to a full commit. wol shard add writes the pin and the lock for you, and a package build never fetches anything. See Crystal shards.
  • wol app new, run and check with four templates: app, cli, tool and ai. A new project builds and passes its own checks the moment it exists, and wol app run --watch restyles a running window when you save a sheet.
  • One pinned compiler and an advisories list. A security advisory for the compiler, its collector or a shard fails the rules until every program that compiles Crystal is rebuilt. See Crystal rules.
  • Gallery, the picture library, as the first bundled app on Wolkit: by date or by place, a viewer with zoom and a filmstrip, a slideshow, and Delete with Undo. Every state has a golden screenshot. See Gallery.
  • Ruby scripts, checked and run exactly like shell scripts with wol script. See Scripts.

What is not done yet

Two things, said plainly:

  • On-screen presentation. Wolkit apps are built and tested in the headless engine. The engine's own window becomes the default only after it passes a parity gate: screen reader, input method, clipboard and drag and drop, the performance budgets, the look, fuzzing and an audit. Until then, apps that must be on screen keep the current path.
  • Images. The engine keeps pictures but does not decode or paint them yet. That is why Gallery's screenshots show empty frames where the pictures go. The app already names every picture, so they appear when the paint stage lands, with no change to Gallery.

After those come drawing helpers for canvas and GPU surfaces, and more bundled apps: documents, music and notes.

Try it

Install the Developer module, open Your first Wolkit app, and tell us what you would build by opening an issue on the WolOS repository. The decisions behind all of this are Crystal is the userland language and libWolkit and Wolkit.

All posts · RSS