Skip to content

Commit 0937393

Browse files
committed
feat: persist backdrop preferences so styling stops needing flags
Every backdrop decision required a flag on the command line, which made the most taste-driven knob in the tool the least reachable one. Closes the Phase 1 half of #27. `cloche config` stores preferences in ~/.config/cloche/config.json, and both `capture` and `polish` read them with no flags: pin a palette and scene, or keep the picker random over a chosen subset of backdrops. `config options` lists every palette with its gradient/space kind so a menu can be rendered from the JSON contract rather than from help text. `polish::style_from_seed_in_pool` takes the allow-lists. Empty lists mean unconstrained, and the scene draw happens after every other field, so existing `--style-seed` values keep rendering identically; a test compares composed card pixels to hold that. Scenes are pinned at the polish layer rather than filtered inside `Scene::generate`, which blends kinds with its own rolls and would bias the pool instead of confining it. `capture` gains `--palette` and `--scene` to match `polish`, on the CLI and on the MCP tool, so a flag can override a pinned preference. Config names are validated into warnings rather than errors: a typo in a preferences file should not cost a screenshot.
1 parent 840d507 commit 0937393

11 files changed

Lines changed: 1221 additions & 69 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,26 @@ All notable changes to this project are documented here. The format follows
99
### Added
1010
- `cloche codex-payload --card` attaches the polished presentation image instead
1111
of the default raw capture frame.
12+
- **Persisted backdrop preferences.** `cloche config` stores styling choices in
13+
`~/.config/cloche/config.json` (`$XDG_CONFIG_HOME` / `%APPDATA%` honored,
14+
`CLOCHE_CONFIG` overrides), and both `cloche capture` and `cloche polish` read
15+
them with no flags. Pin a palette and scene with
16+
`cloche config set --mode pinned --palette orion-emission --scene jwst`, or
17+
keep the picker random over a chosen subset with
18+
`cloche config set --palettes carina-hubble,pleiades-reflection --scenes alma,veil`.
19+
`cloche config options` lists every palette (with its gradient/space kind) and
20+
scene; `cloche config show` prints the effective preferences.
21+
- **`cloche capture --palette` and `--scene`**, matching the flags `polish`
22+
already had, plus the same two arguments on the MCP `capture` tool. Explicit
23+
flags override the persisted config.
24+
- **`cloche schema --for config`** emits the JSON contract for the new command.
1225

1326
### Changed
27+
- A named random pool can reach the gradient palettes again. Random styling
28+
still defaults to space palettes only, but a palette listed in the config pool
29+
is drawn from regardless of its kind.
30+
- An unreadable or malformed config is reported in the result's `warnings` and
31+
falls back to defaults instead of failing the capture.
1432
- Relicensed from Apache-2.0 to MIT. `LICENSE`, `Cargo.toml`, `CONTRIBUTING.md`,
1533
and the README badge now agree on MIT.
1634
- Capture JSON renames `outputDir` to `outDir` to match flat `--out-dir` /

README.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,57 @@ cloche polish shot.png --scene cmb --style-seed 42
147147
Scenes: `nebula`, `jwst`, `hubble`, `galaxy`, `alma`, `ring`, `butterfly`,
148148
`edge-on`, `sun`, `sdo`, `cluster`, `deep-field`, `lensing`, `veil`, `remnant`,
149149
`cmb`. `--scene` applies only to space palettes. On a gradient palette it is a
150-
no-op warning. The same flag is available on the MCP `polish` tool.
150+
no-op warning. `--palette` and `--scene` are available on `cloche capture`,
151+
`cloche polish`, and the matching MCP tools.
152+
153+
## Backdrop Preferences
154+
155+
Pinning a look on every invocation gets old, so `cloche config` stores the
156+
choice once and both `capture` and `polish` read it with no flags:
157+
158+
```bash
159+
cloche config options # every palette (with kind) and scene
160+
cloche config show # current preferences and where they live
161+
162+
# Always use one backdrop.
163+
cloche config set --mode pinned --palette orion-emission --scene jwst
164+
165+
# Or keep it random, but only across the backdrops you like.
166+
cloche config set --mode random --palettes carina-hubble,pleiades-reflection
167+
cloche config set --scenes alma,veil
168+
169+
cloche config set --clear all # back to defaults
170+
```
171+
172+
Preferences live at `~/.config/cloche/config.json`
173+
(`$XDG_CONFIG_HOME`/`%APPDATA%` are honored, and `CLOCHE_CONFIG` overrides the
174+
path outright):
175+
176+
```json
177+
{
178+
"polish": {
179+
"mode": "random",
180+
"palette": null,
181+
"scene": null,
182+
"palettes": ["carina-hubble", "pleiades-reflection"],
183+
"scenes": ["alma", "veil"]
184+
}
185+
}
186+
```
187+
188+
- `mode` is `random` or `pinned`. `pinned` applies `palette`/`scene`; `random`
189+
leaves them on file and draws from the pools instead.
190+
- `palettes` and `scenes` are the random pool. Empty means no constraint: every
191+
space palette, and the seed's own scene pick. Naming a gradient palette here
192+
is the only way to get the gradient look back into the random rotation.
193+
- Precedence is flag, then config, then the built-in random pick, so a one-off
194+
`--palette` always wins.
195+
- A missing file means defaults. A malformed one is reported in the result's
196+
`warnings` and falls back to defaults rather than costing you the capture.
197+
198+
`cloche config show` and `cloche config options` emit the same JSON contract as
199+
every other command (`cloche schema --for config`), so a picker UI can read the
200+
menu and the current selection without parsing help text.
151201

152202
## Command Reference
153203

@@ -161,11 +211,17 @@ cloche capture --target window --title Firefox --out-dir /tmp/cloche-demo --form
161211
cloche capture --target window --window-id 0x3400003 --out-dir /tmp/cloche-demo --format json
162212
cloche capture --target window --app firefox --out-dir /tmp/cloche-demo --format json
163213
cloche capture --target region --presentation both --clipboard --out-dir /tmp/cloche-demo --format json
214+
cloche capture --target active --palette orion-emission --scene jwst --out-dir /tmp/cloche-demo
164215
cloche polish /tmp/diff.png --format json
165216
cloche polish /tmp/diff.png --out /tmp/diff-card.png --palette ember-glow --scene jwst --style-seed 12345
166217
cloche reels render --input raw.mp4 --out demo.mp4 --cues cues.json --title "Create a project"
167218
cloche reels render --engine hyperframes --input raw.mp4 --out demo.mp4 --cues cues.json \
168219
--palette ember-glow --style-seed 12345 --fps 60 --width 1080 --height 1920 --workers 1
220+
cloche config show
221+
cloche config options
222+
cloche config set --mode pinned --palette orion-emission --scene jwst
223+
cloche config set --mode random --palettes carina-hubble,pleiades-reflection
224+
cloche config set --clear all
169225
cloche gallery --limit 10
170226
cloche gallery --root /tmp --html /tmp/cloche.html --title "My Shots" --open
171227
cloche latest
@@ -174,6 +230,7 @@ cloche open /tmp/cloche-demo
174230
cloche schema
175231
cloche schema --for polish
176232
cloche schema --for reel-render
233+
cloche schema --for config
177234
cloche codex-payload --thread-id THREAD_ID /tmp/cloche-demo
178235
cloche codex-payload --thread-id THREAD_ID --card /tmp/cloche-demo
179236
cloche mcp

implementation-notes.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,39 @@ so old sidecars still deserialize. MCP `capture` now advertises `format`
507507
the flat layout; the wrapper still forces `--format json` and rejects other
508508
values. README Command Reference lists `schema --for reel-render` beside
509509
polish.
510+
## Persisted backdrop preferences (2026-07-25)
511+
512+
Issue #27 Phase 1: a config the CLI and MCP paths read so backdrop choice stops
513+
being a per-invocation flag. Phase 2 (`cloche studio`) is deliberately not here;
514+
the config is the durable thing a UI would write to, so it lands first.
515+
516+
- **JSON, not TOML.** The `[polish]` sketch in the issue implied TOML, but a
517+
TOML crate is a new dependency for one small file. `serde_json` is already in
518+
the tree and every other Cloche surface is JSON, so `config.json` it is. Same
519+
reasoning as the `setup` agent-config work above.
520+
- **Pool selection lives in `polish`, not `space.rs`.** `style_from_seed_in_pool`
521+
takes palette and scene allow-lists. The scene is picked at the `polish` layer
522+
and pinned via the existing `style.scene` field rather than teaching
523+
`Scene::generate` about pools: that function blends kinds with its own rolls
524+
(a JWST roll can layer onto a galaxy), so filtering inside it would only bias
525+
the result, not confine it. Pinning confines it exactly.
526+
- **Empty pool means unconstrained, and the scene draw happens last.**
527+
`style_from_seed(seed)` is now `style_from_seed_in_pool(seed, &[], &[])`, so
528+
every existing `--style-seed` had to keep rendering identically. Drawing the
529+
scene after every other field keeps the rng sequence untouched when no scene
530+
pool is set; `empty_pools_reproduce_the_unconstrained_style` compares composed
531+
pixels, not just field values, to hold that.
532+
- **Config names are validated by the caller, not by `polish`.** Clap
533+
pre-validates flags, but a hand-edited config can name anything.
534+
`config::resolve_style` filters unknown names into `warnings` and `polish`
535+
treats its allow-lists as advisory, falling back to the built-in space pool
536+
rather than erroring. A typo in a preferences file should not cost a
537+
screenshot.
538+
- **`run_polish` takes a config path.** Resolving `config::path()` inside it
539+
would make every polish test depend on the developer's own preferences file.
540+
The path is threaded from `polish()` so tests point at a temp file or a
541+
nonexistent one.
542+
- **Reels are untouched.** `resolve_reel_style` has its own seed/palette pairing
543+
for brand continuity with a still card, and the issue scopes the preferences
544+
to `capture`/`polish`. Wiring reels to the config would silently change
545+
existing reel renders.

0 commit comments

Comments
 (0)