ime
ime 让自绘文本控件接入系统输入法。宿主代持真实输入元素和合成会话,guest 交换有限的编辑状态、选择范围和光标几何,不获得 DOM。
基本示例
const session = await host.ime.openSession('main', {
text: '',
selection: { start: 0, end: 0 },
inputMode: 'text',
multiline: false,
revision: 0n,
});
host.events.on('ime.textUpdate', ({ handle, update }) => {
if (handle !== session) return;
editor.apply(update);
host.ime.updateState(session, editor.snapshot());
});
适用场景
- 自绘文本框、代码编辑器、终端和画布内表单。
- 中文、日文、韩文等合成输入。
- 移动端软键盘、选择范围和候选窗口定位。
只处理按键快捷键时使用 input。富文本数据模型、撤销栈、拼写检查策略和协同编辑属于 guest。
L1 边界
宿主独占的是操作系统输入法、候选窗口、软键盘和与应用焦点绑定的合成会话。guest 不能从按键可靠重建这些系统设施;文档模型、排版、撤销和富文本语义仍由 guest 持有。
能力声明
{
"capabilities": ["host:ime.openSession"],
"events": [
"ime.textUpdate",
"ime.composition",
"ime.formatUpdate",
"ime.characterBoundsRequest",
"ime.ended"
]
}
updateState、updateGeometry 和 closeSession 都由 ime-session 句柄的 rw 权限授权,不是独立 capability。
Reference
方法
- ime.openSession():创建随焦点存活的输入法会话。
- ime.updateState():同步 guest 已接受的文本和选择状态。
- ime.updateGeometry():更新插入点与字符矩形。
- ime.closeSession():幂等关闭会话。
事件
- ime.textUpdate:插入、删除或替换文本。
- ime.composition:合成范围或合成结束状态变化。
- ime.formatUpdate:输入法建议的临时格式范围。
- ime.characterBoundsRequest:宿主请求指定字符范围的几何信息。
- ime.ended:会话永久结束且句柄失效。
状态模型
ImeState {
text: string
selection: { start: u32, end: u32 }
composition?: { start: u32, end: u32 }
inputMode: "text" | "numeric" | "email" | "url" | "search"
multiline: boolean
revision: u64
}
Rect { x: f32, y: f32, width: f32, height: f32 }
ImeGeometry {
revision: u64
editorBounds: Rect
caretBounds: Rect
characterBounds?: Array<{ index: u32, rect: Rect }>
}
所有索引统一按 UTF-16 code unit 计数,起止位置必须位于 Unicode 标量边界,不能切开代理对。原生绑定必须按同一规则转换,不能用 UTF-8 字节或 Unicode 标量序号代替。start <= end,所有范围都必须落在当前 revision 的文本内。
characterBounds 的 index 是该 Unicode 标量在 UTF-16 中的起始 offset;数组按 index 严格递增且不重复。对于代理对只给一项。矩形使用 canvas 逻辑像素,宽高非负,所有值有限。
完整工作流
- 用户点中自绘文本控件,应用调用
openSession。 - 宿主建立系统输入会话并送达
textUpdate/composition。 - guest 应用编辑操作,再以
updateState确认权威状态。 - 布局变化时 guest 调用
updateGeometry。 - 控件失焦或销毁时调用
closeSession;宿主也可以因失焦主动结束。
原生 WASM 示例
ImeHandle ime = host_ime_open_session(MAIN_CANVAS, initial_state());
void on_ime_text_update(ImeTextUpdate update) {
editor_apply(update);
ImeState state = editor_snapshot();
host_ime_update_state(ime, state);
}
大文本不应在每个按键后整段复制;v0.1 绑定必须为状态大小设上限,未来若真实编辑器证明需要增量快照,应在兼容版本中增加明确结构。
生命周期与竞态
- 句柄类型为
Handle<ime-session, rw, app-context, focus>。 - 会话随绘图面焦点、应用视图和宿主输入元素共同存活。
- 每次状态更新带单调 revision,过期更新返回
stale-state。 openSession接受任意初始 revision;此后每次成功updateState的 revision 必须严格增大。updateGeometry引用当前 revision 但不推进它。- 每个会话同一时间最多有一个尚未由
updateState确认的textUpdate;宿主在确认前不得发送下一个文本变更。guest 拒绝过期更新时仍应回传当前权威状态以解除等待。 - 宿主主动或 guest 显式结束时使句柄失效;若应用声明了
ime.ended,发送一次终止事件。随后update*返回stale-handle。 - 同一绘图面同一时间最多一个活动会话。
安全与预算
- 信任档位:green;内容属于用户与本应用的直接交互。
- 宿主不得把密码管理器内部数据、其他输入框内容或全局剪贴板混入事件。
- v0.1 预算键为
ime.maxSessions、ime.maxTextCodeUnits、ime.maxCharacterBounds、ime.updatesPerSecond。 - 密码输入模式若未来引入,必须独立定义回显、日志和截图限制,不能复用普通
text。
错误与测试
特有错误包括 not-focused、stale-state、invalid-range、stale-handle 和 limit-exceeded。一致性测试至少覆盖合成开始/更新/提交/取消、代理对边界、严格 revision、单个未确认编辑、选择方向、失焦、绘图面销毁、过期 revision 和事件重入。