src/stores/, each a focused slice of state with its own actions. Some larger stores are split into a directory of slices (agent-connection/, agent-capabilities/, settings/, ground-station/).
Why Zustand
A Zustand store is a plain function that returns state and actions. There are no context providers and no reducers. Components subscribe to the fields they read and re-render only when those fields change. That matters for a ground control station. A MAVLink stream delivers hundreds of decoded frames a second. A React context that re-rendered on every frame would freeze the page; fine-grained subscriptions plus coalesced notifications (below) keep it smooth.Store map
These are representative stores, not the full list. Browsesrc/stores/ for the rest.
Ring buffers for telemetry
telemetry-store holds one fixed-capacity ring buffer per telemetry message (RingBuffer in src/lib/ring-buffer.ts). When a buffer is full the oldest sample is overwritten, so memory stays flat however long the flight is. Most buffers are sized for about 60 seconds at the message’s usual rate:
The full list is in
src/stores/telemetry-store.ts. A buffer offers push, latest, get, last(n), nearest(ts) and toArray.
Coalesced notifications
Pushing a sample mutates the buffer in place and then callsscheduleVersionBump() from src/stores/coalesced-version.ts. All the pushes inside one animation frame collapse into a single increment of the store’s _version field. Components select _version and re-read the buffers, so the UI updates at most once per display frame however fast the wire runs. Any store fed by live telemetry follows the same rule.
The drone manager bridge
drone-manager (src/stores/drone-manager.ts) owns the connected vehicles, their protocol adapters and the selected vehicle. When a vehicle connects, bridgeTelemetry() in src/stores/drone-manager-bridge.ts subscribes to the adapter’s callbacks and routes each one to the stores:
bridgeTelemetry() returns the unsubscribe functions, and they all run when the connection closes.
Parameters and configuration panels
Every flight controller configuration panel reads and writes parameters through theusePanelParams hook (src/hooks/use-panel-params.ts). It takes an options object and returns the values, load state and write actions:
commitToFlash makes them permanent on the flight controller.
Demo mode
Demo mode runs a simulated fleet entirely in the browser with no backend.MockFlightEngine (src/mock/engine.ts) writes synthetic telemetry into the same stores a real link would, and registers a MockProtocol per simulated vehicle with the drone manager, so configuration panels, flight logs and sensor health work too. The fleet itself is described on Dashboard.
The first-run welcome modal has a Demo Mode switch, on by default. You can also turn Demo Mode on or off in Settings, or with Toggle Demo Mode in the command palette (⌘K).
Persistence
Stores that must survive a reload use Zustand’spersist middleware with a version number and a migration function. When the stored shape changes, the version goes up in the same change and the migration converts old data. Persisted stores include:
Live telemetry stores never persist. They start empty on every page load.
Selectors
Components subscribe to specific fields:useShallow so a new object with equal values does not trigger a render.
Next steps
MAVLink protocol
The adapters that feed the stores.
Code recipes
Add a store, a panel or a decoder.