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.
| Group | Examples |
|---|---|
| The original fourteen | now (the daily-ritual widget: greeting, headline, open signals, mood), signals, muse, files, projects, memory, square, wallet, calendar, inbox, notes, weather, clock, custom |
| You | agenda, todos, focus, habit, soul, constellation, capture, thoughts, knowledge, twin, onthisday |
| Work | board, taskgraph, agents, threads, studio, skills, generators, artefacts, search, recent |
| World | citizens, connections, pulse |
| Money | spend, market, stall, models, purchases |
| Instruments | timer, 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
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} ]'