termino
~/docs/custom/status-bar

StatusBarcustom

Agent bottom bar: mode, model, task, clock and token counter

~/custom/status-bar
live
$ termino status-bar — agent bottom bar
────────────────────────────────────────────────────────
agent opencode/deepseek-v4refactoring 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
proptypedefaultdescription
mode / modelstring"agent" / model idIdentity segment
taskstring-Current task, truncated to fit
time / tokensstring-Right-aligned widgets
runningbooleanfalseTurns ● and mode bright
widthnumber62Total bar width
colorsPartial<NeoColors>NEOBevel palette

##Keymap

~/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.

tsxstatus-bar.tsx
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";
5
6export 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}
17
18export 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);
54
55 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-deps
60 }, [mode, model, task, time, tokens, running, width, color, colors]);
61
62 const widthFix = Math.max(width, 4);
63 return h("box", { flexDirection: "column", gap: 0, width: widthFix }, h(Canvas, { rows, width: widthFix }));
64}
65