Roid Runner
The Roid Runner is the live-signal panel of the application. It connects to a backend Roid Hub server over WebSocket, dispatches the current graph's Slang script as a long-running process, and streams frame-by-frame OHLCV bars and indicator output back to a lightweight-charts canvas with per-channel visibility controls, stacked panes, and a live log.
Sub-pages
RoidRunnerPanel — Top-Level Orchestrator
RoidRunnerPanel is the root component that owns all state. It renders the toolbar header, the full-height chart area, and a resizable bottom strip containing the roid list, channel panel, properties panel, and log panel side by side. All child components are fully controlled — every callback is defined in the panel and threaded downward as props.
Props
| Prop | Type | Description |
|---|---|---|
| theme | 'light' | 'dark' | Propagated to every child component for consistent theming. |
State Variables
logsLogEntry[]Accumulated log messages shown in RoidRunnerLogger.
connectedbooleanWhether the WebSocket hub connection is currently open.
roidsRoidInfo[]List of running roid processes returned by get_roids.
selectedRoidIdstring | nullWhich roid's data is currently displayed in the chart.
slangViewer{ roidId, slang } | nullWhen non-null, SlangViewerModal is shown for this roid.
displayBarsBarData[]OHLCV bars rendered by RoidRunnerChart for the selected roid.
lastBarBarData | nullMost recent bar appended in real-time (incremental chart update).
displayOutputsMap<string, OutputPoint[]>Accumulated indicator output series for the selected roid.
outputTicknumberIncremented each time pendingOutputsRef gets new points, triggering the chart effect.
channelsstring[]Ordered list of channel labels shown in RoidRunnerChannels.
sourceChannelsSet<string>Symbol names (e.g. "AAPL") shown at top of channel list, no eye/stack controls.
hiddenChannelsSet<string>Labels currently toggled off in the channel panel.
stackedChannelsSet<string>Node names whose series are moved into a dedicated stacked pane.
latestValuesMap<string, number>Most recent numeric value per channel label (shown inline in channel panel).
slangNodesParsedNode[]Parsed nodes from the selected roid's slang (for properties panel).
selectedNodestring | nullvarName of the node highlighted in the properties panel.
bottomHeightnumberHeight in px of the resizable bottom strip (default 150).
roidListWidthnumberWidth in px of the roid list column (default 200).
channelsWidthnumberWidth in px of the channels column (default 180).
propertiesWidthnumberWidth in px of the properties column (default 180).
Ref-based Accumulators
Per-roid data is stored in refs (not state) to avoid unnecessary re-renders during high-frequency streaming. When the user switches roids, these refs are read and copied into state via refreshDisplayBars().
| Ref | Type | Purpose |
|---|---|---|
| selectedRoidRef | string | null | Mirror of selectedRoidId used inside event callbacks to avoid stale closures. |
| roidBarsRef | Map<roid_id, BarData[]> | Per-roid OHLCV bar accumulator. Bars are pushed on every frame message. |
| roidOutputsRef | Map<roid_id, Map<label, OutputPoint[]>> | Per-roid indicator output accumulator. Populated from roid_output frames and get_bars responses. |
| pendingOutputsRef | OutputPoint[] | Output points queued for incremental chart append. Drained by the RoidRunnerChart effect when outputTick changes. |
| roidHiddenRef | Map<roid_id, Set<string>> | Persists each roid's hidden-channel selection across roid switches. |
| roidStackedRef | Map<roid_id, Set<string>> | Persists each roid's stacked-pane selection across roid switches. |
| roidNodesRef | Map<roid_id, ParsedNode[]> | Caches parsed slang nodes per roid so get_slang is only requested once. |
| hubRef | RoidHubWS | Stable reference to the WebSocket hub singleton. Not re-created on reconnect; instead resetRoidHubWS() recreates the global and hubRef is updated. |
Layout Structure
The panel uses inline flexbox layout. The bottom strip is split horizontally by four drag-resize handles (4 px wide, cursor: col-resize). The chart fills all vertical space above the bottom strip; the bottom strip height is controlled by a vertical cursor: row-resize handle.
(roidListWidth)
(channelsWidth)
(propertiesWidth)
(flex: 1)
Resize Constraints
| Handle | State | Min | Max |
|---|---|---|---|
| Horizontal (bottom strip height) | bottomHeight | 80 px | 500 px |
| Roid list width | roidListWidth | 100 px | 500 px |
| Channels panel width | channelsWidth | 100 px | 500 px |
| Properties panel width | propertiesWidth | 100 px | 500 px |
Hub Message Flow
All WebSocket messages arrive as HubMessage objects with a cmd string. The panel's setMessageCallback handler dispatches on msg.cmd.
run_roidserver → clientConfirms that a new roid process was spawned. Contains roid_id and status. Panel appends the new RoidInfo to the roids list and auto-selects it.
kill_roidserver → clientConfirms a roid was killed. Panel removes it from the list, clears its refs, and resets chart/channels/properties if it was selected.
get_roidsserver → clientReturns the current roids array. Used after connect and after manual refresh.
get_barsserver → clientReturns the full cached bar + output history for a roid as a JSON-strings array. Panel parses all frames and bulk-loads the chart.
roid_outputserver → client (streaming)Real-time frame data. Inner JSON has type "frame" (bars + outputs), type "log" (runner log line), or other (raw string). Appended to refs and displayed if the roid is selected.
get_slangserver → clientReturns the Slang source of a roid. Stored in roidNodesRef and shown in SlangViewerModal.
pongserver → clientHeartbeat reply. Silently ignored.
roid_output Frame Format
The roid_output message carries a data string which is itself JSON. Panel parses it and branches on inner.type:
// type === 'frame'
{
"type": "frame",
"bars": [
{ "symbol": "AAPL", "t": 1715000000, "o": 170.1, "h": 172.5, "l": 169.8, "c": 171.3, "v": 5230000 }
],
"outputs": [
{
"node": "sma",
"t": 1715000000,
"channels": { "values": 168.4, "timestamps": 1715000000 }
},
{
"node": "pattern",
"t": 1715000000,
"channels": { "signal": 1, "confidence": 82.5, "timestamp": 1715000000 }
}
]
}
// type === 'log'
{ "type": "log", "level": "info" | "error" | "success", "message": "…" }Channel label derivation
If the output node has exactly one non-timestamp channel, the label is simply node (e.g. sma). If there are multiple channels, each gets a node.channel label (e.g. ichimoku.conv). The timestamp and confidence keys are filtered out from channel panel rows.
Pattern arrow markers
When a node has both signal and confidence channels, the chart renders arrow markers on the candlestick series: signal = 1 → yellow arrowUp below bar; signal = −1 → red arrowDown above bar. Text shows the confidence percentage.
SlangRegistry
SlangRegistry.ts is a thin adapter that bridges the runner UI to the shared Slang parser infrastructure. It imports all sub-parsers as side effects (registering their commands into the unified SlangParser registry — intentionally excluding SlangChartParser which depends on graph-editor internals), then exposes parseSlang().
export interface ParsedNode {
varName: string; // left-hand side of the assignment
funcName: string; // command name (e.g. "ICHIMOKU")
inputRefs: string[]; // positional input variable references
params: Record<string, string>; // resolved param name → raw value
}
export function parseSlang(source: string): ParsedNode[]Config-only commands (those with an empty nodeType in their definition) are filtered out — only real data nodes appear in the properties panel. The result is cached in roidNodesRef so parsing only happens once per roid.