@pokit/tabs-core
@pokit/tabs-core contains the framework-agnostic state, types, and process management used by tabbed terminal adapters.
Installation
bun add @pokit/tabs-coreThis package is typically a dependency of tabs adapter implementations (like @pokit/opentui), not installed directly.
Core pieces
State Management
Shared state reducer for tabs UI:
import { createInitialState, reducer } from '@pokit/tabs-core';
const state = createInitialState();
const newState = reducer(state, { type: 'TAB_SELECT', tabId: 'tab-1' });Process Manager
Handles spawning and managing tab processes:
import { ProcessManager } from '@pokit/tabs-core';
const pm = new ProcessManager({
onOutput: (tabId, data) => {
/* handle output */
},
onExit: (tabId, code) => {
/* handle exit */
},
});
pm.spawn({ id: 'dev', exec: 'npm run dev', cwd, env });
pm.killAll();Types
import type {
TabStatus, // 'running' | 'success' | 'error' | 'killed'
TabProcess, // Process state
ActivityNode, // Event-driven activity
GroupNode, // Event-driven group
EventDrivenState, // Full UI state
} from '@pokit/tabs-core';Status Indicators
import { STATUS_INDICATORS, getStatusIndicator } from '@pokit/tabs-core';
// STATUS_INDICATORS = {
// running: '●',
// success: '✓',
// error: '✗',
// killed: '○',
// }
const indicator = getStatusIndicator('running'); // '●'State Shape
type EventDrivenState = {
tabs: TabProcess[];
activeTab: number;
scrollOffset: number;
};
type TabProcess = {
id: string;
label: string;
exec: string;
status: TabStatus;
output: string[];
exitCode: number | null;
};Actions
type Action =
| { type: 'TAB_ADD'; tab: TabProcess }
| { type: 'TAB_SELECT'; index: number }
| { type: 'TAB_OUTPUT'; tabId: string; lines: string[] }
| { type: 'TAB_EXIT'; tabId: string; code: number }
| { type: 'SCROLL'; offset: number };Process Manager API
class ProcessManager {
constructor(callbacks: ProcessManagerCallbacks);
spawn(spec: TabSpec & { id: string; cwd: string; env: Record<string, string> }): void;
kill(id: string): void;
killAll(): void;
isRunning(id: string): boolean;
}Constants
// Maximum output lines to keep per tab
const MAX_OUTPUT_LINES = 1000;
// Output batching interval
const OUTPUT_BATCH_MS = 16;Usage in Adapter
import { createInitialState, reducer, ProcessManager } from '@pokit/tabs-core';
function createTabsAdapter(): TabsAdapter {
return {
async run(items, options) {
let state = createInitialState();
const pm = new ProcessManager({
onOutput: (tabId, data) => {
state = reducer(state, { type: 'TAB_OUTPUT', tabId, lines: [data] });
render(state);
},
onExit: (tabId, code) => {
state = reducer(state, { type: 'TAB_EXIT', tabId, code });
render(state);
},
});
// Spawn processes
for (const item of items) {
pm.spawn({ ...item, cwd: options.cwd, env: options.env });
}
// Wait for completion
await waitForExit(pm);
},
};
}