# @cplieger/web-terminal-engine 中文文档

规范地址：https://npmcn.com/package/%40cplieger/web-terminal-engine/

版本：6\.5\.0
npm 元数据更新时间：2026-11-13T08:40:06.470Z
中文译文更新时间：2026-11-13T08:54:14.949Z
软件包许可：MPL-2\.0

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40cplieger/web-terminal-engine
原文对照：https://npmcn.com/package/%40cplieger/web-terminal-engine/?lang=original
项目主页：https://github.com/cplieger/web-terminal-engine
源码仓库：https://github.com/cplieger/web-terminal-engine

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：387 kB，29 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2026-06-29T13:01:06.986Z
- 最新版本发布：2026-11-13T08:40:06.107Z
- 近 30 天下载量：9393（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：70（近 12 个月 70 个）

## 中文简介

浏览器终端引擎: VT500 屏幕缓冲器, DOM 渲染器, 二进制线协议

## 安装

```sh
npm install @cplieger/web-terminal-engine
```

也可以使用：`pnpm add @cplieger/web-terminal-engine`、`yarn add @cplieger/web-terminal-engine`、`bun add @cplieger/web-terminal-engine`

## README

# @cplieger/网络终端引擎

[![npm](https://img.shields.io/npm/v/@cplieger/web-terminal-engine)](https://www.npmjs.com/package/@cplieger/web-terminal-engine) [![JSR](https://jsr.io/badges/@cplieger/web-terminal-engine)](https://jsr.io/@cplieger/web-terminal-engine)

> 浏览器虚拟终端渲染器 [`cplieger/web-terminal-engine`](https://github.com/cplieger/web-terminal-engine) Go模块:基于DOM的VT500屏幕,并有OSC 8超链接支持,回滚,键盘映射器,鼠标编码器和二进制线解码器. 零运行时间依赖关系.

网络地铁-引擎跨语言终端库的浏览器半个. 与 Go 服务器侧套件对等(`vt`, (中文(简体) ), `terminal`) 在一个版本的 WebSocket 协议上;参见 [项目README](https://github.com/cplieger/web-terminal-engine#readme) 整个故事.

## 安装

```sh
npx jsr add @cplieger/web-terminal-engine   # JSR (preferred)
npm i @cplieger/web-terminal-engine          # NPM
```

## 使用量

每个终端一台发动机. `createTerminalEngine` 构建一个渲染器、一个卷轴控制器、一个鼠标控制器、一个模式状态,以及您两个元素、路线上的连接 `screen`, (中文(简体) ), `scroll` 财务报告和审定财务报表 `modes` 框到渲染器,然后返回五部分加一 `dispose()`数字 :

```typescript
import { createTerminalEngine, keyboard } from "@cplieger/web-terminal-engine";

const termWrap = document.getElementById("term") as HTMLElement; // the scroll container
const output = document.getElementById("term-output") as HTMLElement; // receives the rows

const engine = createTerminalEngine({
  output,
  termWrap,
  wsPath: "/ws",
  maxLines: 5000, // optional retained-line cap for the renderer's store
  callbacks: {
    onMessage: (msg) => {
      if (msg.type === "title") document.title = msg.title;
    },
    onOpen: () => {},
    onClose: () => {},
    onConnecting: () => {},
    onServerRestart: () => {},
    onProcessExit: () => {},
    computeSize: () => engine.renderer.computeSize(),
    initialSize: () => engine.renderer.computeSize(),
  },
});

document.fonts.ready.then(() => {
  engine.renderer.updateFontMetrics(); // measure the real font before announcing a size
  engine.connection.connect();
});

const onKeydown = (ev: KeyboardEvent): void => {
  const r = keyboard.mapKeyboardEvent(ev, engine.modes);
  if (r.kind === "send") {
    engine.connection.sendBinary(new TextEncoder().encode(r.bytes));
    ev.preventDefault();
  }
  if (r.kind === "scroll-up" || r.kind === "scroll-down") ev.preventDefault();
};
document.addEventListener("keydown", onKeydown);

// Called when the terminal leaves the page (a route change, a panel close):
function unmountTerminal(): void {
  document.removeEventListener("keydown", onKeydown);
  engine.dispose();
}
```

这五所工厂也自行提供. 用手将它们组合起来,可以明确传递每个状态的依赖(没有默认实例可追溯到),并给渲染器自己设定路由框架:

```typescript
import {
  createConnection,
  createModeState,
  createMouseController,
  createRenderer,
  createScrollController,
} from "@cplieger/web-terminal-engine";

const modes = createModeState();
const scroll = createScrollController({
  scrollEl: termWrap,
  onScrollPosition: () => renderer.handleScrollPosition(),
});
const renderer = createRenderer({
  output,
  termWrap,
  scroll,
  modes,
  requestHistory: (from, max) => connection.requestHistory(from, max),
  historyBudget: () => connection.historyBudget(),
});
const mouse = createMouseController({
  modes,
  termElement: () => termWrap,
  gridElement: () => output, // the rows' own box: no padding, no scrollbar gutter
  cellSize: () => renderer.cellSize(),
  gridSize: () => renderer.gridSize(), // the RENDERED grid, not the size this client would like
  sendReport: (data) => connection.sendEphemeral(data),
});
const connection = createConnection({
  renderer,
  modes,
  mouse,
  callbacks: {
    onMessage: (msg) => {
      if (msg.type === "screen") renderer.handleScreen(msg);
      else if (msg.type === "scroll") renderer.handleScroll(msg);
    },
    onOpen: () => {},
    onClose: () => {},
    computeSize: () => renderer.computeSize(),
  },
});
connection.connect();
```

### 一页两台引擎

两个引擎没有共享:每个引擎都有自己的DOM手柄,计时器,收听器,套接字和模式状态,以及 `--char-w` 自定义属性由每个引擎自行写入 `termWrap`,没有打开 `document.documentElement`两个引擎可以碰撞的一件事是 无法管理的会话代号,它生活在 `sessionStorage` 每个浏览器标签:两个引擎,两个运行时都没有会话管理器(`connect()` 没有 `setSession`)并共享默认值 `sessionIdKey` 同一届会议附后。 传递一个鲜明的 `sessionIdKey` 各项(`"vterm-session-id"` 财务报告和审定财务报表 `"vterm-session-id:2"`说吧 管理引擎( A)`setSession(id)`永远不要薄荷 id,不需要钥匙.

### `dispose()`

`engine.dispose()` 以倒置的构建顺序处理这五个部分: 连接以代码 1000 关闭其套接字( 否) `onClose` ),停止其心跳,历史和重接计时器,放下每个会话的队列的字节并解开您的回调;鼠标控制器解开其四个收听器并取消其待决的动框;渲染器取消其待决帧并闪烁间隔,去除其 `visibilitychange` 听者,空虚 `output`,删除其覆盖和 `--char-w`, (中文(简体) ), `cursor-blink-off` 财务报告和审定财务报表 `term-reverse-video` 标记从 `termWrap`,并删除每个元素引用;滚动控制器删除其 `scroll` 听器; 模式状态重排到 `POWER_ON_MODES`。可以调用 `dispose()` 在引用它的引擎上; 连接在调回后停止。 每一种方法事后都可以调用,并回答一个空值而不是扔出:

| 编 | 方法 | 之后 `dispose()` |
| --- | --- | --- |
| `renderer` | `boundStore()` | 一个新的,空的 `LineStore` 每一次通话 |
| `renderer` | `getHighestIndex()`, (中文(简体) ), `getReplayBoundary()` | `-1` |
| `renderer` | `lastBrowseActivityMs()`, (中文(简体) ), `browseCacheSize()`, (中文(简体) ), `pendingRowCount()` | `0` |
| `renderer` | `replayMaxForResume()` | `5000` |
| `renderer` | `applyResumeTransition()` | `false` |
| `renderer` | `captureViewMemory()`, (中文(简体) ), `pendingRestoreAbs()` | `null` |
| `renderer` | `computeSize()`, (中文(简体) ), `gridSize()` | `{ cols: 0, rows: 0 }` |
| `renderer` | `cellSize()` | `{ width: 0, height: 0 }` |
| `renderer` | `getCursorPx()` | `{ left: 0, top: 0, cellH: 0 }` |
| `scroll` | `isUserScrolledUp()` | `false` |
| `scroll` | `currentScrollTop()` | `0` |
| `connection` | `sendBinary()`, (中文(简体) ), `sendEphemeral()`, (中文(简体) ), `requestHistory()`, (中文(简体) ), `cancelPendingInput()`, (中文(简体) ), `inputReady()` | `false` |
| `connection` | `historyBudget()`, (中文(简体) ), `pendingInputBytes()` | `0` |
| `connection` | `lastAnnouncedSize()` | `null` |
| `connection` | `currentSessionId()` | `""` |
| `modes` | 九位读者, `snapshot()` | 页:1 `POWER_ON_MODES` 数值; `applySnapshot` 财务报告和审定财务报表 `setKnown` 是无头人, `subscribe` 返回无操作 |

`createTerminalEngine` 而当一个收听器或计时器无法获取时,每个工厂都会抛出,而一掷则留下了任何东西:在它被倒置处理之前所建造的部件,所以没有部分被建造的发动机可以被丢出.

## API 密码

-   `createTerminalEngine(options)` 以下五个部分 `output` 财务报告和审定财务报表 `termWrap` 把他们绑起来 它的路线 `screen`, (中文(简体) ), `scroll` 财务报告和审定财务报表 `modes` 框到渲染器前 `onMessage` 看他们。 它返回一个 `TerminalEngine`成员为: `renderer`, (中文(简体) ), `scroll`, (中文(简体) ), `connection`, (中文(简体) ), `mouse`, (中文(简体) ), `modes` 财务报告和审定财务报表 `dispose`选项是 `callbacks`,这是 `ConnectionCallbacks`附加时 `wsPath`, (中文(简体) ), `sessionIdKey`, (中文(简体) ), `maxLines`, (中文(简体) ), `onCursorMove`, (中文(简体) ), `onUserScrollChange` 财务报告和审定财务报表 `initialModes`。它既不连接,也不测量字体。 打电话 `renderer.updateFontMetrics()` 当您的字体准备好时 `connection.connect()` 开始.
-   `createRenderer(options)` 返回 a `Renderer`,驱动 DOM 渲染器 `ScreenMessage` 编号 : `ScrollMessage` 边框 `output` 内部 `termWrap`,并注入一个 `scroll` 控制器和 `modes` 状态. `maxLines` 是其商店的保留行顶。 记忆受限制的消费者通过较少的预算. 盖子上方的历史被分批从上方拆出,活屏幕从不被拆出,因此在终端高度或下方的盖子保持了全屏幕而无回放. 它的方法是 `handleScreen`, (中文(简体) ), `handleScroll`, (中文(简体) ), `handleScrollPosition`, (中文(简体) ), `updateFontMetrics`, (中文(简体) ), `computeSize`, (中文(简体) ), `cellSize`, (中文(简体) ), `gridSize`, (中文(简体) ), `getCursorPx`, (中文(简体) ), `setPredictedCursor`, (中文(简体) ), `bind`, (中文(简体) ), `boundStore`, (中文(简体) ), `captureViewMemory`, (中文(简体) ), `resetScreen`, (中文(简体) ), `resetScrollback`, (中文(简体) ), `getHighestIndex`, (中文(简体) ), `getReplayBoundary`, (中文(简体) ), `noteResumeBounds`, (中文(简体) ), `handleGrid`, (中文(简体) ), `clearGrid` 财务报告和审定财务报表 `dispose`。 。 。 . `getReplayBoundary()` 是简历 `haveThrough`· ; `getHighestIndex()` 还计算了服务器未承诺的应用程序所绘制的行数,并发送它留下了回滚的最后一个屏幕的已过时副本([恢复控制](https://npmcn.com/package/%40cplieger/docs/wire-protocol.md#the-resume-control)) (中文(简体) ). `gridSize()` 回答服务器屏幕的网格:当前附件上一个网格框架的列和行. 在一个到达之前( 一个没有发送的服务器, 或者一个新套接字, 其框架尚未到来) 它会回答测量的列和最后一个框的行 。 连接呼叫 `clearGrid()` 当每个套接字开始时,会话切换从不读取上一会话的网格。 它赞同其 `modes`,所以请 `term-reverse-video` 课开始 `termWrap` 总是匹配 `isReverseVideo()`。 。 。 .
-   `keyboard` 翻译 `KeyboardEvent` 到终端字节序列. `mapKeyboardEvent(ev, modes)`, (中文(简体) ), `keyActionBeforeModes(ev)` (在模式已知之前,一个呼叫者在按键下用一个密钥做什么:每个模式状态处理的密钥都一样得到该动作,某个状态发送的未经修改的密钥是终端的,一个Ctrl,Alt或Meta的和弦有些状态留下的未同意保留浏览器的), `bracketPaste(text, modes)` (贴作: `body` 发件人可缩短发件时间和 `tail` 随它而来, `bracketTextForPaste(text, modes)` (两人同归于尽), `isInterruptInput(text)` (作为工具栏和粘性Ctrl编码的未经修改的逃逸或Ctrl+C), `kittyDisambiguateActive(modes)`, (中文(简体) ), `prepareTextForTerminal`, (中文(简体) ), `ctrlByteFor`,加共享逻辑键编码 `plainCursorKeySeq` 财务报告和审定财务报表 `plainEscapeSeq` 工具栏模块重复使用。 每个模式依赖函数都将模式状态作为参数,而引擎的 `modes` 满足它.
-   `toolbar` 是屏幕上的移动工具栏部件. `bindMobileToolbar({toolbar, send, ids?})` 电线 `pointerdown` 屏幕上Ctrl/rows/Tab/Enter/Esc工具栏的处理器,带有粘接-Ctrl语义和Kitty/DECCKM-aware箭头与物理键路径的字节-等同. `send` 接收每份新闻 `KeyEncoder`, (中文(简体) ), `send(encode)`,以及 `encode(modes)` 在它给出的模式状态下回答字节,所以一个消费者在发送时所知道的模式下编码了一个按键. 它返回一个 `MobileToolbarController` 曝光 `applyStickyCtrl`, (中文(简体) ), `setCtrlArmed`, (中文(简体) ), `isCtrlArmed` 财务报告和审定财务报表 `dispose`。 。 。 . `applyStickyCtrl(text)` 当文本被键入并返回其 Ctrl 和弦的编码器时, 或当文本未变出时, 消耗一个武装 Ctrl.
-   `createMouseController(options)` 返回 a `MouseController`,以及 `encodeSGR` 是它使用的 SGR 1006 鼠标编码器,附于 `termElement()`选项 `MouseInputHandler` 加号 `modes`控制器显示 `resyncGesture`, (中文(简体) ), `disarmGesture` 财务报告和审定财务报表 `dispose`自动门 `mouseMode > 0`。 。 。 . `MouseInputHandler` 携带两个可选成员, 两者都不遵守今天的行为. `gridElement` 指定框为网格的元素。 卷轴容器本身的盒子包括它的垫子和卷轴条沟道,所以不是那个盒子. `gridSize` 从 `renderer.gridSize` 而不是这个客户端的大小。 每份报告被夹入其中,行从下边被测量出,因为屏幕窗口是内容的尾. 规定 `sendReport(data)` 返回报表是否到达了活套接字,因此将其接上 `connection.sendEphemeral`。鼠标输入效果最佳,有四个后果。 发布只针对DELIVERED的媒体,因为从未到来的媒体是自己的解毒剂,应用程序也从未得知按钮下线. 拖放报告只是当应用程序认为按钮已下调时才被扣住的按钮,所以对未交付手势的动作被放在模式1002下并报告为1003下的无按钮. `resyncGesture()` 最后读取的按钮发出一个解禁, 现在已打开, 因此拖动不会被按住 。 连接一旦恢复Ack宣布了服务器的能力,就呼叫它,而不是从 `onOpen`时 `onOpen` ephemeral channel尚不为人所知,所以发布会采取可靠的倒置. 运动是结合到每个动画框一个报告,最新获胜,而新闻,发布和轮子则永远无法结合,因为一个轮子报告是相对于屏幕的notch计数,而结合失去滚动距离. `resyncGesture` 与处理者共用一扇门,每人执行. 由于应用程序停止跟踪而搁浅的姿态是FORGOTTEN, 由下一个观察到的跟踪者决定, 因为发布时没有任何要求作为按键键到达 PTY 。 tmux 在附加、 清除时以同样的方式回答 `mouse_drag_flag` 因为它会禁用模式. `disarmGesture()` 忘记了没有报告的任何姿态 在另一个会话中指向一个终端的套接字时称它,因为记录属于看到媒体的会话. 重点报告不在这里。 这是运输状态,它生活在 `connection.setClientFocus`。 。 。 .
-   `createScrollController(options)` 返回 a `ScrollController`,滚动容器的自动跟踪器 `scrollEl`,可选 `onUserScrollChange` 财务报告和审定财务报表 `onScrollPosition` 召回电话。 它的方法是 `stickToBottom`, (中文(简体) ), `scrollToBottom`, (中文(简体) ), `scrollByPage`, (中文(简体) ), `isUserScrolledUp`, (中文(简体) ), `currentScrollTop`, (中文(简体) ), `restoreView`, (中文(简体) ), `adjustForContentShift` 财务报告和审定财务报表 `dispose`,加上两个渲染器接合. `noteContentShrink(scrollTopBefore)` 说去掉一行 而不是手势 导致卷轴事件即将到来. `reconcileScrollRange()` 当容器本身没有调节收缩时,将偏移回容器的范围之内。 第二个接合之所以存在,是因为将内容从下方缩小到下方,是一种执行行为,而不是特定的行为。 Blink和Gecko在布局时进行调和,而WebKit则没有. 没有校正,一个应用程序清除屏幕后,一个iOS的取景口会被停在内容的末尾. 然后用上面的内容显示背景,直到读者滚动.
-   `createModeState(initial?)` 返回 a `ModeState`,以及 `POWER_ON_MODES` 是其被冻结的默认值。 模式状态是DEC私有模式状态,每个终端一个. 服务器的屏幕拥有模式 。 连接从服务器上写入 `ModesMessage`,以及 `isKnown()` 只有在当前套接字的服务器宣布它们时才属实 : 在每次连接、 关闭和被撕毁时, 连接标记它们未知 `POWER_ON_MODES` 打开 `setSession`。在未知时编码的输入可能使用程序没有的模式,因此依赖消费手模式的输入作为连接方式 `DeferredInput`,它编码过一次 `isKnown()` (鼠标控制器同时不发送报告). 在此之前,读者在切换后回答Power-on值,之前的套接字模式则通过重联. `POWER_ON_MODES` 等于服务器屏幕宣布的内容。 其成员是: `isBracketedPaste`, (中文(简体) ), `isApplicationCursor`, (中文(简体) ), `getMouseMode`, (中文(简体) ), `isMouseSGR`, (中文(简体) ), `isMousePixels`, (中文(简体) ), `isFocusReporting`, (中文(简体) ), `isApplicationKeypad`, (中文(简体) ), `isReverseVideo`, (中文(简体) ), `getKeyboardFlags`, (中文(简体) ), `applySnapshot`, (中文(简体) ), `snapshot`, (中文(简体) ), `isKnown`, (中文(简体) ), `setKnown`, (中文(简体) ), `subscribe(cb)` (称为: `applySnapshot` 和每一变化 `isKnown()`,返回未订阅者)和 `dispose`。 。 。 . `POWER_ON_MODES` 被冻结,所以在更改字段前复制,例如 `{ ...POWER_ON_MODES, reverseVideo: true }`。 。 。 .
-   `decodeWireBinary(buf)` 是二进制 WebSocket 帧的顶级解码器。 它返回一个 `ServerMessage` 或者说 `null` 用于无效或截断的框架.
-   电线相容常数 `WIRE_PROTOCOL_VERSION`, (中文(简体) ), `MIN_SUPPORTED_SERVER_WIRE_VERSION`, (中文(简体) ), `WIRE_INCOMPATIBLE_CLOSE_CODE` 财务报告和审定财务报表 `WIRE_COMPATIBILITY` 发布本客户端发布方向合同. 对非TypeScript消费者来说,与语言中和的JSON文物相同的价值船. 见 [电线相容性表](https://npmcn.com/package/%40cplieger/web-terminal-engine/#readme-wire-compatibility-manifest)。 。 。 .
-   `createConnection(options)` 返回 a `Connection`,以及 `MAX_REPLAY_LINES` 财务报告和审定财务报表 `MAX_OUTBOX_BYTES` (飞行中输入数最多的字节)是它的输出边框. 它是客户端对服务器WebSocket的生命周期. 它拥有套接字,指数后起重接,以及恢复/输入Ack可靠性层,这是命令输入队列外加服务器-重启检测. 选择是 `renderer` 它提供食物 `modes` 它写着 `mouse` 它的再同步,你的 `callbacks`选择 `wsPath` (默认) `"/ws"`和 `sessionIdKey`它的方法是: `connect`, (中文(简体) ), `sendBinary(input)`, (中文(简体) ), `cancelPendingInput`, (中文(简体) ), `pendingInputBytes`, (中文(简体) ), `holdInput`, (中文(简体) ), `sendEphemeral(text)`, (中文(简体) ), `setClientFocus(focused)`, (中文(简体) ), `sendResize`, (中文(简体) ), `lastAnnouncedSize`, (中文(简体) ), `inputReady`, (中文(简体) ), `reconnectNow`, (中文(简体) ), `setSession`, (中文(简体) ), `forgetSession`, (中文(简体) ), `currentSessionId`, (中文(简体) ), `historyBudget`, (中文(简体) ), `requestHistory`, (中文(简体) ), `disconnect` 财务报告和审定财务报表 `dispose`。 。 。 . `sendBinary` 接受任何大小的输入并按顺序交付:最多 `MAX_OUTBOX_BYTES` 其余的等待,只有断绝的连接拒绝。 输入为字节,或为 `DeferredInput` 谁 `encode()` 一旦知道会话模式,连接就会呼叫,所以按键或括号内的粘贴会跟随服务器所宣布的模式。 每个输入都在其会话的队列中等待:它停留在一个会话开关上,一个服务器重启会将其放下,编码与否. 在该套接字宣布大小之前, 任何输入都不会在套接字上进行, 因此新鲜的 PTY 从未运行默认大小的命令 . `holdInput()` 将活动会话的队列从电线上关闭,直到它返回的释放运行为止。 已标记的推迟输入 `paste` 可以通过 `cancelPendingInput()`:在等待模式的同时,整体,否则其身体的未同意的休息,而其 `tail` (一个括号内的面条的收尾括号)仍然发送,并保留了所有其他条目. `sendResize` 宣布大小 `computeSize()` 答案,在同一套接字上跳过重复; `lastAnnouncedSize()` 是此连接在任意套接字上的最后大小( 打开的处理器) `initialSize` 包括),所以一个持回大小的消费者与服务器被告知的内容相比,而不是自己的副本. `inputReady()` 从套接字读取为真 `onInputReady` 在套接字关闭、被替换、结束或被撕下之前 `forgetSession` 在役会议或 `disconnect`所以消费者随时决定是现在发送还是排队 而不是追踪回调 在读出真实时发送的输入会立即熄灭,除非 `holdInput()` 拿着. `sendEphemeral` 包含鼠标报告等最佳输入。 它只当一个套接字正在直播并且已经宣布大小时才发出,返回它是否已经发出,并且从不触及输入队列或字节计数器. 因此,一篇报告被遗忘了,而不是在经过重新粉刷的屏幕上重新播放。 面对一个服务器,它宣布 既不具有能力,它又回到 `sendBinary`,因为一个未确认的控制被取消 后批,完全沉默 是一个更糟糕的失败 比 stale 报告。 在该路径上,报告会搭乘可靠的输入队列,并在重新连接后被重新传送. `setClientFocus` 报告终端部件的焦点。 它发送 `focus` 当服务器声明它得到DEC 1004的答案时控制,否则会放弃报告。 服务器从附件状态、每个附件客户端的报告 以及它自己的 `terminal.WithKeepUnfocused`。客户端在两种情况下均不写焦点字节。 一个客户端不能回答一个会话 可能附带两个设备,并且客户端写 `CSI I` 将超越它所看不到的无重点政策。 下一次回放Ack在能力已知后重新刷回了当前值. `ConnectionCallbacks` 曝光情况 `onMessage(ServerMessage)`, (中文(简体) ), `onOpen`编号 :`onClose`编号 :`onConnecting`编号 :`onPendingInput`编号 :`onServerRestart`编号 :`onProcessExit`, (中文(简体) ), `onWireVersionMismatch`,最终 `onWireIncompatible`, (中文(简体) ), `onResumeBounds`页:1 `computeSize()` 提供者, 可选 `getReplayMax` 财务报告和审定财务报表 `initialSize` 供应商,以及 `onInputReady(size)`,每个套接字开始输入时发射一次,并按其宣布的大小发射一次。 这是在套接字的第一个大小及其恢复之后,所以服务器重启已经放弃了旧进程的输入并开火了 `onServerRestart`输入 `onInputReady` 进入新进程。 无 `initialSize`,每个套接字宣布 `computeSize()` (a) 恢复前 `initialSize` 解答无效宣布无, 而套接字的输入等待第一个 `sendResize`成员可致电 `dispose()` {\\fn黑体\\fs22\\bord1\\shad0\\3aHBE\\4aH00\\fscx67\\fscy66\\2cHFFFFFF\\3cH808080}关于引用它的联系 与 `onProcessExit` 有线, 最终关闭( 4001 进程已退出, 4004 未知会话) 结束会话: 没有备份重接,以及 `reconnectNow()` 将被拒绝,直到 `connect()`, (中文(简体) ), `setSession()` 或者说 `forgetSession()`。明确的底层服务器修订或关闭代码 4002将停止自动重接,直到 `disconnect()` 或重新装入页面。 版本的沉默和未来的修改服务器仍然被容忍. 连接在内部解码框架, 将模式框架应用到其 `modes`,并回答其简历和历史问题 `renderer`。消费者只需要向渲染器发送屏幕和滚动框 `createTerminalEngine` 为你做。 更喜欢用电线 `WebSocket` 加号 `decodeWireBinary` 除非你需要全权控制.
-   `controlFrame(msg)` 财务报告和审定财务报表 `wsURL(proto, host, path?)` 是客户端对服务器协议的低级助手. `controlFrame` 构建 0x00 前置的 JSON 控制框和 `wsURL` 构建 WebSocket URL 。 这个 `connection` 在内部使用,然后出口给先进的消费者.

电线类型( R)`WireRun`, (中文(简体) ), `ScreenMessage`, (中文(简体) ), `ScrollMessage`, (中文(简体) ), `ModesMessage`, (中文(简体) ), `TitleMessage`, (中文(简体) ), `ResumeAckMessage`, (中文(简体) ), `ServerMessage`, (中文(简体) ), `ControlMessage`)从包根再导出并匹配Go服务器的线条格式字节为字节.

## 电线相容性表

同样的相容性编号也作为语言中立的JSON文物被公布,因此不能导入TypeScript的消费者——多克文件,外壳放出门,任何语言的CI脚本——可以在不刮取来源的情况下读取:

```sh
# npm consumer
jq -r .wireCompatibility.protocolVersion \
  node_modules/@cplieger/web-terminal-engine/wire-compatibility.json

# JSR consumer (published as an included file; JSR exports are modules only)
curl -fsSL https://jsr.io/@cplieger/web-terminal-engine/<version>/wire-compatibility.json
```

```json
{
  "schemaVersion": 1,
  "generatedBy": "web/src/test-helpers/wire-manifest.ts",
  "wireCompatibility": {
    "protocolVersion": 4,
    "minimumServerProtocolVersion": 3,
    "incompatibleCloseCode": 4002
  }
}
```

它产生于 `WIRE_COMPATIBILITY` (没有第二批数字),检查到回波,因为公布的管道没有建设步骤,并用再生和活化测试来守卫,这样它就无法停止。 它的值被固定在 TypeScript 常数和 GO 上 `terminal` 常数,所以三个表面不能分.

**你所依赖的** (主治公共文物,由套件版改版:

-   文件存在于每个 npm tarball 和 JSR 的软件包根上, 在路径上 `wire-compatibility.json`,并且可以从 npm 导入为 `@cplieger/web-terminal-engine/wire-compatibility.json`。 。 。 .
-   `schemaVersion` 是描述此文件的LAYOUT的整数,而不是线条协议. 首先读一下,拒绝你不明白的价值观.
-   内a `schemaVersion`时 `wireCompatibility` 保持其名称、类型和意义,并等同于相应的名称 `WIRE_COMPATIBILITY` 同一释放的成员.
-   可以在 MINOR 释放中在任意一个对象下添加新字段,因此可以任意分析(未知密钥).

**合同以外的内容**数字 : `generatedBy` 是一个信息来源说明。 清单中故意没有软件包版本——出版步骤注入到 `package.json` 编号 : `jsr.json` 在退票后, 这里有一个版本会将冷冻在 repo 占位符上; 从这些文件读取软件包版本 .

**更改清单** (MAJOR,并且总是发布注释):去掉或更名一个字段,改变字段的类型或含义,移动文件,或起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起 `schemaVersion`· 更改电线修订版NUMBERS并不是一个明显的中断——报告它们就是文件的目的.

## 布局形状表格

`layout-shapes.json`,在表单和可导入的npm旁的软件包根 `@cplieger/web-terminal-engine/layout-shapes.json`,列出分割视图布局记录,并列出Go服务器是否接受其形状。 每个条目是 `{ name, layout, valid }`时 `layout` 拥有服务器的 `PaneLayout` 字段( r)`left`, (中文(简体) ), `right`, (中文(简体) ), `selected`, (中文(简体) ), `handle`, (中文(简体) ), `open`) (中文(简体) ). Go测试将服务器的规则定在它身上,因此一个在发送前检查出一个布局的客户端可以对照相同的条目测试它的检查. 它只覆盖形状:服务器也拒绝一个命名一个不活的会话的布局.

## 仅浏览器

此软件包取决于 `document`, (中文(简体) ), `HTMLElement`, (中文(简体) ), `MessageChannel`,以及其他DOM API,因此它只在类似浏览器的环境中运行. 同伴Go服务器在Go的任何地方运行.

## 许可证

MPL-2.0. 见 [自由主义](https://npmcn.com/package/%40cplieger/web-terminal-engine/LICENSE)。 。 。 .

第三方的归属 [第三次议会 NoTICES.md](https://npmcn.com/package/%40cplieger/web-terminal-engine/THIRD_PARTY_NOTICES.md)。 。 。 .

## 常见问题

### @cplieger/web-terminal-engine 是什么？

浏览器终端引擎: VT500 屏幕缓冲器, DOM 渲染器, 二进制线协议

### 如何安装 @cplieger/web-terminal-engine？

在项目目录运行 npm install @cplieger/web-terminal-engine，或使用 pnpm add / yarn add / bun add。

### @cplieger/web-terminal-engine 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @cplieger/web-terminal-engine 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @cplieger/web-terminal-engine 可以商用吗？

@cplieger/web-terminal-engine 使用 MPL-2\.0 许可。弱 Copyleft：可以商用，也可以和闭源代码一起使用；但如果分发时修改了 MPL 授权的文件，这些文件的修改需以 MPL 开源。以上为概括，不构成法律意见，请以许可原文为准。

### @cplieger/web-terminal-engine 的下载量有多少？

根据 npm 官方下载统计，@cplieger/web-terminal-engine 在 2026/10/11 – 2026/11/09 这 30 天内被下载 9,393 次，日均约 313 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @cplieger/web-terminal-engine 还在维护吗？

最新版本 6\.5\.0 发布于 2026/11/13（今天）；至今共发布 70 个版本，其中过去 12 个月发布了 70 个；首次发布于 2026/06/29。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

