Skip to content

Althafdudekula/TermUI

 
 

Repository files navigation

TermUI

Build terminal applications in TypeScript.

Documentation npm version License Bun 1.3+ 600 tests passing TypeScript 5.9

GSSoC 2026 Good first issues Contributors Stars

GSSoC 2026 contributors: star this repo before opening a PR. The star-check workflow blocks unstarred merges. Read CONTRIBUTING.md for the full point system.

📖 Docs site: API docs, guides, and examples live at termui.io. The source is at Karanjot786/TermUI_Docs.

What is TermUI?

TermUI is a TypeScript framework for building terminal apps. You get a layout engine, JSX support, React-style hooks, focus management, global state, theming, animations, routing, real-time data, error boundaries, and a hot-reload dev server. No curses bindings. No C extensions. Pure TypeScript.

Quick Start

bunx create-termui-app my-app
cd my-app
bun install
bun run dev

Requires Bun 1.3.0 or newer.

Manual Setup

bun add @termuijs/core @termuijs/widgets @termuijs/jsx
import { App } from '@termuijs/core'
import { Box, Text } from '@termuijs/widgets'
import { useState, useKeymap, ErrorBoundary } from '@termuijs/jsx'

function Counter() {
    const [count, setCount] = useState(0)

    useKeymap({
        '+':      () => setCount((c) => c + 1),
        'ctrl+c': () => process.exit(0),
    })

    return (
        <Box border="round" padding={1}>
            <Text bold>Count: {count}</Text>
            <Text dim>Press + to increment, ctrl+c to quit</Text>
        </Box>
    )
}

render(
    <ErrorBoundary fallback={(e) => <Text color="red">{e.message}</Text>}>
        <Counter />
    </ErrorBoundary>
)

Packages

Package What it does
@termuijs/core Screen buffer, input parsing, event system, flexbox layout, caps flags, WCAG utilities
@termuijs/widgets 40+ widgets: Box, Text, Table, VirtualList, StreamingText, ChatMessage, ToolCall, Card, Sidebar, LineChart, HeatMap, and more
@termuijs/ui Select, Tabs, Modal, Toast, NotificationCenter, imperative prompts, PasswordInput, NumberInput, PathInput
@termuijs/jsx JSX runtime with useState, useEffect, useKeymap, useMotion, ErrorBoundary, and a complete focus system
@termuijs/store Zustand-style global state with selector subscriptions and batch updates
@termuijs/testing In-memory test renderer: render, query, fireKey, waitFor, renderToString
@termuijs/tss Terminal Style Sheets with variables, pseudo-classes, AutoThemeProvider, and useTheme
@termuijs/motion Spring-physics and easing animations; respects NO_MOTION
@termuijs/router File-based screen routing with typed params, guards, and ErrorBoundary wrapping
@termuijs/data Real-time system data: CPU, memory, disk, network, processes; raw API and reactive hooks
@termuijs/dev-server Hot-reload dev server with graceful restart in under 200ms
@termuijs/quick Fluent builder API for dashboards in ~20 lines
create-termui-app Project scaffolding CLI

Features

useKeymap

Declare key bindings as a map. Cleaner than chained if-statements. Multiple calls in one component are additive.

import { useKeymap } from '@termuijs/jsx'

function App() {
    useKeymap({
        'ctrl+c': () => process.exit(0),
        'ctrl+s': () => save(),
        '/':      () => openSearch(),
        '?':      () => showHelp(),
    })
    return <Box>...</Box>
}

Focus management

Four hooks for building keyboard-accessible interfaces:

import { useFocusManager, useFocus, useFocusTrap, useKeyboardNavigation } from '@termuijs/jsx'

// App root: owns global focus state
function App() {
    const { FocusContext, focus, blur, focused } = useFocusManager()
    return (
        <FocusContext.Provider value={{ focus, blur, focused }}>
            <Screen />
        </FocusContext.Provider>
    )
}

// Modal: Tab key stays inside
function Modal({ fieldIds }) {
    useFocusTrap(fieldIds)
    return <Box>...</Box>
}

// List: standard arrow key navigation
function List({ items }) {
    const { selectedIndex } = useKeyboardNavigation({ items, loop: true })
    return <Box>...</Box>
}

ErrorBoundary

Wrap any subtree. Errors show a fallback instead of crashing the app.

import { ErrorBoundary } from '@termuijs/jsx'

<ErrorBoundary fallback={(err) => <Text color="red">Error: {err.message}</Text>}>
    <Dashboard />
</ErrorBoundary>

Capability flags

TermUI checks the terminal environment before rendering unicode or animations. Set environment variables to get ASCII output in CI or reduced-motion output for accessibility.

NO_UNICODE=1  # ASCII fallbacks for all widgets
NO_MOTION=1   # Skip all animations; static output
NO_COLOR=1    # Disable ANSI color sequences
import { caps } from '@termuijs/core'

const bullet = caps.unicode ? '●' : '*'

Notifications

import { NotificationCenter, useNotifications } from '@termuijs/ui'

function App() {
    return (
        <Box>
            <NotificationCenter position="top-right" />
            <Dashboard />
        </Box>
    )
}

function Dashboard() {
    const { notify } = useNotifications()

    useKeymap({
        's': async () => {
            await save()
            notify('Saved', { type: 'success', duration: 2000 })
        },
    })
    return <Box>...</Box>
}

Imperative prompts

Ask for user input without restructuring your component tree.

import { prompt } from '@termuijs/ui'

const ok = await prompt.confirm('Delete all logs?')
if (ok) deleteLogs()

const name = await prompt.text('Enter a name:')
const env = await prompt.select('Choose environment:', ['dev', 'staging', 'prod'])

Global state with batch updates

import { createStore, batch } from '@termuijs/store'

const useAppStore = createStore((set) => ({
    count: 0,
    label: '',
    increment: () => set((s) => ({ count: s.count + 1 })),
}))

// Multiple updates; one render pass
batch(() => {
    useAppStore.setState({ count: 10 })
    useAppStore.setState({ label: 'done' })
})

Theming

import { AutoThemeProvider, useTheme } from '@termuijs/tss'
import { tokensToTSS, nordTheme } from '@termuijs/tss'

// Detect terminal background and pick the closest theme
<AutoThemeProvider fallback="dracula">
    <App />
</AutoThemeProvider>

// Switch at runtime
const { theme, setTheme } = useTheme()
setTheme('catppuccin')

// Use token objects directly
const primary = nordTheme['--primary']

AI widgets

Display AI tool usage and streaming output with purpose-built widgets:

import { StreamingText, ChatMessage, ToolCall } from '@termuijs/widgets'

<ChatMessage role="assistant" content="Here is the result..." />
<ToolCall name="read_file" status="running" args={{ path: '/etc/hosts' }} />
<StreamingText text="Generating response..." speed={30} />

VirtualList

Renders only visible rows. A list of 1,000,000 items performs the same as a list of 10.

const list = new VirtualList({
    totalItems: 1_000_000,
    renderItem: (index) => `Log line ${index}`,
    onSelect: (index) => inspect(index),
})

Reactive system data

import { useCpu, useMemory, useHttpHealth } from '@termuijs/data'

function Dashboard() {
    const cpu  = useCpu(500)
    const mem  = useMemory(1000)
    const checks = useHttpHealth(['https://api.example.com/health'], 5000)

    return (
        <Box flexDirection="column">
            <Text>CPU: {cpu.usage.toFixed(1)}%</Text>
            <Text>MEM: {(mem.used / mem.total * 100).toFixed(1)}%</Text>
        </Box>
    )
}

Testing

import { render } from '@termuijs/testing'

const t = render(<Counter />)
t.fireKey('+')
expect(t.getByText('Count: 1')).toBeTruthy()

// Async assertions
await t.waitFor(() => {
    expect(t.getByText('Loaded')).toBeTruthy()
})

// ANSI-free snapshot
expect(t.renderToString()).toContain('Count: 1')
t.unmount()

Architecture

Application Layer:    @termuijs/ui · @termuijs/quick · create-termui-app
Component Layer:      @termuijs/widgets · @termuijs/jsx · @termuijs/store · @termuijs/testing
Feature Layer:        @termuijs/tss · @termuijs/router · @termuijs/motion
Core Layer:           @termuijs/core · @termuijs/data
Dev tooling:          @termuijs/dev-server

Every layer depends only on the layers below it. Use any package independently.

Examples

System dashboard (Quick API)

import { app, gauge, table } from '@termuijs/quick'
import { useCpu, useMemory } from '@termuijs/quick'

app('System Monitor')
    .rows(
        app.cols(
            gauge('CPU', () => useCpu().usage / 100),
            gauge('MEM', () => useMemory().percent / 100),
        ),
        table('Processes', {
            columns: ['Name', 'PID', 'CPU%'],
            data: () => processes.top(10).map((p) => ({
                Name: p.name,
                PID: p.pid,
                'CPU%': p.cpu.toFixed(1),
            })),
        }),
    )
    .run()

AI assistant dashboard

import { app, chatMessage, toolCall, streamingText } from '@termuijs/quick'

app('AI Assistant')
    .rows(
        chatMessage({ role: 'user', content: 'Show me the top processes' }),
        toolCall({ name: 'get_processes', status: 'running' }),
        streamingText({ text: 'Fetching system data...', speed: 40 }),
    )
    .run()

Running the examples

git clone https://github.com/Karanjot786/TermUI.git
cd TermUI
bun install
bun run build

cd examples/dashboard
bun run dev

Six examples: dashboard, jsx-dashboard, showcase, system-monitor, todo-app, widget-gallery.

Project structure

packages/
  core/              Screen buffer, input, events, layout, caps flags
  widgets/           Widget library (40+ widgets)
  ui/                High-level components and inputs
  jsx/               JSX runtime, hooks, focus system, ErrorBoundary
  store/             Global state management
  testing/           Test renderer
  tss/               Terminal Style Sheets and theming
  motion/            Spring animation engine
  router/            File-based routing
  data/              System data providers and hooks
  dev-server/        Hot-reload dev server
  quick/             Fluent builder API
  create-termui-app/ Project scaffolding CLI
examples/
  dashboard/         Real-time system monitor
  jsx-dashboard/     JSX-based dashboard
  showcase/          Widget gallery
  system-monitor/    Advanced monitor
  todo-app/          Interactive todo list
  widget-gallery/    All widgets in one place

Development

bun install
bun run build      # Build all 13 packages
bun run test       # Run all 600 tests
bun run typecheck  # Type-check all packages

Requirements

  • Bun 1.3.0 or newer for development
  • Node.js 18+ to run published @termuijs/* packages from npm
  • A terminal with TTY support (256-color or truecolor recommended)

License

MIT

About

TermUI is a TypeScript/JavaScript framework for building terminal apps. Flexbox layout, JSX, hooks, global state, theming, animations, routing, and hot reload. No native dependencies.

Resources

License

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 100.0%