feat(tui): wire Presets screen to storage for CRUD & apply #108

Merged
claude-desktop merged 1 commit from tui/preset-wire-91 into main 2026-04-12 15:47:57 +00:00
Collaborator

Summary

  • Connect PresetsScreen to GalleryStorage for full preset lifecycle: load on screen entry, create (a), rename (e/r), delete (d), duplicate (y)
  • Wire Enter to apply focused preset to the Generate screen via new AppAction::ApplyPreset, navigating there automatically
  • Wire E (export to ~/.local/share/loom/presets/<name>.toml) and I (import all .toml from that directory)
  • Add Screen::as_any_mut() trait method and GenerateScreen::apply_preset() for cross-screen downcast

Closes charles/loom#91

Test plan

  • cargo clippy -p loom-tui -- -D warnings clean
  • cargo test -p loom-tui passes (118 tests)
  • Manual: navigate to Presets screen, verify presets load from DB
  • Manual: a creates, d deletes, y duplicates, Enter applies and navigates to Generate

🤖 Generated with Claude Code

## Summary - Connect `PresetsScreen` to `GalleryStorage` for full preset lifecycle: load on screen entry, create (`a`), rename (`e`/`r`), delete (`d`), duplicate (`y`) - Wire `Enter` to apply focused preset to the Generate screen via new `AppAction::ApplyPreset`, navigating there automatically - Wire `E` (export to `~/.local/share/loom/presets/<name>.toml`) and `I` (import all `.toml` from that directory) - Add `Screen::as_any_mut()` trait method and `GenerateScreen::apply_preset()` for cross-screen downcast Closes charles/loom#91 ## Test plan - [x] `cargo clippy -p loom-tui -- -D warnings` clean - [x] `cargo test -p loom-tui` passes (118 tests) - [ ] Manual: navigate to Presets screen, verify presets load from DB - [ ] Manual: `a` creates, `d` deletes, `y` duplicates, `Enter` applies and navigates to Generate 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Connect PresetsScreen to GalleryStorage for full preset lifecycle:
- Load presets from storage on screen entry (lazy, once)
- CRUD keys: a (create), e/r (rename), d (delete), y (duplicate)
- Enter applies preset to Generate screen via AppAction::ApplyPreset
- E exports focused preset to ~/.local/share/loom/presets/<name>.toml
- I imports all .toml files from the presets directory
- Add Screen::as_any_mut() for cross-screen downcast (apply preset)
- Add GenerateScreen::apply_preset() to populate params from a Preset

Closes charles/loom#91

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Collaborator

Review — PR #108: Presets screen wired to storage for CRUD & apply

Well-rounded. CRUD + export/import + apply-to-generate all work. Good sanitize_filename implementation.

Concern: as_any_mut on Screen trait

This PR adds fn as_any_mut(&mut self) -> &mut dyn std::any::Any to the Screen trait, requiring all screen implementations to add the boilerplate. PR #109 does the same thing independently. Coordinate to avoid merge conflicts — ideally this goes into a shared base PR.

Concern: action_rename appends " (renamed)"

This is acknowledged as a workaround, but it's a one-way operation — pressing r repeatedly appends more suffixes (guarded by ends_with check, so actually it doesn't). Still, the rename UX is odd: the user presses r and the name silently changes. No feedback about what happened. Consider at minimum a notification showing old → new name, or better yet, reusing the inline EditMode pattern from the entities screen (#101).

Concern: action_import assigns fresh UUIDs

Good for avoiding collisions, but this means re-importing the same TOML file creates duplicates every time. Consider checking for name collisions and either skipping or updating existing presets.

Concern: e and r both map to rename

KeyCode::Char('e') => self.action_rename(ctx),
KeyCode::Char('r') => self.action_rename(ctx),

This wastes a keybind. If e is meant for "edit" and r for "rename", they should do different things. If they're aliases, remove one and free the key for another action.

Minor

  • apply_preset preserves prompt/seed/dimensions, only overwriting model/sampler/steps/CFG. Good design choice — documented clearly.
  • Export path ~/.local/share/loom/presets/ is hardcoded via loom_core::config::data_dir(). Make sure this aligns with XDG on all platforms.
## Review — PR #108: Presets screen wired to storage for CRUD & apply **Well-rounded.** CRUD + export/import + apply-to-generate all work. Good `sanitize_filename` implementation. ### Concern: `as_any_mut` on `Screen` trait This PR adds `fn as_any_mut(&mut self) -> &mut dyn std::any::Any` to the `Screen` trait, requiring all screen implementations to add the boilerplate. PR #109 does the same thing independently. Coordinate to avoid merge conflicts — ideally this goes into a shared base PR. ### Concern: `action_rename` appends " (renamed)" This is acknowledged as a workaround, but it's a one-way operation — pressing `r` repeatedly appends more suffixes (guarded by `ends_with` check, so actually it doesn't). Still, the rename UX is odd: the user presses `r` and the name silently changes. No feedback about *what* happened. Consider at minimum a notification showing old → new name, or better yet, reusing the inline `EditMode` pattern from the entities screen (#101). ### Concern: `action_import` assigns fresh UUIDs Good for avoiding collisions, but this means re-importing the same TOML file creates duplicates every time. Consider checking for name collisions and either skipping or updating existing presets. ### Concern: `e` and `r` both map to rename ```rust KeyCode::Char('e') => self.action_rename(ctx), KeyCode::Char('r') => self.action_rename(ctx), ``` This wastes a keybind. If `e` is meant for "edit" and `r` for "rename", they should do different things. If they're aliases, remove one and free the key for another action. ### Minor - `apply_preset` preserves prompt/seed/dimensions, only overwriting model/sampler/steps/CFG. Good design choice — documented clearly. - Export path `~/.local/share/loom/presets/` is hardcoded via `loom_core::config::data_dir()`. Make sure this aligns with XDG on all platforms.
charles force-pushed tui/preset-wire-91 from 2b21e2ce8a to 9e5cad1bc5 2026-04-12 14:32:32 +00:00 Compare
claude-desktop changed target branch from tui/image-ctx-85 to main 2026-04-12 15:36:30 +00:00
charles force-pushed tui/preset-wire-91 from 9e5cad1bc5 to 07442e0ed0
Some checks failed
QA / qa (pull_request) Has been cancelled
2026-04-12 15:47:52 +00:00
Compare
claude-desktop deleted branch tui/preset-wire-91 2026-04-12 15:47:58 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/loom!108
No description provided.