Terminal
A terminal view that displays ANSI and Unicode output.
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
linesfor declarative logs andwrite()/writeln()for streams; replacinglinesalso wipes imperative output. - Give the container a definite height; explicit
cols/rowsoverride 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
commandandargspassed separately, and cleaned up on session switch or unmount.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
readOnly | boolean | false | Disables input and data, and never autofocuses; selection and copy still work. |
autoFocus | boolean | false | Focuses an interactive terminal after initialization; ignored when read-only. |
autoScroll | boolean | true | Scrolls to the bottom after output; when off, keeps the viewport, and input doesn't scroll either. |
lines | readonly (string | Uint8Array)[] | undefined | Log records, each followed by CRLF; an unchanged prefix appends, anything else resets and replays. |
cols | number | fit / initial 80 | Fixed column count; otherwise derived from the container. |
rows | number | fit / initial 24 | Fixed row count; otherwise derived from the container. |
fontSize | number | 13 | Font size in px; changes trigger a fit. |
fontFamily | string | ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace | Font stack; changes trigger a fit. |
theme | ITheme | host tokens | Partial xterm theme; overrides only the colors you set. |
labels | Partial<TerminalLabels> | { ariaLabel: 'Terminal' } | Accessible name of the terminal region and its input textarea. |
Events
| Event | Payload | Description |
|---|---|---|
data | string | Keyboard, 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. |
ready | TerminalInstance | The display is initialized; the component ref exposes the same methods. |
Exposed Methods
| Method | Signature | Description |
|---|---|---|
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() | () => void | Queues a scrollback clear; the current prompt line stays. |
reset() | () => void | Queues a buffer and parser reset, dropping content and terminal modes. |
focus() | () => void | Focuses a ready interactive terminal. |
fit() | () => TerminalSize | null | Fits unpinned axes and returns the size; hidden containers keep their last size; null when unavailable. |
getSize() | () => TerminalSize | null | Current 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(), andclear()share one ordered queue; binary data is decoded as UTF-8. linesis watched deeply, so push, index assignment, replacement, and clearing all work; after mutating bytes inside aUint8Array, 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.0and@xterm/addon-fit ^0.10.0(both MIT); the engine stylesheet ships interminal/style.css. - Colors come from inherited TuffEx tokens and follow theme and contrast changes; a
ResizeObservercoalesces fits into one frame. - Source:
packages/tuffex/packages/components/src/terminal/; process sessions live inpackages/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
autoFocusoff unless the user explicitly opens an interactive terminal; log updates never move focus.