@cplieger/网络终端引擎
浏览器虚拟终端渲染器
cplieger/web-terminal-engineGo模块:基于DOM的VT500屏幕,并有OSC 8超链接支持,回滚,键盘映射器,鼠标编码器和二进制线解码器. 零运行时间依赖关系.
网络地铁-引擎跨语言终端库的浏览器半个. 与 Go 服务器侧套件对等(vt, (中文(简体) ), terminal) 在一个版本的 WebSocket 协议上;参见 项目README 整个故事.
安装
npx jsr add @cplieger/web-terminal-engine # JSR (preferred)
npm i @cplieger/web-terminal-engine # NPM
使用量
每个终端一台发动机. createTerminalEngine 构建一个渲染器、一个卷轴控制器、一个鼠标控制器、一个模式状态,以及您两个元素、路线上的连接 screen, (中文(简体) ), scroll 财务报告和审定财务报表 modes 框到渲染器,然后返回五部分加一 dispose()数字 :
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();
}
这五所工厂也自行提供. 用手将它们组合起来,可以明确传递每个状态的依赖(没有默认实例可追溯到),并给渲染器自己设定路由框架:
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)返回 aRenderer,驱动 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()还计算了服务器未承诺的应用程序所绘制的行数,并发送它留下了回滚的最后一个屏幕的已过时副本(恢复控制) (中文(简体) ).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)返回 aMouseController,以及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时onOpenephemeral channel尚不为人所知,所以发布会采取可靠的倒置. 运动是结合到每个动画框一个报告,最新获胜,而新闻,发布和轮子则永远无法结合,因为一个轮子报告是相对于屏幕的notch计数,而结合失去滚动距离.resyncGesture与处理者共用一扇门,每人执行. 由于应用程序停止跟踪而搁浅的姿态是FORGOTTEN, 由下一个观察到的跟踪者决定, 因为发布时没有任何要求作为按键键到达 PTY 。 tmux 在附加、 清除时以同样的方式回答mouse_drag_flag因为它会禁用模式.disarmGesture()忘记了没有报告的任何姿态 在另一个会话中指向一个终端的套接字时称它,因为记录属于看到媒体的会话. 重点报告不在这里。 这是运输状态,它生活在connection.setClientFocus。 。 。 .createScrollController(options)返回 aScrollController,滚动容器的自动跟踪器scrollEl,可选onUserScrollChange财务报告和审定财务报表onScrollPosition召回电话。 它的方法是stickToBottom, (中文(简体) ),scrollToBottom, (中文(简体) ),scrollByPage, (中文(简体) ),isUserScrolledUp, (中文(简体) ),currentScrollTop, (中文(简体) ),restoreView, (中文(简体) ),adjustForContentShift财务报告和审定财务报表dispose,加上两个渲染器接合.noteContentShrink(scrollTopBefore)说去掉一行 而不是手势 导致卷轴事件即将到来.reconcileScrollRange()当容器本身没有调节收缩时,将偏移回容器的范围之内。 第二个接合之所以存在,是因为将内容从下方缩小到下方,是一种执行行为,而不是特定的行为。 Blink和Gecko在布局时进行调和,而WebKit则没有. 没有校正,一个应用程序清除屏幕后,一个iOS的取景口会被停在内容的末尾. 然后用上面的内容显示背景,直到读者滚动.createModeState(initial?)返回 aModeState,以及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文物相同的价值船. 见 电线相容性表。 。 。 . createConnection(options)返回 aConnection,以及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页:1computeSize()提供者, 可选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脚本——可以在不刮取来源的情况下读取:
# 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
{
"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. 见 自由主义。 。 。 .
第三方的归属 第三次议会 NoTICES.md。 。 。 .