Skip to content

Make the fleet dashboard usable on a phone (touch zoom, narrow layout) - #86

Merged
MJohnson459 merged 1 commit into
mainfrom
fleet-dashboard-phone
Jul 31, 2026
Merged

Make the fleet dashboard usable on a phone (touch zoom, narrow layout)#86
MJohnson459 merged 1 commit into
mainfrom
fleet-dashboard-phone

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

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

  • One pane at a time below 760 px, behind a tab bar at the bottom of the
    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.
  • Picking a robot in the roster navigates to the map — what the desktop
    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.
  • Pinch to zoom, since there is no wheel: one finger pans, two zoom about
    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.
  • A zone picker beside the dispatch box listing the floor's taught zones.
    It writes goto <zone> into the same box rather than sending it, so the
    grammar stays the robot's and no second command language appears in the
    server — it just removes the keyboard from the common case.
  • Phone-specific fixes: 100dvh, env(safe-area-inset-bottom) under the tab
    bar, 16 px inputs (Safari zooms the page on a smaller one and never zooms
    back), touch-action: none on the canvas, finger-sized controls under
    pointer: coarse, and a header/toolbar that wraps instead of hiding the
    map's follow and fit off 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.mjs and
ui_test.mjs reads the stylesheet and holds it there. The pinch arithmetic
(pinchSpan/pinchUpdate) is pure and tested for the 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 this found, all hidden by a desk

  • hidden did not hide. .revisions and .dispatch set display: flex,
    which outranks the attribute's UA-stylesheet display: none — the promote
    picker rendered empty on every floor with no candidate.
  • The canvas backing store was resized on width alone, so a height change
    (tab switch, toolbar rewrap, mobile URL bar) 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.

How it was verified

Against a live stack (container broker on isolated ports, fleet server, three
scripted robots on the office_world bundle):

  • ui_test.mjs 24/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.py64 passed.
  • browser_check.mjs grew a phone pass (390x844, dpr 3, mobile metrics,
    touch emulation, real Input.dispatchTouchEvent): coarse pointer, one pane
    • tab bar, no sideways scroll on any pane, backing store follows the pane,
      roster 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.
  • In a touch-enabled Playwright context: a tap 20 px off a marker selected
    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 dropoff
    chosen 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 triptych
screenshot; 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 safe
area, 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.

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.
@MJohnson459
MJohnson459 merged commit bcda7de into main Jul 31, 2026
4 checks passed
@MJohnson459
MJohnson459 deleted the fleet-dashboard-phone branch July 31, 2026 21:28
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.

1 participant