diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index bb770ffc..63d548bd 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -20,6 +20,7 @@ - [Third-party fault reproducers stay out of the suite](third-party-fault-reproducers-stay-out-of-the-suite.md) — a reproducer asserting a library's fault is the failing-first proof only; code goes in the issue and commit body - [Pre-push CI evidence](pre-push-ci-evidence.md) — a passing pre-push hook prints nothing; cite the pushed SHA, count examples with bin/rspec --dry-run - [Turbo Back navigation quirks](turbo-back-navigation-quirks.md) — frame-advance has two races (the late visit cancels Back or a click, issue #62); filters now plain Drive GET visits + no-cache; stuck full-width progress bar = cancelled visit; Back within one frame of a render loses the saved scroll (#404); wait_for_turbo_idle, wait_for_scroll_events +- [Turbo Stream strings are html_safe](turbo-stream-string-is-html-safe.md) — turbo_stream.update(target, "text") inserts the string as HTML; escape card and place names with ERB::Util.html_escape or use a partial (spec 082) - [PR #5 follow-ups](pr5-open-followups.md) — back-button flake fixed by PR #9 (merged 2026-10-02), watch CI; uncaptured failure - [Card scanner direction](card-scanner-direction.md) — spec 011 executed 2026-10-08 (34/35 right card first, margin 300 kept by ruling); next = sdd-review Mode B, then finishing - [Shipped percentile is upper-middle](shipped-percentile-upper-middle.md) — ScannerFindings.percentile(…, 50) takes the upper middle for even n; use one conventional median helper and state it diff --git a/.claude/memory/headless-firefox-narrow-frame.md b/.claude/memory/headless-firefox-narrow-frame.md index 4df89b4d..8485e36f 100644 --- a/.claude/memory/headless-firefox-narrow-frame.md +++ b/.claude/memory/headless-firefox-narrow-frame.md @@ -21,3 +21,4 @@ Related: [[pr-screenshots-workflow]]. - **Keyboard-only selects (spec 041, 2026-11-06):** under WebDriver in headless Firefox, arrow keys don't change a closed ` +

Using a photo? Take the whole card, upright, filling most of the photo as the live guide does, on a plain background.

+ + <% if art %>

Loading artwork matching…

<% end %> + <%= render "layouts/notice_foot" %> + + diff --git a/app/views/scanners/_list_sheet.html.erb b/app/views/scanners/_list_sheet.html.erb new file mode 100644 index 00000000..402dd61d --- /dev/null +++ b/app/views/scanners/_list_sheet.html.erb @@ -0,0 +1,15 @@ +<%# locals: (sitting:, entries:, conditions:, added_any:) %> +<%# The sitting's list as a sheet (spec 082 decision 9): today's heading as its title, focusable for spec 047's focus after an + Undo; its own live region; today's entries; the notice's foot. An answer replaces the title's text and the entries' section, + never the dialog, so an open sheet stays open (decision 15). %> + +
+

<%= sitting_title(sitting, entries, conditions) %>

+
+
+
+
+ <%= render "scanners/sitting", sitting:, entries:, conditions:, added_any: %> + <%= render "layouts/notice_foot" %> +
+
diff --git a/app/views/scanners/_picker.html.erb b/app/views/scanners/_picker.html.erb index 72b4c3a6..57289967 100644 --- a/app/views/scanners/_picker.html.erb +++ b/app/views/scanners/_picker.html.erb @@ -1,21 +1,23 @@ -<%# locals: (condition:, location_id:, conditions:, choices:) %> +<%# locals: (condition:, location_id:, conditions:, choices:, focus: nil) %> <%# The sitting's one control (spec 042 decision 2, spec 040 decision 3): native selects and "Set" in one form, grouped under a - legend, answered by a Turbo Stream that redraws the panel, so it works by keyboard and without script (AC-7.1; a small - controller only puts focus back on "Set" after the redraw, spec 047, from the panel's `sitting_focus_data`). The + legend, in the destination sheet (spec 082 decision 9), answered by a Turbo Stream that redraws it, so it works by keyboard and without script (AC-7.1; a small + controller only puts focus back on "Set" after the redraw, spec 047, from the sheet's `sitting_focus_data`). The fieldset is the choice page's c-choices (a flex column, no border): the two fields stack, as spec 040's did. %> -<%= form_with url: scanner_sitting_path, method: :patch, scope: :sitting, class: "c-form c-scanner__place" do |form| %> -
- This sitting's cards -
- <%= form.label :condition, "Condition", class: "c-field__label" %> - <%= form.select :condition, options_for_select([ [ "Not specified", "" ], *conditions.map { |key, (label, short)| [ "#{label} (#{short})", key ] } ], condition.to_s) %> -
- <% if choices.any? %> +<%= tag.div id: "scanner_picker", data: sitting_focus_data(focus) do %> + <%= form_with url: scanner_sitting_path, method: :patch, scope: :sitting, class: "c-form c-scanner__place" do |form| %> +
+ This sitting's cards
- <%= form.label :location_id, "Location", class: "c-field__label" %> - <%= form.select :location_id, location_options(choices, selected: location_id.to_s, leading: [ [ "No location", "" ] ]) %> + <%= form.label :condition, "Condition", class: "c-field__label" %> + <%= form.select :condition, options_for_select([ [ "Not specified", "" ], *conditions.map { |key, (label, short)| [ "#{label} (#{short})", key ] } ], condition.to_s) %>
- <% end %> -
-
<%= form.submit "Set", class: "c-btn c-btn--secondary c-btn--sm", data: { sitting_focus_target: "set" } %>
+ <% if choices.any? %> +
+ <%= form.label :location_id, "Location", class: "c-field__label" %> + <%= form.select :location_id, location_options(choices, selected: location_id.to_s, leading: [ [ "No location", "" ] ]) %> +
+ <% end %> +
+
<%= form.submit "Set", class: "c-btn c-btn--secondary c-btn--sm", data: { sitting_focus_target: "set" } %>
+ <% end %> <% end %> diff --git a/app/views/scanners/_scanner.html.erb b/app/views/scanners/_scanner.html.erb index 47880ed7..b939615d 100644 --- a/app/views/scanners/_scanner.html.erb +++ b/app/views/scanners/_scanner.html.erb @@ -1,43 +1,61 @@ -<%# locals: (sitting:, entries:, conditions:, choices:, added_any:, summary: nil) %> +<%# locals: (sitting:, entries:, conditions:, choices:, added_any:, heading:, line: nil, unloaded: [], named: false, measurement: nil, framing: "full") %> +<%# The one-screen scanner (spec 082; the shell is spec 080's `c-scan`): a top bar with the sitting's destination, the camera's + stage, a bottom bar whose main button is always in view, the pane a result opens in, and the two sheets that keep the camera + running (decision 9). State is in the markup (decision 19): `scan-shell` sets `c-scan--result` and the lines' `hidden` from + what the regions hold. Measurement mode renders the same shell with its run button and sheet (decision 5). %> <% art = scanner_art %> -
data-card-reader-art-index-url-value="<%= scanner_art_index_path(art[:name]) %>" data-card-reader-art-settings-value="<%= art[:settings].to_json %>"<% end %> - data-action="camera:ready->card-reader#cameraReady camera:stopped->card-reader#cameraStopped camera:unavailable->card-reader#cameraUnavailable"> -

Loading the scanner…

-