# 工具栏与弹窗

用组件库构建命令入口，正确同步适用性、选区和异步表单。

## 工具栏由宿主构建

MSFE 不要求固定工具栏或菜单。QuireKit 应用从 `@quirekit/ui/vue` 使用 `QuireButton`、`QuireIconButton`、`QuireSelect`、`QuirePopover` 等组件；其他宿主可以选择自己的组件。编辑行为统一调用 MSFE 的公共接口。

| UI | 读取 | 执行 |
| --- | --- | --- |
| 粗体按钮 | `isActive('bold')`、`can().toggleBold()` | `commands.toggleBold()` |
| 标题级别 | `getAttributes('heading')` | `commands.setHeading(level)` |
| 代码块语言 | 当前 NodeView 的 `attrs.language` | `nodeView.updateAttributes({ language })` |
| 图片插入 | 对话框自己的表单状态 | `commands.insertImage({ url, alt, title })` |
| 自定义状态 | 已安装的语义查询 | 自己的具名命令或字段 patch |

`getAttributes()` 对不存在、混合值或未确认解析返回 `null`；不要把它显示为某个默认值并误覆盖多选区。

## 状态订阅与启用条件

使用 [状态订阅](/guide/state) 更新当前选区相关的启用状态。`can()` 不能执行副作用；`isActive()` 不能只依赖 DOM 上是否有加粗样式。控件销毁时释放订阅。

QuireKit 的有限选项选择器使用 `QuireSelect`，不要通过普通文本输入模拟不受约束的语言名。图标按钮提供可访问名称；切换按钮提供状态；键盘操作不依赖 hover。

## 点击时的选择意图

简单按钮可以在宿主事件策略允许时保留编辑器焦点，然后执行命令并 `focus()`。弹出菜单或对话框需要捕获目标；不能要求所有按钮都阻止 pointerdown，因为这会破坏键盘、触摸和输入控件。

短暂菜单保存 `captureTarget()`；异步对话框使用 `captureAnchor()` 和文档作用域。不要读取“打开弹窗后新的选区”来替换用户最初选中的文本。完整函数见 [选区与异步目标](/guide/selection)。

## 只读和错误反馈

写入控件应同时考虑只读、locked、组合输入、功能安装和语法适用性；最终以命令结果为准。`readonly`、`stale-target`、`not-applicable` 可给出不同反馈，不需要把底层异常直接展示给最终用户。

查看源码、复制和折叠等读取动作不必一律禁用。示例 [容器控件](/extensions/containers) 与 [代码块工具栏](/extensions/code-blocks) 展示了两种不同协议。

## 复杂应用中的边界

菜单项只发命令；扩展描述行为；codec 处理精确源码；组件库负责视觉和交互状态。不要把格式按钮的业务逻辑复制到快捷键、右键菜单和浮动栏三处。创建一个具名命令供所有入口复用，并用命令链组织一次操作。
