Yunfei He c8be3f5672 docs(divergences): record the tab measure-vs-paint width quirk (shared with Ink, KEEP) (#201)
A literal tab in <Text> is measured as 0 columns by string-width but painted at
its tab-stop width (wrap-ansi / terminal), so the reserved yoga width disagrees
with what's drawn (ab\tcd measures 4, paints ~10). This is a shared upstream
quirk — Ink v7.0.4 does the same and likewise doesn't normalize tabs — so it's
aligned with Ink, not a divergence; recorded under Non-Behavioral Notes so it's
not rediscovered as a parity gap. KEEP (literal tabs in TUI text are vanishingly
rare). The note also captures the fix direction if ever needed (expand tabs to
spaces at the shared squash chokepoint, upstream of string-width) and the one
behavior change that would then become the actual divergence. [VOUCHED @hyf0]

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

vue-tui

Status — the runtime API is stabilizing; the CLI and dev toolkit are still experimental and may change. Bug reports 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

S
Description
Vue 3 terminal UI framework — 备份
Readme 3 MiB
Languages
TypeScript 99.2%
Vue 0.7%