bricks/aidocs/wterm.md
2025-10-05 06:39:58 +08:00

347 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `bricks.Wterm` 技术文档
> 基于 xterm.js 的终端组件,通过 WebSocket 与后端交互,支持自动连接、心跳保活、终端尺寸自适应等功能。
---
## 概述
`bricks.Wterm` 是一个基于 [xterm.js](https://xtermjs.org/) 的前端终端控件类,继承自 `bricks.JsWidget`。它用于在网页中嵌入一个可交互的终端界面,并通过 WebSocket 连接到后端服务进行数据通信。
该组件主要功能包括:
- 终端渲染(使用 xterm.js
- WebSocket 通信管理
- 自动发送终端尺寸
- 心跳机制保活连接
- 键盘输入转发
- DOM 生命周期绑定(如页面显示/隐藏时自动处理连接)
---
## 依赖
- **xterm.js**:核心终端库
- **FitAddon for xterm.js**:用于自适应容器大小
- `bricks.js` 框架运行环境
- 全局函数 `schedule_once(fn, delay)`:用于延迟执行任务(类似 `setTimeout` 封装)
---
## 类定义
```js
class bricks.Wterm extends bricks.JsWidget
```
---
## 构造函数
### `constructor(opts)`
初始化终端组件。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts.ws_url` | `String` | WebSocket 服务器地址(必需) |
| `opts.ping_timeout` | `Number` | 心跳间隔时间(秒),默认为 `19` 秒 |
#### 示例
```js
const term = new bricks.Wterm({
ws_url: 'ws://localhost:8080/ws',
ping_timeout: 20
});
```
#### 内部逻辑
1. 调用父类构造函数。
2. 初始化 `socket``null`
3. 设置心跳超时时间。
4. 使用 `schedule_once` 延迟调用 `open()` 方法(避免同步阻塞)。
5. 绑定事件:
- `'domon'` → 当元素可见时触发 `send_term_size`
- `'domoff'` → 当元素不可见时触发 `destroy`
---
## 核心方法
### `async open()`
建立终端和 WebSocket 连接。
#### 流程说明
1. 创建 `Terminal` 实例并应用配置(包括宽高百分比等)。
2. 将终端挂载到 `this.dom_element`(由父类提供)。
3. 创建 WebSocket 连接至 `opts.ws_url`
4. 加载 `FitAddon` 插件以实现自适应布局。
5. 调整字体大小(依赖全局 `bricks.app.charsize`)。
6. 设置 WebSocket 回调:
- `onmessage`:接收服务器数据并写入终端
- `onclose`:清理心跳任务
- `onopen`:连接成功后发送初始尺寸并启动心跳
7. 监听键盘输入,转发至服务端。
8. 监听终端尺寸变化,自动同步。
9. 主动发送一次终端尺寸并聚焦。
#### 接收消息类型(来自服务端)
| 类型 | 数据结构 | 动作 |
|------|----------|------|
| `"heartbeat"` | `{ data: { type: "heartbeat" } }` | 打印日志“connection alive” |
| `"data"` | `{ data: { type: "data", data: "..." } }` | 调用 `term.write(...)` 显示内容 |
| 其他 | 任意 | 打印原始消息内容 |
#### 发送消息类型(至服务端)
| 类型 | 数据格式 | 触发条件 |
|------|---------|----------|
| `"input"` | `{ type: "input", data: key }` | 用户按键输入 |
| `"resize"` | `{ type: "resize", width, height, rows, cols }` | 终端或窗口尺寸改变 |
| `"heartbeat"` | `{ type: "heartbeat" }` | 定期发送心跳 |
---
### `send_data(d)`
向服务器发送用户输入的数据。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `d` | `String` | 键盘输入字符 |
#### 示例
```js
this.send_data('hello');
```
#### 实现
```js
this.socket.send(JSON.stringify({ type: "input", data: d }));
```
---
### `send_term_size()`
向服务器发送当前终端的尺寸信息。
#### 发送内容
```json
{
"type": "resize",
"width": <容器宽度>,
"height": <容器高度>,
"rows": <行数>,
"cols": <列数>
}
```
#### 注意事项
- 若 WebSocket 尚未就绪(`readyState !== 1`),会捕获异常并打印 `'ws not ready'`
-`open()` 中被调用两次:首次连接 + 终端初始化后。
---
### `send_heartbeat()`
发送心跳包以维持连接活跃。
#### 发送内容
```json
{ "type": "heartbeat" }
```
---
### `heartbeat()`
周期性执行的心跳函数。
#### 行为逻辑
1. 检查 WebSocket 是否处于打开状态(`readyState === 1`)。
2. 如果是,则调用 `send_heartbeat()`
3. 使用 `schedule_once` 调度下一次心跳(间隔为 `this.ping_timeout` 秒)。
> ⚠️ 此方法采用递归调度方式实现定时循环。
---
### `charsize_sizing()`
根据全局字体设置调整终端字号。
#### 依赖
```js
var cs = bricks.app.charsize; // 假设为数字(例如 14
```
#### 实现
```js
this.term.setOption('fontSize', cs);
```
---
### `term_resize()`
响应外部容器尺寸变化的回调函数。
#### 功能
调用 `fitAddon.fit()` 让终端自适应新尺寸。
> 注释掉 `this.send_term_size()` 可能是为了防止重复发送(已在 xterm 的 `onResize` 中处理)。
---
## 资源释放与销毁
### `close_websocket()`
关闭并清理 WebSocket 连接。
#### 清理项
- 调用 `close(1000, 'close now')`
- 解除所有事件监听器
-`this.socket` 设为 `null`
#### 异常处理
捕获任何错误并重置 `socket` 引用,确保不会残留无效引用。
---
### `close_terminal()`
关闭并销毁 xterm 终端实例。
#### 清理项
- 调用 `fitAddon.dispose()`
- 调用 `term.dispose()`
-`this.term` 设为 `null`
---
### `destroy()`
完整销毁组件资源(响应 `'domoff'` 事件)。
#### 执行步骤
1. 打印调试日志
2. 取消心跳任务(如果存在)
3. 解绑 `element_resize` 事件
4. 关闭 WebSocket如有
5. 销毁终端实例(如有)
#### 日志输出示例
```text
------domoff event, destory this widget
---1--domoff event, destory this widget
---2--domoff event, destory this widget
---3--domoff event, destory this widget
```
---
## 事件绑定
| 事件名 | 触发时机 | 处理函数 |
|--------|---------|---------|
| `'domon'` | DOM 元素变为可见 | `send_term_size()` |
| `'domoff'` | DOM 元素隐藏或移除 | `destroy()` |
| `'element_resize'` | 容器尺寸变化 | `term_resize()` |
> 使用 `this.bind(event, handler)` 和 `this.unbind(...)` 管理事件。
---
## 静态注册
```js
bricks.Factory.register('Wterm', bricks.Wterm);
```
允许通过工厂模式创建实例:
```js
bricks.Factory.create('Wterm', options);
```
---
## 使用示例
```html
<div id="terminal-container" style="width:100%; height:500px;"></div>
<script>
// 假设已加载 xterm.js 和 FitAddon
const terminalWidget = new bricks.Wterm({
ws_url: 'ws://your-terminal-server/ws/session-id',
ping_timeout: 20
});
// 挂载到 DOM
terminalWidget.render('#terminal-container');
</script>
```
---
## 注意事项
1. **必须引入 xterm.js 和 FitAddon**
```html
<script src="https://unpkg.com/xterm@5.x.x/lib/xterm.js"></script>
<script src="https://unpkg.com/xterm-addon-fit@0.x.x/lib/FitAddon.js"></script>
```
2. `schedule_once(fn, delay)` 需要全局可用(通常由框架提供)。
3. 字体大小依赖 `bricks.app.charsize`,请确保其存在。
4. WebSocket 服务需支持以下协议字段:
- `type: "input"` / `"resize"` / `"heartbeat"`
- 并能返回 `type: "data"` 或 `"heartbeat"` 消息。
---
## 版本兼容性
| 库名 | 推荐版本 |
|------|----------|
| xterm.js | `>= 4.0.0 < 6.0.0` |
| xterm-addon-fit | 对应版本 |
---
## 总结
`bricks.Wterm` 是一个功能完整的 Web 终端组件适用于远程 shell在线 IDE调试工具等场景其设计模块化生命周期清晰结合 bricks 框架可轻松集成进复杂前端系统
---
**建议扩展方向**
- 支持主题切换
- 添加复制粘贴快捷键拦截
- 增加连接失败重试机制
- 支持二进制数据传输如图像