Code

Theme Reference

This page describes the theme package format, one part after the other. Use it next to the tutorial in Make a Theme. Every name on this page comes from the validator (aivory-theme), the desktop renderer or the JSON Schema of the AIVory Code repository.

  • One folder of TOML, JSON, Markdown, fonts and images. No code
  • The validator checks the folder with aivoryd theme dev, theme check and theme pack
  • Catalog version 1: 45 components, 14 data roots, 13 actions
  • Desktop first: the desktop app is the only client that draws a theme today. See What is not finished

Folder Layout

m y - t t s l p a a p R t h o t a a s s r E h e k r y d p f w r s s e A e m e i o e h o a t e e v D m e n n u s o l t s t t i M e . s g t k n d c / s s e E t . s s t e h < / / w . o t / o / n f i s m m e p a o m / d l m n / m n a l . e t g t > s e L o . / s I m j / C l s E o N n S E m t t 8 o o o l . . p o a o e p p p a t p r p n k x s t t t y t n e t i e t c i i i o f g v i f n s r o o o u , i o e e n n n t a e n s f f e a a a n . w a t i o n l l l p d j l l r s , a p i ( e r g m r $ ( w t o , a e ( s r a s t g q n t e r f . e u a r q r , w s i m i u o o e , r e n i o p p b e g r m t l p f d i s e i u , o ) s . d a o s r k ) n n s s e d a a t e e y l a x t d ) L t a e I i m b o c C c p y p i E l t s N . e " i i S s t o o E v d o n n g e k a f , s e l o i k n , n l p t s l e l o " t y u p h s . i e p n n a n g t e L h n I ( e C 1 m i E 4 e s N 4 . S 0 t r E x o e 9 m q f 0 l u i 0 ) i l ) r e e d )
Path Rule
theme.toml Required.
layouts/<class>/<screen>.json <class> is desktop, phone, fold or watch. The class must be in displays.
layouts/desktop/ All 8 screens are required: warroom, session, inbox, tasks, repos, settings, focus, decision.
layouts/watch/ Only warroom and decision.
layouts/phone/, layouts/fold/ The 8 screen names. A class that is in displays needs at least one file.
parts/<name>.json The name uses [a-z0-9_-]. A part never refers to itself, also not through other parts.
strings/<lang>.toml <lang> is a language tag such as en or pt-BR. If one file exists, strings/en.toml is required.
*.json Only in layouts/ and parts/.
Fonts Only in assets/fonts/.
Images Only in assets/images/ and previews/.
theme.lock, <file>.upstream The daemon writes them when you add or update a theme. Do not make them.

Files and folders whose name starts with a dot are not part of a theme. The unpacker leaves them out.


The Manifest: theme.toml

The manifest accepts only the keys below. An unknown key is an error.

Key Type Required Default Rule
id string yes 3 to 40 characters of [a-z0-9-]. It does not start or end with -.
name string yes 1 to 80 characters. Shown in Settings.
description string no "" 500 characters or fewer.
version string yes SemVer x.y.z, for example 1.0.0 or 1.0.0-rc1. With --tag, it must equal the tag without the v.
author.name string yes 1 to 80 characters. Write it as the table [author].
author.url string no none Starts with https://.
license string yes An SPDX expression such as MIT or MIT OR Apache-2.0. The validator allows letters, digits, ., -, +, spaces and parentheses, up to 120 characters.
min_app string yes SemVer. The theme is rejected by an app that is older.
catalog integer yes The catalog version that the theme needs. The only version is 1.
displays list yes Any of desktop, phone, fold, watch. It must contain desktop. No duplicates.
tokens list of strings no ["tokens.toml"] 1 to 8 relative .toml files. A later file overrides keys of an earlier file. Tables merge key by key.
id = "harbor"
name = "Harbor"
description = "A calm dark theme with one decision at a time."
version = "0.1.0"
license = "MIT"
min_app = "2026.1.0"
catalog = 1
displays = ["desktop"]
tokens = ["tokens.toml"]

[author]
name = "Jane Doe"
url = "https://github.com/janedoe"

Tokens

A token file is TOML with the tables below. Layouts use token names, not raw values. The tables accept only the keys that are listed here. Unknown keys are an error.

colors

A color is #rrggbb or #rrggbbaa. All of these are required.

Token Use
colors.bg Page background.
colors.surface Background of panels, cards and buttons.
colors.text Text.
colors.muted Secondary text and hairlines.
colors.accent Primary buttons, active tabs and the active nav item.
colors.analysis The analysis panel and the second color of segments charts.
colors.state.needsYou A session that waits for you.
colors.state.working A session that works.
colors.state.idle An idle session.
colors.state.parked A parked session.
colors.state.error A session with an error.
colors.state.done A finished session.

In a layout you write these names in props such as color, bg, border and tone: bg, surface, text, muted, accent, analysis, state.needsYou or only needsYou.

The app also checks 12 pairs for contrast (for example text on bg and surface, 4.5:1; muted, 3:1). A pair that fails keeps the value of Minimal, and the app notes it. The theme still loads.

fonts

Token Type Required Value
fonts.ui string yes sans, serif, mono, or the path of a bundled font file, for example assets/fonts/Inter.ttf.
fonts.mono string yes The same values.

serif is not bundled in the desktop app. It keeps the sans face and logs a note.

type

The five text sizes in px. Each is required and must be 6 to 72. Components pick one with the prop size. The app scales them with the interface text size.

Token Typical value
type.xs 11
type.sm 12 to 13
type.md 14 to 15
type.lg 18 to 20
type.xl 24 to 28

space and radii

Both are open tables: you choose the names. A value is a number from 0 to 200.

Table Used by Notes
space.<name> The props gap and pad. "pad": "edge" reads space.edge. A number works too. An unknown name gives the default.
radii.<name> The prop radius. "radius": "card" reads radii.card. A number works too.

The names radii.button, radii.card, radii.tile, radii.pane and radii.rail_item also set the corners of the screens that the app draws itself. The app caps them at 48.

motion and effects

Token Type Range Desktop app today
motion.easing string linear, ease, easeIn, easeOut, easeInOut Not read yet
motion.durations.<name> integer, ms 0 to 5000 Not read yet
effects.glass_alpha number 0 to 1 Used by panel with variant: "glass" (default 0.6)
effects.glow number 0 to 1 Not read yet
effects.scanlines number 0 to 1 Not read yet
effects.blur number 0 to 40 Not read yet

motion has only easing and the table durations. A key such as fast_ms directly in [motion] is an error. Write [motion.durations] and fast = 120.


Strings

strings/<lang>.toml holds texts. A layout reads one as $strings.key. A nested table gives a dotted key: [nav] with home = "Home" is $strings.nav.home.

Rule Value
Key characters [A-Za-z0-9_]
Value A string of 200 characters or fewer, or a table.
Required file strings/en.toml, when any strings file exists.
Every language A key that a layout uses must exist in every language file.

The desktop app reads strings/en.toml today. It does not pick a language by locale yet. A missing key shows the key itself.


Layout Files

A layout file is JSON with two keys:

{
  "$schema": "https://example.com/optional",
  "root": { "type": "panel", "title": "$strings.title", "children": [] }
}

$schema is optional and must be a string. Any other key at the top is an error. Do not put screen or display in the file: the path of the file sets both.

Nodes

A node is an object with type (a component of the catalog), or an object { "use": "<part>" }. A use node accepts only use and when.

Key Meaning
type The component name. Required, unless the node is a use node.
id 1 to 64 characters of [A-Za-z0-9_-]. It names the state of the node (scroll position, selected tab). Set it on nodes that sit in more than one place.
children A list of nodes.
fallback One node. Required on scenes and effects.
when Show the node only when a test is true. See below.
on Controls: press and key. See Actions.
role "analysis", and only on a panel.
tiers sessions only.
classes responsive only.
any other key A prop of the component.

A node may sit only in children, fallback, tiers.<tier>.template and classes.<class>. A node inside another prop is an error.

Values

Form Example Meaning
Literal "Open", 3, true Used as is. A text is 200 characters or fewer.
Data path "$gates.needsYou.count" Read from the snapshot. See Data Paths.
Literal dollar "$$5 per hour" $$ at the start gives a text with one $.
Table pick { "path": "$item.state", "map": { "needsYou": "alert" }, "else": "plain" } Pick a value by the text of the path. else is the value for a key that the table does not have.
List ["a", "b"] Used by segments and tabs.

when

{ "path": "$asks", "is": "empty" }
is value True when
nonEmpty none The path has a value that is not empty: not null, not "", not 0, not false, not an empty list or object.
empty none The opposite of nonEmpty.
eq a literal The value is equal. Numbers compare as numbers.
ne a literal The value is not equal.
in a list of literals The value is one of the list.

A when has one test. It accepts only path, is and value.

Parts

A part is a file parts/<name>.json with the same shape as a layout file. A layout inserts it with { "use": "<name>" }. Use parts for a top bar or a card that many screens share. The validator walks parts like layouts, in the class desktop.


Component Catalog

The catalog has 45 components. The catalog version is 1. In a watch layout only these components are allowed: stack, list, row, text, readout, badge, gauge, decision and composer.

Props that most components accept

Prop Type Meaning
width, height number Size in px.
grow number A number over 0 fills the free space.
pad space name or number Padding.
bg color name Background.
radius radii name or number Corner radius.
border color name A 1 px border.

spacer reads size only. card and panel read width, height, grow, pad and radius. Text clips with an ellipsis. A component does this, and a theme cannot turn it off.

Layout

Component Props Notes
stack dir (row or column, default column), gap, align (start, center, end), justify (center, end, between), wrap The dir prop is dir, not direction.
grid columns (1 to 12, default 2), gap Cells have equal width.
layer none The first child is the base. The other children lie over it.
scroll dir (row or column), gap Scrolls its children.
spacer size Without size it fills the free space.
repeat items (a list path), limit, dir, gap, wrap The children are the template. $item is valid inside.
responsive classes See Window width.
sessions tiers, gap, include, item_width See Session tiers.

Frame

Component Props Notes
panel variant, title, role, gap Variants: flat (default), glass, chamfer, window, monitor. With role: "analysis" the title is “Analysis” and $analysis.* is valid inside.
frame segments (a list of labels), title A panel with square corners and a row of segment labels on top.
nav dir (row or column), variant (pill or plain), gap, size, radius The app lists the screens: War room, Inbox, Tasks, Repos, Settings. You pick the variant and the place.
button label, variant (primary), composer, size, radius, on composer is the id of the composer whose text a dispatch or newSession sends.
segment_button label, selected, composer, on A button in pill shape. A true selected fills it.
switch label, value, size, on A toggle that shows value.
tabs items (a list of labels), size The children are the pages. The first child belongs to the first label.
look_switch size The app lists the looks. The user can always return to Minimal.
key_hint keys, label A key cap with a label. It shows text only.

Content

Component Props Notes
text value, size, color, weight (bold), mono, align (center, end), lines lines sets the number of lines (default 1).
readout label, value, unit, color, dir (row) A label with a value.
badge value, state, tone With state, the badge uses the state color and shows the state name when value is empty. Else tone picks the color.
image none Draws its fallback, or nothing. Not yet supported.
list items (a list path), limit, gap, empty (a text), size The children are the template. $item is valid inside.
row title, sub, meta, state, on, radius, size One line. A press fires on.press.
card title, sub, state, gap, on The children go below the title. A press fires on.press.
log items, field (default text), limit (default 50), size A list of lines in mono type.
files label, added, removed A file name with two bars for added and removed lines.
diff value A text. A line that starts with + is green and one that starts with - is red.

Session components

Component Props Notes
decision session, free_text The question, the reason, the risk flag, the steps and the option buttons of one ask. The app owns this content. session defaults to $item, then the open dialog, then the selected session, then the oldest ask.
decision_queue limit, free_text, gap All open asks as a list of decisions.
transcript session, size The chat lines of a session. session defaults to $item.id, else $selected.id.
terminal session, size The same lines in terminal style.
composer mode, session, codebase, placeholder A text box with the Send button of the app. mode is send, dispatch or newSession. Without mode: send when a session is known, else dispatch.
settings_section none The app draws the Settings screen here.

A settings layout without a settings_section is not used. Settings > Display must stay reachable, so the app then shows its own Settings screen.

Charts

Component Props Notes
chart kind, items (a list path), field (default pct), max, height (default 60), color Kinds that the desktop app draws: segments, stacked (reads active, waiting, idle of each item, for example $events), strip, area, bar. Any other kind, for example line or spark, draws thin columns.
gauge label, value, max (default 100), unit, color, kind The desktop app draws every kind as a bar.
ruler steps (2 to 60, default 10) A scale with ticks.

Effects and scenes

Component Parameters Validator rule
rain speed: "activity" or a number from 0.1 to 10 Needs fallback.
scramble, scanlines, rings none checked Need fallback.
city3d height: tokens, cost or turns Needs fallback.
fsn3d none checked Needs fallback.
radar sweep_s: a number from 2 to 20. rings_min: a list of up to 6 numbers. Needs fallback.
bloom strand: tokensHour or tokensTotal Needs fallback.
camera_wall none checked Needs fallback.

The desktop app draws the fallback node of every effect and scene. The scenes and effects themselves are not yet supported. Build a theme that looks right with the fallback.

Window width

responsive holds a node for each width class under classes:

Class Window width
compact below 960 px
regular 960 to 1439 px
wide 1440 px and more

A class without a node uses the next smaller class. On the phone class only compact is allowed. On fold: compact and regular.

Session tiers

sessions draws one template for each count of live sessions. A live session is not parked and not done.

{ "type": "sessions", "gap": "gap", "tiers": {
  "1":     { "layout": "grid", "template": { "type": "card", "title": "$item.title" } },
  "13-30": { "layout": "list", "template": { "type": "row",  "title": "$item.title" } }
} }
Tier Live sessions
1 0 or 1
2-4 2 to 4
5-12 5 to 12
13-30 13 to 30 (required)
31+ 31 and more
Rule Detail
Keys of a tier layout and template. Both are required.
layout list, grid or scene. The tiers 13-30 and 31+ must use list.
Missing tier A tier without a template uses the next smaller tier. If none is smaller, the first tier that exists.
list Groups the sessions by codebase. In 31+ the app closes groups that have no decision and no error.
grid, scene Items flow in rows. item_width (default 240 px) sets the least width of an item.
include "all" also shows parked and done sessions.
Order The model sets the order: live sessions first, an open ask first, then the longest wait.

Data Paths

A text that starts with $ is a data path. Segments use [A-Za-z0-9_]. A number segment is a list index: $sessions.0.title. A path that leads nowhere gives an empty text. Layouts have no formulas. The app computes all counts, orders and formats.

Root Type Fields
$sessions list id, title, codebase, part, provider, model, state, since (ms), cost (USD), tokens, controlled, contextPct, prompt, ask, subagents, linesTotal, composer, task
$asks list id, title, codebase, ask. One item for each session with an open ask, longest wait first.
$gates object needsYou, working, blocked, idle. Each has count and ids. Also asks (a list of session ids).
$codebases list id, name, path, branch, worktrees, daemon, languages (a list), parts (a list of name and paths)
$repos object count, items. Each item: id, name, path, branch, worktrees, sessions (live), languages (one text)
$usage list provider, label, pct, used, resetsAt, estimate
$events list at, active, waiting, idle, text. One item for each minute of the last hour.
$inbox object open: the number of open inbox items
$tasks object queued: the number of queued tasks
$daemon object version, name, demo, host
$intent object mode (alert, monitor or idle), asks, errors
$selected object The same fields as one item of $sessions. The session of the Session or Focus screen, or of the open decision dialog.
$display object class (desktop), width (px), tier (1, 2-4, 5-12, 13-30 or 31+)
$analysis list source, label, value, unit. Valid only inside a panel with "role": "analysis".
$item any The current element. Valid only inside repeat, list and sessions templates.
$strings text $strings.key. See Strings.

Values of a session:

Field Values
state needsYou, error, working, idle, parked, done
ask null, or an object with id, kind (permission, plan, question, error), text, options, and sometimes steps, reason, risky
composer mode (plan, edit, auto), provider, model, effort (low, medium, high, max)
subagents A list of id, name, state, task

A session has no transcript tail in the snapshot. Use transcript or terminal to show the lines. When a session is selected and $intent.mode is not alert, $intent.mode reads one.


Actions

A control calls one action of the app, and nothing else:

{ "type": "button", "label": "Open session",
  "on": { "press": { "action": "focus", "id": "$item.id" } } }

on accepts only press and key. There are no timers and no load events. A key event needs a combo such as "Ctrl+K" (modifiers Ctrl or Cmd, Alt, Shift, plus one key). The combos Ctrl+Shift+L, Esc and Escape belong to the app.

An argument name uses [a-z][A-Za-z0-9]*. An argument is a data path or a literal.

Action Arguments What the app does Daemon method
focus id Opens the Focus screen of a session. none (a screen change)
open screen: warroom, inbox, tasks, repos, settings or decision (with id) Opens a screen. decision opens the decision layout as a dialog with a Close button. none (a screen change)
dispatch codebase (optional) Starts work from the text of a composer that the button names in composer. session/dispatch
newSession codebase Starts a session with the text and the mode, model and effort of that composer. session/new
send none Sent by the composer component with its own Send button. session/send
answer none Answered by the decision component with its own option buttons. session/answer
pause id Interrupts the current turn. session/pause
park id Stops the session and keeps it resumable. session/park
resume id Resumes a session. session/resume
stop id Shows a confirm that the app owns, then kills the agent process. session/stop
autopilot on: true or false Shows a confirm that the app owns, then sets the autopilot. autopilot/set
permissionMode id, mode: plan, edit or auto Sets the permission mode. session/mode
setLook none The validator accepts the name. Use the look_switch component: on another node the app logs an error and does nothing. settings/set
Warning Text for an agent comes only from a composer that the user types into. An argument named text, prompt or message on send, dispatch, newSession or answer is a validator error. A theme cannot put a fixed prompt on a button, answer a decision itself, or change the label of an option. The texts of the stop and autopilot confirms are fixed.

Assets and Limits

Item Limit
Theme archive (.aivory-theme.zip) 10 MB. The unpacked size is at most 100 times the archive size.
All files unpacked 20 MB
Files 500
One asset (font or image) 4 MB
Image size 4096 x 4096 px
One layout or part file 512 KB, 4,000 nodes, depth 40
Every other file 1 MB
Font families 8
One text in a layout or in a strings file 200 characters
Tokens files 1 to 8
Path 240 bytes, no backslash, none of the characters < > : " ? * $ or the pipe, no Windows device names, no part that ends with a dot or a space
Asset rule Detail
File types TOML, JSON, Markdown, LICENSE, TTF, OTF, PNG, JPG, WebP, SVG. All other files are rejected.
Fonts The validator parses each font and reads its family name. A font is used when fonts.ui or fonts.mono holds its path.
Images The validator reads the header of PNG, JPG and WebP files.
SVG Must be static: no script, foreignObject, iframe, embed, object, audio, video, animate, set, entities, javascript:, @import or external href.
Licenses assets/fonts/ and assets/images/ each need a file LICENSE, LICENSE.txt or LICENSE.md next to the assets.
Links Links and special files are not allowed.

The image component does not draw files yet, so no layout can show a file of assets/images/ today. Images in previews/ are for the Settings screen and for the registry.


Validator

Run the validator in one of three ways:

Command Use
aivoryd theme dev <DIR> Checks on each save and prints the errors. --once checks once and ends. --interval-ms sets the poll time (default 1000).
aivoryd theme check <DIR> Checks once. --tag v1.0.0 also checks that version equals the tag without the v.
aivoryd theme pack <DIR> Checks, then writes <id>-<version>.aivory-theme.zip and prints its SHA-256. --out <DIR> picks the folder.

A good theme prints ok: <id> <version>. A bad theme prints one line for each error and the count. The exit code is 1.

An error has the form <file> <JSON pointer>: <rule>. A pointer such as /root/children/2/on/press/action leads to the key.

Common errors

Mistake Message
A desktop screen is missing layouts/desktop/tasks.json: required screen is missing
A component name is wrong layouts/desktop/tasks.json /root/type: unknown component "panl"
A data root is wrong parts/topbar.json /root/children/2/value: unknown data root "gate"; use codebases, sessions, asks, gates, usage, events, inbox, tasks, repos, daemon, analysis, intent, selected, display
An action name is wrong layouts/desktop/warroom.json /root/children/1/children/0/tiers/1/template/on/press/action: unknown action "delete"
Text on a button for an agent layouts/desktop/warroom.json .../on/press/text: text for an agent comes only from a composer that the user types into
An event other than press or key .../on/hover: an action fires on a user event only: "press" or "key" (no timers, no load events)
$item outside a template layouts/desktop/repos.json .../title: $item is only valid inside repeat, list and sessions
$analysis outside an analysis panel layouts/desktop/session.json /root/children/1/title: $analysis is only valid inside a panel with "role": "analysis"
A scene without a fallback layouts/desktop/tasks.json /root: "city3d" needs a "fallback" node
A strings key is missing layouts/desktop/inbox.json .../value: unknown strings key "nope" (every language needs it)
No strings file at all parts/topbar.json .../label: the package has no strings table (strings/<lang>.toml)
An unknown part layouts/desktop/tasks.json /root/children/0/use: unknown part "top"
A tier name is wrong layouts/desktop/warroom.json .../tiers/12-30: unknown tier; use 1, 2-4, 5-12, 13-30, 31+
The tier 13-30 is missing layouts/desktop/warroom.json .../tiers: tier "13-30" is required (from 13 sessions the theme shows rows)
A grid for 13-30 layouts/desktop/warroom.json .../tiers/13-30/layout: tiers 13-30 and 31+ must use layout "list"
item instead of template .../tiers/13-30/item: unknown key of a tier and .../tiers/13-30: a tier needs a "template" node
Extra key in a layout file layouts/desktop/tasks.json /screen: unknown key; a layout file has "$schema" and "root"
Bad JSON layouts/desktop/tasks.json: not valid JSON: key must be a string at line 1 column 3
A color is not a color tokens.toml /colors/accent: "blue" is not a color (#rrggbb or #rrggbbaa)
A text size is out of range tokens.toml /type/md: text size must be 6 to 72 px
An unknown token key tokens.toml: unknown field `warn`, expected one of `bg`, `surface`, `text`, `muted`, `accent`, `analysis`, `state` in `colors`
fast_ms in [motion] tokens.toml: unknown field `fast_ms`, expected `durations` or `easing` in `motion`
A font is not in the package tokens.toml /fonts/ui: "assets/fonts/x.ttf" is not sans, serif, mono or a font file of the package
An effect is out of range tokens.toml /effects/glass_alpha: value must be 0 to 1
A bad id theme.toml /id: id "Harbor" must be 3 to 40 characters of [a-z0-9-]
A bad version theme.toml /version: "1.0" is not a SemVer version (x.y.z)
The version differs from the tag theme.toml /version: version "0.1.0" must equal the tag "v0.2.0" without the v
min_app is too high theme.toml /min_app: the theme needs app 99.0.0 or newer
An unknown catalog theme.toml /catalog: catalog 2 is not supported (this app has 1)
desktop is not in displays theme.toml /displays: displays must include "desktop"
An unknown manifest key theme.toml: TOML parse error ... unknown field `extra`, expected one of `id`, `name`, `description`, `version`, `author`, `license`, `min_app`, `catalog`, `displays`, `tokens`
A file type is not allowed run.sh: file type not allowed (a theme has TOML, JSON, Markdown, fonts and images only)
An image is not an image assets/images/a.png: the image header is not valid
An asset folder has no license assets/fonts: the folder needs a LICENSE file next to its assets

A Complete Minimal Theme

aivoryd theme init harbor writes this theme. It passes aivoryd theme dev harbor --once and prints ok: harbor 0.1.0. It has the 8 required screens. Each screen is a panel with the daemon version. Replace the panels to build your theme in the tutorial.

theme.toml

id = "harbor"
name = "harbor"
description = "A starter theme."
version = "0.1.0"
license = "MIT"
min_app = "2026.1.0"
catalog = 1
displays = ["desktop"]
tokens = ["tokens.toml"]

[author]
name = "Your Name"

tokens.toml

[colors]
bg = "#ffffff"
surface = "#f4f5f7"
text = "#15161b"
muted = "#6b7280"
accent = "#2563eb"
analysis = "#7c3aed"

[colors.state]
needsYou = "#d97706"
working = "#2563eb"
idle = "#6b7280"
parked = "#9ca3af"
error = "#dc2626"
done = "#16a34a"

[fonts]
ui = "sans"
mono = "mono"

[type]
xs = 11
sm = 12
md = 14
lg = 18
xl = 24

[space]
sm = 8
md = 12

[radii]
card = 8

[motion]
easing = "easeOut"

[motion.durations]
fast = 120

strings/en.toml

title = "Fleet"

layouts/desktop/warroom.json

{
  "root": { "type": "panel", "id": "warroom", "title": "$strings.title", "children": [
    { "type": "text", "value": "$daemon.version" }
  ] }
}

The other seven files (session, inbox, tasks, repos, settings, focus, decision) have the same content with another id. The folder also holds LICENSE and README.md.


What Is Not Finished

These parts exist in the format or in the code, but they do not work yet. The desktop renderer is apps/desktop of the AIVory Code repository.

Part State
Scenes (city3d, fsn3d, radar, bloom, camera_wall) Validated. The desktop app draws the fallback.
Effects (rain, scramble, scanlines, rings) Validated. The desktop app draws the fallback.
image Has no props. It draws its fallback, or nothing.
gauge kind The desktop app draws a bar for every kind.
chart kind The desktop app draws columns: no smooth line, spark or arc.
motion, effects.glow, effects.scanlines, effects.blur Validated and stored. The desktop app does not read them.
phone, fold and watch layouts Validated and served by theme/get (see the API). No client draws them yet.
Language by locale The desktop app reads strings/en.toml only.
Built-in looks as packages The eight looks are compiled into the app. theme init --from <id> works for installed themes only.
Reload and error panel in the app theme dev prints errors in the terminal. It does not reload the app and shows no panel.
Install from @registry/name Not supported. theme add takes a local folder, github:<owner>/<repo>[@tag] or a GitHub link.
Theme blocks (block add) Not built. Copy a file by hand.

Next Steps