Keymap 宿主

OpenTUI Keymap 与宿主无关。宿主将运行时的焦点模型、层次结构、按键事件和生命周期适配为 KeymapHost<TTarget, TEvent>,以便共享引擎可以对其工作。

该包附带两个内置宿主:

  • @opentui/keymap/opentui 用于基于 CliRendererRenderable 构建的终端应用
  • @opentui/keymap/html 用于以 HTMLElement 为根的浏览器 UI

如果您尚未阅读共享模型,请从 Keymap 概览开始。

宿主的作用

keymap 核心不知道 DOM 节点、终端可渲染对象或任何其他 UI 树。它只知道宿主目标和宿主事件。

  • 本地层上的 target 是一个宿主目标对象。
  • focusfocus-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)应解析为的修饰键。能力值为 supportedunsupportedunknown;当事件类型可以表示修饰键但实际终端传递取决于协议支持时,终端宿主使用 unknown

元数据字段含义
platformmacoswindowslinuxunknown
primaryModifiermacOS 上为 super,Windows/Linux 上为 ctrl,或 unknown
modifiers宿主对 ctrlshiftmetasuperhyper 的能力

宿主按键事件必须满足 KeymapEvent

成员描述
name规范化的按键名称
ctrl / shift / meta修饰键状态
super / hyper可选的额外修饰键状态
preventDefault()阻止匹配的事件到达宿主目标
stopPropagation()阻止后续监听器看到该事件并将 propagationStopped 设为 true
propagationStopped调用 stopPropagation() 后为 true

如果您的宿主实现了 onRawInput(),必须在按键解析之前调用每个监听器,并在监听器返回 true 时停止。

内置宿主辅助函数

两个内置宿主包都导出适配器特定的辅助函数。从 @opentui/keymap 导入共享的 Keymap、字符串化工具和核心类型。

宿主适配器裸 keymap 辅助函数默认辅助函数
@opentui/keymap/opentuicreateOpenTuiKeymapHost(renderer)createOpenTuiKeymap(renderer)createDefaultOpenTuiKeymap(renderer)
@opentui/keymap/htmlcreateHtmlKeymapHost(root)createHtmlKeymap(root)createDefaultHtmlKeymap(root)
  • 宿主适配器返回一个 KeymapHost 实现。
  • 裸辅助函数创建一个仅安装宿主的 new Keymap(host);解析器和插件阶段仍为可选。
  • 默认辅助函数从裸辅助函数开始,并安装该宿主的小型默认插件集。

OpenTUI 宿主

@opentui/keymap/opentui 是用于基于 @opentui/core 构建的终端应用的宿主包。

提供的功能

  • createOpenTuiKeymapHost(renderer) - 将 CliRendererRenderable 适配为 KeymapHost
  • createOpenTuiKeymap(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.keyInputkeypresskeyrelease
原始输入拦截renderer.prependInputHandler(...)
合成命令事件name: "command" 的新 KeyEvent
宿主元数据运行时平台;支持 ctrlshiftmeta;当终端能力报告 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: KeyboardEvent
  • normalizeHtmlKeyName(key) — 将浏览器 event.key 值规范化为 keymap 名称
  • createHtmlKeymapEvent(event?) — 将 KeyboardEvent 包装在 HtmlKeymapEvent
  • createHtmlKeymapHost(root) — 将 HTMLElement 子树适配为 KeymapHost
  • htmlEventMatchResolver — 用于浏览器按键事件的 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 结果说明
ArrowLeftleft导航键被规范化为共享的规范名称
Enterreturn规范名称为 return;显示字符串化工具将其渲染为 enter
Aa可打印名称被转换为小写
F12f12功能键被规范化为小写
altKeymetakeymap 使用 meta 表示 Alt/Option
metaKeysuperkeymap 使用 super 表示平台 Meta 键

HTML 匹配器还为带 Shift 的可打印标点符号添加了无 Shift 匹配候选项,因此 ":""?" 等绑定作为字面按键工作,而不是强制使用 shift+semicolon 风格的拼写。

宿主行为

行为HTML 适配器
根目标传递给 createHtmlKeymapHost() / createHtmlKeymap()HTMLElement
聚焦目标document.activeElement(当它是根或后代时)
父级遍历HTMLElement.parentElement
目标销毁跟踪MutationObserver 监视根子树(可用时)
宿主销毁跟踪无;根可达性即生命周期模型
按下/释放事件根上捕获阶段的 keydownkeyup 监听器
原始输入拦截HTML 宿主未提供
合成命令事件createHtmlKeymapEvent() 无 DOM 事件
宿主元数据浏览器平台;支持 ctrlshiftmetasuper;不支持 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 和扩展点。