▚StatusBarcustom
Agent bottom bar: mode, model, task, clock and token counter
$ termino status-bar — agent bottom bar
╭────────────────────────────────────────────────────────╮
● agent opencode/deepseek-v4 │ refactoring tool-call.tsx ⏱ 0:00 ▮12k
╰────────────────────────────────────────────────────────╯
╭───────────────────────────────────╮
│mode · model · task · time · tokens│
╰───────────────────────────────────╯
live task cycles, clock + token counter tick
##API
Full-width three-row strip: raised top edge, content row, sunken bottom edge. Left segment shows ● mode + model, center task text (auto-truncated), right-aligned clock and tokens.
properties
| prop | type | default | description |
|---|---|---|---|
| mode / model | string | "agent" / model id | Identity segment |
| task | string | - | Current task, truncated to fit |
| time / tokens | string | - | Right-aligned widgets |
| running | boolean | false | Turns ● and mode bright |
| width | number | 62 | Total bar width |
| colors | Partial<NeoColors> | NEO | Bevel palette |
##Keymap
—Display only
##Notes
- ▸The ● pulse + bright mode segment signal an active agent turn, like opencode's TUI bottom bar.
- ▸Feed `time`/`tokens` from your own interval — the bar itself is pure display.
##Source
Real implementation — this is the code that runs in your terminal.
1import { createElement as h, useMemo } from "react";2import { Canvas } from "./canvas";3import { cellsWidth, strWidth, type CursorCell } from "./chart";4import { NEO, neoEdge, type NeoColors } from "./neo";56export interface StatusBarProps {7 mode?: string;8 model?: string;9 task?: string;10 time?: string;11 tokens?: string;12 running?: boolean;13 width?: number;14 color?: string;15 colors?: Partial<NeoColors>;16}1718export function StatusBar({19 mode = "agent",20 model = "opencode/deepseek-v4",21 task,22 time,23 tokens,24 running = false,25 width = 62,26 color = "#7aa2f7",27 colors,28}: Readonly<StatusBarProps>) {29 const c = { ...NEO, ...colors };30 const rows = useMemo(() => {31 const inner: CursorCell[] = [];32 const push = (ch: string, fg: string) => {33 inner.push({ ch, fg });34 };35 const used = () => cellsWidth(inner);36 const timePart = time ? ` ${time}` : "";37 const tokensPart = tokens ? ` ${tokens}` : "";38 const tail = timePart + tokensPart;39 const tailWidth = strWidth(tail);40 push("● ", running ? color : c.dim);41 push(`${mode} `, running ? c.text : c.dim);42 push(`${model}`, c.dim);43 push(" │ ", c.dim);44 if (task) {45 const room = width - used() - tailWidth;46 const clipped = strWidth(task) > room ? task.slice(0, Math.max(0, room - 1)) + "…" : task;47 push(clipped, c.text);48 }49 while (used() < width - tailWidth) {50 push(" ", c.text);51 }52 if (time) push(` ${time}`, c.dim);53 if (tokens) push(` ${tokens}`, c.dim);5455 const top = neoEdge(width, true, c);56 const mid = inner;57 const bot = neoEdge(width, false, c);58 return [top, mid, bot];59 // eslint-disable-next-line react-hooks/exhaustive-deps60 }, [mode, model, task, time, tokens, running, width, color, colors]);6162 const widthFix = Math.max(width, 4);63 return h("box", { flexDirection: "column", gap: 0, width: widthFix }, h(Canvas, { rows, width: widthFix }));64}65