UI shell
AXIS chrome: topbar, watchlist, status bar, settings, plugin manager, logs, and layout chrome around the chart.
This page
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
| Component | Path | Role |
|---|---|---|
| Topbar | ui/Topbar.tsx | Axes pickers, Load/Run/Live, theme, modals |
| Watchlist | ui/Watchlist.tsx | Symbols, quotes, click-to-load |
| StatusBar | ui/StatusBar.tsx | Status, engine/storage badges, trade snippet |
| StudioHost | ui/studio/StudioHost.tsx | Full-page Runtime / Wire / Settings / Workers / Plugins |
| SettingsPage | ui/settings/SettingsPage.tsx | Appearance, chart, live, keys, editor, theme |
| PluginsPage | ui/plugins/PluginsPage.tsx | Catalog / install / library |
| WirePage | ui/wire/WirePage.tsx | Compose-recipe wiring (source × stream × engine × storage × dataset) |
| SystemLogs | ui/SystemLogs.tsx | Ring buffer of store.logs |
| ResultsPanel | ui/ResultsPanel.tsx | Bottom drawer (see results doc) |
| Icons | ui/icons.tsx | Lucide wrappers |
| ResizeHandle | ui/ResizeHandle.tsx | Panel widths/heights |
Conceptual model
Rendering…
Topbar responsibilities
Grouped left→right (axis-tb-group, data-tb-group):
| Group | Contents |
|---|---|
| brand | Logo + AXIS |
| market | Symbol, Interval, Type (TopbarField), Compare |
| data | Source (+ CSV upload), Load, Reload (icon-only) |
| compute | Engine, Stream, Run, Live, Replay |
| layout | Multi-chart layouts + recipes |
| panels | List, Editor, Library, Data (Source Manager), Scripts, Layers, Alerts, Data window, Inputs, Results |
| system | Studio (Runtime / Wire / Settings / Workers / Plugins), Theme (margin-left: auto) |
| Control | Effect |
|---|---|
| Symbol / interval | Store + Load (Enter / blur reload) |
| Source / Stream / Engine | setActivePlugin + side effects |
| Chart type | setChartType (candles, HA, …) |
| Compare | Second-symbol overlay |
| Load / Reload | One-shot historical fetch → chart |
| Data panel | Data Source Manager — background deep history |
| Scripts panel | Applied indicators/strategies list |
| Run | Editor doc → runAndApply; accent only while status === 'running' |
| Live | multiplex start/stop (stops Replay) |
Chart themes
Settings / command palette expose ten curated presets (void dark/light, classic, mono, obsidian, graphite, pacific, dusk, porcelain, parchment). High-end soft surfaces — not neon high-contrast. See src/theme/presets.ts.
| Replay | Bar replay over loaded bars |
| Panel toggles | isPanelOpen / dual-write chrome |
| Settings / Plugins | Modal open |
| Detach editor | Bridge + popout |
Integrated labels: ui/TopbarField.tsx (.axis-tb-field*).
catalogTick forces memo re-list when plugins install/remove.
Command palette
Ctrl/Cmd+K — CommandPalette + command-registry.ts: panels, theme, layouts, Run, symbol focus, editor toggles (ruler, debug, pins, profiler), jump to line, git push/pull, save library. Palette entries render any recorded shortcut chords.
Keyboard shortcuts
Global chord dispatch hub (src/ui/shortcuts/): capture-phase handling with a dialog-skip guard so shortcuts never fire while a modal is open. Wired to three scopes:
- App-level — palette, Run, panel toggles, replay, layouts
- Chart — keymap routed through the dispatch hub (cancel draft, layouts, replay)
- Editor —
src/editor/cm-line-ops.tsPrec.highline ops: duplicate line, toggle comment, indent/unindent, delete line
Recorder and discovery:
- Recorder: Settings → Keyboard → record a chord; per-user overrides persist on the app store
- Shortcuts modal: lists default bindings with live rendering
- Conflict flags: scope-aware (
app.escapevschart.cancel-draftlayer by design — no false conflict)
Alerts panel
Dockable Alerts (src/alerts/* + AlertsPanel.tsx): local-first book for price, % change, drawing touch, indicator plots, Pine alert() / alertcondition(), and on-chain kinds. Delivery: browser Notification, optional HTTPS webhook, optional L2 webhook. Live ticks evaluate price/drawing/plot kinds; strategy/indicator runs evaluate Pine alert() events (last bar). Continuous server-side arming is not required for session use.
Workspace snapshots
storage/workspace-snapshot.ts + optional chrome menus capture layout/plugin prefs for restore (distinct from OHLCV — bars still re-fetch on Load).
Panel docks (side-by-side)
Left/right docks with 2+ open panels lay out horizontally (row), not stacked under each other:
- Example: Indicators left of Editor on the right strip
- Column width = sum of panel widths (capped)
- Each panel keeps its own width (resize handle on chart-facing edge)
- Bottom dock still stacks vertically
See ui/panels/dock-layout.ts, FloatableShell.tsx.
Mobile shell (phones / tablets)
src/ui/mobile/MobileShell.tsx + src/ui/responsive.ts — reactive phone | tablet | desktop mode from viewport + pointer: coarse (desktop ≥1024px is unchanged):
- Phones (≤767px): compact header (brand · venue · symbol · interval) + bottom tab bar (Chart · Panels · Editor · Studio · More) replace the Topbar/StatusBar. Panels and More open as nav overlays.
- Panel sheets: docked/floating panels render as full-viewport sheets — one at a time, dismiss via swipe-down on the title bar. Desktop panel geometry is bypassed, never overwritten, so phone↔desktop switches preserve layouts. Phones boot chart-first.
- Force-single chart: 2H/2V/4 layouts collapse to the active slot with a slot-switcher chip row; other slots stay preserved in
store.chartLayout. - Tablets (768–1023px): desktop shell with a horizontally scrollable topbar.
- Dialogs become bottom sheets; safe-area insets,
100dvhroot, ≥44px touch targets.
Watchlist
- Default universe includes majors (
BTCUSDT, …) refreshSecclamped 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
- Right —
status/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)
| Setting | Default | Effect |
|---|---|---|
| Enable live stream | on | live.preferAfterLoad → multiplex after Load and on boot when bars exist |
| Indicator re-run | every-tick | or bar-close only |
| Compact connection HUD | off | hide plane chips |
Logs
System Logs strip
appendLog(level, message, source)- Cap
MAX_LOGS(500) - Not persisted
- Boot messages, pyodide ready, live errors
Scriptlogs & Profiler
Script log.* output and editor line-timing live on a separate path — see Debugging. Controls sit on the editor header (not the topbar) and do not write into store.logs.
Theming
store.theme → document.documentElement data-theme (dark | light). Void dark is default product aesthetic. Settings → Theme picks a curated preset (void / classic / mono / boutique) and edits chart tokens live.
Accessibility / test hooks
E2E selectors use data-testid on critical controls (axis-btn-load, axis-manager, …) per TESTING.md.
Invariants
- Shell never calls Python.
- Plugin lists always come from registry facades (
listSources, …). - Modal settings persist only on Save (not on every keystroke of endpoint field until save).
- Live off leaves historical bars intact.
Failure modes
| Symptom | Shell-side check |
|---|---|
| Pickers empty | Builtins not registered at boot |
| Live ignores click | Stream none or multiplex error in logs |
| Settings probe fails | Network / CORS—not topbar bug |