import { useContext, useEffect, useRef } from 'react' import { CLEAR_TAB_STATUS, supportsTabStatus, tabStatus, wrapForMultiplexer, } from '../termio/osc.js' import type { Color } from '../termio/types.js' import { TerminalWriteContext } from '../useTerminalNotification.js' export type TabStatusKind = 'idle' | 'busy' | 'waiting' const rgb = (r: number, g: number, b: number): Color => ({ type: 'rgb', r, g, b, }) // Per the OSC 21337 usage guide's suggested mapping. const TAB_STATUS_PRESETS: Record< TabStatusKind, { indicator: Color; status: string; statusColor: Color } > = { idle: { indicator: rgb(0, 215, 95), status: 'Idle', statusColor: rgb(136, 136, 136), }, busy: { indicator: rgb(255, 149, 0), status: 'Working…', statusColor: rgb(255, 149, 0), }, waiting: { indicator: rgb(95, 135, 255), status: 'Waiting', statusColor: rgb(95, 135, 255), }, } /** * Declaratively set the tab-status indicator (OSC 21337). * * Emits a colored dot + short status text to the tab sidebar. Terminals * that don't support OSC 21337 discard the sequence silently, so this is * safe to call unconditionally. Wrapped for tmux/screen passthrough. * * Pass `null` to opt out. If a status was previously set, transitioning to * `null` emits CLEAR_TAB_STATUS so toggling off mid-session doesn't leave * a stale dot. Process-exit cleanup is handled by ink.tsx's unmount path. */ export function useTabStatus(kind: TabStatusKind | null): void { const writeRaw = useContext(TerminalWriteContext) const prevKindRef = useRef(null) useEffect(() => { // When kind transitions from non-null to null (e.g. user toggles off // showStatusInTerminalTab mid-session), clear the stale dot. if (kind === null) { if (prevKindRef.current !== null && writeRaw && supportsTabStatus()) { writeRaw(wrapForMultiplexer(CLEAR_TAB_STATUS)) } prevKindRef.current = null return } prevKindRef.current = kind if (!writeRaw || !supportsTabStatus()) return writeRaw(wrapForMultiplexer(tabStatus(TAB_STATUS_PRESETS[kind]))) }, [kind, writeRaw]) }