Storm AI Widgets

April 6, 2026 · View on GitHub

15 components for AI agent terminal UIs: token streaming, approval workflows, cost tracking, and performance profiling.


Table of Contents

  1. ApprovalPrompt
  2. BlinkDot
  3. CommandBlock
  4. CommandDropdown
  5. ContextWindow
  6. CostTracker
  7. MessageBubble
  8. ModelBadge
  9. OperationTree
  10. PerformanceHUD
  11. ShimmerText
  12. StatusLine
  13. StreamingText
  14. SyntaxHighlight
  15. TokenStream

1. ApprovalPrompt

Tool execution approval dialog with risk levels, keyboard selection, and auto-deny timeout.

Renders a bordered prompt showing a tool name, risk level badge (colored by severity), optional parameter display, and a row of keyboard-selectable options. Supports countdown auto-deny when a timeout is set. Uses useInput for key handling.

Props

NameTypeDefaultDescription
toolstringrequiredTool name displayed in the header.
riskstring--Risk level string (e.g. "high", "medium", "low"). Colors the border: red for high, amber for medium.
paramsRecord<string, unknown>--Tool parameters to display. Values are truncated at 60 characters.
optionsApprovalOption[][{key:"y", label:"approve"}, {key:"n", label:"deny"}, {key:"a", label:"always approve"}]Approval options with key, label, and color.
onSelect(key: string) => voidrequiredCalled when the user presses an option key.
widthnumber--Terminal width hint (unused -- Divider auto-fills).
visiblebooleantrueWhether the prompt captures keyboard input.
timeoutnumber--Auto-deny timeout in milliseconds. Displays a countdown and calls onSelect("n") when expired.
renderOption(option: ApprovalOption, index: number) => ReactNode--Custom render for each approval option.
timeoutMessage(seconds: number) => string(s) => `Auto-deny in ${s}s`Custom countdown message formatter.

ApprovalOption

NameTypeDescription
keystringKeyboard key to press.
labelstringDisplay text next to the key.
colorstringColor for the key character.

Usage

import { ApprovalPrompt } from "@orchetron/storm";

// Basic approval prompt
<ApprovalPrompt
  tool="execute_shell"
  risk="high"
  params={{ command: "rm -rf /tmp/cache", cwd: "/home/user" }}
  onSelect={(key) => {
    if (key === "y") runTool();
    else if (key === "a") alwaysApprove("execute_shell");
    else deny();
  }}
/>

// With auto-deny timeout
<ApprovalPrompt
  tool="file_write"
  risk="medium"
  timeout={30000}
  onSelect={handleApproval}
/>

// Custom options
<ApprovalPrompt
  tool="deploy"
  risk="high"
  options={[
    { key: "y", label: "deploy now", color: "#00FF00" },
    { key: "s", label: "stage first", color: "#FFAA00" },
    { key: "n", label: "cancel", color: "#FF0000" },
  ]}
  onSelect={handleDeploy}
/>

When to use

Any time an AI agent needs human confirmation before executing a tool -- file writes, shell commands, API calls, deployments. The risk level coloring provides instant visual severity assessment. The timeout auto-deny prevents blocking when running unattended.


2. BlinkDot

Status indicator dot that blinks based on operation state.

Renders a colored dot () whose behavior changes by state: blinks when running, stays solid for pending/streaming/completed/failed/cancelled. Uses imperative animation -- timer stops automatically for terminal states to avoid wasting CPU.

Props

NameTypeDefaultDescription
stateDotStaterequiredCurrent state. One of: "pending", "running", "streaming", "completed", "failed", "cancelled".
intervalnumberpersonality.animation.durationSlowBlink interval in milliseconds.
dotCharacterstring"●"Character displayed when the dot is visible.
offCharacterstring" " (space)Character displayed when the dot is hidden during blink.
renderDot(char: string, state: DotState) => ReactNode--Custom render for the dot.

State Colors

StateColor
pendingcolors.tool.pending
runningcolors.tool.running
streamingcolors.tool.pending
completedcolors.tool.completed
failedcolors.tool.failed
cancelledcolors.tool.cancelled

Usage

import { BlinkDot } from "@orchetron/storm";

// Running operation -- dot blinks
<BlinkDot state="running" />

// Completed -- solid green dot
<BlinkDot state="completed" />

// Custom character and speed
<BlinkDot state="running" dotCharacter="*" interval={300} />

When to use

Compact status indicator for individual operations, tool calls, or list items. Pairs well with OperationTree nodes or MessageBubble metadata. The automatic CPU optimization (timer stops for terminal states) makes it safe to render hundreds of these.


3. CommandBlock

Collapsible command output display with exit code badge and duration.

Renders a bordered block showing a command string (bold, with collapse toggle indicator), expandable output content, a colored exit code badge, and duration. Supports keyboard toggle (Enter/Space) and copy (c key) when focused.

Props

NameTypeDefaultDescription
commandstringrequiredThe command that was run.
outputReactNode--Command output content.
exitCodenumber--Exit code. 0 shows green checkmark, non-zero shows red X with code.
durationnumber--Duration in milliseconds, displayed formatted (e.g. "1.2s").
collapsedbooleanfalseWhether the output is collapsed.
onToggle() => void--Toggle callback for collapse/expand.
isFocusedboolean--Whether this block receives keyboard input. Shows hint text when true.
onCopy(text: string) => void--Called when user presses "c" while focused. Receives the output text.
ansiOutputboolean--When true, passes output through without stripping ANSI codes.
renderHeader(command: string, exitCode?: number) => ReactNode--Custom render for the command header row.
toggleIndicators{ collapsed?: string; expanded?: string }{ collapsed: "▸", expanded: "▾" }Override collapse/expand toggle characters.
exitCodeSymbols{ success?: string; failure?: string }{ success: "✓", failure: "✗" }Override exit code symbols.

Usage

import { CommandBlock } from "@orchetron/storm";

// Basic command block
<CommandBlock
  command="npm test"
  output="PASS src/index.test.ts\n42 tests passed"
  exitCode={0}
  duration={1234}
/>

// Collapsible with toggle
<CommandBlock
  command="git diff"
  output={diffOutput}
  collapsed={isCollapsed}
  onToggle={() => setCollapsed(!isCollapsed)}
  isFocused={true}
/>

// With ANSI color output
<CommandBlock
  command="ls --color"
  output={ansiColoredOutput}
  ansiOutput={true}
  exitCode={0}
/>

When to use

Displaying tool execution results in an AI chat interface. Each tool call that runs a command should wrap its output in a CommandBlock. The collapse feature keeps long outputs manageable, and the exit code badge provides instant pass/fail visibility.


4. CommandDropdown

Fuzzy-searchable command palette dropdown with keyboard navigation.

Renders a vertical list of items with a highlighted selection indicator. Features type-to-filter fuzzy matching (matched characters shown bold), scrollable window with overflow indicators, and full keyboard navigation (up/down/enter/escape/backspace).

Props

NameTypeDefaultDescription
itemsreadonly CommandItem[]requiredItems to display. Each has name and description.
selectedIndexnumber0Currently selected index.
maxVisiblenumber6Maximum visible items before scrolling. Shows ... overflow indicators.
highlightColorstringcolors.brand.primaryColor for the selected item.
isFocusedboolean--Whether the dropdown captures keyboard input.
onSelect(item: CommandItem) => void--Called when the user presses Enter on an item.
onSelectionChange(index: number) => void--Called when keyboard navigation changes the selected index.
onClose() => void--Called on second Escape press (first Escape clears filter).
selectionIndicatorstring"▸ "Override the selection indicator string.
renderItem(item: CommandItem, isSelected: boolean) => ReactNode--Custom render for each dropdown item.

CommandItem

NameTypeDescription
namestringItem name (searchable).
descriptionstringDescription shown dim after the name.

Usage

import { CommandDropdown } from "@orchetron/storm";

const commands = [
  { name: "/help", description: "Show available commands" },
  { name: "/model", description: "Switch AI model" },
  { name: "/clear", description: "Clear conversation" },
  { name: "/export", description: "Export chat to file" },
];

<CommandDropdown
  items={commands}
  selectedIndex={selectedIdx}
  isFocused={true}
  onSelect={(item) => executeCommand(item.name)}
  onSelectionChange={(idx) => setSelectedIdx(idx)}
  onClose={() => hideDropdown()}
/>

When to use

Slash command palettes, autocomplete menus, or any searchable item picker in an AI agent interface. The fuzzy matching makes it fast to find commands without exact typing. Escape behavior (clear filter first, then close) follows standard UI conventions.


5. ContextWindow

Token/context usage visualization with segmented bar and optional sparkline history.

Renders a progress bar showing LLM context window usage. Supports a simple filled/empty bar or a segmented bar where each segment (system, user, tools, assistant) gets its own color. Includes percentage, token counts, remaining tokens, and an optional sparkline showing historical usage.

Props

NameTypeDefaultDescription
usednumberrequiredTokens currently used.
limitnumberrequiredMaximum context window size.
breakdownArray<{ label: string; tokens: number; color?: string }>--Segment breakdown for multi-color bar. Each segment gets its own color.
compactboolean--When true, renders single-line bar only. When false with breakdown, shows bar + legend list.
barWidthnumber24Width of the bar in characters.
historynumber[]--Historical token usage values. Renders a sparkline below the bar when provided.
renderBar(used: number, limit: number) => ReactNode--Custom render for the entire context bar.
sparklineCharsstring[]["▁","▂","▃","▄","▅","▆","▇","█"]Override sparkline block characters.

Color Behavior

The bar color changes based on fill level:

  • Green: usage <= 70%
  • Amber: usage 70-90%
  • Red: usage > 90%

Usage

import { ContextWindow } from "@orchetron/storm";

// Simple usage bar
<ContextWindow used={6000} limit={8192} />

// Segmented breakdown with legend
<ContextWindow
  used={6700}
  limit={8192}
  breakdown={[
    { label: "System", tokens: 1200 },
    { label: "User", tokens: 3400, color: "#4CAF50" },
    { label: "Assistant", tokens: 2100 },
  ]}
/>

// Compact with history sparkline
<ContextWindow
  used={4096}
  limit={8192}
  compact
  history={[1000, 2000, 3500, 4096, 3800, 4096]}
/>

When to use

Any AI chat interface where context window management matters. Shows users how much of the model's context is consumed, broken down by role. The sparkline history helps identify context growth patterns. Critical for long conversations approaching the token limit.


6. CostTracker

API cost tracking display with per-token pricing and color-coded spend levels.

Calculates and displays running cost based on input/output token counts and per-million pricing. Supports compact (single line) and expanded (breakdown table) modes. Cost color shifts from green to amber to red as spend increases.

Props

NameTypeDefaultDescription
inputTokensnumberrequiredNumber of input/prompt tokens consumed.
outputTokensnumberrequiredNumber of output/completion tokens consumed.
inputCostPer1Mnumber3Cost per 1 million input tokens.
outputCostPer1Mnumber15Cost per 1 million output tokens.
currencystring"$"Currency symbol.
sessionTotalnumber0Pre-existing session cost to add to the running total.
compactbooleanfalseSingle line (true) vs expanded breakdown table (false).
renderCost(cost: number, currency: string) => ReactNode--Custom render for the cost display.

Color Thresholds

Total CostColor
< $0.10Green
$0.10 - $0.99Amber
>= $1.00Red

Usage

import { CostTracker } from "@orchetron/storm";

// Expanded breakdown
<CostTracker inputTokens={50000} outputTokens={12000} />
// Renders:
//   Total: \$0.33
//     Input:  50K tokens x \$3/M = \$0.15
//     Output: 12K tokens x \$15/M = \$0.18

// Compact single-line
<CostTracker inputTokens={50000} outputTokens={12000} compact />
// Renders: \$0.33 (50K in x \$3/M + 12K out x \$15/M)

// Custom pricing (e.g. demo model)
<CostTracker
  inputTokens={100000}
  outputTokens={25000}
  inputCostPer1M={2.5}
  outputCostPer1M={10}
  sessionTotal={5.50}
/>

When to use

Status bars, session summaries, or cost dashboards in AI agent interfaces. Helps users stay aware of API spend in real time. The color-coded thresholds provide instant visual feedback when costs are climbing. Pair with StatusLine or TokenStream for a complete metrics bar.


7. MessageBubble

Chat message display with role-based icons, markdown rendering, timestamps, and action buttons.

Renders a message with a role-appropriate symbol on the left (auto-resolved from role or manually overridden), content area, optional metadata line, optional timestamp, and action hints with keyboard shortcuts. When markdown is true and children is a string, content is rendered through Markdown.

Props

NameTypeDefaultDescription
symbolstringRole-based defaultSymbol displayed on the left. Overrides role default.
symbolColorstringRole-based defaultColor for the symbol. Overrides role default.
role"user" | "assistant" | "system" | "tool"--Message role. Auto-sets symbol and color (see table below).
childrenReactNoderequiredMessage content. Strings are wrapped in <Text>.
metastring--Metadata line rendered dim and italic below the message (e.g. timing, token counts).
timestampstring--Timestamp rendered dim on the right side of the message.
markdownboolean--When true and children is a string, renders through Markdown.
actionsMessageAction[]--Action hints displayed below the message with keyboard shortcuts.
isFocusedbooleantrueWhether the bubble captures keyboard input for actions.
renderSymbol(symbol: string, color: string) => ReactNode--Custom renderer for the role symbol.

Role Defaults

RoleSymbolColor
user>Brand primary
assistantBrand primary
systemWarning
toolInfo

MessageAction

NameTypeDescription
labelstringDisplay label for the action.
keystringKey the user presses to trigger.
onAction() => voidCallback when the action key is pressed.

Usage

import { MessageBubble } from "@orchetron/storm";

// User message
<MessageBubble role="user">What files changed?</MessageBubble>

// Assistant message with markdown
<MessageBubble role="assistant" markdown>
  {"Here are the changes:\n\n```diff\n+ added line\n- removed line\n```"}
</MessageBubble>

// Tool result with metadata and actions
<MessageBubble
  role="tool"
  meta="12 tokens, 0.3s"
  timestamp="14:32"
  actions={[
    { key: "r", label: "retry", onAction: () => retryTool() },
    { key: "c", label: "copy", onAction: () => copyResult() },
  ]}
>
  File written successfully.
</MessageBubble>

// Custom symbol
<MessageBubble symbol="!" symbolColor="#FF0000">
  Warning: approaching token limit.
</MessageBubble>

When to use

The primary building block for AI chat interfaces. Every message in a conversation should be wrapped in a MessageBubble. The role system provides instant visual distinction between user input, AI responses, system messages, and tool outputs. Actions enable inline interactions without leaving the chat flow.


8. ModelBadge

AI model name and provider badge with capability indicators and context size.

Renders a diamond icon colored by provider, the model name in bold, optional capability tags (e.g. [vision], [tools], [code]), and max context window size. Provider colors are built-in with support for custom overrides.

Props

NameTypeDefaultDescription
modelstringrequiredModel name (e.g. "my-model-v2").
providerstring--Provider name: "cloud", "local", "enterprise", "research", "community", "custom". Determines diamond color.
capabilitiesstring[]--Capability tags rendered as dim badges (e.g. ["vision", "tools", "code"]).
maxTokensnumber--Max context window tokens. Formatted as K/M (e.g. 128000 -> "128K").
colorstring | numberProvider-basedOverride color for the badge diamond.
renderModel(model: string, provider?: string) => ReactNode--Custom render for the model display.
providerColorsRecord<string, string>--Override or extend the built-in provider color map. Merged with defaults.

Built-in Provider Colors

ProviderColor
cloudBrand primary (amber)
enterpriseGreen
researchBlue
communityPurple (#D18EE2)
customOrange (#FF7000)
localGray (#888888)
defaultLight gray (#AAAAAA)

Usage

import { ModelBadge } from "@orchetron/storm";

// Basic badge
<ModelBadge model="demo-model" />
// Renders: ◆ demo-model

// Full badge with all features
<ModelBadge
  model="demo-model"
  provider="cloud"
  capabilities={["vision", "tools", "code"]}
  maxTokens={200000}
/>
// Renders: ◆ demo-model [vision] [tools] [code] 200K

// Local model
<ModelBadge model="demo-local" provider="local" maxTokens={128000} />

// Custom provider colors
<ModelBadge
  model="my-model"
  provider="internal"
  providerColors={{ internal: "#FF5500" }}
/>

When to use

Status bars, message headers, model selectors, or anywhere the current model identity needs to be displayed. Pair with StatusLine for a persistent model indicator. The capability badges help users understand what the active model can do.


9. OperationTree

Hierarchical operation progress display with animated spinners and tree connectors.

Renders a tree of operations where each node has a status icon, label, optional detail text, and duration. Running nodes animate with a braille spinner using imperative requestRender(). Timer automatically starts/stops based on whether any nodes are in the running state. Supports arbitrary nesting depth with configurable tree connector characters.

Props

NameTypeDefaultDescription
nodesOpNode[]requiredArray of operation nodes (can be nested via children).
maxDepthnumber--Maximum tree depth to render. Omit for unlimited.
showDurationbooleantrueWhether to show duration for nodes that have durationMs.
renderNode(node: OpNode, state: { depth: number }) => ReactNode--Custom render for each operation node.
spinnerFramesstring[]["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"]Custom spinner animation frames.
spinnerIntervalnumber80Spinner animation interval in milliseconds.
statusIconsPartial<Record<string, string>>{ pending:"○", completed:"✓", failed:"✗", cancelled:"⊘" }Override status icons by status key. Running nodes use the spinner instead.
treeConnectors{ branch?: string; last?: string; pipe?: string; space?: string }{ branch:"├─", last:"└─", pipe:"│ ", space:" " }Override tree connector characters.

OpNode

NameTypeDescription
idstringUnique identifier for the node.
labelstringDisplay label.
status"pending" | "running" | "completed" | "failed" | "cancelled"Current operation status.
childrenOpNode[]Nested child operations.
detailstringOptional detail text shown dim after the label.
durationMsnumberOperation duration in milliseconds.

Usage

import { OperationTree } from "@orchetron/storm";

<OperationTree
  nodes={[
    {
      id: "plan",
      label: "Planning",
      status: "completed",
      durationMs: 450,
      children: [
        { id: "analyze", label: "Analyze request", status: "completed", durationMs: 200 },
        { id: "strategy", label: "Select strategy", status: "completed", durationMs: 250 },
      ],
    },
    {
      id: "execute",
      label: "Executing",
      status: "running",
      children: [
        { id: "read", label: "Read file", status: "completed", detail: "src/index.ts", durationMs: 50 },
        { id: "edit", label: "Apply edit", status: "running", detail: "line 42" },
        { id: "test", label: "Run tests", status: "pending" },
      ],
    },
  ]}
/>
// Renders:
// ├─ ✓ Planning (450ms)
// │  ├─ ✓ Analyze request (200ms)
// │  └─ ✓ Select strategy (250ms)
// └─ ⠋ Executing
//    ├─ ✓ Read file src/index.ts (50ms)
//    ├─ ⠙ Apply edit line 42
//    └─ ○ Run tests

When to use

Displaying multi-step AI agent workflows -- plan-and-execute patterns, tool chains, parallel operations. The animated spinners on running nodes give immediate visual feedback about what the agent is doing. Stale refs are automatically cleaned up when nodes leave the tree.


10. PerformanceHUD

Live FPS, render time, cell diff, and memory overlay for development profiling.

Developer tool that renders a compact bordered box showing real-time rendering metrics. Tracks FPS and render time history as mini sparklines. All metrics are color-coded: FPS green >30 / amber 15-30 / red <15; render time green <8ms / amber 8-16ms / red >16ms. Uses dim styling to minimize visual interference.

Props

NameTypeDefaultDescription
visiblebooleantrueWhether the HUD is rendered.
renderTimeMsnumber0Render time of last frame in milliseconds.
fpsnumber0Current frames per second.
componentCountnumber--Number of components rendered.
cellsChangednumber0Number of cells changed in last frame.
totalCellsnumber0Total cells in buffer.
memoryMBnumber--Memory usage in megabytes.
position"top-right" | "bottom-right" | "top-left" | "bottom-left""top-right"Position hint (sets alignSelf for flex layout).
renderMetric(label: string, value: string, sparkline: string) => ReactNode--Custom render for each metric row.
historySizenumber20Number of history samples to keep for sparklines.
titlestring"Storm HUD"HUD title text.

Usage

import { PerformanceHUD } from "@orchetron/storm";

<PerformanceHUD
  fps={58}
  renderTimeMs={2.3}
  cellsChanged={120}
  totalCells={4800}
  memoryMB={24.3}
  componentCount={42}
/>
// Renders:
// ╭─────────────────────╮
// │ Storm HUD           │
// │ FPS: 58 ▅▆▇█▇▆  RT: 2.3ms ▁▁▂▁▁ │
// │ Cells: 120/4.8K     │
// │ Mem: 24.3 MB        │
// │ Components: 42      │
// ╰─────────────────────╯

// Toggle visibility with a keyboard shortcut
<PerformanceHUD visible={showHud} fps={metrics.fps} renderTimeMs={metrics.rt} />

When to use

Development and debugging only. Add to your app's root layout to monitor rendering performance. The sparklines show trends over the last 20 frames. Useful for identifying slow renders, excessive cell diffs, or memory leaks during widget development.


11. ShimmerText

Loading shimmer animation -- a bright highlight window sweeps across text.

Renders text with a 3-character bright shimmer window that continuously sweeps from left to right. Uses imperative mutation for each of three text segments (before/shimmer/after) with requestRender(). Stops cleanly when active is set to false.

Props

NameTypeDefaultDescription
textstringrequiredThe text to display with shimmer effect.
baseColorstringcolors.thinking.symbolColor of the non-shimmer text.
shimmerColorstringcolors.thinking.shimmerColor of the bright shimmer highlight.
intervalnumberpersonality.animation.durationFastSpeed of the shimmer sweep in milliseconds per step.
boldboolean--Whether the entire text is bold.
activebooleantrueWhen false, stops the animation and shows static text.
shimmerWidthnumber3Width of the shimmer highlight window in characters.
renderSegment(text: string, isShimmer: boolean) => ReactNode--Custom render for each text segment (before, shimmer, after).

Usage

import { ShimmerText } from "@orchetron/storm";

// Basic thinking indicator
<ShimmerText text="Thinking..." />

// Custom colors and speed
<ShimmerText
  text="Analyzing codebase..."
  baseColor="#666666"
  shimmerColor="#FFFFFF"
  interval={100}
  bold
/>

// Wider shimmer window
<ShimmerText text="Processing request" shimmerWidth={5} />

// Stop animation when done
<ShimmerText text="Analysis complete" active={isProcessing} />

When to use

"Thinking" indicators while waiting for AI responses. More visually engaging than a static "Loading..." message. The sweeping shimmer implies ongoing processing. Pair with BlinkDot for a combined status indicator, or use inside MessageBubble as the content while streaming hasn't started yet.


12. StatusLine

Bottom status bar with three layout modes: custom, built-in, and powerline segments.

Supports three modes: (1) Custom layout with arbitrary left/right React nodes, (2) Built-in layout with brand, model, tokens, turns, and extra key-value pairs, and (3) Powerline segments with colored segments and triangle separators. All text renders dim by default.

Props

NameTypeDefaultDescription
leftReactNode--Left-side content (custom layout mode).
rightReactNode--Right-side content (custom layout mode).
brandstring--Brand name shown with lightning prefix (built-in layout).
modelstring--Model display name (built-in layout).
tokensnumber--Token count, formatted with K/M suffixes (built-in layout).
turnsnumber--Turn count (built-in layout).
extraRecord<string, string | number>--Arbitrary key-value pairs rendered on the right (built-in layout).
backgroundColorstringcolors.surface.raisedBackground color for the status line.
segmentsStatusLineSegment[]--Powerline-style segments with per-segment colors. Overrides all other layout modes.
renderSegment(segment: StatusLineSegment, index: number) => ReactNode--Custom render for each powerline segment.
powerlineSeparatorstring"▶"Override the powerline separator character.

StatusLineSegment

NameTypeDescription
textstringText content of the segment.
colorstringForeground color.
bgstringBackground color.

Usage

import { StatusLine } from "@orchetron/storm";

// Built-in layout
<StatusLine
  brand="Storm"
  model="demo-model"
  tokens={4200}
  turns={12}
  extra={{ cost: "\$0.42" }}
/>
// Renders: ⚡ Storm demo-model          tokens:4.2K  turns:12  cost:\$0.42

// Custom layout
<StatusLine
  left={<Text bold color="cyan">My App</Text>}
  right={<Text dim>Ready</Text>}
/>

// Powerline segments
<StatusLine segments={[
  { text: "NORMAL", color: "#000", bg: "#88C0D0" },
  { text: "main", color: "#FFF", bg: "#5E81AC" },
  { text: "src/index.ts", color: "#D8DEE9", bg: "#3B4252" },
]} />

When to use

The persistent bottom bar in any AI agent TUI. The built-in layout mode provides everything needed for an AI chat status bar with minimal configuration. Powerline mode is ideal for more customized, vim-style status bars.


13. StreamingText

Typewriter-style streaming text display with blinking cursor.

Renders text with an optional animated blinking cursor () at the end when streaming is active. Supports a typewriter "animate" mode that reveals text character by character at a configurable speed, with an onComplete callback. Cursor blink and typing animation both use imperative requestRender().

Props

NameTypeDefaultDescription
textstringrequiredText content to display.
colorstring | number--Text color.
cursorbooleantrueWhether to show a cursor when streaming.
streamingboolean--Whether the text is currently streaming. Cursor only shows when both cursor and streaming are true.
animatebooleanfalseWhen true, reveals text character by character (typewriter effect).
speednumber2Characters revealed per tick when animate is true (~30fps ticks).
onComplete() => void--Callback fired when all text has been revealed in animate mode.
cursorCharacterstring"▊"Override the cursor character.
cursorBlinkIntervalnumber530Cursor blink interval in milliseconds.
renderCursor(char: string, visible: boolean) => ReactNode--Custom render for the cursor. Receives the cursor character and current visibility state.

Usage

import { StreamingText } from "@orchetron/storm";

// Live streaming with cursor
<StreamingText text={partialResponse} streaming={true} />

// Completed stream (no cursor)
<StreamingText text={fullResponse} streaming={false} />

// Typewriter reveal animation
<StreamingText
  text="Hello! I'm your AI assistant."
  animate
  speed={3}
  onComplete={() => setReady(true)}
/>

// Custom cursor
<StreamingText
  text={response}
  streaming={isStreaming}
  cursorCharacter="_"
  cursorBlinkInterval={400}
/>

When to use

Displaying AI model output as it streams in. The blinking cursor provides visual feedback that the model is still generating. The typewriter mode is useful for welcome messages or staged reveals. Both timers clean up automatically when streaming stops or the component unmounts.


14. SyntaxHighlight

Code syntax highlighting supporting 100 languages with multiline state tracking.

Regex-based syntax highlighter with zero external dependencies. Handles keywords, types, strings (including multiline and template literals), comments (line and block), numbers, operators, preprocessor directives, and HTML/XML tags. Supports Tree-sitter integration when available for higher accuracy. Extensible via registerLanguage().

Props

NameTypeDefaultDescription
codestringrequiredSource code to highlight.
languagestring--Language identifier (e.g. "typescript", "python", "rust").
widthnumber--Width constraint for the output.

Supported Languages (100)

Systems: C, C++, Rust, Go, Zig, Nim, Odin, V, Crystal, Mojo

Application: Java, Kotlin, Swift, Scala, Dart, C#, F#, Objective-C

Scripting: JavaScript, TypeScript, Python, Ruby, PHP, Perl, Lua, R, Julia, MATLAB

Functional: Haskell, Elixir, Erlang, OCaml, Elm, PureScript, Lean, Idris, Agda, Coq, Roc, Unison, Gleam

Lisp family: Clojure, Common Lisp, Scheme, Racket, Fennel, Janet, Hy

AltJS: CoffeeScript, LiveScript, ReScript, Reason

Shell: Bash, Fish, PowerShell, Nix, Zsh

Web: HTML, CSS, SVG, Svelte, Vue, Astro, MDX

Data/Config: JSON, YAML, TOML, INI, XML, Protobuf, Prisma, EdgeQL

Infrastructure: Dockerfile, Terraform/HCL, Makefile, CMake, Puppet, Ansible, Helm, Bicep, TypeSpec

Query: SQL, GraphQL, Cypher

Academic: Prolog, Smalltalk, Fortran, COBOL, TCL

GPU/Hardware: GLSL, HLSL, Solidity, Verilog, VHDL, Assembly, WAT/WASM

Diagram: Mermaid, PlantUML

Other: LaTeX, Regex, Diff/Patch, Groovy, Jsonnet

Usage

import { SyntaxHighlight } from "@orchetron/storm";

<SyntaxHighlight
  code={`function greet(name: string): string {
  return \`Hello, \${name}!\`;
}`}
  language="typescript"
/>

// Python
<SyntaxHighlight code={'def factorial(n):\n  return 1 if n <= 1 else n * factorial(n-1)'} language="python" />

// Width-constrained
<SyntaxHighlight code={sourceCode} language="rust" width={80} />

Extending with Custom Languages

import { registerLanguage } from "@orchetron/storm";

registerLanguage("mylang", {
  keywords: new Set(["fn", "let", "mut", "if", "else", "return"]),
  typeKeywords: new Set(["int", "str", "bool"]),
  lineComment: "//",
  blockCommentStart: "/*",
  blockCommentEnd: "*/",
  stringDelimiters: ['"', "'"],
});

When to use

Inside code blocks, CommandBlock output, Markdown fenced code blocks (which use this internally), or anywhere source code needs to be displayed. The Markdown widget automatically delegates to SyntaxHighlight for fenced code blocks.


15. TokenStream

Live token count, speed, and context progress display.

A compact single-row widget showing model name, token counts (total, input, output), tokens-per-second speed (color-coded), and an optional progress bar when maxTokens is provided. Designed to be fed updated props from your streaming logic -- this is a display widget, not a stream processor.

Props

NameTypeDefaultDescription
tokensnumberrequiredTotal tokens so far.
inputTokensnumber--Input/prompt tokens.
outputTokensnumber--Output/completion tokens.
tokensPerSecondnumber--Current speed in tokens per second. Only shown when streaming is true.
maxTokensnumber--Context window limit. When provided, shows a progress bar.
modelstring--Model name, shown dim with a separator.
streamingboolean--Whether currently streaming. Controls speed display.
colorstring | number--Override color for token counts.
renderMetric(label: string, value: string | number) => ReactNode--Custom render for each metric (label + value pair).

Speed Color

SpeedColor
>= 30 tok/sGreen
< 30 tok/sAmber

Progress Bar Color

UsageColor
<= 70%Green
70-90%Amber
> 90%Red

Usage

import { TokenStream } from "@orchetron/storm";

// Basic streaming display
<TokenStream
  tokens={1650}
  inputTokens={1200}
  outputTokens={450}
  tokensPerSecond={42}
  model="demo-model"
  streaming={true}
/>
// Renders: demo-model . 1.7K tokens (1.2K in / 450 out) . 42 tok/s

// With context progress bar
<TokenStream
  tokens={6500}
  maxTokens={8192}
  model="demo-local"
  streaming={false}
/>
// Renders: demo-local . 6.5K tokens . ██████░░ 79%

// Minimal (just total)
<TokenStream tokens={500} />

When to use

Real-time streaming status displays during LLM inference. Place in a status bar or above the chat input to show live metrics. The speed indicator helps users gauge generation performance, and the progress bar warns when context is filling up. Pair with CostTracker for cost alongside performance.



Import Summary

All widgets are exported from the main entry point @orchetron/storm:

import {
  ApprovalPrompt,
  BlinkDot,
  CommandBlock,
  CommandDropdown,
  ContextWindow,
  CostTracker,
  MessageBubble,
  ModelBadge,
  OperationTree,
  PerformanceHUD,
  ShimmerText,
  StatusLine,
  StreamingText,
  SyntaxHighlight,
  TokenStream,
} from "@orchetron/storm";

Architecture Notes

  • Imperative animation pattern: All animated widgets (BlinkDot, ShimmerText, StreamingText, OperationTree) use ref mutation + requestRender() instead of React state. This is because Storm's custom React reconciler does not flush state updates the way React DOM does. See the pitfalls guide for details.

  • Plugin props: Every widget wraps its raw props through usePluginProps("WidgetName", rawProps), allowing plugins to intercept and modify any widget's props globally.

  • Personality system: Animated widgets read timing defaults from usePersonality(), which provides theme-aware animation durations (durationFast, durationSlow).

  • Cleanup: All timer-based widgets register cleanup via useCleanup() to prevent leaked intervals on unmount.

  • Memoization: All widgets are wrapped in React.memo() to minimize re-renders in Storm's reconciler.