Make the fleet dashboard usable on a phone (touch zoom, narrow layout) - #86
Merged
Conversation
The M3 dashboard's network path was verified from a phone on cellular (m3-verification.md §6), but the layout was a desk's: three columns collapsing to three stacked panes, so a 390 px screen got a map canvas a couple of hundred pixels tall with the roster above it and the detail pane below the fold. The phone is the realistic off-LAN client — it is what an operator has in a corridor, which is exactly where "where is the robot and what is it doing" gets asked. Below 760 px the panes become one at a time behind a bottom tab bar, within thumb reach, and the map gets the whole screen. Two things follow from losing the side-by-side view: picking a robot in the roster now navigates to the map (the desktop layout gets that for free by showing both), and the canvas gained pinch-to-zoom, there being no wheel. One finger pans, two zoom about the point between them, and a two-finger drag carries the map along; markers get a larger hit target when the pointer is a fingertip. Dispatch gained a zone picker that *writes* `goto <zone>` into the command box rather than sending it — the grammar stays the robot's, and the keyboard leaves the common case. The breakpoint is a silent seam: CSS decides what is displayed, JS decides when a selection navigates, and disagreement yields a tab bar over stacked panes rather than an error. It lives in layout.mjs and ui_test.mjs reads the stylesheet and holds it there — as it does for every pane having a tab, and for `touch-action: none` on the canvas, without which the browser eats both gestures before a pointer event arrives. The pinch arithmetic is pure and tested for the same reason the Q5 transform is: a division by a zero span puts NaN in the view scale and blanks the map for good. Three pre-existing bugs fell out, all of which a desk hides: - `hidden` did not hide. `.revisions` and `.dispatch` set `display: flex`, which outranks the attribute's UA-stylesheet `display: none`, so the promote picker rendered empty on every floor with no candidate. - The canvas backing store was resized on width alone, so a height change — a tab switch, the toolbar rewrapping, a mobile URL bar sliding away — left `clearRect` unable to reach the bottom of it and the previous frame's scale bar stayed on screen under the new one. - The scale bar was drawn in the dark theme's near-white over a basemap whose free space is white. A canvas gets no cascade, so it now reads `--dim` off the element and is legible in both themes. Verified against a live stack (container broker, fleet server, three scripted robots on the office_world bundle): ui_test.mjs 24/24 under node, the fleet server and registry suites 64/64, and browser_check.mjs grew a phone pass — 390x844 at device scale 3 with mobile metrics and touch emulation, driven by real Input.dispatchTouchEvent gestures. In a touch-enabled Playwright context a tap 20 px off a marker selected it, and after a pinch centred on that marker a tap at the same point still did, which is what shows the zoom anchors where the fingers are. A `goto dropoff` chosen from the picker dispatched through the API and wrote the expected audit row. Measurements in m3-verification.md §9. A real device is still the acceptance: emulation gets the viewport and the touch points right and the thumb wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Make the fleet dashboard usable one-handed on a phone.
M3's network path was already verified from a phone on cellular
(m3-verification.md §6); the layout was still a desk's — three columns
collapsing to three stacked panes, so a 390 px screen got a map canvas a
couple of hundred pixels tall with the roster above it and the detail pane
below the fold.
What changed
screen (thumb reach), so the map gets the whole display. Between 760 and
1100 px the panes stack and scroll as before; the desktop layout is
untouched.
layout gets for free by showing both panes. The third tab is labelled with
the selected robot's id, so the selection is legible without switching.
the point between them, a two-finger drag carries the map along. Robot
markers get a 24 px hit target under a coarse pointer instead of 14 px.
It writes
goto <zone>into the same box rather than sending it, so thegrammar stays the robot's and no second command language appears in the
server — it just removes the keyboard from the common case.
100dvh,env(safe-area-inset-bottom)under the tabbar, 16 px inputs (Safari zooms the page on a smaller one and never zooms
back),
touch-action: noneon the canvas, finger-sized controls underpointer: coarse, and a header/toolbar that wraps instead of hiding themap's
followandfitoff the right edge.The breakpoint is a silent seam — CSS decides what is displayed, JS decides
when a selection navigates, and disagreement gives a tab bar over stacked
panes rather than an error — so it lives in the new
server/ui/layout.mjsandui_test.mjsreads the stylesheet and holds it there. The pinch arithmetic(
pinchSpan/pinchUpdate) is pure and tested for the reason the Q5 transformis: a division by a zero span puts NaN in the view scale and blanks the map
for good.
Three pre-existing bugs this found, all hidden by a desk
hiddendid not hide..revisionsand.dispatchsetdisplay: flex,which outranks the attribute's UA-stylesheet
display: none— the promotepicker rendered empty on every floor with no candidate.
(tab switch, toolbar rewrap, mobile URL bar) left
clearRectunable toreach the bottom of it and the previous frame's scale bar stayed on screen
under the new one.
whose free space is white. A canvas gets no cascade, so it now reads
--dimoff the element and is legible in both themes.How it was verified
Against a live stack (container broker on isolated ports, fleet server, three
scripted robots on the
office_worldbundle):ui_test.mjs24/24 under node — the pinch maths, the CSS/JS breakpoint,every pane having a tab,
touch-action: none.test_ui.py,test_fleet_server.py,test_map_registry.py— 64 passed.browser_check.mjsgrew a phone pass (390x844, dpr 3, mobile metrics,touch emulation, real
Input.dispatchTouchEvent): coarse pointer, one paneroster tap lands on the map, two fingers zoom. 14/15 — the one failure
is "the robot answered on task/status", expected here because the scripted
robots publish state but run no task server.
it (inside the touch target, outside the mouse one), and after a pinch
centred on that marker a tap at the same screen point still selected it —
which is what shows the zoom anchors where the fingers are. A
goto dropoffchosen from the picker dispatched through the API and wrote the expected
audit row. Desktop at 1600x900 re-checked in both colour schemes.
Docs:
docs/fleet/README.md§9 gained an "On a phone" section and a triptychscreenshot; measurements and the three bugs are
m3-verification.md§9;CLAUDE.md's M3 paragraph and
mote_fleet/README.md's file table updated.A real device is still the acceptance — emulation gets the viewport and
the touch points right and the thumb wrong. iOS Safari's
100dvh, the safearea, the input-zoom rule and the on-screen keyboard are confirmed only in
Chrome. Filed as task #309.
Branch
fleet-dashboard-phone, commit d300a8d. Not pushed.