@ebowwa/tui-core
v1.1.0
Published
Composable TUI framework with built-in observability - no AI, just vibes
Downloads
468
Maintainers
Readme
@ebowwa/tui-core
A composable TUI framework with built-in observability. No AI, just vibes.
Features
- Composable Components - Box, Text, Input, List, Table, Panel, StatusBar
- Built-in Observability - Metrics, tracing, logging, and dashboard
- Type-Safe - Full TypeScript support with strict types
- React-Based - Built on Ink for familiar React patterns
- Terminal-Native - Designed for terminals, respects the medium
Installation
bun add @ebowwa/tui-coreQuick Start
import { App, Box, Text, List, StatusBar, ObservabilityDashboard } from "@ebowwa/tui-core";
import { render } from "ink";
function MyApp() {
const items = [
{ id: "1", label: "Option One" },
{ id: "2", label: "Option Two" },
{ id: "3", label: "Option Three" },
];
return (
<App observability={{ logLevel: "info" }}>
<Box flexDirection="column">
<Text bold color="cyan">Select an option:</Text>
<List items={items} focused onActivate={(item) => console.log(item)} />
<StatusBar left={[{ content: "Ready" }]} />
</Box>
</App>
);
}
render(<MyApp />);Components
Layout
Box
Fundamental layout container with flexbox behavior.
<Box flexDirection="row" gap={1} padding={1}>
<Box flexGrow={1} borderStyle="round">
<Text>Left panel</Text>
</Box>
<Box width={20}>
<Text>Right sidebar</Text>
</Box>
</Box>Text
Text display with styling.
<Text bold color="green">Success!</Text>
<Text dim>Muted text</Text>
<Text underline color="cyan">Link</Text>Input
Text input with cursor support.
<Input
label="Name:"
placeholder="Enter your name"
onSubmit={(value) => console.log(value)}
/>List
Selectable list with keyboard navigation.
<List
items={items}
focused
mode="single"
onActivate={(item) => console.log("Activated:", item)}
onSelectionChange={(ids) => console.log("Selected:", ids)}
/>Table
Data table with sorting.
<Table
data={users}
columns={[
{ key: "name", header: "Name", sortable: true },
{ key: "email", header: "Email", width: "40%" },
{ key: "role", header: "Role", width: 20 },
]}
showRowNumbers
/>Panel
Container with optional header and collapsible behavior.
<Panel
title="Details"
collapsible
collapsed={isCollapsed}
onCollapseChange={setIsCollapsed}
>
<Text>Panel content</Text>
</Panel>StatusBar
Bottom status bar with sections.
<StatusBar
left={[
{ content: "Mode: Normal", icon: "◉" },
{ content: "3 items" },
]}
right={[
{ content: "12:34:56", icon: "⏰" },
]}
/>Observability
Built-in observability with no configuration required.
Access Observability
import { useObservability } from "@ebowwa/tui-core";
function MyComponent() {
const obs = useObservability();
// Record a metric
obs.recordMetric("custom.metric", 42, { tag: "value" });
// Start a span
const span = obs.startSpan("operation.name");
// ... do work ...
obs.endSpan(span);
// Log
obs.log("info", "Something happened", { detail: "value" });
return <Text>Content</Text>;
}Observability Dashboard
Built-in dashboard to view metrics, traces, and logs.
import { ObservabilityDashboard, useObservability } from "@ebowwa/tui-core";
function Dashboard() {
const obs = useObservability();
return <ObservabilityDashboard observability={obs} />;
}Pre-Recorded Metrics
The framework automatically records:
tui.render.count- Number of renderstui.render.duration- Render duration in mstui.key.press- Key press eventstui.focus.change- Focus change eventstui.component.mount- Component mount eventstui.error.count- Error counttui.memory.heap- Heap memory usage
Export Data
const obs = useObservability();
// Export all data
const data = obs.export();
console.log(JSON.stringify(data, null, 2));Hooks
useApp
Access the application instance.
const app = useApp();
app.focus("component-id");
app.setMode("insert");
app.exit(0);useTerminalSize
Get current terminal dimensions.
const { width, height } = useTerminalSize();useKeyboard
Handle keyboard input.
useKeyboard((input, key) => {
if (key.name === "return") {
console.log("Enter pressed");
}
});useStore
Reactive state with store.
const [value, setValue] = useStore(store, "my-key", "default");
setValue("new value");Theming
import { App, useTheme } from "@ebowwa/tui-core";
const customTheme = {
name: "custom",
colors: {
primary: "cyan",
secondary: "magenta",
success: "green",
warning: "yellow",
error: "red",
info: "blue",
text: "white",
textMuted: "gray",
background: "black",
surface: "gray",
border: "gray",
focus: "cyan",
},
spacing: { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 },
borderRadius: 0,
animationDuration: 200,
};
function MyComponent() {
const theme = useTheme();
return <Text color={theme.colors.primary}>Themed!</Text>;
}Architecture
src/
├── index.ts # Main exports
├── types.ts # All type definitions
├── core/
│ └── app.tsx # Base TUI application
├── components/
│ ├── box.tsx # Layout container
│ ├── text.tsx # Text display
│ ├── input.tsx # Input field
│ ├── list.tsx # Selectable list
│ ├── table.tsx # Data table
│ ├── status-bar.tsx # Status bar
│ └── panel.tsx # Panel container
├── observability/
│ ├── core.ts # Observability implementation
│ └── dashboard.tsx # Built-in dashboard
├── state/
│ ├── store.ts # State management
│ └── hooks.ts # React hooks
└── events/
└── emitter.ts # Event systemPhilosophy
- No AI - Just vibes, not whimsical
- Composable - Build complex UIs from simple parts
- Observable - See what's happening inside
- Professional - Production-ready, polished
- Terminal-Native - Designed for the terminal medium
License
MIT
