@avala-ai/panel-sdk. A panel is a small TypeScript project: you write lifecycle
hooks, subscribe to MCAP topics, and draw. Panels run sandboxed — inside an
iframe with an opaque origin and a strict Content Security Policy — and talk to
the viewer over a versioned, typed protocol, so they stay stable across
viewer releases.
Preview — not yet published to npm.
@avala-ai/panel-sdk (0.1.x) lives in
the Avala monorepo and is not on the npm registry yet, so the npx flow
below does not work today — build the CLI from source instead (see
Building the CLI from source). Running a panel
inside Mission Control depends on the panel host, which is in active
development — until it ships, build and preview against the local dev harness
(avala-panel serve). Distribution (pack / install) and the marketplace are
on the roadmap; see Distribution and
Marketplace.Building the CLI from source
Until the package is published, build it from the monorepo once:npx avala-panel / avala-panel
in the commands below):
init notices it is running from a source checkout and points the new panel’s
@avala-ai/panel-sdk dependency at that checkout, so npm install and
npm run build work without the package being on the registry. After
publication, init emits the normal published version range instead — nothing
in your panel changes.
Getting Started
Scaffold a new panel project:npm run build compiles src/ into a self-contained dist/index.js.
npm run serve starts the local dev harness at http://localhost:5180: it
loads your built panel in a sandboxed iframe and feeds it a synthetic recording
(a moving pose on /robot/pose, a draining battery on /diagnostics/battery),
so you can see your panel react to real message flow with no server, no login,
and no MCAP file. Run npm run watch in a second terminal to rebuild on save.
Panel Lifecycle
You pass an object implementing lifecycle hooks todefinePanel. The runtime
performs the handshake with the host, validates your settings, and calls your
hooks — you never touch postMessage.
Lifecycle Order
When a panel is first added to the layout:onInit— set up your DOM elements and stateonSettingsChange— called immediately with the initial settingsonSeek— called with the current timeline position
onMessage is called for every incoming message in
chronological order. When the user scrubs the timeline, onSeek is called first,
followed by onMessage for messages in the new range.
Data Access
Declare the topics you want intopics, and receive typed messages through
onMessage. Each MessageEvent carries topic, logTime (nanoseconds since
recording start), schemaName, and the decoded data.
Basic Example
Multiple Topics
List several topics and route onmessage.topic:
Dynamic Subscription
Add or drop subscriptions at runtime — for example, in response to a settings change (this is exactly what the Plot example does):Context API
ThePanelContext passed to every hook exposes the panel DOM, settings, state,
and viewer controls.
state is not persisted across sessions — use it for transient things like
canvas references. For persistent configuration, use settings.Panel Settings
Declare settings and the host renders a form; values are validated against your schema before your hooks see them. An invalid value falls back to the previous valid value (or the default) — your panel never receives an out-of-schema value.Permissions
Panels are deny-by-default. A panel declares the capability scopes it needs inpanel.yml; the host grants only those. Each scope carries a risk tag shown
to whoever installs the panel.
Validate your manifest at any time:
Security Model
Panels run in an iframe withsandbox="allow-scripts" and no
allow-same-origin, so panel code executes at an opaque origin and cannot reach
the viewer’s cookies, storage, or DOM. A Content Security Policy blocks external
hosts and network access (connect-src 'none'): your panel receives its data
through the protocol, not over the wire. Messages are authenticated by protocol
shape and frame identity, and the host↔panel protocol is semver’d — the viewer
refuses to run a panel whose major protocol version it does not understand.
The sandbox contains a panel from the viewer’s own surfaces, but it is not a
complete data-exfiltration boundary (a panel can still leak data it was given via
navigation). The viewer therefore treats permissions and panel review as the
data boundary: panels are deny-by-default, are hosted on an isolated origin, and
should be granted data scopes only after review. Grant annotations:write and
other high-risk scopes deliberately.
Building
dist/index.js — the module the sandboxed iframe
loads. Because the SDK is bundled in, there is nothing else to resolve at runtime.
Distribution
Distribution and org-scoped install are in development. The
pack command
below already produces the install artifact; the Mission Control install flow
and marketplace are being built next.<id>-<version>.avala-panel.json — a self-contained bundle carrying
the manifest and every module the build emitted: the entry plus any code-split
chunks (tsup splits ESM output by default when a panel uses dynamic
import()). It is the input to the (in-development)
GitHub-install flow and, later, the marketplace. Panels follow semantic
versioning, read from panel.yml.
Marketplace
The marketplace is on the roadmap (ROADMAP 3.1). The
panel.yml manifest
already carries the fields a marketplace needs — publisher, a signature
slot, and declared permissions — so panels built today are forward-compatible
with it.Example: Plot Panel
Theplot-panel example (shipped in the SDK repo) plots a numeric value from any
topic over time. It demonstrates the topic-picker pattern and runtime
subscription:
plot-panel and trajectory-panel sources in the SDK repository’s
examples/ directory.
Next Steps
Panel Types
Explore the built-in panel types available in the MCAP viewer.
MCAP Viewer
Learn about the multi-sensor viewer that hosts custom panels.
Fleet Dashboard
Build fleet-level dashboards that incorporate custom panel visualizations.