Doctor Wu d8d9296905 feat(components): add ScrollBox component (#237)
* feat(runtime): add ScrollBox component

* refactor(components): move ScrollBox into components package

* feat(runtime): add mouse input composable

* refactor(components): delegate ScrollBox mouse input

* docs: describe mouse input composable

* fix(runtime): consume unsupported SGR mouse input

* fix(components): gate ScrollBox input on raw mode support

* fix(runtime): require escape prefix for SGR mouse input

* refactor(components): rename ScrollBox input props to wheel/keyboard

Follow the components boolean-prop convention (bare noun, default false):
enableMouse/enableKeyboard/isActive -> wheel/keyboard, both opt-in. Mouse-
wheel is off by default because enabling terminal mouse tracking suppresses
the terminal's native text selection window-wide. Record the convention in
components-design-principles.md.

* refactor(components): rename ScrollBox linesPerWheel + input tests

- Rename the wheel-step prop wheelLines -> linesPerWheel.
- Drop Home/End keyboard scroll for now — keyboard is PageUp/PageDown only.
- Add tests: keyboard paging, and SGR mouse-mode disable on signal-exit
  (fs.writeSync path, mirroring the bracketed-paste test).

* feat(examples): add ScrollBox streaming demo

A streaming-log demo of <ScrollBox wheel keyboard>: new lines arrive on a
timer and stick to the bottom until you scroll up (wheel / PageUp / PageDown),
then hold position while output keeps arriving. Press q to quit.

---------

Co-authored-by: Yunfei He <i.heyunfei@gmail.com>
2026-07-04 11:25:50 +08:00

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 npm version @vue-tui/components npm version @vue-tui/vite npm version @vue-tui/testing npm version

  • 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
<ScrollBox> Bounded scroll viewport with mouse-wheel scrolling and sticky-bottom behavior
<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
useMouseInput(handler, opts?) Handle terminal mouse input — currently SGR wheel events with ref-counted mouse-mode ownership
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

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