Skip to content

Every widget

A Widget is a small, typed record:

{
id, beingId, type: WidgetType,
title?: string,
x, y, w, h, // grid units
config: Record<string, unknown>,
visible: boolean,
pinned: boolean,
order: number,
}

Layout is per-user and persisted as a whole: GET /api/desktop/widgets returns your current arrangement, PUT /api/desktop/widgets replaces it. Drag, resize, snap to the grid — the desktop remembers exactly where you left each one, the way a good room remembers where you like the chair. Widgets can be hidden (visible: false) without losing their config, and pinned so they survive any future layout suggestions untouched.

WidgetType is a kind, not an enum

It used to be a closed z.enum of fourteen, and every saved layout was validated against it — so once the widget library grew past those fourteen, the newer widgets rendered perfectly but could never be placed, because saving a layout containing one would fail the enum check. WidgetType (packages/contracts/src/desktop.ts) is now a validated string: lowercase letters, digits and underscores, optionally namespaced as namespace.id (the same shape a plugin-contributed widget uses), capped at 80 characters. KNOWN_WIDGET_TYPES is a separate, non-enforcing manifest of the types this build ships — used for labels and defaults, not for rejecting a layout — so a plugin’s widget type is always saveable, and a widget the desktop doesn’t currently have a component for renders an honest ”‘‹type›’ is not installed” placeholder tile rather than silently substituting something else or losing your layout.

What’s in the library today

The library is well past its original fourteen and grouped loosely by what each widget is for. This list is a snapshot, not a promise of an exact count or that every entry below is already wired up end-to-end — new widgets and the containerless (borderless, chrome-free) skin are active work as this page is written, so treat the groupings as the durable fact and the roster as something that grows.

GroupExamples
The original fourteennow (the daily-ritual widget: greeting, headline, open signals, mood), signals, muse, files, projects, memory, square, wallet, calendar, inbox, notes, weather, clock, custom
Youagenda, todos, focus, habit, soul, constellation, capture, thoughts, knowledge, twin, onthisday
Workboard, taskgraph, agents, threads, studio, skills, generators, artefacts, search, recent
Worldcitizens, connections, pulse
Moneyspend, market, stall, models, purchases
Instrumentstimer, countdown, calculator, converter, worldclock, breathe, palette, device, storage

now, signals, muse, files, projects, memory, square, wallet, calendar, inbox, and notes are the ones documented elsewhere in these docs in their own right — see The Twin, Muse, The Studio, Signals and the omnibar, and Calendar, to-dos, and the Hush — the rest are smaller, more focused surfaces over the same underlying data.

Widget suggestions

The desktop can propose a widget it thinks you’d want, based on what the Twin has learned about you — this arrives as a widget.suggest WebSocket event ({ widgetType, reason, config? }), not a widget that appears on its own. Nothing rearranges itself without asking; a suggestion is an offer you accept or dismiss, in keeping with the desktop’s whole attention model.

The custom widget and generators

Running the Widget maker generator in the Studio produces a self-contained HTML snippet — a countdown, a habit tracker, a quote of the hour, a live feed from a public JSON URL — which becomes the config of a custom widget. This is the same generator pipeline used for landing pages and full apps, just aimed at something small enough to live in one glass panel.

Example: fetching and re-laying-out your widgets

Terminal window
curl -s http://localhost:4000/api/desktop/widgets -b cookies.txt
curl -s -X PUT http://localhost:4000/api/desktop/widgets \
-H 'content-type: application/json' -b cookies.txt \
-d '[
{"id":"w1","beingId":"me","type":"now","x":0,"y":0,"w":4,"h":2,"config":{},"visible":true,"pinned":true,"order":0},
{"id":"w2","beingId":"me","type":"signals","x":4,"y":0,"w":3,"h":3,"config":{},"visible":true,"pinned":false,"order":1}
]'