前端

Odoo 编辑器里的斜杠命令为什么不是“弹个菜单就行”:Powerbox、UserCommand 与搜索更新链路讲透

很多人第一次用 Odoo 编辑器里的“/”命令,会把它理解成一个普通下拉菜单。但从 powerbox_plugin.js、search_powerbox_plugin.js、user_command_plugin.js 和 Powerbox 组件来看,官方真正做的是一套命令层:先注册用户命令,再映射成 powerbox item,按分类与可用性过滤,用 overlay 承载,再根据输入中的 search term 实时模糊筛选。本文结合源码,讲清 Odoo 为什么把斜杠命令做成编辑器内部命令系统,而不是小组件效果。

前端
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

现在很多编辑器都有斜杠命令。用户按下 /,弹出一个列表,然后插入标题、列表、图片、按钮、卡片。

表面上看,Odoo 里的 Powerbox 好像也只是这样:

  • 打开一个菜单;
  • 上下选择;
  • 回车执行。

但如果你去读 powerbox_plugin.jssearch_powerbox_plugin.jsuser_command_plugin.js,就会发现官方真正设计的不是“菜单组件”,而是一套编辑器命令层

它至少拆成了四件事:

  1. 先有 user_commands 这种抽象命令;
  2. 再把命令映射成 powerbox_items
  3. 用分类、可用性和关键字组织展示层;
  4. 最后才由 overlay 里的 Powerbox 组件负责交互呈现。

也就是说,Powerbox 的关键不是“弹窗长什么样”,而是:

编辑器怎样把可执行能力变成可搜索、可过滤、可上下文感知的命令系统。

一、UserCommandPlugin 说明 Odoo 先定义“命令”,再考虑 UI

user_command_plugin.js 非常短,但非常关键。

它把 user_commands 收集成一个只读命令字典,每个命令至少有:

  • id
  • run
  • 可选的 title
  • description
  • icon
  • isAvailable

这意味着 Odoo 的第一层抽象不是菜单项,而是用户命令

这是个很成熟的设计,因为命令本身应该先独立于展示形式存在。否则你很快就会把执行逻辑死绑在某个按钮或菜单上。

一旦命令被抽象出来,它就可以被:

  • toolbar 调用;
  • slash 命令调用;
  • 搜索型命令调用;
  • 未来别的入口复用。

二、PowerboxPlugin 做的不是注册命令,而是“把命令翻译成可展示可执行的候选项”

PowerboxPlugin 的资源里有三类内容特别关键:

  • user_commands
  • powerbox_categories
  • powerbox_items

这里的设计非常漂亮。

user_commands 是能力定义层

决定命令能做什么。

powerbox_items 是展示映射层

它通过 commandId 引用某个命令,再补上:

  • categoryId
  • 可能覆盖的 title
  • description
  • icon
  • keywords
  • commandParams
  • 额外 isAvailable

powerbox_categories 是信息架构层

它决定命令如何分组、如何排序。

这说明 Powerbox 不是把命令和 UI 强耦合,而是插了一个很关键的中间层:

同一个命令,可以按当前场景被组织成不同的 Powerbox 展示项。

三、makePowerboxCommands() 体现了“继承 + 覆盖”的命令组装思路

makePowerboxCommands() 会:

  • 取出所有 powerbox_items
  • 根据 commandId 找到原始 user command
  • 继承 title / description / icon
  • 再允许 item 覆盖部分属性
  • 用 category 词典补上 categoryName
  • command.run(item.commandParams, context) 封成最终执行器
  • command.isAvailableitem.isAvailable 叠加成联合判断

这套设计特别适合编辑器生态。

因为很多时候你真正想复用的不是“某个菜单项”,而是:

  • 同一条基础命令;
  • 在不同类目里不同命名;
  • 在不同上下文里带不同默认参数;
  • 甚至只在部分选区里可用。

Powerbox 正好提供了这个翻译层。

四、为什么 isAvailable 特别重要

Powerbox 不是一个静态功能目录。

getAvailablePowerboxCommands() 会读取当前编辑选区,再结合:

  • blacklist selector
  • 命令本身的 isAvailable
  • item 额外的 isAvailable

最后过滤出当前真能执行的命令。

这点很关键,因为编辑器命令天然是上下文敏感的:

  • 某些命令只在正文块里可用;
  • 某些命令在特定节点里禁止;
  • 某些命令需要当前选区满足结构条件。

所以 Powerbox 的本质不是“把所有功能列出来”,而是:

只在当前上下文里暴露真正成立的命令空间。

五、overlay 在这里不是视觉容器,而是命令交互壳

PowerboxPlugin 通过 overlay.createOverlay(Powerbox) 创建展示壳,并把响应式 state 传进去:

  • commands
  • currentIndex
  • showCategories

随后 openPowerbox() / updatePowerbox() / closePowerbox() 控制这层壳体。

也就是说,Powerbox 组件自身主要负责:

  • 渲染命令列表;
  • 高亮当前项;
  • 滚动对齐;
  • 把点击 / 鼠标悬停转成激活。

真正重要的命令组装、筛选和执行策略,都在插件层。

这正是好架构该有的样子:

  • UI 管显示;
  • 插件管命令逻辑。

六、SearchPowerboxPlugin 说明“/”不是打开菜单,而是进入命令搜索模式

很多人会把 slash command 误解成:

  • 输入 /
  • 打开一个菜单
  • 完。

search_powerbox_plugin.js 显示 Odoo 做得更细。

它在:

  • beforeinput_handlers
  • input_handlers
  • delete_handlers
  • post_undo_handlers
  • post_redo_handlers

这些事件上都挂了逻辑。

当用户输入 / 时,它会:

  1. 记录历史 savepoint;
  2. 打开 Powerbox;
  3. 记下 slash 所在 offset;
  4. 后续持续判断当前 selection 是否还处于“搜索态”;
  5. / 到当前光标之间截出 searchTerm
  6. fuzzyLookup 在可用命令中实时筛选;
  7. 命令执行时把 searchTerm 写进 context,并恢复 savepoint。

这说明 slash command 的本质不是“菜单快捷键”,而是:

在正文输入流里临时开启一段命令检索会话。

七、为什么要有 savepoint restore

这部分特别体现编辑器的精细程度。

用户输入 /hea 去搜 heading,本质上只是为了选命令,不是为了把 /hea 这串字符永久留在文档里。

所以 onBeforeInput 遇到 / 时会做 history.makeSavePoint(),后面命令真正应用时再通过 historySavePointRestore?.() 把这段搜索输入回滚掉。

这一步非常关键。

否则 slash 搜索就会污染正文历史,导致:

  • 文档里残留搜索词;
  • undo 栈混进一堆临时字符;
  • 命令执行前后内容边界变脏。

也就是说,Powerbox 不只是可搜索,还把搜索过程和最终内容变更分得很清楚。

八、模糊搜索不是锦上添花,而是命令层的可发现性保障

filterCommands(searchTerm) 里用的是 fuzzyLookup,而且匹配维度不只标题,还包括:

  • title
  • categoryName
  • description
  • keywords

这非常重要。

因为命令系统如果只靠精确名称,扩展一多就会变得很难找。Powerbox 把搜索维度做宽,等于在提升:

  • 命令可发现性;
  • 新手可用性;
  • 插件命令的暴露机会。

所以它不是装饰功能,而是命令平台能不能规模化的关键。

九、二开里最容易犯的几个误区

误区 1:把 slash command 当成一个菜单组件改皮肤

这样会只看到 UI,忽略底下真正的命令注册与可用性体系。

误区 2:直接在 Powerbox 组件里塞业务逻辑

命令能力应该优先落在 user_commands / powerbox_items,而不是硬写在展示层。

误区 3:忽略 isAvailable

结果命令在不该出现的上下文里也能弹出来,用户一用就报错。

误区 4:把搜索输入当正式内容

没有 savepoint restore,slash 搜索体验很快就会变得很脏。

十、结论

Odoo 编辑器里的 Powerbox,核心从来不是一个“漂亮的斜杠菜单”。

从源码设计看,它真正做的是:

  • UserCommandPlugin 抽象命令;
  • powerbox_items 把命令映射成当前入口的展示候选;
  • isAvailable 把命令空间收束到当前上下文;
  • 用 overlay 承载交互壳;
  • SearchPowerboxPlugin/ 输入流变成临时命令搜索会话;
  • 用 savepoint 把搜索过程与最终内容改动分开。

所以更准确的理解应该是:

Powerbox 不是菜单组件,而是 Odoo HTML 编辑器里的命令发现与执行系统。

理解了这一点,你在做编辑器命令扩展时,就不会只想着“加个条目”,而会更自然地去设计:

  • 命令抽象层;
  • 上下文可用性;
  • 搜索关键字;
  • 以及执行前后对正文与历史的影响。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。