feat(tui): #140 entity face crop dialog (manual rect editor) #149
No reviewers
Labels
No labels
area:agents
area:ai
area:config
area:dashboard
area:design
area:design-review
area:devtools
area:entities
area:gallery
area:generate
area:image
area:infra
area:meta
area:model-browser
area:navigation
area:presets
area:security
area:sessions
area:settings
area:sharing
area:test
area:ux
area:webhook
area:workdir
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/loom!149
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "tui/face-crop-dialog"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
FaceCropoverlay for manually adjusting an entity's face bounding box on its reference image. Opens viacfrom the entity Detail pane when aface_rectexists, and automatically right after an AI face-detection result so the user can fine-tune before committing.+/-grow/shrink centered,[/]width only,;/'height only. Preset1resets to the AI-detected rect,2snaps to a centered square using the image's min dimension.Enterpersists the normalised rect via storage,Escdiscards.FaceRectmoved from the TUI screen toloom_core::models::entityso it lives onEntity. Adds aface_rect_jsoncolumn to theentitiestable with a migration, wires serde round-trip throughsave_entity/row_to_entity.AppAction::SetEntityFaceRectfor the manual-save path (keeps the existingEntityFaceResultflow for the AI result, which now also opens the editor).Closes charles/loom#140
Test plan
cargo build -p loom-tui -p loom-core -p loom-gtk -p loom-clicargo test -p loom-tui -p loom-core— 157 + core suite passing, including new unit testscargo fmt --all+cargo clippy -p loom-tui -p loom-core --no-depsentitiesscreen → select a character with reference image →Ctrl+Fdetects face → overlay opens → arrows/+/-/[/]/;/'adjust →Entersaves, reload confirms the rect persists.Added unit tests:
face_rect_clamp_within_unit— clamping produces in-bounds rectapply_manual_face_rect_persists_roundtrip— ±1px arrow → save → load retains valueset_face_rect_persists— face_rect ends up on the focused entity🤖 Generated with Claude Code
Nice clean split of
FaceRectintoloom_core::models::entitywith a proper DB migration and serde round-trip. Overall solid, a few notes:Correctness
FaceCropOverlay::reset_centered_squareusesside = iw.min(ih) as i32(full min dimension), whereas the constructor's no-initial fallback usesmin/3. With a non-square image the resulting rect will be non-square afterclamp_px(height or width will be trimmed to fit), so the "2 centered square" preset won't actually produce a square. Either compute a true centered square using the smaller dimension and use that for both w+h, or rename the preset.initial_pxis wrapped inOptionbut is alwaysSome(_). Drop theOption— it makesreset_aisimpler.render_stateful_widgetrewrites it next frame. In practice ratatui-image's protocol handler may overwrite those cells — worth a manual smoke to confirm the outline is actually visible over the rendered image (not just over the placeholder).Nits
ensure_imageswallows every failure silently (path missing, picker lock poisoned, image::open failure). At minimum log atwarnso a user with a broken reference image gets a clue.apply_manual_face_rectandapply_face_resultnow duplicate 8 lines of persistence logic verbatim. Extract apersist_entityhelper.+/-is handled byscale(step * 2, step * 2)— i.e. Shift++grows by 20 pixels. Document that the "×10" in the hint line is only true for arrow translation, not scaling.Review feedback addressed in
214676a:reset_centered_squarenow computes a true centered square frommin(iw, ih)for both width and height and centers it directly — no longer relies onclamp_px(which treats w/h independently) to preserve aspect.Option<_>aroundinitial_px(alwaysSome(_));reset_aiis now a two-liner.persist_entity_face_recthelper fromapply_manual_face_rect+apply_face_resultonEntitiesScreen— DRY'd the clamp/assign/updated_at/save_entityblock.ensure_imagenow emitstracing::warn!on every failure path (missing path, no picker, poisoned lock,image::openerror) with the path for context.Shift ×10from scalingShift ×2.cargo fmt,cargo build,cargo test -p loom-tui,cargo clippy -p loom-tui -- -D warningsall pass.214676a5de5edd4f741b