Files
vue-tui/README.md
T
Yunfei He d2af0c83b8 docs(readme): present both usage modes in Quick Start (#233)
* docs(readme): present both usage modes in Quick Start

Quick Start now covers the two ways to use vue-tui:

1. Scaffold a project (the @vue-tui/vite template) — Vue SFCs + terminal HMR
   dev server + vue-tsc.
2. Use @vue-tui/runtime standalone — no plugin, no build step; components as
   h() render functions, run with `node app.mjs`.

Both snippets are verified to run against the published packages. Folds in the
former "Add to an existing project" + "Example" sections and drops the now-stale
Example entry from the table of contents.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): tighten Quick Start prose

Drop the redundant build/type-check sentence from the template section, and trim
the standalone section's trailing explainers (the h()-vs-template note and the
Node TypeScript-runner paragraph) down to the renderToString one-liner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): drop the renderToString aside from Quick Start

It's a separate (non-interactive) capability — tangential to the Quick Start's
goal of getting an interactive app running. Belongs in API docs, not here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): reframe standalone mode (decoupled from plugin, not "no build")

"no plugin, no build step" was misleading — TS/SFC/JSX all need a compile step
(same as Ink's JSX), so "no build" isn't a real differentiator. The actual point
is that @vue-tui/runtime is a standalone renderer and the @vue-tui/vite plugin
only adds the SFC + HMR dev workflow on top, so the runtime can be used on its
own in any existing project.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): mode 1 code-free; mode 2 uses SFC (not h) + JSX note

- Scaffold (recommended): drop the component snippet — keep it to the commands.
- Standalone: author with an SFC and mount with createApp (not h() render
  functions), and note JSX via @vitejs/plugin-vue-jsx. Verified an SFC builds and
  renders with plain @vitejs/plugin-vue (no @vue-tui/vite).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): bullet the standalone tooling notes; clearer plugin line

Split the two trailing sentences into bullets, and rewrite the @vue-tui/vite
line to be direct: it does the SFC setup for you and adds terminal HMR (option 1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): frame @vue-tui/vite around HMR, not "SFC setup"

The standalone tooling note implied @vue-tui/vite's job is the SFC compilation.
Its value is the terminal HMR dev server; reframe the bullet accordingly (it
bundles plugin-vue only as the mechanism that makes terminal HMR work).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): reword the HMR bullet to read naturally

Replace the clipped "— that's option 1" with a normal sentence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): drop the awkward "option 1" callback in the standalone note

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): show the concrete [vue(), vueTui()] config in the HMR note

Now that @vue-tui/vite no longer bundles plugin-vue, make the standalone-mode
HMR bullet concrete: you compose them, compiler first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): "HMR support" wording in the standalone note

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

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

12 KiB

vue-tui

Public beta — the @vue-tui/runtime API is stabilizing toward 1.0; dev-mode HMR is still experimental. Bug reports welcome.

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

@vue-tui/runtime · @vue-tui/components · @vue-tui/vite · @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 the @vue-tui/vite plugin (npm run 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

There are two ways to use vue-tui — scaffold a full project, or drop the runtime into an existing one.

A ready-to-develop setup: Vue SFCs and a terminal HMR dev server via the @vue-tui/vite plugin.

npx tiged vuejs-ai/vue-tui-starter/vite my-app
cd my-app
npm install
npm run dev      # in-process terminal dev server with HMR

Edit src/app.vue and watch the terminal update instantly.

2. Use the runtime standalone

@vue-tui/runtime is a standalone Vue renderer, independent of the @vue-tui/vite plugin. Author components as SFCs and mount them with createApp, using your own build:

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

const count = shallowRef(0);

useInput((input) => {
  // "+" is Shift+"=" on most keyboards, so accept the bare "=" too.
  if (input === "+" || input === "=") count.value++;
  if (input === "-") count.value--;
});
</script>

<template>
  <Box>
    <Text>Count: </Text>
    <Text bold color="green">{{ count }}</Text>
    <Text dimColor> (+/= and - to change)</Text>
  </Box>
</template>
// main.ts
import { createApp } from "@vue-tui/runtime";
import App from "./app.vue";

createApp(App).mount();

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/vite Vite plugin — add vueTui() to vite.config.ts for an in-process terminal dev server with HMR (npm run dev) plus a production build (vite build). Experimental; may change.
@vue-tui/testing Test harness — render in an isolated fake terminal, simulate input, assert output frame by frame
@vue-tui/components High-level components built on the runtime primitives — currently <Spinner> (animated loading), with more to come.

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

High-level Components

The @vue-tui/components package adds higher-level components composed from the runtime primitives — published separately from the core.

Component Description
<Spinner> Animated loading spinner — built-in dots/line presets or custom frames, optional label

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

To run an example with terminal HMR, use vanilla vite@8 (the recommended setup): cd examples/basic-template && npm run dev. See that example's README.md for the in-monorepo caveat.

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