Skip to content

feat(ui): mark every arm-driving control; explicit view buttons on library cards - #108

Open
Mokuroh54 wants to merge 2 commits into
stagingfrom
feat/robot-action-affordance
Open

feat(ui): mark every arm-driving control; explicit view buttons on library cards#108
Mokuroh54 wants to merge 2 commits into
stagingfrom
feat/robot-action-affordance

Conversation

@Mokuroh54

@Mokuroh54 Mokuroh54 commented Sep 2, 2026

Copy link
Copy Markdown

Now based directly on staging (was stacked on #107). The two commits were rebased off feat/studio-panels-rework onto staging on 2026-09-03; #107 is no longer a prerequisite. #113 (fix/rtc-backend-guard) still has this branch as its base and needs the same rebase.

Robot-action affordance

Makes it unmistakable which buttons drive the arm, with one shared affordance and one source of truth.

  • robot Button variant (amber; new --robot/* tokens in both themes, 8.8:1 light / 10.8:1 dark) and the existing destructive for anything that ends with the arm de-energized.
  • RobotActionButton / ReleaseActionButton wrappers pin the icon (Zap / PowerOff) and a mandatory tooltip from lib/robotActions.ts, keyed by session kind. A vitest parity test asserts the energizing kinds equal SessionStartBody.kind in docs/api/openapi.json, and fails loudly if that enum disappears.
  • 21 buttons in 9 components switched. Amber: teleop, record, both inference starts, replay, manual/auto/zero-pose calibration, wiggle. Red: every Stop / Quit / Abort / Cancel-calibration. Read-only controls (detect port, calibrate-all picker, libraries, training, settings) stay neutral.
  • Tooltips in en and zh-CN.

Deliberate side effects of the rule: recording's Done (was green) and Quit are both red now; the inference Abort buttons are solid red rather than outline.

Not included: a header "arm held" chip. There is no shared page chrome, and session_changed carries no session id, so the chip needs a per-hint fetch of the current session. Deferred.

Library cards

  • Dataset card: click toggles selection (role=button, Enter/Space, focus ring). Footer Select → outline View dataset (Eye) that opens the episode viewer, rendered only where a viewer is wired. Click-to-view removed. CollectPanel is the only consumer and needed no change; selectedDataset drives only the header chip and the Train panel.
  • Run card: card click and hover affordance removed; View run leads the action row, hidden for imported jobs as the click was. HubJobCard's click opens an external Hub page and is left as is.
  • library.datasets.select/selected are dead keys, kept for a separate catalog cleanup.

Re-applied onto staging (2026-09-03)

staging restructured the calibration panel since this was written (1a917364: one control per arm row, mode picked first), so the RobotConfigDialog hunks were re-applied by hand against that layout:

  • The Auto / Manual mode toggles stay neutral: they pick a flow, they do not start one. The per-mode Start buttons underneath (manual, auto, CAN zero pose) are the amber ones, plus the batch picker's Start and the pre-start confirm.
  • Wiggle is now an icon-only control inside DeviceSlotCell. It becomes an amber icon RobotActionButton (bolt replaces the hand glyph, spinner while wiggling); the native title comes off so the shared tooltip does not fire beside it, and the aria-label keeps the icon named. Hand and Square imports drop as a result.
  • CollectPanel's Start keeps staging's mt-auto pin wrapper; only the button inside changes.
  • The deploy panel has no Start/Stop pair on this base: the coaching work (Coaching sessions (DAgger) #95) replaced it with RunVerbs. Its verbs cannot take RobotActionButton (their variant is the armed-state channel, their title carries the blocked reason, and they use aria-disabled on purpose), so the affordance rides on the icon alone: each verb's Play glyph is the bolt (second commit). variant, title, aria-disabled, the ring and RunVerbs.test.tsx are untouched.
  • InferenceSessionDialog's surviving Stop and both Abort buttons keep the red release treatment. The coaching flow's quiet "End session" ghost button also ends the session de-energized but was deliberately left quiet by its author; listed here, not converted.

Checks

Against staging at 526e0412: tsc both projects clean; vitest 294/294 (29 files) including the parity and i18n tests; lint 38 problems before and after (all pre-existing); npm run build ok; frontend/dist and package-lock.json untouched.

Manual checklist: amber legible in both themes; hover tooltips repaint on language switch; disabled Teleop chip shows the setup-gap reason, not the robot tooltip; card click selects, View dataset opens the viewer without changing selection; View run opens the monitor; the slot-cell wiggle bolt spins while wiggling.

🤖 Generated with Claude Code

Mokuroh54 and others added 2 commits September 3, 2026 08:08
…icit view buttons

Robot-action affordance
- Button gains a `robot` variant (amber, new --robot/* tokens in both
  themes) and a `destructive` reuse for anything that ends with the arm
  de-energized. `RobotActionButton` / `ReleaseActionButton` wrappers pin
  the icon (Zap / PowerOff) and a mandatory tooltip resolved from
  `lib/robotActions.ts`, keyed by session kind; a vitest parity test ties
  the energizing kinds to `SessionStartBody.kind` in the OpenAPI snapshot.
- 21 buttons across 9 components switched: every session starter
  (teleop, record, both inference starts, replay, manual/auto/zero-pose
  calibration, wiggle) is amber; every Stop / Quit / Abort / Cancel-
  calibration is red. Read-only controls stay neutral so the signal keeps
  its meaning. No release-torque UI is added (none exists).
- Tooltip strings in en + zh-CN.

Library cards
- Dataset card: clicking the card toggles selection (role=button,
  keyboard, focus ring); the footer Select is replaced by an outline
  "View dataset" button that opens the episode viewer and renders only
  where a viewer is wired. Click-to-view is removed.
- Run card: card-level click and hover affordance removed; a "View run"
  button leads the action row, hidden for imported jobs as the click was.
- `library.datasets.select/selected` are now dead keys, kept for a
  separate catalog cleanup.

Cosmetic only: no request, storage or form-value change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The coaching work replaced the panel's Start/Stop pair with RunVerbs, whose
three verbs all start an inference session. Their `variant` is the
armed-state channel and their `title` carries the blocked reason, so they
cannot take RobotActionButton; the shared affordance rides on the icon
alone: the Play glyph inside each label becomes the bolt from
lib/robotActions.ts. Variants, title, aria-disabled and the ring are
untouched, and RunVerbs.test.tsx asserts nothing about the icon.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Mokuroh54
Mokuroh54 changed the base branch from feat/studio-panels-rework to staging September 3, 2026 15:10
@Mokuroh54
Mokuroh54 force-pushed the feat/robot-action-affordance branch from facc738 to a2d3657 Compare September 3, 2026 15:10
Mokuroh54 added a commit that referenced this pull request Sep 3, 2026
Slice 3.4 of the DRTC integration (design: docs/drtc/SLICE3.md, "S3.4 as
built"). Remote inference is now startable, watched and stopped from the
Deploy panel; the SFU and the Modal policy server are still started by
hand, which is what the generated command line is for.

- Deploy panel: a fourth run mode, "Run it remotely" (verb grid 3 → 2x2),
  reusing the panel's existing checkpoint derivation of camera_bindings /
  camera_dims / checkpoint_state_dim / task and forking only at
  startSession(kind="remote_inference"). Remote-only fields: Hub policy id,
  horizon, fps, video codec, max duration. The ACT temporal-ensemble
  control, the engine picker and the shared duration field are hidden for
  this mode. Guards: armSupportsRemote (single SO-101 only — CAN arms and
  bimanual refused, checked first) and transportReady (the transport
  endpoint must report configured, reachable and an operator in the room;
  "not ready" while the probe is in flight). The heartbeat is held by
  DeployPanel itself, which StudioOverlay keeps mounted, so closing the
  overlay does not let the lease expire.
- The generated `modal run` line (components/remote-inference/
  modalCommand.ts, asserted verbatim in tests): built from the same
  options object the session is started with, plus the transport's
  url/room, so the two halves cannot disagree on horizon/fps/codec/room —
  Portal drops mismatched packets silently, and this is the mitigation.
  Carries --task (shell-quoted) when set; on a local-SFU override adds
  --tailscale --livekit-url and key/secret as literal
  <from livekit.local.yaml> placeholders — the API never exposes secrets.
- Status panel: the eleven-phase ladder from remote_inference.py,
  returning_to_rest during stopping, elapsed/duration, operator,
  chunks/requests, a lead-vs-(horizon - s_min) margin bar, a DEGRADE badge,
  chunk age, e2e p50/p95, rtt, and holds as a per-second RATE (a healthy
  run's cumulative holds freezes after warm-up, so the counter would
  mislead forever after); outcome/error/hint and the log path when a run
  ends.
- Transport section: source (which credential layer supplied the URL),
  url/room, missing variables, the local-override file with a delete
  button (idempotent; never touches livekit.local.yaml), reachable /
  operator-present as a three-state (null = not probed), error code and
  message verbatim.
- Hooks: useRemoteInferenceStatus polls at 1 Hz while a remote session is
  live regardless of the studio being open, refetching eagerly on a
  session_changed hint — the first real consumer of useSessionEvent;
  useRemoteInferenceTransport fetches on demand and exposes
  clearLocalOverride(). Every request targets /api/v1.
- i18n: a new `remoteInference` namespace (en + zh-CN) rather than growing
  `studio`, keeping the shared catalog diff small for the studio-rework
  rebase; codec ids, room, URL, error codes and every character of the
  generated command are data and are not translated.

The whole surface lives under frontend/src/components/remote-inference/;
the footprint in shared files is one run-mode entry, two guard flags, two
hooks and one mounted element in DeployPanel.tsx / deployGuards.ts, so
the rebase across PRs #107/#108 stays small. Known limitation: the block
renders inside the Deploy panel's "skill selected" section, so a run
started elsewhere is not visible until a skill is picked.

Checks: lint delta 0 against the 38-problem baseline; both tsc projects
clean; vitest 27 files / 292 tests; build ok with frontend/dist restored
(CI rebuilds it). No package-lock change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Mokuroh54 added a commit that referenced this pull request Sep 3, 2026
Brings the run-form opener, single-popover policy/checkpoint pickers,
stretched libraries and picker badges (origin/feat/studio-panels-rework
@ 20ae38d) onto the DRTC branch, so the checkpoint is picked from one
popover instead of the old flow. PR #108 (button affordance) deliberately
not included.

Three conflicts resolved: DeployPanel.tsx was rebuilt on the rework's
render body with slice 3.4's remote-inference mode re-expressed on it
(fourth verb, guard flags, the two hooks, the panel-held heartbeat,
runActive feeding the guards and camera-preview pause, remote-hidden
controls, the startSession(kind="remote_inference") fork reusing the
reworked camera derivation, and <RemoteInferenceBlock> now inside the run
form with an unchanged visibility rule). The run form is held open while
a remote run is live (open={formOpen || remoteActive}) because remote's
live surface — telemetry and Stop — is inline in the panel rather than
in the session dialog the local modes open. The two studio catalogs are
the union of both sides; PR #107's new keys arrived in the retired
"skill" vocabulary and were merged under the policy spellings
(deploy.entry "Run a policy", deploy.policy.label,
deploy.checkpoint.pickPolicyFirst). Nothing under
components/remote-inference/ changed.

Checks on the merged tree: lint 38 problems (4 errors) — identical to
both parents' baselines, zero in touched files; both tsc projects clean;
vitest 27 files / 292 tests; build ok with frontend/dist restored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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