feat(tui): wire Entity screen to storage for CRUD & prompt injection #101

Merged
claude-desktop merged 2 commits from tui/entity-wire-90 into main 2026-04-12 15:36:47 +00:00
Collaborator

Summary

  • Wire EntitiesScreen to GalleryStorage via AppCtx::storage() — entities load on screen entry and reload on kind switch
  • CRUD keybindings: a create, r rename, d delete — all persisted to SQLite via save_entity/delete_entity
  • Inline name editor with Enter to confirm and Esc to cancel (cancelling a create removes the placeholder)
  • Enter/i injects the selected entity's prompt (or name fallback) into the Generate screen via new AppAction::AppendToPrompt
  • Screen::append_to_prompt() default trait method, overridden by GenerateScreen to comma-append text

Test plan

  • cargo clippy -p loom-tui -- -D warnings clean
  • cargo test -p loom-tui — 119 tests pass
  • Manual: navigate to Entities, press a, type a name, confirm with Enter — entity persists across kind switches
  • Manual: select entity, press r, edit name, confirm — rename persists
  • Manual: select entity, press d — entity removed from list and storage
  • Manual: select entity with a prompt, press Enter — navigates to Generate with prompt appended

Closes charles/loom#90

🤖 Generated with Claude Code

## Summary - Wire `EntitiesScreen` to `GalleryStorage` via `AppCtx::storage()` — entities load on screen entry and reload on kind switch - CRUD keybindings: `a` create, `r` rename, `d` delete — all persisted to SQLite via `save_entity`/`delete_entity` - Inline name editor with `Enter` to confirm and `Esc` to cancel (cancelling a create removes the placeholder) - `Enter`/`i` injects the selected entity's prompt (or name fallback) into the Generate screen via new `AppAction::AppendToPrompt` - `Screen::append_to_prompt()` default trait method, overridden by `GenerateScreen` to comma-append text ## Test plan - [x] `cargo clippy -p loom-tui -- -D warnings` clean - [x] `cargo test -p loom-tui` — 119 tests pass - [ ] Manual: navigate to Entities, press `a`, type a name, confirm with Enter — entity persists across kind switches - [ ] Manual: select entity, press `r`, edit name, confirm — rename persists - [ ] Manual: select entity, press `d` — entity removed from list and storage - [ ] Manual: select entity with a prompt, press `Enter` — navigates to Generate with prompt appended Closes charles/loom#90 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Connect EntitiesScreen to GalleryStorage via AppCtx for full entity
lifecycle: list on screen entry, create (a), rename (r), delete (d),
and inject prompt into Generate screen (Enter/i) via new
AppAction::AppendToPrompt. Inline name editor with Esc to cancel.

Closes charles/loom#90

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

Review — PR #101: Entity screen wired to storage for CRUD & prompt injection

Solid. CRUD + prompt injection + inline rename all work cleanly. Good test coverage.

Concern: single-key delete without confirmation

Pressing d immediately deletes the selected entity from storage. For a destructive operation, consider requiring the confirm dialog (from #42) or a dd chord like the gallery screen does. Accidental deletion with no undo path is risky.

Concern: load_from_storage called in both handle and render

Both methods check !self.loaded and call load_from_storage. This is defensive but means if render is called before handle (which happens on first paint), storage is read during the render path. The read itself is fast (sqlite), but holding std::sync::Mutex on GalleryStorage during render could contend with other screens doing writes. Consider loading only in handle (or on a dedicated Event::Tick) and skipping the render-path load.

Nit

  • Entity::new(String::new(), self.kind) creates an entity with an empty name. If the user presses a then Enter quickly (empty name), the code removes the placeholder — good. But if they press a then type a name, Entity::new has already been called with an empty name, and the id is set at creation time. If the id is a UUID that's fine, but if it derives from the name, the id would be stale. Verify Entity::new uses UUID-based ids.
  • AppHandle.tx changed to pub(crate) — same change appears in PRs #107, #109. Coordinate to avoid merge conflicts.
## Review — PR #101: Entity screen wired to storage for CRUD & prompt injection **Solid.** CRUD + prompt injection + inline rename all work cleanly. Good test coverage. ### Concern: single-key delete without confirmation Pressing `d` immediately deletes the selected entity from storage. For a destructive operation, consider requiring the confirm dialog (from #42) or a `dd` chord like the gallery screen does. Accidental deletion with no undo path is risky. ### Concern: `load_from_storage` called in both `handle` and `render` Both methods check `!self.loaded` and call `load_from_storage`. This is defensive but means if `render` is called before `handle` (which happens on first paint), storage is read during the render path. The read itself is fast (sqlite), but holding `std::sync::Mutex` on `GalleryStorage` during render could contend with other screens doing writes. Consider loading only in `handle` (or on a dedicated `Event::Tick`) and skipping the render-path load. ### Nit - `Entity::new(String::new(), self.kind)` creates an entity with an empty name. If the user presses `a` then `Enter` quickly (empty name), the code removes the placeholder — good. But if they press `a` then type a name, `Entity::new` has already been called with an empty name, and the `id` is set at creation time. If the id is a UUID that's fine, but if it derives from the name, the id would be stale. Verify `Entity::new` uses UUID-based ids. - `AppHandle.tx` changed to `pub(crate)` — same change appears in PRs #107, #109. Coordinate to avoid merge conflicts.
charles force-pushed tui/entity-wire-90 from 8d0fcad0af to 71347afa97 2026-04-12 14:47:36 +00:00 Compare
claude-desktop changed target branch from tui/image-ctx-85 to main 2026-04-12 15:36:29 +00:00
claude-desktop deleted branch tui/entity-wire-90 2026-04-12 15:36:47 +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!101
No description provided.