Skip to main content
A project dashboard is a JavaScript view that runs in a sandboxed Monitor tab. It can combine current values, plots, logs, tables, controls, and project-specific drawings without adding UI code to your firmware. Ask Embedder for the operator task and the signals it should use:
Embedder discovers the live channels, writes the dashboard files, resolves file diagnostics, and opens the result with dashboard_open.

Dashboard files

Each dashboard lives under a lowercase kebab-case slug:
dashboard.css is optional. Keep custom component code in dashboard.js so later edits preserve the complete view. A current manifest uses API version 3:
The directory and slug must match. Embedder manages code hashes, CSS hashes, and timestamps after validated writes. An external code change is treated as a user edit, which protects it from an unreviewed agent overwrite. Open dashboards appear in the Monitor’s add-tab menu. File changes reload an open dashboard after the project watcher settles. You can also delete a dashboard from that menu.

Bind live data

Declare every channel family the dashboard can read in subscriptions. An exact string matches one channel, and a trailing * matches a prefix. Common Monitor channels are:
  • plot:<port>:<channel> for numeric plot batches
  • serial:<tabId> for serial lines
  • serial-tabs for serial tab connection and configuration state
  • serial-ports for detected ports
  • text:<port>:<channel> for text telemetry
  • trace:<sessionId> for trace events
  • capture:<captureId> for capture manifests
Seed a component from recent data with embedder.history(spec), then update it with embedder.subscribe(spec, callback):
Use the adapters under embedder.data instead of reading payload fields by guess:
  • plotPoints, plotValues, and latestPlotPoint
  • serialLines, serialTabs, and serialPorts
  • textPoint, traceEvents, and captureManifest
An adapter returns the adapted field, not the original payload. Read identifiers such as tabId, port, and channel from the payload itself. Hardware scripts can publish numeric data with embedder_publish_plot(source, points). Dashboards receive it through plot:<source>:<channel> subscriptions without Teleplot firmware output.

Built-in component catalog

Start with embedder.ui.dashboard() and add only the components that support the operator’s task.

Readouts

  • metric: a current numeric value
  • status: a discrete state with neutral, success, warning, error, or info tone
  • gauge: a bounded value with optional thresholds
  • progress: a percentage
  • sparkline: a compact recent numeric trend

Plots and timelines

  • timeSeries: timestamped numeric traces
  • xy: arbitrary connected X/Y points
  • scatter: unconnected X/Y points
  • logic: digital signals over time
  • spectrum: frequency-domain points with optional logarithmic axes
  • band: a trace with low and high bounds
  • stateTimeline: discrete states over time
  • barChart: categorical values
  • histogram: a value distribution
  • heatmap: two-dimensional magnitude cells
Axis plots use uPlot and include zoom, cursor-centered wheel scaling, panning, per-series hover values, and a scrollbar when zoomed.

Data and custom content

  • table: object rows
  • log: serial lines or event messages
  • serialMonitor: the Monitor’s serial UI in a dashboard card
  • card: static text or custom DOM
  • drawing: a responsive SVG or Canvas surface

Layout

  • group: related controls or readouts in one card
  • tabs: task-specific views that keep their child components mounted
  • add: attach a custom object that exposes an element

Controls

  • button: a synchronous local UI action
  • action: asynchronous work or a hardware action with pending, success, and error states
  • toggle: a Boolean selection
  • slider: a bounded numeric selection
  • select: one choice
  • multiSelect: several choices
Use stable lowercase kebab-case component IDs. The responsive grid restores card order and selected tabs by ID. API v3 requires finite positive minWidth and minHeight values on every card, group, and drawing.

Include the serial monitor card

Use serialMonitor when the dashboard reads or controls a serial session:
Its manifest must include:
The card mirrors output, filtering, timestamps, port and baud controls, newline configuration, connection state, command history, and the send bar. It can reveal the full Monitor tab. Set allowsHardwareWrite to true if the card’s send and connection controls must be enabled. Without that flag, the card remains readable but its write controls are disabled.

Send events and hardware commands

embedder.emit({ name, payload }) reports a declared event to the agent’s next turn. It queues the event but does not wake the agent or perform an action. Declare the name in events and handle an accepted: false result in the UI. Event payloads must be JSON-serializable and no larger than 64 KiB. The default event budget is 20 per turn and 60 per minute. For device actuation, use an action control and embedder.hardware.send:
Hardware writes require all of these conditions:
  • The manifest has allowsHardwareWrite: true.
  • The command uses the ID of an existing serial tab.
  • You approve the dashboard’s current JavaScript hash.
  • No conflicting hardware workflow owns the target.
The dashboard cannot choose UART versus RTT from a device path. It writes through the selected live serial tab. A change to dashboard.js outside Embedder invalidates the code-hash grant; a validated agent edit can carry an existing grant to the new hash.

Build a custom component

Use a custom component for a view the built-ins do not provide, such as a pie, radar, polar plot, compass, Smith chart, waterfall, spectrogram, eye diagram, or 3D surface. Create a drawing and append an SVG or Canvas element:
Choose SVG for discrete shapes and labels. Choose Canvas 2D for thousands of points, per-frame drawing, pixel effects, or a projected 3D surface. Custom component code should:
  • Build the DOM once and expose small mutators such as set, append, or clear.
  • Size from drawing.surface with ResizeObserver.
  • Bound every sample buffer.
  • Ignore missing and non-finite values.
  • Show an empty state until data arrives.
  • Use the injected --dash-* theme variables.
  • Clear timers or animation frames when replacing a component.
The dashboard sandbox blocks network access, dynamic library loading, browser storage, the parent DOM, and the VS Code API. Use embedder.subscribe, embedder.history, embedder.state, and the declared bridge methods instead. Persisted dashboard state is limited to 64 KiB.
The iframe allows scripts but does not receive same-origin access. Manifest subscriptions, event declarations, hardware grants, and the code hash form the boundary between dashboard code and the Monitor host.
Last modified on August 24, 2026