# Phase 20.1: UI Polish and Bug Fixes - Context

**Gathered:** 2026-04-12
**Status:** Ready for planning

<domain>
## Phase Boundary

Fix accumulated UI bugs, alignment issues, and data integrity problems across the dashboard, product detail sidecar, and GH Issues sync layer. This phase covers 19 specific success criteria spanning avatar visual polish, font consistency, product image pipeline fixes, connection-status messaging, serial unit UI alignment, and data integrity corrections.

</domain>

<decisions>
## Implementation Decisions

### Avatar & Card Visual Refresh
- **D-01:** Avatar size is 40px inner circle + 2px purpose-colored ring = 44px total outer diameter. This is unified across card tiles AND Recipients option grid tiles.
- **D-02:** No subtext repositioning. Instead, grow the recipient name and contact-secondary font sizes to proportionally fill the additional vertical space created by the larger avatar (up from 30px to 44px).
- **D-03:** Recipients option grid tiles get full card-style avatar treatment: 44px avatar with purpose-colored ring, real Discord avatar image when available, initials fallback, tooltip on hover. Tile height grows to accommodate.

### Font Strategy
- **D-04:** Bundle Inter font with the application binary. Inter replaces Slint's default system font for all text rendering across the entire app.
- **D-05:** All existing Typography scale sizes (11px, 12px, 13px, 18px) remain as-is. The font change alone should fix the vertical alignment inconsistencies, especially in chip bar and search bar.

### Product Image Pipeline
- **D-06:** Card item squares use `image-fit: contain` (letterboxed) when a product image is available. The existing colored background shows around edges where the image doesn't fill the square.
- **D-07:** Debug and fix the Shopify image auto-fetch pipeline in `live_client.rs` (sync_products, lines ~1196-1258). The code exists but images are not downloading — this is a bug fix, not new logic.
- **D-08:** Wire the missing Image element in `card.slint` item squares. Currently only the initials fallback renders (`!sq.has-image` branch). Add the `sq.has-image` branch with an Image element using the `image-url` field.

### Connection-Status Empty State Messaging
- **D-09:** Per-state contextual messages replace the current two-state text:
  - State 0 (Not configured): "Open Settings to connect your GitHub project" (keep existing)
  - State 1 (Connecting): "Syncing..."
  - State 2 (Connected): "No orders to show"
  - State 4 (Connected, no Shopify): "Connect Shopify to see orders"

### Claude's Discretion
- Exact font size increases for recipient name and contact-secondary to fill the avatar space (D-02)
- Inter font weight variants to bundle (Regular 400, Medium 500, SemiBold 600, Bold 700 recommended)
- How to load image-url string into a Slint Image source in card item squares (may require Rust-side image loading similar to avatar pipeline)
- Root cause diagnosis of Shopify image download failure
- All remaining 10 success criteria (SC2, SC3, SC8-16, SC17) are specific bug fixes with clear expected behavior — no design decisions needed

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Data Architecture
- `.planning/DATA-FLOW.md` -- Authoritative reference for all data entities, field sources, sync directions

### UI Components (primary modification targets)
- `crates/app/ui/card.slint` -- RecipientCard component, ItemSquareData struct, avatar ring (lines 298-336), item squares (lines 440-470)
- `crates/app/ui/option-grid.slint` -- Recipients option grid tiles (lines 70-100), product tiles with image rendering (lines 183-200)
- `crates/app/ui/product-detail.slint` -- Product detail sidecar with product-image display (lines 70-75)
- `crates/app/ui/dashboard.slint` -- connection-status property (line 104), status dot (lines 449-465), empty state (lines 641-658)
- `crates/app/ui/tokens.slint` -- Colors and Typography globals (font sizes 11/12/13/18px)
- `crates/app/ui/settings-modal.slint` -- Shopify/Discord token sections to unify
- `crates/app/ui/lookup-modal.slint` -- Lookup modal (SC17: ww-product issue creation)
- `crates/app/ui/state-transition-modal.slint` -- State transition modal (SC14: assignment preservation)

### Data Layer & Sync
- `crates/app/src/live_client.rs` -- Sync pipeline, Shopify image auto-fetch (lines 1196-1258)
- `crates/integrations/src/github/product_images_branch_client.rs` -- Product image GitHub branch storage
- `crates/service/src/db/sqlite.rs` -- SqliteStore, assigned_card_id storage
- `crates/app/src/dashboard/assignment.rs` -- Unit assignment logic (SC11, SC14)

### Code Tips
- `code_tips/` -- SQLite gotchas, Slint quirks (must read before modifying code)

### Prior Phase Context
- `.planning/phases/19.1-lookupmodal-dual-mode-parent-product-adds-and-serial-unit-picks/19.1-CONTEXT.md` -- LookupModal is now the unified product/unit entry point
- `.planning/phases/20-offline-mode-hardening/20-CONTEXT.md` -- Offline-first architecture, breathing dot animation, connection-status states

### Todo Files (bug details)
- `.planning/todos/pending/2026-04-12-product-images-not-downloading-from-shopify.md`
- `.planning/todos/pending/2026-04-12-product-images-not-displayed-in-gui.md`
- `.planning/todos/pending/2026-04-11-assigned-card-id-uses-name-not-unique-id.md`
- `.planning/todos/pending/2026-04-11-unit-assignment-not-reflected-in-lookup-and-sidecar.md`
- `.planning/todos/pending/2026-04-12-state-change-unassigns-unit-from-card-even-without-unassign-checkbox.md`
- `.planning/todos/pending/2026-04-12-lookup-modal-product-create-missing-ww-product-issue.md`

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- **Avatar ring pattern** (card.slint:298-336): 30px outer ring + 26px inner circle with purpose-color border, has-avatar-image conditional rendering. Scale up to 44px/40px and replicate in option-grid.slint.
- **ProductImagesBranchClient** (integrations): GitHub orphan branch image storage with upload/download. Already used for product images.
- **ItemSquareData struct** (card.slint:2-14): Already has `has-image` and `image-url` fields. Image rendering element is the only missing piece.
- **Option grid image rendering** (option-grid.slint:183-200): Three-path conditional (has-image / image-hint / neither) already implemented for Product Shipped tiles.

### Established Patterns
- **Typography globals**: `tokens.slint` exports `Typography.size-xs` through `Typography.size-lg`. Font family will be set at Slint app level, not per-component.
- **connection-status property**: Integer 0-4 wired from Rust LiveClient through SyncUpdateCallback. Empty state text already conditional on this value.
- **Image loading**: Avatar images loaded from disk cache via Rust-side `slint::Image::load_from_path()`. Product images likely need the same pattern.

### Integration Points
- **Slint font configuration**: Inter font bundle registered at app startup via Slint's font embedding API
- **Card item square image wiring**: Rust `view_model_to_card_data()` already populates `has-image` and `image-url` — only the Slint Image element is missing
- **Option grid avatar wiring**: `build_option_grid_data()` in discovery.rs needs to populate avatar image and purpose color for recipient tiles

</code_context>

<specifics>
## Specific Ideas

- User explicitly chose 40px inner (not the 46-54px range from the todo) for a moderate but noticeable enlargement
- Unified avatar sizing across cards and Recipients grid — same 44px treatment everywhere
- Inter font chosen after visual comparison of Inter/Noto Sans/Roboto rendered in WITwhat's color scheme and typography scale
- "No orders to show" (not "No orders found") for the connected-but-empty state — softer phrasing
- Letterboxed/contain product images in card squares to preserve aspect ratio at small sizes

</specifics>

<deferred>
## Deferred Ideas

None -- discussion stayed within phase scope.

</deferred>

---

*Phase: 20.1-ui-polish-and-bug-fixes*
*Context gathered: 2026-04-12*
