Keymap 宿主
OpenTUI Keymap 与宿主无关。宿主将运行时的焦点模型、层次结构、按键事件和生命周期适配为 KeymapHost<TTarget, TEvent>,以便共享引擎可以对其工作。
该包附带两个内置宿主:
@opentui/keymap/opentui用于基于CliRenderer和Renderable构建的终端应用@opentui/keymap/html用于以HTMLElement为根的浏览器 UI
如果您尚未阅读共享模型,请从 Keymap 概览开始。
宿主的作用
keymap 核心不知道 DOM 节点、终端可渲染对象或任何其他 UI 树。它只知道宿主目标和宿主事件。
- 本地层上的
target是一个宿主目标对象。 focus和focus-within激活来自宿主的焦点目标和父级遍历。- 按键按下和释放事件来自宿主事件流。
- 当宿主报告目标被销毁或移除时,本地层会被清理。
- 宿主元数据描述平台快捷键约定和修饰键能力。
runCommand()和dispatchCommand()需要宿主为编程执行创建合成事件。- 原始输入拦截仅在宿主实现可选的原始输入钩子时存在。
KeymapHost
| 成员 | 必需 | 描述 |
|---|---|---|
metadata | 是 | 平台、主快捷键修饰键和修饰键能力元数据 |
rootTarget | 是 | 宿主层次结构的根目标 |
isDestroyed | 是 | 宿主生命周期标志 |
getFocusedTarget() | 是 | 返回当前聚焦的目标或 null |
getParentTarget(target) | 是 | 用于 focus-within 匹配的父级遍历 |
isTargetDestroyed(target) | 是 | 目标存活检查 |
onKeyPress(listener) | 是 | 订阅按下事件 |
onKeyRelease(listener) | 是 | 订阅释放事件 |
onFocusChange(listener) | 是 | 订阅焦点变更 |
onTargetDestroy(target, listener) | 是 | 当本地层目标被销毁或移除时通知 |
createCommandEvent() | 是 | 创建 runCommand() 和 dispatchCommand() 使用的合成事件 |
onDestroy(listener) | 否 | 可选的宿主销毁通知 |
onRawInput(listener) | 否 | 可选的原始输入钩子,在按键解析之前使用。监听器在消费序列时返回 true |
如果您正在构建自己的运行时适配器,请实现此契约并将其传递给 new Keymap(host)。
宿主元数据可通过 keymap.getHostMetadata() 获取。primaryModifier 是插件语法(如 mod+s)应解析为的修饰键。能力值为 supported、unsupported 或 unknown;当事件类型可以表示修饰键但实际终端传递取决于协议支持时,终端宿主使用 unknown。
| 元数据字段 | 含义 |
|---|---|
platform | macos、windows、linux 或 unknown |
primaryModifier | macOS 上为 super,Windows/Linux 上为 ctrl,或 unknown |
modifiers | 宿主对 ctrl、shift、meta、super 和 hyper 的能力 |
宿主按键事件必须满足 KeymapEvent:
| 成员 | 描述 |
|---|---|
name | 规范化的按键名称 |
ctrl / shift / meta | 修饰键状态 |
super / hyper | 可选的额外修饰键状态 |
preventDefault() | 阻止匹配的事件到达宿主目标 |
stopPropagation() | 阻止后续监听器看到该事件并将 propagationStopped 设为 true |
propagationStopped | 调用 stopPropagation() 后为 true |
如果您的宿主实现了 onRawInput(),必须在按键解析之前调用每个监听器,并在监听器返回 true 时停止。
内置宿主辅助函数
两个内置宿主包都导出适配器特定的辅助函数。从 @opentui/keymap 导入共享的 Keymap、字符串化工具和核心类型。
| 包 | 宿主适配器 | 裸 keymap 辅助函数 | 默认辅助函数 |
|---|---|---|---|
@opentui/keymap/opentui | createOpenTuiKeymapHost(renderer) | createOpenTuiKeymap(renderer) | createDefaultOpenTuiKeymap(renderer) |
@opentui/keymap/html | createHtmlKeymapHost(root) | createHtmlKeymap(root) | createDefaultHtmlKeymap(root) |
- 宿主适配器返回一个
KeymapHost实现。 - 裸辅助函数创建一个仅安装宿主的
new Keymap(host);解析器和插件阶段仍为可选。 - 默认辅助函数从裸辅助函数开始,并安装该宿主的小型默认插件集。
OpenTUI 宿主
@opentui/keymap/opentui 是用于基于 @opentui/core 构建的终端应用的宿主包。
提供的功能
createOpenTuiKeymapHost(renderer)- 将CliRenderer和Renderable适配为KeymapHostcreateOpenTuiKeymap(renderer)- 创建裸Keymap<Renderable, KeyEvent>createDefaultOpenTuiKeymap(renderer)- 创建安装了默认按键、启用字段和元数据字段的 OpenTUI keymap
基本用法
import { createCliRenderer } from "@opentui/core"
import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"
const renderer = await createCliRenderer()
const keymap = createDefaultOpenTuiKeymap(renderer)
keymap.registerLayer({
commands: [
{
name: "quit",
run() {
renderer.destroy()
},
},
],
bindings: [{ key: "q", cmd: "quit" }],
})
如果您想自己控制已安装的插件,请从 createOpenTuiKeymap(renderer) 开始并手动注册插件。
宿主行为
| 行为 | OpenTUI 适配器 |
|---|---|
| 根目标 | renderer.root |
| 聚焦目标 | renderer.currentFocusedRenderable(当仍处于聚焦且未被销毁时) |
| 父级遍历 | Renderable.parent |
| 目标销毁跟踪 | RenderableEvents.DESTROYED |
| 宿主销毁跟踪 | CliRenderEvents.DESTROY |
| 按下/释放事件 | renderer.keyInput 的 keypress 和 keyrelease |
| 原始输入拦截 | renderer.prependInputHandler(...) |
| 合成命令事件 | 带 name: "command" 的新 KeyEvent |
| 宿主元数据 | 运行时平台;支持 ctrl、shift 和 meta;当终端能力报告 Kitty 键盘时支持 super/hyper,否则为 unknown |
createOpenTuiKeymap(renderer) 在渲染器已被销毁时抛出异常。
默认辅助函数
createDefaultOpenTuiKeymap(renderer) 安装:
registerDefaultKeys()registerEnabledFields()registerMetadataFields()
它不安装 leader 令牌、ex 命令、消歧义插件、警告分析器、基础布局回退或 textarea 辅助函数。如果您希望绑定忽略活动键盘布局更改,请从 @opentui/keymap/addons/opentui 调用 registerBaseLayoutFallback()。
事件说明
- 按下和释放事件直接来自渲染器的按键输入流。
- 编程式的
runCommand()和dispatchCommand()调用接收由createCommandEvent()创建的合成KeyEvent。 KeyEvent上的 Kitty 特定字段(如baseCode)对registerBaseLayoutFallback()等插件仍然可用。
OpenTUI 特定插件
@opentui/keymap/addons/opentui 在共享插件集之上添加辅助函数:
registerBaseLayoutFallback()- 匹配 Kitty 基础布局码位createTextareaBindings()- 从共享编辑缓冲区命令集生成 textarea 绑定registerEditBufferCommands()- 向renderer.currentFocusedEditor注册编辑缓冲区命令层registerTextareaMappingSuspension()- 挂起聚焦的TextareaRenderable的内置映射快捷键registerManagedTextareaLayer()- 组合上述辅助函数的高级 textarea 集成
示例:packages/examples/src/keymap-demo.ts
请参阅内置插件了解附带的插件接口,以及自定义插件了解插件编写。
HTML 宿主
@opentui/keymap/html 是用于以 HTMLElement 子树为根的浏览器 UI 的宿主包。
在线演示:HTML keymap 演示
提供的功能
HtmlKeymapEvent— 共享 keymap 事件加上可选的originalEvent: KeyboardEventnormalizeHtmlKeyName(key)— 将浏览器event.key值规范化为 keymap 名称createHtmlKeymapEvent(event?)— 将KeyboardEvent包装在HtmlKeymapEvent中createHtmlKeymapHost(root)— 将HTMLElement子树适配为KeymapHosthtmlEventMatchResolver— 用于浏览器按键事件的 HTML 特定事件匹配器createHtmlKeymap(root)— 创建裸Keymap<HTMLElement, HtmlKeymapEvent>createDefaultHtmlKeymap(root)— 创建安装了默认按键、启用字段、元数据字段和 HTML 事件匹配的 HTML keymap
基本用法
import { createDefaultHtmlKeymap } from "@opentui/keymap/html"
const root = document.getElementById("app")!
const keymap = createDefaultHtmlKeymap(root)
keymap.registerLayer({
commands: [
{
name: "toggle-help",
run() {
document.body.classList.toggle("help-open")
},
},
],
bindings: [{ key: "?", cmd: "toggle-help" }],
})
如果您想自己控制解析器或事件匹配阶段,请从 createHtmlKeymap(root) 开始并手动安装各部分。
按键规范化
| 浏览器输入 | Keymap 结果 | 说明 |
|---|---|---|
ArrowLeft | left | 导航键被规范化为共享的规范名称 |
Enter | return | 规范名称为 return;显示字符串化工具将其渲染为 enter |
A | a | 可打印名称被转换为小写 |
F12 | f12 | 功能键被规范化为小写 |
altKey | meta | keymap 使用 meta 表示 Alt/Option |
metaKey | super | keymap 使用 super 表示平台 Meta 键 |
HTML 匹配器还为带 Shift 的可打印标点符号添加了无 Shift 匹配候选项,因此 ":" 和 "?" 等绑定作为字面按键工作,而不是强制使用 shift+semicolon 风格的拼写。
宿主行为
| 行为 | HTML 适配器 |
|---|---|
| 根目标 | 传递给 createHtmlKeymapHost() / createHtmlKeymap() 的 HTMLElement |
| 聚焦目标 | document.activeElement(当它是根或后代时) |
| 父级遍历 | HTMLElement.parentElement |
| 目标销毁跟踪 | MutationObserver 监视根子树(可用时) |
| 宿主销毁跟踪 | 无;根可达性即生命周期模型 |
| 按下/释放事件 | 根上捕获阶段的 keydown 和 keyup 监听器 |
| 原始输入拦截 | HTML 宿主未提供 |
| 合成命令事件 | createHtmlKeymapEvent() 无 DOM 事件 |
| 宿主元数据 | 浏览器平台;支持 ctrl、shift、meta 和 super;不支持 hyper |
当目标元素从根子树断开连接时,针对它注册的本地层会被移除。
默认辅助函数
createDefaultHtmlKeymap(root) 安装:
registerDefaultKeys()registerEnabledFields()registerMetadataFields()htmlEventMatchResolver通过prependEventMatchResolver(...),使 HTML 特定匹配在规范匹配器之前运行
对于必须在 HTML 匹配器之前运行的任何自定义解析器,请调用 prependEventMatchResolver(...)。
自定义宿主
如果两个内置宿主都不适合您的运行时,请自行实现 KeymapHost 并直接构建引擎:
import { Keymap, type KeymapHost } from "@opentui/keymap"
const keymap = new Keymap(host as KeymapHost<object>)
自定义宿主必须提供保守的元数据。当宿主无法证明平台或修饰键能力时,使用 unknown。
使用 Core 获取裸引擎 API 和扩展点。