Skip to content

fix(annotator): the detail slider drags, and Select shows the plain arrow - #565

Merged
JArmandoAnaya merged 2 commits into
mainfrom
fix/detail-slider-drag
Aug 13, 2026
Merged

fix(annotator): the detail slider drags, and Select shows the plain arrow#565
JArmandoAnaya merged 2 commits into
mainfrom
fix/detail-slider-drag

Conversation

@JArmandoAnaya

@JArmandoAnaya JArmandoAnaya commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Two fixes to the annotation editor, both found in the running app rather than by the suite.

Closes #564. Closes #567.

1. The Detail slider could not be moved with the pointer

Dragging the thumb did nothing, clicking the track did nothing, and [ / ] were the only way
to change the setting.

The press was cancelled. The slider carried onMouseDown={keepFocusOnCanvas}, which calls
preventDefault(). That guard exists for a good reason — every chord in the editor is a
keydown on the annotator's own root, so a control that holds focus switches [, ], Esc
and Enter off with nothing on screen to say why — and it is correct on a button. It is wrong
on a range input, because a range input drags on its default action.

The slider now takes focus for the duration of the gesture and hands it back on release
(onMouseUp / onTouchEndreturnFocusToCanvas), which reaches the root the way
FrameGallery already does after its overlay closes.

Why nothing caught it. The test asserted the guard fired
expect(fireEvent.mouseDown(slider)).toBe(false) — which is the one assertion a control that
does nothing at all will always pass. Nothing anywhere dragged the thumb and looked at the
value, and jsdom could not have: a range input's default action does not exist there. Replaced
with a real pointer drag in chromium, plus a jsdom test that the press is not cancelled and
that mouseup returns focus. fill() and locator.click() both set the value without
exercising the default action, so neither would have caught it either.

2. Select mode showed the four-arrow cursor over a shape

Hovering a box, its edge band, or a vertex of a selected polygon showed move. It now shows the
plain arrow.

This reverses a decision the code argued for, so the argument moved with it. The old one:
a press in that band starts a move, so move is what the cursor owes. What it missed is which
outcome is being promised — a press on a shape selects it, and only becomes a move if the
pointer then travels. Selection is the common case by a wide margin, so move advertised the
rarer one on every hover.

Nothing is withheld: hot is unchanged, so the shape still highlights under the pointer, which
says this is what a press would take without claiming what it would then do.
viewerAffordanceAt has held that position since it was written.

Unchanged: a drag actually in flight still answers move, a selected box's grips keep their
directional resize keywords, and drawing tools keep the crosshair.

A consequence worth naming: the cursor no longer distinguishes the read-only viewer from the
editor. Two tests were built on exactly that contrast and would have gone quietly hollow rather
than red — one vitest case and one e2e title — so both are re-pointed at what still separates
the modes, which is the grips. DESIGN.md is amended in the same commit for the same reason.

Red before green

Both changes, by their own mutation:

slider-drag-restored-defect:  NAMED TEST RED: the detail slider moves under the pointer, and hands the keyboard back
cursor-move-restored:         4 failed | 26 passed
  × answers default over a body, and names the body a press would take
  × answers default over a vertex of the picked polygon
  × answers default over an edge, where the press still starts a move
  × will not show a grip on a box nobody picked

The second is worth reading twice. affordance.test.ts also carries a union-coverage test
asserting every Cursor member is actually produced, and it sourced move only from a
hover — so it would have broken as collateral and been "fixed" by deleting move from the
union, taking the two drag states with it. It now sources move from a moving state instead,
and it correctly stayed green under the mutation: only the four hover assertions moved,
which is the shape that proves they are the ones doing the work.

The gate

bash scripts/check.sh frontend    exit=0
  frontend/annotator test:  Tests  1000 passed (1000)
  frontend/ui-core test:    Tests   968 passed (968)
  build, lint: All checks passed.

bash scripts/check.sh generated   exit=0

bash scripts/check.sh browser     exit=1
  annotator e2e (chromium):  255 passed, 2 failed
  browser cycle, real server:  1 passed

Both failures are timeouts, not assertionsmouse.down and page.reload each exceeded
20 s under load, so neither test reached its claim. One is #550's known pair; the other is
perf.spec.ts's mutation-count gate, which passes in isolation along with all six perf
specs
(6 passed (1.2m)) — checked rather than assumed, since a cursor change is exactly the
sort of thing that could add a style mutation. No Python is touched by either change.

Flags for Armando

  • The slider defect was mine, from fix: one setting, one wait, and a box over the whole object #562. The rule it earns: a test that asserts a guard
    fired is not a test that the control works.
    They are independent, and only the second is what
    somebody uses.
  • onTouchEnd is on argument, not evidence — no touch device in CI and I have not driven
    one. If touch is out of scope for the editor, it comes out.

A range input drags on its default action, so the `preventDefault` guarding the
canvas' focus left a slider that looked alive and could only be moved with the
brackets. Focus now goes back to the canvas on release instead, so the drag
works and the chords are live again the moment it ends.

The test that missed it asserted the guard *fired* — the one assertion a dead
control passes. Replaced with a real pointer drag in chromium, which is the only
place a range input's default action exists at all.

cf. #557
Hovering a box, its edge band or a vertex showed the four-arrow move cursor.
A press there selects, and only becomes a move if the pointer travels, so it
advertised the rarer outcome. The hover highlight is unchanged and still
reports which shape a press would take.

A drag in flight keeps move, and a selected box's grips keep their resize
keywords. This reverses a decision argued in the code; the new argument is at
the change site and in DESIGN.md.

cf. #567
@JArmandoAnaya JArmandoAnaya changed the title fix(annotator): the detail slider drags again fix(annotator): the detail slider drags, and Select shows the plain arrow Aug 12, 2026
@JArmandoAnaya
JArmandoAnaya merged commit 094d3ce into main Aug 13, 2026
15 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the fix/detail-slider-drag branch August 13, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Select mode should show the plain arrow over a shape, not the move cursor The Detail slider cannot be moved with the pointer

1 participant