The dashboard is fully keyboard-driven. Global keys work from any panel; the rest are scoped to the focused panel. The help overlay (?) shows the same reference in-app.

Global

KeyAction
qQuit
[ ]Switch top-level tab (Control Panel / Visualization)
TabCycle to the next panel (Control Panel tab)
Shift+TabCycle to the previous panel (Control Panel tab)
1 2 3 4 5Jump directly to the panel whose title shows [N]
?Toggle help overlay
aArm robot
dDisarm robot
fForce disarm (error state only)

Each panel's title carries a bold-cyan [N] badge that mirrors the number key for that panel: [1] Safety, [2] Commands, [3] Joints, [4] Events, [5] Parameters.

Visualization tab

The Visualization tab renders the live robot in 3D from its URDF topology and joint positions. The camera orbits the robot and reposes in real time as sensor data arrives.

KeyAction
Left / Right or h / lOrbit the camera
Up / Down or k / jTilt the camera
+ / -Zoom in / out
rReset the camera
mCycle render mode (auto / kitty / sixel / iterm2 / half-block / braille / ascii)

Events panel

KeyAction
j / DownScroll down
k / UpScroll up
EnterShow event details
pPause / resume
cClear events

Commands panel

KeyAction
j / DownSelect next
k / UpSelect previous
EnterExecute (or enter argument edit mode when the command declares arguments)

Command edit mode

Active when the selected command declares arguments and Enter is pressed.

KeyAction
Tab / DownFocus next argument
Shift+Tab / UpFocus previous argument
Printable keyAppend to focused argument
BackspaceDelete last char of focused arg
/ hCycle to previous value (enum args only)
/ lCycle to next value (enum args only)
EnterExecute with current values
EscExit edit mode (keeps values)

Enum-typed args ({:in, [...]} in the Spark schema) render as ‹ value › chevrons and respond to / (or h/l) instead of needing the atom typed literally. For non-enum args, h/l continue to append to the buffer; / are no-ops outside of enum picks. Values are parsed before dispatch: "true"/"false" become booleans, ":foo" an atom, numeric an integer or float, otherwise a string.

Joints panel

KeyAction
j / DownSelect next joint
k / UpSelect previous joint
l / RightIncrease position (1% step)
h / LeftDecrease position (1% step)
LIncrease position (10% step)
HDecrease position (10% step)

Parameters panel

KeyAction
j / DownSelect next parameter
k / UpSelect previous parameter
l / RightIncrease value by one step
h / LeftDecrease value by one step
LIncrease value by ten steps
HDecrease value by ten steps
EnterToggle boolean parameter
tCycle to the next bridge tab (Local → bridges → Local)

Step size is 1% of the declared range when min / max are known — the Spark schema's {:float, min: 0.0, max: 1.0} form on the Local tab, or the bridge's flat :min / :max keys on a bridge tab — and the new value is clamped to the bounds. Parameters without bounds use an absolute step of +1 for integers and +0.1 for floats. The same keys dispatch through BB.Parameter.set on the Local tab and BB.Parameter.set_remote on a bridge tab; a successful remote set refetches that bridge's parameter list so the cached values stay in sync.