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.

WebSocket HubLive Signal StreamingCandlestick + Overlay ChartStacked PanesChannel VisibilityPattern MarkersProperties InspectorSlang Source Viewer

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

PropTypeDescription
theme'light' | 'dark'Propagated to every child component for consistent theming.

State Variables

logsLogEntry[]

Accumulated log messages shown in RoidRunnerLogger.

connectedboolean

Whether the WebSocket hub connection is currently open.

roidsRoidInfo[]

List of running roid processes returned by get_roids.

selectedRoidIdstring | null

Which roid's data is currently displayed in the chart.

slangViewer{ roidId, slang } | null

When non-null, SlangViewerModal is shown for this roid.

displayBarsBarData[]

OHLCV bars rendered by RoidRunnerChart for the selected roid.

lastBarBarData | null

Most recent bar appended in real-time (incremental chart update).

displayOutputsMap<string, OutputPoint[]>

Accumulated indicator output series for the selected roid.

outputTicknumber

Incremented 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 | null

varName of the node highlighted in the properties panel.

bottomHeightnumber

Height in px of the resizable bottom strip (default 150).

roidListWidthnumber

Width in px of the roid list column (default 200).

channelsWidthnumber

Width in px of the channels column (default 180).

propertiesWidthnumber

Width 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().

RefTypePurpose
selectedRoidRefstring | nullMirror of selectedRoidId used inside event callbacks to avoid stale closures.
roidBarsRefMap<roid_id, BarData[]>Per-roid OHLCV bar accumulator. Bars are pushed on every frame message.
roidOutputsRefMap<roid_id, Map<label, OutputPoint[]>>Per-roid indicator output accumulator. Populated from roid_output frames and get_bars responses.
pendingOutputsRefOutputPoint[]Output points queued for incremental chart append. Drained by the RoidRunnerChart effect when outputTick changes.
roidHiddenRefMap<roid_id, Set<string>>Persists each roid's hidden-channel selection across roid switches.
roidStackedRefMap<roid_id, Set<string>>Persists each roid's stacked-pane selection across roid switches.
roidNodesRefMap<roid_id, ParsedNode[]>Caches parsed slang nodes per roid so get_slang is only requested once.
hubRefRoidHubWSStable 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.

RoidRunnerHeader (36 px)
RoidRunnerChart (flex: 1)
↕ row-resize handle
RoidList
(roidListWidth)
Channels
(channelsWidth)
Properties
(propertiesWidth)
Logger
(flex: 1)

Resize Constraints

HandleStateMinMax
Horizontal (bottom strip height)bottomHeight80 px500 px
Roid list widthroidListWidth100 px500 px
Channels panel widthchannelsWidth100 px500 px
Properties panel widthpropertiesWidth100 px500 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 → client

Confirms 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 → client

Confirms a roid was killed. Panel removes it from the list, clears its refs, and resets chart/channels/properties if it was selected.

get_roidsserver → client

Returns the current roids array. Used after connect and after manual refresh.

get_barsserver → client

Returns 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 → client

Returns the Slang source of a roid. Stored in roidNodesRef and shown in SlangViewerModal.

pongserver → client

Heartbeat 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:

frame payload
// 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.