[UI shell]

AXIS chrome: topbar, watchlist, status bar, settings, plugin manager, logs, and layout chrome around the chart.

UI shell

The shell is the non-canvas chrome: navigation of axes, market selection, modals, and status. It binds human gestures to store + plugins without owning calculation.

Abstract

ComponentPathRole
Topbarui/Topbar.tsxAxes pickers, Load/Run/Live, theme, modals
Watchlistui/Watchlist.tsxSymbols, quotes, click-to-load
StatusBarui/StatusBar.tsxStatus, engine/storage badges, trade snippet
SettingsDialogui/SettingsDialog.tsxEndpoint, engine, storage, intervals
PluginManagerui/PluginManager.tsxCatalog / install / library
SystemLogsui/SystemLogs.tsxRing buffer of store.logs
ResultsPanelui/ResultsPanel.tsxBottom drawer (see results doc)
Iconsui/icons.tsxLucide wrappers
ResizeHandleui/ResizeHandle.tsxPanel widths/heights

Conceptual model

Topbar responsibilities

ControlEffect
Symbol / intervalStore + Load
Source selectsetActivePlugin('source') + default stream
Stream selectlive.streamId / active stream
Engine selectActive engine (+ pyodide preload side effects)
CSV fileparseOhlcvFile → upload-store → load
LoadHistorical fetch
RunEditor doc → runAndApply
Livemultiplex start/stop
Editor / watchlist togglesLayout flags
Settings / PluginsModal open
Detach editorBridge + popout

catalogTick forces memo re-list when plugins install/remove.

Watchlist

  • Default universe includes majors (BTCUSDT, …)
  • refreshSec clamped 5–120
  • Source-aware labels (e.g. CSV mode)
  • Click sets symbol and loads through active source

Status bar

Single row:

  • Left — Connection HUD (ConnectionHud, data-testid="axis-connection-hud")
    • LIVE badge (off / connecting / live / err)
    • Tick pulse (fixed-width price + age; ping does not resize layout)
    • Engine chip (id · interpret/compile · latency)
    • Plane chips SRC / STR / ENG / STO with transport badges WS / REST / LOCAL / BROKER
    • Source↔stream pairing warning when mismatched
  • Rightstatus / statusMessage, strategy snippet, log toggle, bar/ind/pane counts

Telemetry is ephemeral (store.telemetry); HUD compact mode and live prefs persist via settings.

Live settings (Settings dialog)

SettingDefaultEffect
Auto-start live after Loadofflive.preferAfterLoad → multiplex after successful Load
Indicator re-runevery-tickor bar-close only
Compact connection HUDoffhide plane chips

Logs

  • appendLog(level, message, source)
  • Cap MAX_LOGS (500)
  • Not persisted
  • Boot messages, pyodide ready, live errors

Theming

store.themedocument.documentElement data-theme (dark | light). Void dark is default product aesthetic.

Accessibility / test hooks

E2E selectors use data-testid on critical controls (axis-btn-load, axis-manager, …) per TESTING.md.

Invariants

  1. Shell never calls Python.
  2. Plugin lists always come from registry facades (listSources, …).
  3. Modal settings persist only on Save (not on every keystroke of endpoint field until save).
  4. Live off leaves historical bars intact.

Failure modes

SymptomShell-side check
Pickers emptyBuiltins not registered at boot
Live ignores clickStream none or multiplex error in logs
Settings probe failsNetwork / CORS—not topbar bug

See also