组件/Terminal 终端显示

Terminal 终端显示

显示 ANSI 与 Unicode 输出的终端组件

自 0.6.3BETA

当前组件文档正在开发中

该页面正在持续迁移,示例与 API 可能会继续调整。

安装

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'

用法

交互显示

ready 返回实例,data 派发键盘输入;输入不会自动回显。

示例加载中...

只读日志

暂停由宿主实现:继续收集记录,但不更新传给组件的 lines。

示例加载中...

最佳实践

  • 声明式日志用 lines,流式输出用 write() / writeln();替换 lines 会清掉方法写入的内容。
  • 为容器设置明确高度;显式的 cols / rows 只覆盖对应轴的自动适配。
  • 日志保持 readOnly,不要为只显示日志而创建 PTY(伪终端)。
  • 处理写入 Promise 的 reject:卸载或初始化失败会拒绝全部待处理写入。
  • 特权执行只经可信宿主 SDK:由用户明确触发,command 与 args 分开传,切换会话或卸载时清理。

API 参考

属性

属性类型默认值说明
readOnlybooleanfalse禁用输入与 data 事件,不自动聚焦;仍可选择与复制。
autoFocusbooleanfalse初始化后聚焦交互终端;只读时无效。
autoScrollbooleantrue输出后滚到底部;关闭时保持视口位置,输入也不触发滚动。
linesreadonly (string | Uint8Array)[]undefined日志记录,每条追加 CRLF;前缀不变时只追加,否则重置后重放。
colsnumber自动适配;初始 80固定列数;缺省时按容器计算。
rowsnumber自动适配;初始 24固定行数;缺省时按容器计算。
fontSizenumber13字号(px),变化后重新适配。
fontFamilystringui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace字体栈,变化后重新适配。
themeITheme宿主令牌部分 xterm 主题,只覆盖指定的颜色。
labelsPartial<TerminalLabels>{ ariaLabel: 'Terminal' }终端区域与输入 textarea 的无障碍名称。

事件

事件参数说明
datastring键盘、粘贴与控制序列输入,原样转交会话;只读时不派发。
resize{ cols: number; rows: number }初始尺寸与之后的行列变化;有 PTY 时转交会话。
readyTerminalInstance显示初始化完成;组件 ref 暴露同一组方法。

暴露方法

方法签名说明
write(data)(string | Uint8Array) => Promise<void>排队写入原始数据,解析后 resolve;可在初始化前调用,不加换行。
writeln(data)(string | Uint8Array) => Promise<void>同一队列,追加 CRLF。
clear()() => void排队清除滚动历史,保留当前提示行。
reset()() => void排队重置缓冲区与解析器,清除内容与终端模式。
focus()() => void聚焦已初始化的交互终端。
fit()() => TerminalSize | null适配未固定的轴并返回尺寸;隐藏时保留上次尺寸,不可用时为 null。
getSize()() => TerminalSize | null当前行列;初始化前或释放后返回 null。

类型

/terminal 导出 TerminalProps、TerminalEmits、TerminalInstance、TerminalSize、TerminalData、TerminalLabels、TxTerminalInstance 与 TERMINAL_DEFAULT_LABELS。组件也从 /pro 与根入口导出,Terminal 是可安装包装。

Electron PTY 接入

仅适用于具备 TuffTransport 的可信 Electron 渲染器。主进程持有 PTY 并检查 system.shell 权限;createTerminalSdk 先订阅再创建,signal 中止时自动关闭迟到的会话。

<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">启动 Node REPL</TxButton>
  <TxButton @click="stop">关闭</TxButton>
  <div style="height: 280px">
    <TxTerminal @ready="display = $event" @data="input" @resize="resize" />
  </div>
  <output>{{ status }}</output>
</template>

概述

  • 只负责显示:挂载不会创建 shell、启动进程或申请 Electron 权限,组件也不导入 transport 或 Electron。
  • xterm 与 fit addon 只在 onMounted 中动态加载;服务端渲染不访问浏览器全局对象。
  • 输出、reset() 与 clear() 共用一个有序队列;二进制数据按 UTF-8 解码。
  • 深度监听 lines,支持 push、下标修改、替换与清空;原地改写 Uint8Array 字节后需触发一次响应式更新。
  • 只读模式在 xterm 与事件出口双重阻止输入并关闭光标闪烁;从交互切到只读时输入区失焦。
  • 卸载时释放 xterm、订阅与观察器;待处理写入被 reject,迟到的动态导入不会重建终端。

技术实现

  • 基于 @xterm/xterm ^5.5.0 与 @xterm/addon-fit ^0.10.0(均为 MIT),样式已含在 terminal/style.css。
  • 颜色取自继承的 TuffEx 令牌并跟随主题与对比度变化;ResizeObserver 把适配合并到一帧。
  • 源码:packages/tuffex/packages/components/src/terminal/;进程会话见 packages/utils/transport/sdk/domains/terminal.ts。
查看源码
packages/tuffex/packages/components/src/terminal/index.ts

使用场景

  • 网页或 Electron 日志查看器,暂停与历史由宿主控制。
  • 由可信宿主持有命令执行的交互式 Electron PTY。
  • 无需进程权限的 ANSI / Unicode 输出预览。

无障碍

  • 已启用 xterm 屏幕阅读器支持;只读输出仍可选择与复制。
  • 除非用户明确打开交互终端,否则不开启 autoFocus;日志更新不移动焦点。