29 KiB
29 KiB
xterm 移植独立实验项目实施方案(2026-03-01)
1. 目标与范围
本方案用于落地一个独立实验项目,验证“xterm.js 基线能力”与“原生 textarea 方案”的迁移可行性。
该项目仅用于测试,不替换现有 Web/小程序主链路。
规范基线:
- 渲染与输入语义以
xterm.js@5.3.0源码为准。 - 文档中的换行、回显、光标、粘贴规则均以该版本源码行为校正。
- 若未来升级 xterm 版本,需先做“规范差异审计”再修改本方案。
目标:
- 目录独立:实验代码只在
labs/xterm-standalone/。 - 依赖可复用:允许复用现有依赖版本策略和工具链版本。
- 构建/运行可复用:命令约定复用
dev/build/test/lint/typecheck。 - 业务零耦合:不复用现有业务模块逻辑,不修改现有状态机与会话实现。
- 可删除无副作用:删除实验目录和小程序入口后,主项目行为不变。
2. 非目标
- 不替换
apps/web现有终端实现。 - 不修改
apps/gateway的业务协议和转发逻辑。 - 不改
packages/shared的现有模型定义和状态机实现。 - 不将实验代码接入生产发布链路。
3. 独立性与复用规则
3.1 必须满足
- 实验代码不得
import现有业务模块(apps/*、packages/*的业务实现)。 - 小程序仅新增“入口级改动”(页面按钮 + web-view 跳转)。
- 实验项目失败、下线、删除,不影响主流程连接与发布。
- 禁止在实验项目中运行时依赖
@xterm/*包;xterm 仅作为源码语义参考,不作为可执行依赖。
3.2 允许复用
- 依赖版本策略可复用(例如与根工程保持同主版本)。
- 工程工具可复用(TypeScript、Vite、ESLint、Vitest 配置风格)。
- 运行方式可复用(统一命令命名,不要求命令实现完全一致)。
- 主题 token 可复用现有 Web/小程序的成熟定义(颜色变量、语义色分层、主题切换策略),但实现代码必须保留在实验目录内。
4. 目录规划(实施级)
labs/xterm-standalone/
README.md
package.json
src/
main.ts
app/App.vue
pages/TerminalLabPage.vue
components/
TerminalToolbar.vue
TerminalViewport.vue
TerminalInputBar.vue
TerminalTouchTools.vue
core/
session/sessionMachine.ts
session/sessionTypes.ts
transport/terminalTransport.ts
transport/gatewayTransport.ts
renderer/rendererAdapter.ts
renderer/compatRenderer.ts
renderer/textareaRenderer.ts
input/inputBridge.ts
input/imeController.ts
layout/sizeCalculator.ts
layout/cursorMath.ts
buffer/outputBuffer.ts
sanitize/terminalSanitizer.ts
stores/useTerminalLabStore.ts
styles/terminal-lab.css
5. 总体架构与数据流
Transport(stdout/stderr) -> Sanitizer -> OutputBuffer -> RendererAdapter(compat|textarea) -> View
UserInput(key/ime/paste/touch) -> InputBridge -> xterm-compatible input mapping -> Transport(stdin)
LayoutResize -> sizeCalculator(cols/rows) -> Transport(resize) + Renderer.resize
关键原则:
- 传输层不感知渲染器类型。
- 输入处理统一走
InputBridge,避免各组件各自发送 stdin。 - 渲染器通过统一接口实现,便于模式切换和 A/B 对比。
- 输入映射遵循 xterm 规则:
Enter -> CR,粘贴文本行结束归一到CR。 - 输出渲染遵循 xterm 控制字符纪律:
CR与LF分离语义,convertEol默认false。 - 原生响应优先:软键盘、选区、滚动由原生
textarea通道主导,xterm 仅负责渲染与终端语义,不得接管系统输入响应。 - 事件主权清晰:键盘/鼠标事件先由应用层做区域过滤与路由,再决定是否交给 xterm。
6. 状态机规划
6.1 会话状态机(连接生命周期)
状态集合(与现有模型对齐):
idleconnectingauth_pendingconnectedreconnectingdisconnectederror
合法迁移(必须在 sessionMachine.ts 中强校验):
idle -> connecting | disconnectedconnecting -> auth_pending | error | disconnectedauth_pending -> connected | error | disconnectedconnected -> reconnecting | disconnected | errorreconnecting -> connected | error | disconnecteddisconnected -> connecting | idleerror -> connecting | disconnected
触发事件:
connect_requestsocket_openconnected_framestdout_first_packetdisconnect_frame/ws_closeerror_frame/ws_errormanual_disconnectauto_reconnect_timeout
6.2 输入状态机(IME/普通输入)
状态:
input_idleinput_composinginput_commit_pending
规则:
compositionstart->input_composingcompositionend-> 进入input_commit_pending,提交非 ASCII 候选beforeinput/input非组合态可触发 ASCII 兜底发送- 超时守卫(如 1.8s)未收到
compositionend时自动恢复到input_idle
6.3 触摸焦点状态机(移动端)
动作集合:
BLUR_ONLYFOCUS_KEYBOARDPASS_NATIVEPASS_SCROLL
决策优先级:
hasSelectionStart || hasSelectionEnd->PASS_NATIVEscrollLike->PASS_SCROLLmoved->PASS_NATIVE!inBand->BLUR_ONLYinBand->FOCUS_KEYBOARD
硬约束(新增):
- 只有
FOCUS_KEYBOARD动作允许触发原生键盘弹出。 FOCUS_KEYBOARD必须满足“光标附近行”条件(inBand=true),禁止在非光标邻近区域弹键盘。PASS_NATIVE/PASS_SCROLL禁止preventDefault,确保原生选区和滚动不被破坏。- 禁止在终端根容器上全局拦截
touchstart/touchmove/click;仅允许在明确手势分支下做最小拦截。 - 移动端
compat仅承担回显渲染,输入焦点必须由原生textarea锚点持有。 - 禁止在非用户手势路径下调用
terminal.focus()触发软键盘。
6.4 渲染模式状态机
状态:
renderer_compatrenderer_textarea
切换事件:
switch_to_compatswitch_to_textarea
切换过程必须原子化:
oldRenderer.dispose()newRenderer.mount(container)replay bufferapply size/themefocus restore(仅桌面自动聚焦)
6.5 换行模式状态(xterm 对齐)
状态:
convertEol=false(默认)convertEol=true(由CSI 20 h打开)
规则:
LF/VT/FF进入lineFeed():始终y+1,并在触底时滚屏。convertEol=true时,lineFeed()额外执行x=0;false时保留列位置。CR仅执行x=0,不改变y。NEL(ESC E/C1.NEL)等价x=0 + index()(下移一行并在必要时滚屏)。- 右边界写入是否自动折行由
DECAWM(wraparound)控制。 - 自动折行触发条件按 xterm 口径:字符写入将越过末列时触发换行;换行行需标记
isWrapped=true。 - 显式
LF后目标行应清除isWrapped,避免把硬换行误判为软换行。
6.6 屏幕缓冲状态(主屏/备用屏,强制)
状态:
buffer_normal(主屏)buffer_alternate(备用屏)
切换规则(xterm 对齐):
CSI ? 47 h/CSI ? 1047 h:切换到备用屏。CSI ? 47 l/CSI ? 1047 l:返回主屏。CSI ? 1049 h:保存光标状态并切换到备用屏。CSI ? 1049 l:返回主屏并恢复保存光标状态。- 对齐
xterm.js@5.3.0现实行为:1049不强制清空备用屏历史(按源码注释口径处理)。
实现约束:
TerminalCore必须同时维护normalBuffer与alternateBuffer,仅一个为activeBuffer。- 两个缓冲区的
cursor、scroll region、isWrapped状态独立维护,禁止互相污染。 - 切屏仅切换活动缓冲区引用,不得重建对象导致历史丢失。
1049路径必须包含saveCursor/restoreCursor,并保证退出全屏后提示符位置正确。- 该能力只影响输出缓冲与光标状态,不改变“原生输入主通道”与事件归属策略。
7. 组件职责与接口
7.1 TerminalLabPage.vue
职责:
- 页面编排、模式切换、连接/断开、状态展示。
- 组装
store + renderer + inputBridge。
7.2 TerminalViewport.vue
职责:
- 只负责承载渲染容器(不直连 transport)。
- 提供挂载点给
RendererAdapter。
7.3 TerminalInputBar.vue
职责:
- 文本输入框、发送按钮、粘贴、快捷键入口。
- 通过
InputBridge.send()发送,不直接访问 transport。
7.4 TerminalToolbar.vue
职责:
- 展示连接状态、延迟、渲染模式。
- 触发
connect/disconnect/clear/switchRenderer。
7.5 TerminalTouchTools.vue
职责:
- 方向键、Enter、Ctrl+C、Tab、Paste。
- 输出标准控制序列(如
ESC[A)。
8. 传输与协议实现要点
8.1 TerminalTransport 抽象(必须)
接口:
connect(params)send(data, meta?)resize(cols, rows)disconnect(reason?)on(listener)getState()
8.2 网关帧协议(实验项目内按同协议实现)
入站:
initstdin(含可选meta: { source, txnId })resizecontrol(ping/pong/disconnect)
出站:
stdoutstderrcontrol(connected/disconnect/pong)error
9. 渲染管线设计
9.1 输出处理链
- 收到
stdout/stderr。 - 执行
sanitizeTerminalOutput(过滤同步更新模式等控制序列噪音)。 - 追加到
OutputBuffer(条目上限 + 字节上限双阈值)。 outputRevision++。- 渲染器增量写入;必要时重放。
9.2 缓冲策略(必须)
maxEntries下限保护(建议 >= 200)。maxBytes下限保护(建议 >= 64KB)。- 先按条目裁剪,再按字节裁剪。
- 至少保留最新一条,避免全清空闪烁。
9.3 兼容渲染规则(自研)
- 使用
write()增量写入,不用writeln()改写换行语义。 convertEol默认值必须为false(与 xterm 默认一致)。- 显式
LF不等于CRLF:CR和LF分别处理,禁止业务层自行合并。 fit后同步resize(cols, rows)。
9.4 textarea 渲染规则
- 采用“只读显示区 + 输入区”或“单 textarea 双模式”方案(二选一,建议前者)。
- 输出区按文本流追加,必要时使用
requestAnimationFrame批量刷新。 - 默认自动滚到底;用户手动上滚时暂停自动跟随。
- 不允许使用“仅按字符串估算”的粗略光标算法作为最终实现。
- 必须模拟 xterm 的 C0/C1 换行语义:
CR、LF、VT、FF、NEL。 - 必须显式维护软换行标记(等价
isWrapped),区分“自动折行”与“显式换行”。
9.5 精准光标内核(强制)
为满足“回显后光标位置必须精准计算”,实验项目必须引入统一终端状态内核(TerminalCore),并将其作为两种渲染模式的唯一真相源。
实现要求:
stdout/stderr必须先写入TerminalCore,由内核解析控制序列并更新光标、屏幕缓冲、滚动区域状态。compat模式与textarea模式都必须消费同一份TerminalCore状态快照。- 禁止直接通过
selectionStart + 文本长度推断“终端光标”,该方法仅可用于“输入框本地插入点”,不能代表远端终端光标。 TerminalCore至少要正确处理本项目目标程序涉及的序列族:CR/LF/VT/FF/NEL/BS/TAB、CSI A/B/C/D/H/f/J/K/m、SM/RM(含 Ps=20)、DECSET/DECRST(含 DECAWM)。TerminalCore必须维护以下状态:cursorX/cursorY、baseY、viewportY、scrollTop/scrollBottom、convertEol、wraparound、line.isWrapped。- 每次输出处理后,必须产出
cursor: { x, y, globalRow }与viewport快照,供触摸激活带与滚动定位复用。
9.6 颜色渲染规范(xterm 对齐,强制)
目标:
- 颜色与文本属性语义必须对齐
xterm.js@5.3.0的SGR(CSI ... m)行为。 compat模式与textarea模式必须共享同一套属性状态机,避免模式切换后颜色跳变。
实现边界:
- 必须支持基础 SGR 属性:
0/1/2/3/4/7/8/9/21/22/23/24/25/27/28/29/53/55。 - 必须支持前景/背景标准色:
30-37、40-47。 - 必须支持高亮色:
90-97、100-107。 - 必须支持扩展色:
- 前景
38 - 背景
48 - 下划线颜色
58
- 前景
- 扩展色子模式必须支持:
;5;INDEX(256 色索引);2;R;G;B(TrueColor)
- 必须支持默认色复位:
39复位前景49复位背景59复位下划线颜色
颜色状态模型(建议):
fg:{ mode: default | p16 | p256 | rgb, value }bg:{ mode: default | p16 | p256 | rgb, value }underlineColor:{ mode: default | p256 | rgb, value }flags:bold/dim/italic/underline/inverse/invisible/strikethrough/overlineunderlineStyle:none/single/double/curly/dotted/dashed
重置纪律:
SGR 0必须重置前景、背景、下划线样式与颜色到默认(等价 xterm_processSGR0)。22仅取消bold与dim,不影响颜色。24仅取消下划线与下划线样式,不清空其它属性。27仅取消inverse。28仅取消invisible。29仅取消删除线。
渲染映射规则:
inverse采用“渲染时交换 fg/bg”策略,不直接改写底层存储值。invisible保留背景渲染,前景以透明或背景同色处理(与终端“隐藏文字”语义一致)。bold只作为属性位,不应强行映射为高亮颜色(除非显式启用“粗体映射亮色”策略)。blink在 xterm 5.3 源码中为已记录属性但渲染支持有限,实验项目默认可不做动画实现,但要保留属性位兼容。
主题与调色板约束:
- 16 色与 256 色索引色应来自统一调色板定义,避免
xterm与textarea调色板不一致。 - TrueColor (
rgb) 必须按原值渲染,不经过主题二次量化。 - 主题切换只影响“默认色与索引色映射”,不应篡改已存在的 TrueColor 单元格。
9.7 主题复用与轻量化策略(强制)
目标:
- 主题实现参考并复用现有 Web/小程序成熟方案,保持视觉一致与维护成本可控。
- xterm 移植以“核心稳定 + 轻量高效”为优先级,禁止默认堆叠非必要能力。
主题复用规则:
- 统一采用语义 token(如
terminalFg/terminalBg/cursor/selection/ansi16),禁止在组件内硬编码颜色。 - 默认前景/背景与
ansi16从现有主题配置映射生成,避免与现有产品主题出现明显偏差。 - 256 色表采用固定映射(标准 xterm256 表),不随业务主题动态重算。
- 实验项目只维护“主题映射层”,不复制现有 Web/小程序整套业务主题逻辑。
xterm 轻量化规则:
- 不引入
@xterm/*运行时依赖;实现以TerminalCore + 自研渲染器为主。 - 兼容能力按需实现并按模块拆分,首屏仅加载连接、输入、回显、resize 必需逻辑。
- 默认使用原生 DOM/Canvas 路径,禁止引入重型图形加速依赖作为前置条件。
- 非核心能力(复杂链接检测、额外动画)默认关闭,确保输入回显路径最短。
性能预算(建议):
- 实验页 JS 增量目标:gzip <= 150KB(超限需在评审记录中说明来源与必要性)。
- 终端首屏可输入目标:页面进入后 1 秒内可完成输入与回显(普通开发机基线)。
- 持续输出下避免长卡顿:连续主线程阻塞 >100ms 视为性能问题并需定位。
降级开关(必须):
compatLiteMode:关闭可选增强,仅保留连接、输入、回显、resize。rendererFallback=textarea:低端设备或异常场景可一键降级。- 主题映射失败时自动回退默认主题,禁止阻塞会话连接与输入流程。
9.8 主屏/备用屏落地实现(强制)
最小实现步骤:
- 在
TerminalCore新增bufferSet = { normal, alternate, active }与savedCursor。 - 解析
DECSET/DECRST时接入47/1047/1049分支,不允许只做日志忽略。 switchToAlternate():切换active=alternate,同步viewport/baseY/cursor。switchToNormal():切换active=normal,恢复主屏可视与滚动位置。1049h先saveCursor()再切换;1049l先切回主屏再restoreCursor()。- 渲染器读取统一
activeBuffer快照,禁止自行缓存“上一屏”造成穿透渲染。
兼容边界:
- 若目标程序未使用备用屏序列,行为与当前方案完全一致。
- 若收到未知私有模式,记录日志并安全忽略,不得破坏当前缓冲状态。
- 对于不在首期范围的高级 TUI 能力,可延后,但
47/1047/1049不可缺失。
10. 页面渲染与布局规则
10.1 基础布局
- 顶部:状态栏(state、latency、mode)。
- 中部:终端视口(xterm 或 textarea 输出)。
- 底部:输入栏与触摸工具。
10.2 尺寸与 PTY 计算规则(必须统一)
变量:
containerInnerWidthPxcontainerInnerHeightPxcharWidthPxlineHeightPx
公式:
cols = max(20, floor(containerInnerWidthPx / charWidthPx))rows = max(8, floor(containerInnerHeightPx / lineHeightPx))
约束:
- 变化阈值去抖:
|cols-lastCols| + |rows-lastRows| >= 1才发resize。 - 初次挂载后至少重试 fit/measure 3~10 次(应对路由切换延迟布局)。
11. 光标与坐标计算规则(重点)
说明:本节区分两类光标,避免语义混淆。
- 终端光标:远端 PTY 回显语义对应的光标(用于激活带判定)。
- 输入光标:本地输入框
selectionStart/selectionEnd(用于编辑行为)。
11.1 行高计算
优先级:
- 优先取真实渲染行高(DOM 测量)。
- 无法测量时回退:
lineHeightPx = fontSize * lineHeight。
11.2 触点行号计算
localRow = floor((clientY - viewportTop) / rowHeightPx)- compat 模式:
touchRow = viewportY + localRow - textarea 模式:
touchRow = viewportTopRow + localRow - 若
clientY不在视口边界内,判定为无效触点。
11.3 compat 光标行号
cursorLocalRow = core.cursorYcursorBaseRow = core.baseYcursorRow = cursorBaseRow + cursorLocalRow(全局行号)- 激活带判断:
abs(touchRow - cursorRow) <= activationRadius(建议 2 行)。
11.4 textarea 光标位置计算
输入(终端光标,来自 TerminalCore):
coreCursorRowcoreCursorColviewportTopRowrows
规则:
cursorRow = clamp(coreCursorRow, 0, +inf)。cursorVisualRow = cursorRow - viewportTopRow。- 仅当
0 <= cursorVisualRow < rows时认为光标在当前可视窗口内。 - 激活带判断统一使用
cursorRow(全局行号),禁止改用输入框selectionStart。 coreCursorRow语义需与 xterm 对齐:globalRow = baseY + cursorY。
输入(本地输入光标,仅编辑用途):
selectionStart(光标字符索引)text(当前显示文本)cols(当前列数)
算法(必须实现成纯函数):
- 从
text[0..selectionStart)顺序扫描。 - 碰到
\n:row += 1; col = 0。 - 普通字符:
col += charDisplayWidth(ch)。 - 若
col >= cols:按自动换行规则折行:row += floor(col / cols); col = col % cols。
charDisplayWidth(ch) 规则:
- ASCII:宽度 1。
- CJK/全角:宽度 2(可用简化 east-asian-width 判定)。
- 控制字符:宽度 0。
11.5 坐标到光标索引(可选增强,仅输入框)
若需要“点按定位光标”:
- 先由
(x,y)推导目标(row,col)。 - 再线性扫描文本映射回最近字符索引。
- 时间复杂度 O(n),长文本需缓存行起始索引优化。
11.6 原生键盘弹出门控(强制)
必须按以下判定顺序执行:
sessionState === connected。- 当前手势动作被状态机判定为
FOCUS_KEYBOARD。 inBand === true(即abs(touchRow - cursorRow) <= activationRadius)。- 不存在活动原生选区(否则走
PASS_NATIVE)。
执行动作:
readOnly=false。blur -> focus顺序触发输入锚点激活。- 进入短保护窗口,防止 iOS 瞬时 blur 立刻收回键盘。
禁止动作:
- 非
inBand区域触摸时强制focus()。 PASS_NATIVE/PASS_SCROLL分支触发键盘弹出。
12. 输入处理规则(必须)
12.1 统一发送入口
- 所有输入只走
InputBridge.sendRaw()。 - 键盘
Enter必须映射为CR (\r)(与 xtermKeyboard.ts一致)。 - 普通文本输入(keypress/input)按字符原样发送,不做全局换行重写。
12.2 meta 标记
- 常规按键:
meta.source = "keyboard"。 - 语音/候选提交:
meta.source = "assist",可携带txnId去重。
12.3 粘贴规则
- 粘贴前执行 xterm 对齐的行结束归一:
/\r?\n/g -> '\r'。 - 若开启 bracketed paste,按
ESC[200~ + text + ESC[201~包裹发送。 - 粘贴文本直接写入 stdin,不走逐键模拟。
12.4 快捷键规则
Ctrl/Cmd + C:有选中则复制;无选中则发\u0003。Ctrl/Cmd + V:读剪贴板后发送。- 方向键默认发送:
ESC[A/ESC[B/ESC[C/ESC[D;应用光标模式下发送:ESCOA/ESCOB/ESCOC/ESCOD。
12.5 反劫持策略(强制)
- 输入主通道固定为原生
textarea,禁止把系统软键盘直接绑定到 xterm 隐藏输入节点。 - 兼容层键盘监听仅用于桌面对照模式;移动端默认禁用兼容层直连 stdin 的按键采集。
- 触摸事件默认透传,只有
FOCUS_KEYBOARD分支可执行最小化preventDefault。 - 原生输入法事件(
compositionstart/update/end、beforeinput)必须完整透传到输入状态机,不得被 xterm 监听提前吞掉。 - 发生冲突时遵循优先级:系统行为 > 原生输入通道 > xterm 视图行为。
12.6 区域过滤与事件归属(强制)
区域定义(建议使用 data-zone 标记):
terminal-output-zone:终端可视回显区(仅显示与文本选区)。native-input-zone:原生输入锚点区(软键盘与 IME 主入口)。app-control-zone:应用工具栏、按钮、菜单区。app-overlay-zone:应用弹层、右键菜单、浮层工具区。
归属规则:
- 键盘事件默认归
native-input-zone与应用层快捷键系统;xterm 仅消费“已路由到终端”的标准控制输入。 - 鼠标点击在
app-control-zone/app-overlay-zone必须 100% 透传应用层,禁止被 xterm 截获。 wheel/touchmove默认交给最近可滚动容器;仅终端主视口滚动时才进入终端滚动链路。- 文本选择相关事件优先给系统原生选区,xterm 不得覆盖浏览器/系统的选区手柄行为。
实现约束:
- 在捕获阶段先执行
eventOwnershipRouter,返回APP | NATIVE | XTERM,再分发事件。 - 未命中白名单事件时默认
APP,禁止“默认给 xterm”。 - 仅以下事件允许进入 xterm:
stdin字符输入、终端方向键控制序列、明确授权的复制粘贴桥接。 - 右键菜单、双击词选、三击行选等交互由应用策略统一控制,不由 xterm 内建策略隐式决定。
13. 关键风险与强约束
- 风险:textarea 模式无法完整支持 TUI 全屏程序。
约束:文档中明确为已知限制,不作为首期阻塞项。 - 风险:IME 双发或丢字。
约束:保留composition + beforeinput/input + fallback三层机制和去重窗口。 - 风险:移动端误弹键盘。
约束:触摸状态机按优先级表执行,禁止随意新增preventDefault分支。 - 风险:模式切换导致历史丢失。
约束:统一OutputBuffer,切换只换渲染器,不换数据源。 - 风险:xterm 事件劫持导致软键盘、选区、滚动异常。
约束:移动端输入以原生textarea为唯一焦点源,xterm 不持有输入焦点。
14. 分阶段实施计划(可直接执行)
阶段 A:骨架与接口
- 建立目录与模块骨架。
- 完成
sessionMachine + transport + rendererAdapter。 - 页面可连接、可收发、可显示状态。
阶段 B:xterm 基线能力
- 接入 xterm 渲染器。
- 跑通输入、输出、resize、快捷键、粘贴。
- 完成基线测试用例。
阶段 C:textarea 迁移能力
- 实现 textarea 渲染器。
- 接入光标/坐标计算与输入法链路。
- 完成 compat 内核 vs textarea 对比测试。
阶段 D:小程序入口接入
- 小程序新增入口按钮 + web-view 页面。
- 地址可配置,未配置时只提示不报错。
- 不改主流程逻辑。
阶段 E:删除演练
- 删除
labs/xterm-standalone/。 - 删除小程序入口页面与按钮。
- 验证主工程构建与现有测试正常。
15. 测试与验收清单
功能验收:
- 连接状态迁移严格符合状态机。
- stdout/stderr 能稳定显示,无额外换行污染。
- Enter 语义对齐 xterm:按键 Enter 发送
CR,不发送裸LF。 - 中文输入(含候选提交)无明显丢字/双发。
- 模式切换后输出可重放,连接不中断。
- 光标精准性:同一份输出流在
compat与textarea模式下,终端光标(row,col)一致。 - 光标附近弹键盘:仅在光标邻近行轻触触发原生键盘;非邻近行不弹键盘。
- 长按选区场景下,键盘不误弹,原生选区与手柄保持可用。
convertEol=false时,LF仅下移不回列 0;convertEol=true时LF同时回列 0。CR只回列 0;NEL执行“回列 0 + 下移一行”。- 粘贴文本行结束归一为
CR,并在 bracketed paste 模式下正确包裹。 - 自动折行与显式换行可区分:软换行行具备
isWrapped=true语义。 - 颜色回显一致性:同一输出流在
compat与textarea模式下,SGR属性(粗体/下划线/反显/隐藏/删除线)渲染结果一致。 - 颜色指令完整性:
30-37/40-47/90-97/100-107/38/48/58与39/49/59/0/22/24/27/28/29复位行为符合预期。 - 扩展色准确性:
38;5;INDEX、48;5;INDEX、38;2;R;G;B、48;2;R;G;B、58;2;R;G;B能精准渲染且模式切换后不漂移。 - 主题切换约束:切换主题后默认色与索引色生效,历史 TrueColor 单元格颜色值保持不变。
- 主题复用一致性:实验页主题映射与现有 Web/小程序基线一致,同主题下无明显色偏。
- 轻量化约束:运行时不依赖
@xterm/*,未启用增强能力不进入首屏加载链路。 - 降级策略有效:
compatLiteMode与rendererFallback=textarea能生效且核心会话稳定。 - 原生响应优先:软键盘弹出/收起、长按选区、滚动手势在移动端与普通
textarea行为一致,无明显被劫持现象。 - 焦点纪律正确:移动端会话中
textarea始终为输入焦点主体,xterm 不直接持有键盘焦点。 - 区域归属正确:
app-control-zone/app-overlay-zone的键鼠事件不被 xterm 捕获,应用可独立演进交互逻辑。 - 路由兜底正确:未声明事件默认归应用层处理,不出现“事件默认落到 xterm”导致的不可控行为。
- 备用屏切换正确:
?1049h进入全屏后不污染主屏历史,?1049l退出后恢复原提示符与光标位置。 47/1047兼容正确:切换主/备用屏时两屏内容与滚动状态互不污染。
性能验收:
- 高频输出不明显卡顿(基线:普通开发机可持续滚动)。
- 缓冲裁剪后不出现白屏或大面积闪烁。
回归验收:
- 仅新增小程序入口改动;主页面行为一致。
- 删除实验目录后,主项目
typecheck/lint/test/build通过。
16. xterm 源码对照
本方案关键规则对应 xterm.js@5.3.0 源码:
Enter -> CR:src/common/input/Keyboard.ts。- 粘贴换行归一(
/\r?\n/g -> \r):src/browser/Clipboard.ts。 LF/VT/FF -> lineFeed、CR -> carriageReturn、NEL -> nextLine:src/common/InputHandler.ts。convertEol默认false,且受CSI 20 h/l控制:src/common/services/OptionsService.ts+InputHandler.ts。- 自动折行(DECAWM)与
isWrapped:src/common/InputHandler.ts#print。 - 光标全局行号语义(
cursorY相对baseY):typings/xterm.d.ts中IBuffer定义。 SGR属性解析与颜色扩展(含38/48/58、39/49/59、0/22/24/27/28/29):src/common/InputHandler.ts#charAttributes。- 主屏/备用屏切换与
1049光标保存恢复:src/common/InputHandler.ts#setModePrivate+resetModePrivate。 - 缓冲区实现与切屏基础结构:
src/common/buffer/BufferSet.ts。
17. 完成定义(DoD)
- 本文档中的状态机、组件职责、算法规则均已对应到代码模块。
- 实验项目可独立开发与验证,且不污染主业务逻辑。
- 小程序只增加测试入口,无业务耦合。
- 已完成删除演练并记录结果。
- 换行与光标行为已与
xterm.js@5.3.0源码逐条对齐,并有对应验收用例。 - 开发者仅依赖本方案即可按阶段落地实现。
- 主屏/备用屏与
1049光标恢复能力已落地并通过验收用例。