feat(review): the review stage fills, its arrows sit at the picture, 1:1 pans (r2c)

The operator: "fit and 1:1 modes as well as moving the forward and back
arrows closer to the edge of the image ... mouse click and pan for 1:1
mode if it exceeds page width (defeat drag drop of image)". Ruled: "Fit
may enlarge."

- Fit: the picture's box is the stage's inner box, and object-fit: contain
  draws it whole at the largest size that fits, up or down, never
  cropped. It works with or without JS. 1:1 is natural pixels.
- The Fit | 1:1 toggle shows for every picture; the per-picture hide is
  gone. It stays hidden without JS.
- The mode persists as `stage-one` on <html>, set by the head script
  before the stage exists, so a 1:1 reel never paints a stage in Fit.
  Anything stored but "one" reads as Fit. Storage never raises.
- The arrows sit wholly outside the DRAWN picture (near edge 8px),
  clamped 8px inside the stage. They sit over the picture only when it
  spans the stage, and never over the rail. They are re-placed on load,
  resize, mode switch and 1:1 scroll, and keep their CSS spot until the
  drawn box is known.
- 1:1 drag-to-pan when the picture overflows either axis: the picture
  follows the pointer, a 4px threshold, pointer capture, grab/grabbing.
  The picture is draggable=false. The stage's reveal button moves out of
  the scrolled content to sit over the stage (a pan carried it off), so
  no control is a pan source.

Contract docs/contracts/r2c_review_stage.contract.md (heid contract
panel 4/4 folded; it changed the no-flash mechanism). Declared test
changes: the Nyx stage-edge arrow test is replaced; the stage class and
the toggle's `hidden` are updated. tests/mutations/r2c.toml 16/16. 803
passed.
This commit is contained in:
vh
2026-09-24 00:20:15 -07:00
parent 0781aa5ee5
commit 7151a45ec2
6 changed files with 667 additions and 58 deletions
+3 -1
View File
@@ -586,7 +586,9 @@ def test_only_a_picture_gets_the_fit_toggle_and_blur_stays_honest(tmp_path):
c = _client(tmp_path)
pic = c.get("/b/g/view?f=a.png").text
assert 'id="vtoggle"' in pic and ">Fit<" in pic and ">1:1<" in pic
assert 'class="vstage fit is-blurred"' in pic and "blur is cosmetic" in pic
# r2c: the mode moved to <html> (never a Fit flash); the stage says only what it holds
assert 'class="vstage is-img is-blurred"' in pic and "blur is cosmetic" in pic
assert re.search(r'<span class="vtoggle" id="vtoggle" hidden', pic)
vid = c.get("/b/g/view?f=v.webm").text
assert 'id="vtoggle"' not in vid and re.search(r"<video\b[^>]*\bcontrols\b", vid)