Components/Terminal

Terminal

A terminal view that displays ANSI and Unicode output.

Since 0.6.3BETA

This component doc is in progress

This page is still being migrated. Demos and API details may change.

Installation

pnpm add @talex-touch/tuffex
import { TxTerminal } from '@talex-touch/tuffex/terminal'
import '@talex-touch/tuffex/base.css'
import '@talex-touch/tuffex/terminal/style.css'

Usage

Interactive Display

ready hands over the instance and data emits keyboard input; input is never echoed automatically.

Loading demo...

Read-only Logs

Pausing belongs to the host: keep collecting records, but stop updating the lines passed in.

Loading demo...

Best Practices

  • Use lines for declarative logs and write() / writeln() for streams; replacing lines also wipes imperative output.
  • Give the container a definite height; explicit cols / rows override fitting on their own axis only.
  • Keep logs readOnly, and don't create a PTY just to display them.
  • Handle rejected write promises: unmounting or a failed initialization rejects every pending write.
  • Run privileged commands only through a trusted host SDK: on an explicit user action, with command and args passed separately, and cleaned up on session switch or unmount.

API Reference

Props

PropTypeDefaultDescription
readOnlybooleanfalseDisables input and data, and never autofocuses; selection and copy still work.
autoFocusbooleanfalseFocuses an interactive terminal after initialization; ignored when read-only.
autoScrollbooleantrueScrolls to the bottom after output; when off, keeps the viewport, and input doesn't scroll either.
linesreadonly (string | Uint8Array)[]undefinedLog records, each followed by CRLF; an unchanged prefix appends, anything else resets and replays.
colsnumberfit / initial 80Fixed column count; otherwise derived from the container.
rowsnumberfit / initial 24Fixed row count; otherwise derived from the container.
fontSizenumber13Font size in px; changes trigger a fit.
fontFamilystringui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospaceFont stack; changes trigger a fit.
themeIThemehost tokensPartial xterm theme; overrides only the colors you set.
labelsPartial<TerminalLabels>{ ariaLabel: 'Terminal' }Accessible name of the terminal region and its input textarea.

Events

EventPayloadDescription
datastringKeyboard, paste, and control-sequence input to forward unchanged; never emitted when read-only.
resize{ cols: number; rows: number }The initial size and later changes; forward to the PTY if there is one.
readyTerminalInstanceThe display is initialized; the component ref exposes the same methods.

Exposed Methods

MethodSignatureDescription
write(data)(string | Uint8Array) => Promise<void>Queues raw output, even before readiness; resolves once parsed and adds no newline.
writeln(data)(string | Uint8Array) => Promise<void>Same queue, with CRLF appended.
clear()() => voidQueues a scrollback clear; the current prompt line stays.
reset()() => voidQueues a buffer and parser reset, dropping content and terminal modes.
focus()() => voidFocuses a ready interactive terminal.
fit()() => TerminalSize | nullFits unpinned axes and returns the size; hidden containers keep their last size; null when unavailable.
getSize()() => TerminalSize | nullCurrent columns and rows; null before initialization or after disposal.

Types

/terminal exports TerminalProps, TerminalEmits, TerminalInstance, TerminalSize, TerminalData, TerminalLabels, TxTerminalInstance, and TERMINAL_DEFAULT_LABELS. The component is also exported from /pro and the root; Terminal is the installable wrapper.

Electron PTY Integration

For trusted Electron renderers with TuffTransport only. The main process owns the PTY and checks system.shell; createTerminalSdk subscribes before creating, and an aborted signal closes a late session automatically.

<script setup lang="ts">
import type { TerminalInstance, TerminalSize } from '@talex-touch/tuffex/terminal'
import type { TerminalSessionHandle } from '@talex-touch/utils/transport'
import { createTerminalSdk, useTuffTransport } from '@talex-touch/utils/transport'
import { onBeforeUnmount, ref, shallowRef } from 'vue'

const props = defineProps<{ directory: string }>()
const display = shallowRef<TerminalInstance | null>(null)
const status = ref('')
const sdk = createTerminalSdk(useTuffTransport())
let session: TerminalSessionHandle | null = null
let active: AbortController | null = null

function fail(reason: unknown) {
  status.value = reason instanceof Error ? reason.message : String(reason)
}
function stop() {
  active?.abort()
  active = null
  void session?.close().catch(fail)
  session = null
}
async function start() {
  stop()
  const controller = (active = new AbortController())
  display.value?.reset()
  const size = display.value?.getSize()
  try {
    session = await sdk.create({ command: 'node', args: ['-i'], cwd: props.directory, cols: size?.cols, rows: size?.rows }, {
      signal: controller.signal,
      onData(data) {
        if (active === controller)
          void display.value?.write(data).catch(fail)
      },
      onExit(exit) {
        if (active !== controller)
          return
        session = null
        status.value = `exit=${exit.exitCode}, signal=${exit.signal ?? 'none'}`
      },
    })
  }
  catch (reason) {
    if (active === controller)
      fail(reason)
  }
}
const input = (data: string) => void session?.write(data).catch(fail)
const resize = (size: TerminalSize) => void session?.resize(size.cols, size.rows).catch(fail)
onBeforeUnmount(stop)
</script>

<template>
  <TxButton :disabled="!display" @click="start">Start Node REPL</TxButton>
  <TxButton @click="stop">Close</TxButton>
  <div style="height: 280px">
    <TxTerminal @ready="display = $event" @data="input" @resize="resize" />
  </div>
  <output>{{ status }}</output>
</template>

Overview

  • Display only: mounting never creates a shell, starts a process, or requests Electron privileges, and the component imports neither transport nor Electron.
  • xterm and the fit addon load dynamically in onMounted; server rendering touches no browser globals.
  • Output, reset(), and clear() share one ordered queue; binary data is decoded as UTF-8.
  • lines is watched deeply, so push, index assignment, replacement, and clearing all work; after mutating bytes inside a Uint8Array, trigger one reactive update.
  • Read-only mode blocks input at both xterm and the event boundary and stops the cursor blink; switching from interactive to read-only blurs the input.
  • Unmount disposes xterm, subscriptions, and observers; pending writes reject, and a late dynamic import can't recreate the terminal.

Technologies

  • Built on @xterm/xterm ^5.5.0 and @xterm/addon-fit ^0.10.0 (both MIT); the engine stylesheet ships in terminal/style.css.
  • Colors come from inherited TuffEx tokens and follow theme and contrast changes; a ResizeObserver coalesces fits into one frame.
  • Source: packages/tuffex/packages/components/src/terminal/; process sessions live in packages/utils/transport/sdk/domains/terminal.ts.
查看源码
packages/tuffex/packages/components/src/terminal/index.ts

Use cases

  • Browser or Electron log viewers whose host owns pause and history.
  • Interactive Electron PTYs whose trusted host owns command execution.
  • ANSI / Unicode output previews that need no process privileges.

Accessibility

  • xterm screen-reader support is on; read-only output stays selectable and copyable.
  • Leave autoFocus off unless the user explicitly opens an interactive terminal; log updates never move focus.