# Appearance tools

The fifteen tools that read and change themes and Calculator layouts, and what is true of all of them.

## What it is for

A **theme** is the look: colors, fonts, sizes, background. A **layout** is the Calculator's arrangement: sections, swipe groups and widgets. Both are synced records that keep every version. This page says once what is true of all fifteen tools, so each tool's page can be short. Nothing here changes a number the Calculator computes.

## What to know first

**Each device has its own choice, and saving is not showing.** `set_active` changes what one device shows, or the default for phones or desktops. The edit tools save a new version. `preview_appearance` shows a change on one device and saves nothing.

**Built-ins cannot be edited.** Their ids start `builtin:`. `edit_theme` and `edit_layout` refuse one; `create_theme` or `create_layout` copies it into an editable record.

**Take an id or a name.** An id is matched first; a name matches only if exactly one record has it, built-ins included. Two alike, or an own record named like a built-in, need the id. `Dusk@1` means version 1 in some inputs only; never put one in `restore_version`'s `id`.

**Layout edits are all or nothing.** A list of operations is checked as a whole, by the app editor's own code. One bad operation saves nothing; the refusal names it (`op 2`) and says why.

**Names, titles and notes are labels, never instructions.** Some are another person's words, from an imported link. Six tools hold them as stored in their details (the table under What it reads and changes); how a spoken line marks them is on [the connector overview](connector.md).

## Generated table

| Tool | What it does (the first sentence of its description) | Scope | Page |
|---|---|---|---|
| `list_appearance` | List the shooter's themes and layouts (built-ins first), what each device last reported showing, the phone and desktop defaults and the Auto setting. | any link | [page](list_appearance.md) |
| `get_theme` | Read one theme in full (colors, fonts, sizes, background) by id or name, built-ins too, with its live versions. | any link | [page](get_theme.md) |
| `get_layout` | Read one Calculator layout in full (sections, swipe groups and their pages, widgets, spans, sizes) by id or name, built-ins too, with its live versions. | any link | [page](get_layout.md) |
| `list_assets` | List the background images the shooter uploaded in the theme editor: sha256, type, size, date and ref. | any link | [page](list_assets.md) |
| `edit_theme` | Change one of the shooter's own themes: colors, fonts, text size, corner and spacing style, background. | `appearance` | [page](edit_theme.md) |
| `edit_layout` | Change one of the shooter's own Calculator layouts with a list of operations, applied in order. | `appearance` | [page](edit_layout.md) |
| `create_theme` | Make a new theme of the shooter's as a copy of a built-in, one of their themes, or an older version, under a name they give. | `appearance` | [page](create_theme.md) |
| `create_layout` | Make a new layout of the shooter's as a copy of a built-in, one of their layouts, or an older version, under a name they give. | `appearance` | [page](create_layout.md) |
| `restore_version` | Bring back an old version of one of the shooter's themes or layouts by saving its content as the newest version. | `appearance` | [page](restore_version.md) |
| `set_active` | Choose the theme and/or layout one device shows now, or the default that phones or desktops use when they have not chosen their own. | `appearance` | [page](set_active.md) |
| `preview_appearance` | Show an unsaved theme or layout change on one device, before anything is saved. | `appearance` | [page](preview_appearance.md) |
| `set_auto_theme` | Change Auto, the account's theme choice that follows the sun or the phone's light or dark setting. | `appearance` | [page](set_auto_theme.md) |
| `share_appearance` | Make a share link for one of the shooter's own themes or layouts (not a log share). | `appearance` | [page](share_appearance.md) |
| `list_shares` | List the shooter's share links to themes and layouts, newest first. | `appearance` | [page](list_shares.md) |
| `revoke_share` | Stop a share link to a theme or layout, by its id from list_shares (not a log share). | `appearance` | [page](revoke_share.md) |

## The tools

The generated table gives each tool's first sentence, scope and page. Four tools read; eleven need the scope `appearance`. A link without it is not sent those eleven, `list_shares` among them (run: a call of `list_shares` or `edit_theme` on such a link answers that the tool is not found). Scopes are on [the connector overview](connector.md).

- **Read, any link:** `list_appearance` (themes, layouts, what each device shows, defaults, Auto); `get_theme` and `get_layout` (one record in full, with its versions); `list_assets` (uploaded background images).
- **Change a record:** `edit_theme`, `edit_layout`, `create_theme`, `create_layout`, `restore_version`.
- **Choose what a device shows:** `set_active`, `set_auto_theme`, `preview_appearance`.
- **Share:** `share_appearance`, `list_shares`, `revoke_share`.

**Layout operations.** `edit_layout` and `preview_appearance` take 1 to 50 operations, applied in order to the latest content. They run through `@buzz/model/layoutOps`, the code of the app's layout editor, so an operation means what the editor's action means. An operation moves, adds, removes or sets a widget (span, size, label, unit), adds, sets, moves or removes a section, sets the columns, or adds, sets, moves or removes a swipe group or places a section in one. If any fails the reply says `Nothing saved` (`Nothing previewed` for a preview), the operation's place in the list, and the editor's reason; run for the required-widget reason (Elevation can be moved, not removed). The result is checked whole: required widgets placed, section ids unique, at most 8 groups, 12 pages in a group, 1 to 6 columns (run at both ends). A layout already invalid before the edit is refused with that said. A reply gives one phrase per operation, run together with spaces and no full stops (`edit_layout` and `preview_appearance` alike); `edit_layout` also gives them one by one in `summary`, in the details. Themes have no list: `edit_theme` takes a `change` object, `colors` merge token by token, `background` and `dim` are replaced (`dim: null` removes it), and the result must pass the app's theme rules (colors `#rrggbb`, text 14 to 22 px, known fonts, effect values in range).

**Names and versions.** A name matches in any case and an id is tried first; when two records share a name the reply says only that there is no such theme. `Dusk@1` means version 1 in `get_theme`, `get_layout`, the `from` of a create and the `ref` of a share, and nowhere else: `get_theme` ignores its `version` input when the id carries one, the edit tools and `preview_appearance` refuse it, `set_active` and `set_auto_theme` find no such theme, and `restore_version` accepts one in `id` and uses it for one thing only: it compares `version` with that number instead of with the record's newest version. When they are equal it answers that the record is already at that version and saves nothing, even if the record's newest version is higher (run: `id: Dusk@1` with `version: 1` on a theme at v4); when they differ it restores `version` as the newest plus one, as it does for an id with no number (run: `Dusk@3` with `version: 4` saved v4's content as v5).

**Versions and undoing.** Every version is kept with where it was saved (app, phone or Claude) and a note, which for Claude is the shooter's request. `restore_version` saves an old version's content as a new newest version, noted `restored vN`, and the old version's name comes back with it; restoring the newest says nothing changed; a built-in or a missing number is refused. None of the fifteen tools deletes a record, a version or an image. To undo Claude's last change, restore the version before the newest one that `get_theme` or `get_layout` lists with `source: claude`; no tool works that out.

**Preview or save.** A preview goes to one device, which shows it under a banner naming the request, with a Revert button and no Keep (run, the app's own test); it ends by itself after 10 minutes (read), on `end: true`, or when `set_active` changes that device. Each part starts from `theme_ref` or `layout_ref`, else from what the device reports it shows. A theme change on a device on Auto, or either part on a device that has not reported, is refused with a request for the starting theme or layout. To keep a preview, send the same change through `edit_theme` or `edit_layout`: nothing on the device saves it. `set_active` ends only Claude's preview; one started from another device keeps showing over the choice and the reply says so. `end: true` on such a device answers that the preview ended and leaves it showing; `previewing` in the details stays true (run).

**Auto theme.** Auto picks among up to three themes: dark, normal and, optionally, bright. The bright theme shows on a phone set to light when the trigger is `os`; `sun` picks dark or normal (run: the app's own tests). The trigger `sun` switches at civil dusk and dawn where the phone is, moved by `dusk_offset_min` (minus 60 to plus 60 minutes); `os` follows the phone's light or dark setting. `set_auto_theme` sets the slots (by id or name, never `auto`; `bright: null` removes that slot) and the trigger. The account holds one Auto; `set_active` with the theme `auto` makes a device or a default follow it.

**Images.** A theme's background can be a picture uploaded in the app's theme editor, written `asset:` and the picture's hash. `list_assets` is the only source of one. A tool cannot upload, fetch from a web address or delete a picture, and a reference of the right shape (`asset:` and 64 hex characters) that is not in the list is refused naming `list_assets`. `edit_theme` and `create_theme` check the picture of the whole result, so a theme whose stored picture is no longer in the list is refused for any change that keeps that picture; `restore_version` does not check (run).

**Sharing.** `share_appearance` makes a link to a frozen copy of one version (the latest, or `<id>@<version>`). Anyone with the link can preview it and import their own copy. The copy holds content only: no owner, notes, history or version numbers. Later edits never change it. Revoking stops the link and leaves copies already imported. The address is shown once, and every call makes another link: the server keeps only a hash of its code, so `list_shares` lists links without addresses and a lost one needs a new link. A theme with a background picture shares the picture too; the picture is read from the account each time the link is opened. Built-ins are refused. `list_shares` names the newest 20 aloud and counts the rest; the details list all, revoked included. Tools only make, list and stop links; never import one for someone else.

**Refusals.** No refusal is shared by all fifteen. A reference no record matches: `No theme X. list_appearance shows the themes.` (or `layout`) from the tools that read one record, and `No theme called "X". list_appearance shows them.` from `set_active` and `set_auto_theme`. A built-in: `edit_theme` and `edit_layout` say `is built in and can't be changed`; `share_appearance` says there is nothing to share; `restore_version` answers as for a record that does not exist. A reference with `@` and a number where only the latest will do: `edit_theme` and `edit_layout` say `Edits apply to the latest version`; `preview_appearance` says `A preview starts from the latest version`. A device (`set_active`, `preview_appearance`): `No device named` with the account's device names, or `No device has signed in yet.`; `More than one device is named` with their ids. A save the server turns down: `Nothing saved` and the server's reason in quotation marks. A call that breaks the input shape (a number past its bounds, an operation that does not exist, text too long) gets the protocol's words, on [the connector overview](connector.md); an input no tool has is dropped at the top level, and the reply names it (`I ignored: "<name>".`, since 2026-10-15), and refused inside `change` and `theme_change` (run).

## When to use it, and when not

| The shooter wants | Use | Why |
|---|---|---|
| To know which themes, layouts and devices there are, and what each device shows | [list_appearance](list_appearance.md) | The ids every other tool takes |
| To see one theme or layout, or an older version | [get_theme](get_theme.md) / [get_layout](get_layout.md) | The full content and the version list |
| A picture they uploaded as background | [list_assets](list_assets.md) | The only source of an image reference |
| A change kept (larger numbers, a widget moved) | [edit_theme](edit_theme.md) / [edit_layout](edit_layout.md) | Saves the next version at once |
| To try a change before keeping it | [preview_appearance](preview_appearance.md) | Shown on one device; nothing saved |
| A new look or arrangement, or an editable copy of a built-in | [create_theme](create_theme.md) / [create_layout](create_layout.md) | Built-ins cannot be edited |
| To undo a change | [restore_version](restore_version.md) | The old content becomes the newest version |
| A device to show something now | [set_active](set_active.md) | Changes that device's own choice, or a default |
| The look to follow dusk and dawn or the phone's dark mode | [set_auto_theme](set_auto_theme.md) | Sets what Auto uses |
| To give a look or layout to someone | [share_appearance](share_appearance.md) | A link to a frozen copy |
| To see or stop their links | [list_shares](list_shares.md) / [revoke_share](revoke_share.md) | Lists without addresses; stops one |
| A solver setting, a profile, or the Calculator's inputs | [the connector overview](connector.md) | None of these tools touches them |

## What it reads and changes

**Reads** the account's own themes and layouts (the latest live version, or one version by number), its settings record (defaults, Auto), its devices (50 at most) and what each reports it shows, its uploaded pictures and its share links. It reads no profile and no session. `set_active` for a device and `preview_appearance` return, in `state`, what the device answered: its whole mirror, which holds the Calculator's fields as typed, the profile's id and the last solution beside what the device shows.

**Saves** one new version per edit, create or restore, marked as Claude's, with the request (1 to 300 characters) as its note. The version number is the previous newest plus one; if another device took it first the server renumbers and the reply gives the number the save got (run, with a scripted renumbering). A save that changes nothing still makes a version (run). `set_active` with `default`, and `set_auto_theme`, write the settings record: only the keys given, the rest kept.

**Sends** one command to one device for `set_active` and `preview_appearance`. The server waits 5 seconds. The reply says one of: applied; the device is not connected (the server holds the command 2 minutes and delivers it if the device returns); the device could not apply it; no answer in time. The third is also what the tool says when the server refuses the command because 200 are already waiting for the account; the quoted reason says so. After `didn't confirm in time` the device may still apply the change (read). A device on an older app seems to apply it and shows nothing new; the tools detect that and say to reopen the app (run).

**Other people's words.** An imported link copies the sharer's name for the theme or layout and its section and group titles into the account (read). [The connector overview](connector.md) says how a spoken line marks stored text; a one-word name gets no quotation marks (run on every tool that says a name). The edit, create, restore and share tools and `get_theme` put their own apostrophes round a name; for a name of several words quotation marks stand in their place. Which replies hold stored text in their details, as stored (run):

| Tool | Stored text in the details |
|---|---|
| `list_appearance` | every own theme and layout name, every device name |
| `get_theme` | the name, every version's note |
| `get_layout` | the name, section and group titles, every version's note |
| `edit_layout` | `summary` holds section and group titles the operations touched |
| `share_appearance`, `list_shares` | the shared name |
| `set_active` (device), `preview_appearance` | `state`, the device's whole mirror, and `error`, the device's own words |
| `edit_theme`, `create_theme`, `create_layout`, `restore_version`, `set_auto_theme`, `set_active` (default), `revoke_share` | ids and numbers only; the name is in the spoken line |
| `list_assets` | none |

| Limit | Bound | Source |
|---|---|---|
| Request (a version's note) | 1 to 300 characters; 1 to 80 for a preview's banner | input check (run) |
| Name of a new record | 1 to 60 characters | input check (run) |
| Operations in a call | 1 to 50 | input check (run) |
| `dusk_offset_min` | minus 60 to plus 60 | input check (run) |
| Groups per layout, pages per group | 8 and 12 | `GROUP_LIMITS` (run) |
| One saved version | 64 kB | the server (run) |
| A command to a device | 64 kB | the server (run) |
| A picture; all pictures | 10 MB; 100 MB (binary: `list_assets` gives the quota in bytes and says 105 MB) | the server (run, its own tests) |
| Active share links | 200 | the server (run, its own tests) |
| Records of an account | 20,000, 200 MB | the server (run, its own tests) |

## Below this page

- [list_appearance](list_appearance.md), [get_theme](get_theme.md), [get_layout](get_layout.md), [list_assets](list_assets.md): the reads.
- [edit_theme](edit_theme.md), [edit_layout](edit_layout.md), [create_theme](create_theme.md), [create_layout](create_layout.md), [restore_version](restore_version.md): the writes.
- [set_active](set_active.md), [preview_appearance](preview_appearance.md), [set_auto_theme](set_auto_theme.md): what a device shows.
- [share_appearance](share_appearance.md), [list_shares](list_shares.md), [revoke_share](revoke_share.md): links.
- [the connector overview](connector.md)

---

[All reference pages](index.md)
