构建自绘文本编辑器
自绘编辑器不能只监听键盘。按键控制用 input,真正文本合成用 ime,复制粘贴用 clipboard,持久化由 store 完成。
{
"apiVersion": "0.1",
"canvases": { "editor": { "mode": "draw" } },
"capabilities": [
"host:input.focus",
"host:ime.openSession",
"host:clipboard.readText",
"host:clipboard.writeText",
"host:store.get",
"host:store.put"
],
"events": ["input.focusChange", "ime.textUpdate", "ime.composition", "ime.ended"],
"apiLimits": {}
}
1. 聚焦并打开 IME
await host.input.focus('editor');
const ime = await host.ime.openSession('editor', {
text: document.text,
selection: document.selection,
inputMode: 'text',
multiline: true,
revision: document.revision,
});
2. 应用编辑操作
host.events.on('ime.textUpdate', async ({ update, baseRevision }) => {
if (baseRevision !== document.revision) return;
document.replace(update.range, update.text);
await host.ime.updateState(ime, document.snapshot());
scheduleSave();
});
文档模型在 guest 中是权威状态。IME 事件是编辑请求,应用后再回传新 revision。
3. 同步几何
await host.ime.updateGeometry(ime, {
revision: document.revision,
editorBounds: layout.editor,
caretBounds: layout.caret,
});
在选择、滚动、字体或 canvas 尺寸变化后更新,不要无变化地逐帧发送。
4. 复制与粘贴
async function copy() {
await host.clipboard.writeText(document.selectedText());
}
async function paste() {
const text = await host.clipboard.readText();
if (text !== null) document.replaceSelection(text);
}
两个函数都要从可信用户操作直接进入。一次授权不得转成后台剪贴板读取。
5. 持久化
const previous = await host.store.get('documents/current');
await host.store.put(
'documents/current',
encodeDocument(document),
previous ? { version: previous.version } : 'absent',
);
处理 conflict,不要静默覆盖另一窗口的更新。
6. 关闭
await host.ime.closeSession(ime);
失焦、文档关闭和视图销毁都要清理会话。