Theme chooser catalog and preparation interface
2026-09-24; branch impl/theme-catalog, based on 1d2efe3f. This checkpoint
adds a language-neutral JSON interface for a future real chooser. It does
not implement the touch UI or prove decoded wallpapers, surface rendering,
or reboot behavior. The themes proposal remains open.
The handheld-theme-command package now also exposes:
k230-theme list --json
k230-theme preview THEME_ID --json
k230-theme preview THEME_ID --background BACKGROUND_ID --json
k230-theme activate THEME_ID --background BACKGROUND_ID --expected-generation GENERATION --json
Omit --background in both preview and activation to use the source’s
default choice. THEME_ID, BACKGROUND_ID, and GENERATION are returned by
the preceding commands. Activation requires the exact reviewed generation;
an updated clone or different background choice requires a new preview.
The normal omarchy-theme-set NAME command is unchanged.
The catalog discovers pinned built-ins, unchanged standalone clones under
~/.config/omarchy/themes/NAME, and collection clones with themes/NAME
children. It never runs Git or theme scripts. Duplicate built-in/user names
remain separately selectable. IDs survive content updates; generation IDs
still include the source content, path, selected asset, and trusted adapter
identity. Listing uses bounded directory metadata, without preparing every
theme or decoding images. Symlink children and invalid names are skipped;
the 512-entry scan limit includes ignored files and collection children.
Preview uses the existing upstream resolver and immutable staging path. Its
palette, appearance JSON path, icon selector, compatibility diagnostics and
background entries are suitable for a Rust, C or QML consumer. Background
paths refer to staged assets and each explicitly says decode_status: unverified. Suffix-based image/video classification does not prove the
file’s actual format or codec. The compatibility applied list describes
resolver inputs, not observed rendering by all shell surfaces.
Cancel requires no mutation: discard the preview response. Preview never changes the active pointer. Apply reuses the existing prepare/commit/rollback acknowledgement transaction and returns success only after it completes. The active selection is reported from the committed pointer; a removed source can leave a usable retained generation with no catalog entry ID.
Host command:
python3 tests/test_theme_catalog.py
PASS: 8 tests
Tests exercise unchanged standalone/collection discovery, origin collisions, cheap bounded listing, source preservation, staged asset retention after source removal, cancel/no active mutation, opaque asset choice, stale preview rejection, missing receiver, successful acknowledged commit and commit rollback. The acknowledgement cases use the actual transaction with a host fake transport; they do not prove a shell receiver or visible scene switch.
Remaining: cross-build the changed command output, consume this interface
in a real chooser, bounded thumbnail decode/cache, per-theme wallpaper
memory, user overlays/fit options, video playback, all themed surfaces and
physical touch/reboot/performance proof. No OpenSpec task is checked by this
partial backend checkpoint. The narrow package command, when the reserved
Qt build releases the slot, is nix build .#handheld-theme-command --max-jobs 1 --cores 4 --no-link --print-out-paths.