Skip to content

fix(ui): contain non-square item art and keep Details open across scans (#60, #61) - #63

Merged
DysektAI merged 8 commits into
masterfrom
fix/scan-result-card-art-and-details
Aug 15, 2026
Merged

fix(ui): contain non-square item art and keep Details open across scans (#60, #61)#63
DysektAI merged 8 commits into
masterfrom
fix/scan-result-card-art-and-details

Conversation

@DysektAI

@DysektAI DysektAI commented Aug 15, 2026

Copy link
Copy Markdown
Member

Fixes #60 and #61 in one batch of scan-result-card fixes.

#61 — Details dropdown auto-collapses on every scan

RefreshResult() unconditionally reset _detailsOpen = false, and every scan enqueue raises MenuVM.PropertyChanged synchronously, so with Auto Scan on the Details section collapsed on every scan.

  • Details state now lives in a small testable ResultCardUiState (src/App/Presentation/ResultCardUiState.cs).
  • Scan-driven refreshes preserve the user's Details choice; only explicit actions (toggle, recent-scan click, search pick) close it. SelectRecent and SelectItem keep collapsing deliberately.
  • Bonus correctness fix surfaced by the new UI test: aria-expanded="@_uiState.DetailsOpen" was rendered with a bool, and Blazor omits bool attributes when false — the toggle never exposed its expanded state while collapsed. It now renders an explicit lowercase "true"/"false" string.

#60 — Non-square item icons overflow the art frame

Reproduced in isolation: percentage max-height on the grid item is ignored by Chromium (indefinite grid-area height), so the 64×127 Pevko icon painted 73×146 below the 88×64 frame onto the verdict row — and square icons overflowed slightly too (68×68).

  • .item-art img is now absolutely positioned to the frame box (5px inset) with object-fit: contain — deterministic at every breakpoint, letterboxes without cropping/stretching.
  • overflow: hidden on the frame as a clip invariant.

Verification

  • New unit suite ResultCardUiStateTests (7 facts) locking the scan-driven preserve rule — the UI harness can't reach a scan-driven refresh without a game client.
  • New durable UI fact Scan_result_card_contains_non_square_icons_and_details_state_follows_user_actions: seeds a tiny offline catalog (Pevko 1×2 portrait, Makarov PM 2×1 landscape, M4A1 square, all with locally installed icons) and asserts art containment at desktop + narrow breakpoints and the Details user-action contract via aria-expanded.
  • The existing WebView smoke test was refactored into a reusable UiSession (launch/CDP/trace/cleanup) shared by both facts — original assertions unchanged, still passes.
  • scripts/verify.ps1 -Mode Fast passes (csharpier, markdown lint, agent docs, Debug build + analyzer gate, 368 unit tests); Release build and unit tests pass; full UI suite (2/2) passes with screenshots under artifacts/ui-tests/.

Summary by cubic

Contain non-square result-card item art and keep the Details section open across scan-driven refreshes. Previously, tall/square icons overflowed the frame and Details collapsed on every auto scan; now art is letterboxed and Details only closes on explicit user actions, with aria-expanded rendered as an explicit "true"/"false" string.

  • Move Details/copy state into ResultCardUiState so scan-driven refreshes preserve it; ToggleDetails, SelectRecent, and SelectItem collapse it.
  • Contain art by absolutely positioning .item-art img with a 5px inset, object-fit: contain, and an overflow: hidden frame.

Tests and hardening

  • Add ResultCardUiState unit tests and a durable UI test verifying art containment and the Details user-action contract at desktop and narrow breakpoints.
  • Extract a reusable UiSession (launch/CDP/trace/cleanup); on startup failure, stop the owned process, dispose Playwright/browser, and delete the temp profile.
  • Seed a hermetic offline catalog for items, tasks_v2, hideout, crafts, barters, and maps; constructor rolls back only owned files, logs rollback and cleanup failures to catalog-seed-cleanup-errors.log, and Dispose never throws.
  • Search selection matches the ShortName exactly; resize and failure-capture paths honor cancellation; docs note result-card coverage in the UI smoke scope.

Written for commit 42e4b0c. Summary will update on new commits.

RefreshResult() reset _detailsOpen on every scan-driven PropertyChanged,
collapsing the Details section every time Auto Scan enqueued a result.
The user's Details choice now lives in ResultCardUiState and is preserved
by scan-driven refreshes; only explicit user actions (toggle, recent-scan
click, search pick) change it.

Also render aria-expanded as an explicit lowercase string: Blazor omits
bool-valued attributes when false, so the toggle never exposed its
expanded state while collapsed.
Percentage max-width/max-height on a grid item can leave the grid area
height indefinite in Chromium, so tall intrinsic icons (e.g. the 64x127
Pevko grid image) painted below the 88x64 frame onto the verdict row;
square icons overflowed slightly too. The image is now absolutely
positioned to the frame box (5px inset) with object-fit: contain, which
letterboxes any aspect ratio without cropping or stretching, plus an
overflow: hidden invariant on the frame.
Extract the launch/CDP/cleanup machinery from the WebView smoke test into
a reusable UiSession so a second durable fact can share it, and add a
regression fact that:

- seeds a tiny offline catalog (portrait 1x2 Pevko, landscape 2x1
  Makarov PM, square M4A1 with locally installed icons) so the result
  card renders hermetically without network or a developer cache;
- asserts the art image stays inside the frame at desktop and narrow
  breakpoints for portrait, landscape, and square icons (#60);
- asserts Details follows user actions: closed after a deliberate search
  pick, open via the toggle, closed again by a deliberate switch and by
  a recent-scan click, with aria-expanded exposed throughout (#61).

Unit coverage for the scan-driven preserve rule lives in
ResultCardUiStateTests; the harness cannot reach a scan-driven refresh
without a game client.
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Details panels now remain open when scan results refresh.
    • Selecting a different result resets selection-specific details and copy status.
    • Copy status updates correctly for successful and failed clipboard actions.
    • Result icons now fit correctly within their frames, including portrait, landscape, and square images.
  • Tests

    • Added coverage for details behavior, copy status, icon layouts, recent scans, and narrow-window displays.

Walkthrough

The PR centralizes result-card UI state, constrains non-square artwork within its frame, and adds catalog-backed WebView coverage. The UI test harness now manages application resources, diagnostics, artifacts, process shutdown, and cache fixtures through shared session components.

Changes

Result card UI behavior

Layer / File(s) Summary
Centralized result-card state and transitions
src/App/Presentation/ResultCardUiState.cs, src/App/Pages/App/Index.razor, tests/RatScanner.Tests/ResultCardUiStateTests.cs
ResultCardUiState now controls details visibility and copied-item status. Refreshes preserve open details, item selection closes details, and clipboard actions update copied state. Unit tests cover these transitions.
Constrained result artwork and UI regression coverage
src/App/Pages/App/Index.razor.css, tests/RatScanner.UiTests/WebViewSmokeTests.cs
The artwork frame clips overflow and sizes images within fixed bounds. The UI test covers portrait, landscape, and square icons, details state, recent-scan selection, and narrow layouts.
Shared WebView session and catalog fixtures
tests/RatScanner.UiTests/WebViewSmokeTests.cs
UiSession centralizes startup, Playwright resources, resizing, diagnostics, screenshots, failure evidence, and cleanup. CatalogCacheSeed manages fixture cache files, backups, restoration, and cleanup reporting.

Estimated code review effort: 5 (Critical) | ~90 minutes

Merge Risk: 🟡 Moderate · up to 46f27

The PR fixes localized result-card behavior, but its new UI-test setup can depend on network refreshes, obscure the original test failure during cleanup, or leave existing cache files renamed if setup fails. These bounded test-harness risks should be addressed or explicitly accepted before merge.

Fixed issue severity: Medium

Possibly related PRs

Suggested labels: size:XL

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue [#60] by containing portrait, landscape, and square images without distortion and adding responsive UI regression coverage.
Out of Scope Changes check ✅ Passed The test refactor and cleanup hardening support the new UI regression coverage and do not introduce unrelated product changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes both primary fixes: containing non-square item art and preserving Details state across scans.
Description check ✅ Passed The description directly explains the two fixes, implementation details, regression tests, refactoring, and verification results.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scan-result-card-art-and-details

Comment @coderabbitai help to get the list of available commands.

cubic-dev-ai[bot]

This comment was marked as resolved.

@DysektAI DysektAI self-assigned this Aug 15, 2026
Addresses the PR review finding: if UiSession.StartAsync threw after
Process.Start succeeded (DevToolsActivePort timeout, Playwright connect,
tracing, or endpoint write), the launched RatScanner process, its temp
WebView2 profile, and the run directory leaked — breaking the
single-instance guard for every later UI run. The whole post-start
sequence now shares one failure path that stops the owned process,
disposes any Playwright/browser resources created so far, and best-effort
deletes the profile before rethrowing the original startup failure.

Also hardens the suite while touching this file:
- SelectCatalogItemAsync matches the ShortName <small> exactly instead of
  substring-matching item names, so a query like "PM" cannot land on a
  different list item.
- Cancellation token is plumbed into resize waits and failure-evidence
  writes.
- CatalogCacheSeed cleanup retries transient WebView2 file locks and
  surfaces persistent cleanup failures instead of swallowing them.
Comment thread tests/RatScanner.UiTests/WebViewSmokeTests.cs Outdated
@coderabbitai coderabbitai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Aug 15, 2026
coderabbitai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

Addresses the review round on the previous commit. All three findings
target CatalogCacheSeed teardown/rollback and its hermeticity claim:

- Dispose no longer throws: during a test failure the AggregateException
  would have replaced the real assertion failure while the using scope
  unwound, hiding the evidence MarkFailedAsync captured. Cleanup failures
  are now written to catalog-seed-cleanup-errors.log in the run's own
  artifact directory (UiSession.CreateRunDirectory is shared so the seed
  and session land in the same folder), matching UiSession.DisposeAsync's
  report-don't-throw teardown pattern.

- The constructor rolls back on partial seeding: if backing up or writing
  a fixture file fails, every file moved to .bak is restored and written
  fixtures are removed before rethrowing, so the developer's real cache
  can never be left renamed.

- The cache seed now covers every family the app refreshes at startup
  (tasks_v2, hideout, crafts, barters, maps alongside items). The test
  previously still fetched those catalogs from the network — 'hermetic'
  in name only. Verified in the run log: 0 'Fetching data' lines, all
  core caches loaded from offline storage.
cubic-dev-ai[bot]

This comment was marked as resolved.

If a backup File.Move threw during seeding, the developer's real cache
file stayed at the original path unrecorded, and the previous rollback
(which deleted every cache key before restoring recorded backups) would
delete that unowned file permanently. Seeding now tracks both the
backups moved aside and the paths it wrote; rollback restores the
former and deletes only the latter, leaving every file the seed never
touched exactly as found.
cubic-dev-ai[bot]

This comment was marked as resolved.

RollbackSeeding swallowed restore/delete failures silently, unlike the
same class of cleanup failure in Dispose, which reports to
catalog-seed-cleanup-errors.log. Rollback now collects and writes its
failures to the same diagnostics file (best effort, non-throwing), so a
stranded .bak or leftover fixture file never goes undiagnosed.
@gitar-bot

This comment was marked as resolved.

@sonarqubecloud

This comment was marked as resolved.

@DysektAI
DysektAI merged commit f0e0a24 into master Aug 15, 2026
12 checks passed
@DysektAI
DysektAI deleted the fix/scan-result-card-art-and-details branch August 15, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: non-square item icons overflow the scan result art frame

1 participant