import React from 'react';
import { Box, Text } from '@anthropic/ink';
import type { Theme } from '@anthropic/ink';
import type { RunProgress } from '../progress/store.js';
import { RUN_STATUS_COLOR, STATUS_DOT } from './status.js';
import { capTabsForDisplay, tabLabel } from './selectors.js';
import { truncateLabel } from './AgentList.js';
/**
* Per-tab name width budget. Long workflow names truncate (keeping the `#xxxx` short-code suffix so
* same-name runs stay distinguishable). Sized for a ~120-col terminal: ~6 tabs fit per row.
*/
const TAB_LABEL_MAX = 18;
/**
* Hard ceiling on simultaneously rendered tabs. Defensive fallback: even if active runs accumulate
* (long-lived session, runaway launcher), the row must never overflow the terminal width and
* re-introduce the garbled overlapping render seen previously. Surplus runs are folded into `+N`.
*/
const MAX_TABS = 6;
/**
* Top run tab row: one tab per run (status dot + name + #short code).
* The current tab is highlighted with an orange ═ underline.
*
* Defenses against overflow:
* - Per-tab name truncated via truncateLabel (keeps `#xxxx` suffix for disambiguation).
* - Row capped at MAX_TABS; remainder rendered as a `+N` marker so total width is bounded.
*/
export function TabsBar({ runs, activeRunId }: { runs: RunProgress[]; activeRunId: string | null }): React.ReactNode {
if (runs.length === 0) {
return (no runs);
}
const { runs: visible, overflow } = capTabsForDisplay(runs, MAX_TABS);
return (
{visible.map(r => {
const active = r.runId === activeRunId;
const label = truncateLabel(tabLabel(r.workflowName, r.runId), TAB_LABEL_MAX);
const underline = '═'.repeat(label.length + 2);
return (
{STATUS_DOT[r.status]}
{label}
{active ? underline : ''}
);
})}
{overflow > 0 ? (
+{overflow}
) : null}
);
}