Files
vue-tui/README.md
T
Yunfei He e25a89528b docs(readme): align the homepage status banner with the public-beta message (#208)
The top-level README carried a terser "Status" banner, while
packages/runtime/README.md had the fuller "Public beta" banner that
actually conveys the call to action — seeking public feedback to lock
the @vue-tui/runtime API down before 1.0. Bring the homepage in sync so
both read identically.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 13:43:43 +08:00

11 KiB

vue-tui

Public beta — the @vue-tui/runtime API is stabilizing, and we're now seeking public feedback to lock it down before 1.0. The CLI and dev tooling remain experimental and may change. Feedback and bug reports are very welcome.

The Vue framework for terminal UIs. Build with components, develop with HMR, test with confidence.

@vue-tui/runtime · @vue-tui/cli · @vue-tui/testing

  • Vue SFC & JSX — write terminal interfaces with <template>, TSX, or both
  • Flexbox layout — powered by Yoga, the same engine behind React Native
  • Dev toolkit (experimental) — HMR in the terminal via vue-tui dev
  • Input & focus — keyboard handling, focus management, Tab navigation, Kitty keyboard protocol
  • Testing harness — out-of-the-box component-level terminal testing — render, simulate input, assert frames

Flappy Bird — one of the examples included in the repo

Flappy Bird built with vue-tui

Quick Start

npx tiged vuejs-ai/vue-tui-starter my-app
cd my-app
npm install
npm run dev

Edit app.vue and watch the terminal update instantly.

Add to an existing project

npm install @vue-tui/runtime

Example

// src/main.ts
import { createApp } from "@vue-tui/runtime";
import App from "./app.vue";

createApp(App).mount();
<!-- src/app.vue -->
<script setup lang="ts">
import { shallowRef } from "vue";
import { Box, Text, useInput } from "@vue-tui/runtime";

const count = shallowRef(0);

useInput((input) => {
  if (input === "+") count.value++;
  if (input === "-") count.value--;
});
</script>

<template>
  <Box>
    <Text>Count: </Text>
    <Text bold color="green">{{ count }}</Text>
    <Text dimColor> (+/- to change)</Text>
  </Box>
</template>

For non-interactive output — snapshots, CI logs, piped commands — renderToString(App) renders a single frame to a string instead of mounting.

Table of Contents

Packages

Package Description
@vue-tui/runtime The core framework — Vue 3 renderer for the terminal with components (Box, Text, Static, etc.), composables (useInput, useFocus, useApp, etc.), and yoga-based flexbox layout. API stabilizing.
@vue-tui/cli Development tool — vue-tui dev starts your app with Vite-powered HMR. Experimental; may change.
@vue-tui/testing Test harness — render in an isolated fake terminal, simulate input, assert output frame by frame

Examples

Example Description
basic-template Vue SFC with <template> syntax
basic-jsx Same app in TSX
coding-agent AI coding agent with LLM streaming and interactive UI
flappy-bird Physics-based terminal game with reactive state and borders

Components

Component Description
<Box> Flexbox container — direction, wrap, align, justify, gap, padding, margin, borders, background
<Text> Styled text — color, bold, italic, underline, strikethrough, dimColor, wrap/truncate modes
<Spacer> Expands to fill available space (flex-grow: 1)
<Newline> Inserts line breaks (configurable count)
<Static> Renders a list of items once, above the redrawn region
<Transform> Applies a string transform function to each rendered line

Composables (Hooks)

Composable Description
useInput(handler, opts?) Handle keyboard input — receives (input, key) with modifier and arrow key detection
usePaste(handler, opts?) Handle bracketed paste — receives the pasted text as a single event
useFocus(opts?) Component-level focus — returns { isFocused, focus }
useFocusManager() App-level focus control — focusNext(), focusPrevious(), focus(id)
useApp() App lifecycle — { exit(error?), waitUntilRenderFlush() }
useWindowSize() Reactive terminal dimensions — { columns, rows }
useStdin() Access stdin stream and raw mode control
useStdout() Write directly to stdout
useStderr() Write directly to stderr
useBoxMetrics(ref) Measure a <Box> via a template ref — reactive { width, height, left, top, hasMeasured } (or measureElement(el) for a one-off { width, height } read)
useCursor() Control the terminal cursor — setCursorPosition(pos) in output coordinates
useIsScreenReaderEnabled() Whether a screen reader is active — returns a boolean for adapting accessible output
useAnimation(opts?) Frame-based animation driver — reactive { frame, time, delta } + reset()

Testing

The @vue-tui/testing package renders components in an isolated environment and lets you simulate input and assert visual output:

npm install -D @vue-tui/testing
import { defineComponent, shallowRef } from "vue";
import { expect, test } from "vitest";
import { render } from "@vue-tui/testing";
import { Box, Text, useInput } from "@vue-tui/runtime";

test("counter responds to + and - keys", async () => {
  const Counter = defineComponent(() => {
    const count = shallowRef(0);
    useInput((input) => {
      if (input === "+") count.value++;
      if (input === "-") count.value--;
    });
    return () => (
      <Box>
        <Text>Count: {count.value}</Text>
      </Box>
    );
  });

  const { lastFrame, stdin } = await render(Counter);
  expect(lastFrame()).toContain("Count: 0");

  await stdin.write("+");
  expect(lastFrame()).toContain("Count: 1");

  await stdin.write("-");
  expect(lastFrame()).toContain("Count: 0");
});

Development

Requires pnpm and Node.js 22+.

pnpm install          # install dependencies
vp run ready          # lint, typecheck, test, and build (the full check)
vp run -r test        # run tests across all packages
vp run -r build       # build all packages
vue-tui dev           # start an example with HMR

Contributing

Contributions welcome! vue-tui is evolving fast — please open an issue before starting large changes. If you use AI tools, disclose it in your PR and make sure you've reviewed and tested everything before submitting.

Credits

vue-tui is built on the ideas pioneered by Ink — component model, yoga-based layout, focus system, and rendering pipeline — adapted to Vue's philosophy. Thanks to Vadim Demedes, Sindre Sorhus, and the Ink contributors.

License

MIT