All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.4.0 - 2026-08-10
Added
Trajectory commands read properly in the event log. A
BB.Message.Actuator.Command.Trajectorypublished on[:actuator | joint]haswaypointsrather than aposition, so it missed the actuator summary clause and fell through to the genericinspect— a truncated blob of waypoint keyword lists. It now summarizes asshoulder ← trajectory 4 waypoints over 2400ms(with×5/×∞appended when the trajectory repeats), and the detail pane collapses each waypoint toposition@timeinstead of listing thevelocity: nil, acceleration: nilthatbb0.29 made optional. The dev robot gains atrajectorycommand that publishes a real trajectory per joint and then sweepsshoulderandelbowthrough its waypoints over ~2.4s, so the 3D view shows motion passing through the waypoints instead of snapping between targets.
Changed
Follows the
positions→configurationsrename inbbcore (breaking).bb0.27.0's multi-DoF joint work renamedBB.Robot.Runtime'spositions/1toconfigurations/1— a joint's configuration is only a float when it has one degree of freedom — so the TUI crashed with anUndefinedFunctionErrorduring dashboard init againstbb>= 0.27.BB.TUI.Robot'spositions/2is nowconfigurations/2and routes to the new accessor both locally and over:rpc. The joints panel and the visualization drive single-DoF joints, so values remain floats and behaviour is otherwise unchanged. Themix.exsrequirement moves from~> 0.20to~> 0.28 and >= 0.28.1to encode the new API — the floor is 0.28.1 rather than 0.27.0 becausebb0.27.0 and 0.28.0 close a compile-time cycle (BB.Message.Optionpattern-matches the geometry structs whose modules import it back) that deadlocks Elixir 1.19's parallel compiler;bbbroke that cycle in 0.28.1, so the~> 1.19floor and the two-cell CI matrix stay as they are. Mirrorsbb_liveviewv0.3.0's migration.Multi-DoF joints flow through the TUI (breaking). Joint configurations are stored verbatim, shaped to the joint's type — a float for single-DoF joints, a
BB.Math.Transform2Dfor planar, aBB.Math.Transformfor floating — soBB.TUI.State'supdate_positions/2is renamedupdate_configurations/2and dashboard init seeds missing joints with their type's identity. Planar and floating joints appear in the joints panel as read-only rows with a compact pose ((x, y, θ°)/ translation(x, y, z)), target-adjust keys skip them, and event details render their transform entries compactly. Driving them is not possible by design:BB.Actuator.set_position!/4takes a single number, and bb has no command API for a transform target.BB.TUI.Viz.RobotScenedelegates forward kinematics to bb core. The scene is built fromBB.Robot.Kinematics.all_link_transforms/2— one flat node per visual link carrying its base-frame transform — instead of a hand-rolled single-DoF FK walk, so planar and floating joints pose correctly in the 3D view and the local FK code is deleted. Requiresrobot.topology(every runtime-builtBB.Robothas it).
0.3.1 - 2026-07-31
Added
usage-rules.md, shipped in the package. Matches the conventionbbandbb_liveviewalready follow, so an agent working in a Beam Bots workspace picks up rules for the TUI layer alongside the framework's own (mix usage_rules.sync <file> bb_tui). Covers the entry points and how to pick between them, the full option table,BB.TUI.Rendererfor consumer-owned payloads, command cancellation, and the anti-patterns that actually bite — declaring the dashboard intopology, calling the blockingrun/2from a supervision tree, or setting:nodewithout connecting first. Added{:usage_rules, "~> 1.2", only: [:dev]}for the sync tooling.
Fixed
- Continuous commands are no longer reported as timed out while they are still running. Commands dispatched from the UI were awaited with a UI-side deadline (
:bb_tui, :command_timeout, default 30s), so a continuous command — one that only returns when it stops or is cancelled — surfaced{:error, :timeout}in the result panel after 30 seconds even though it was running normally, with no way to stop it.BB.Command.await/2is now called with:infinity; runaways stay bounded by the command's own DSLtimeout. MatchesBB.LiveView.Components.Command, which bb_tui mirrors.
Added
- Cancel a running command with
cin the commands panel. Command execution is now two-phase — one async starts the command and reports{:command_started, _}, a second awaits it and reports{:command_result, _}— so the running command's pid reaches state (BB.TUI.State.Commands.executing_pid) and can be cancelled.BB.TUI.Robot.cancel_command/2routes locally or over:rpclike every other runtime call. Cancelling resolves the pending await, so the result panel shows the cancellation instead of hanging on the throbber.
Removed
- The
:bb_tui, :command_timeoutconfig key. It no longer has anything to bound now that the await is:infinity— the DSLtimeoutand the new cancel key cover both cases. Setting it is now a no-op and can be deleted from consumer config.
0.3.0 - 2026-06-23
Added
- Configurable subscription paths.
BB.TUI.run/2(andstart/2/start_ssh/2) now accept a:subscribe_pathsoption that overrides the default control-plane set the dashboard subscribes to — e.g.[[:state_machine], [:command]]to narrow it, or a downsampled observability topic instead of the high-rate sensor firehose. Threaded through both the local and SSH transports; default behaviour is unchanged when the option is omitted. Thanks to @lostbean. - Consumer-supplied renderers.
BB.TUI.run/2(andstart/2/start_ssh/2) now accept a:renderersoption — a%{path_prefix => module}map that lets a consumer teach the dashboard how to render a payload on a PubSub path it owns, without bb_tui knowing the payload's struct. A module implements the newBB.TUI.Rendererbehaviour:summarize/2returns the event-log line (ornilto fall back to the genericinspect/2), and the optionalobserved/2feeds an at-a-glance status-bar slot. Messages route to a renderer by longest-matching prefix, like a routing table. Fully additive — with no:renderers, dispatch is unchanged. Threaded through both the local and SSH transports. Thanks to @lostbean.
0.2.0 - 2026-06-19
Added
- 3D visualization tab. A new top-level tab (
[/]to switch) renders the live robot in the terminal in 3D, built from its URDF topology and joint positions via forward kinematics. The camera orbits, tilts, zooms, and resets (←/→/h/l,↑/↓/k/j,+/-,r), and the arm reposes in real time as sensor data arrives. Built onExRatatui'sViewport3DandThreeD.Nodescene-graph. - Battery / power readout in the status bar. When the robot publishes
BB.Message.Sensor.BatteryStateorBB.Message.Sensor.PowerState, the status bar shows an at-a-glance segment — charge percentage (colored green / yellow / red by remaining charge, with a bolt while charging), falling back to bus voltage when percentage is unmeasured. Latest-reading-wins; the event log keeps the history. Especially useful when driving a headless robot over SSH. - Hardware-error and estimator events. The dashboard now also subscribes to
[:safety]and[:estimator], soBB.Safety.HardwareErrordetail (the component and reason behind an error badge) and estimator output (Odometry/Pose) surface in the event log. Safety state transitions already arrived via[:state_machine], so the badge was already accurate — this adds the missing diagnostic detail. - Dev demo commands.
Dev.TestRobotgainspower(drains a simulated battery so the status-bar readout shifts green → yellow → red) anddiagnostics(publishes a hardware-error report and an estimator pose so both surface in the event log).
0.1.0 - 2026-06-04
Initial release — a terminal dashboard for Beam Bots robots, built on ExRatatui.
Added
- Dashboard layout. A multi-panel terminal UI — title bar, Safety, Joint Control, Commands, Events, Parameters, and a status bar — composed through ExRatatui's reducer runtime. Pure state transitions live in
BB.TUI.State;BB.TUI.Appwires input and async results to those transitions. - Safety panel. Arm / disarm / force-disarm controls with a confirmation popup for force-disarm, plus an animated throbber while disarming. Reflects the robot's live safety state (
:armed/:disarmed/:disarming/:error). - Joint control panel. Position table showing joint type (revolute / prismatic / continuous), units (degrees / mm), visual range bars, last-commanded target markers, and simulated-joint tags. Direct keyboard position adjustment in 1%-of-range and 10%-of-range steps.
- Commands panel. Lists available robot commands with Ready / Blocked indicators based on runtime state. Argument-less commands execute on Enter; commands with declared arguments open an inline edit mode (Tab / Shift+Tab to cycle fields, type-to-edit, Enter to run, Esc to cancel). Argument types — boolean, integer, float, atom, enum (
{:in, [...]}), and string — are parsed before dispatch. Entered values are preserved per command across executions. - Parameters panel. Live parameter table grouped by path with real-time updates and schema-aware editing (min / max bounds drive 1%-of-range stepping). Bridge tabs surface remote-parameter lists fetched per bridge, editable through the same keys; press
tto cycle tabs. - Event stream. Scrollable, color-coded event log with summaries and timestamps taken from
BB.Message.wall_time(publish time, not arrival time). Pause / resume, clear, and Enter to open a detail popup showing the full payload. - High-rate sensor handling. The event log debounces repeats of the same
{path, payload-type}within a one-second window so a fast sensor can't flood it, and sensor-driven re-renders are coalesced to ~30fps — keeping the UI responsive under high-rate telemetry while key presses, command results, and safety / parameter / state changes still render immediately. Both windows are tunable. - Status bar, help overlay, and theme system. Status bar shows robot name, safety indicator, runtime state, and contextual key hints; a scrollable help overlay lists the full keybinding reference; a consistent color palette provides semantic styles (safety colors, focus borders, panel headers).
- Keyboard-driven navigation. Tab / Shift+Tab to cycle panels, number keys to jump directly to a panel, and vim-style
j/k/h/lwithin panels. - SSH transport. Serve the dashboard over SSH; multiple operators can connect simultaneously, each with an isolated session (built on ExRatatui's
:sshtransport). - Distribution attach. Run the TUI on the robot node and attach a thin renderer from any connected BEAM node (built on ExRatatui's
:distributedtransport). - Nerves support. Register the dashboard as a
nerves_sshsubsystem so operators can attach over SSH on-device. - Runtime inspection. Snapshot, trace, and inject events into a running TUI via
ExRatatui.Runtime— useful for debugging SSH sessions that aren't otherwise observable. mix bb.tuitask. Standalone launch —mix bb.tui --robot MyApp.Robot, with--sshand distribution options.mix bb_tui.installIgniter task. Addsbb_tuito a project, imports formatter rules, optionally scaffolds aBBrobot, and wires up launch for the default,--ssh, or--nervesinstall shapes.- Headless test suite. Full coverage using Mimic and ExRatatui's test backend, including end-to-end tests that drive a real server via
ExRatatui.Runtime.inject_event/2.