Wox macOS 菜单插件详解:用启动器直接搜索并执行任意应用菜单项
2026/9/20 7:12:14 网站建设 项目流程
  • 桌面应用
  • AI 应用
  • 插件系统

【免费下载链接】Wox

A cross-platform launcher that simply works

项目地址:https://gitcode.com/gh_mirrors/wo/Wox
点击查看免费下载

菜单插件(Menus Plugin)是 Wox 内置的系统插件之一,它把当前 macOS 应用的菜单栏变成一个可全文搜索的命令集:只要让目标应用保持焦点,在 Wox 中输入菜单项名称的一部分,即可定位并直接执行对应的菜单命令。本篇文章将基于 Wox 仓库中的官方文档与插件源码,完整讲解该插件的使用方式、触发机制、菜单解析原理与底层实现,帮助你快速上手,也能理解它背后的 macOS 辅助功能(Accessibility)调用链。

快速开始

菜单插件的使用方式非常简单,遵循"聚焦应用 → 打开 Wox → 输入菜单名"三步流程:

  1. 让目标应用保持焦点(例如 Safari 或其他任意 Mac 应用);
  2. 打开 Wox,直接输入菜单项名称的一部分;
  3. 在结果列表中选中目标菜单项,回车即可执行对应菜单命令。

官方文档给出的两个典型搜索词:

export preferences

例如在 Safari 聚焦时输入preferences,Wox 会匹配到「Safari → Settings…」这类位于应用菜单下的菜单项,选中后等效于用鼠标点击该菜单。这个插件只在 macOS 上提供,其元数据中SupportedOS字段明确限定为Macos(见 menus_darwin.go)。

如何触发:全局搜索与「menus」关键词

从插件元数据可以看出,该插件注册了两种触发方式:

TriggerKeywords: []string{ "*", "menus", },
  • *表示全局搜索:不依赖任何前缀,直接在 Wox 中输入菜单项文字即可命中;
  • menus作为显式关键词:输入menus后再接菜单名,可以更精确地在菜单插件范围内过滤。

由于全局匹配到的是形如父菜单->子菜单的完整路径,当目标应用的菜单层级较深时,建议直接输入目标菜单项名称的片段(如export),模糊匹配会自动定位。

匹配规则与结果呈现

插件拿到菜单项后,通过 Wox 的模糊匹配引擎进行过滤(见 menus_darwin.go):

filteredMenus := lo.Filter(menuNames, func(menu string, _ int) bool { match, score := plugin.IsStringMatchScore(ctx, menu, query.Search) return (match && score > 20) || (!query.IsGlobalQuery() && query.Search == "") })

两个细节值得注意:

  1. 分数阈值:只有匹配且得分大于 20 的菜单项才会出现在结果中,避免把无关菜单项当作搜索结果;
  2. 空搜索兜底:当不是全局查询(即通过menus关键词进入)且搜索词为空时,会列出当前应用的全部菜单项,方便你浏览整棵菜单树。

每个结果项都带有当前活动窗口应用的图标(当活动窗口图标不可用时回退到内置的菜单图标,图标定义见 default_plugin.go),执行动作名为plugin_menus_execute,回车后触发对应菜单命令。

插件如何获得"当前应用":QueryEnv 特性声明

菜单插件必须知道当前哪个应用处于焦点,这依赖 Wox 的 QueryEnv 机制。插件在元数据中显式声明了两个环境变量需求(见 menus_darwin.go):

Features: []plugin.MetadataFeature{ { Name: plugin.MetadataFeatureQueryEnv, Params: map[string]any{ "requireActiveWindowPid": "true", "requireActiveWindowIcon": "true", }, }, },

Wox 主程序在 metadata.go 中解析这些参数:requireActiveWindowPid要求查询环境附带当前活动窗口的进程 ID,requireActiveWindowIcon要求附带活动窗口图标。在查询回调中,插件据此拿到query.Env.ActiveWindowPid

if query.Env.ActiveWindowPid == 0 { i.api.Log(ctx, plugin.LogLevelError, "Active window pid is not available") return plugin.QueryResponse{} }

若拿不到 PID(例如 Wox 自身获得焦点、或当前窗口不可枚举),插件会直接返回空结果并记录错误日志。这也是为什么使用前必须保持目标应用为焦点窗口

菜单是怎么读出来的:Accessibility API 调用链

从源码结构看,菜单的读取与执行横跨三层:

  1. 插件层menus_darwin.go:负责查询编排、结果过滤、缓存与动作分发;
  2. cgo 桥接层util/menus/menus_darwin.go:通过 cgo 声明对 Objective-C 函数的调用:
/* #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Foundation -framework Cocoa -framework AppKit char** getMenuItems(int pid, int* count); void performMenuAction(int pid, const char* title); */

Go 侧提供GetAppMenuTitles(pid)读取菜单标题列表、ExecuteActiveAppMenu(pid, title)执行菜单动作,并负责C.CStringC.GoString与内存释放等跨语言转换。

  1. Objective-C 实现层util/menus/menus_darwin.m:核心是 macOS 的 Accessibility 框架,调用链如下:
  • AXUIElementCreateApplication(pid)依据进程 PID 创建应用的可访问性元素;
  • AXUIElementCopyAttributeValue(app, kAXMenuBarAttribute, ...)获取应用菜单栏;
  • 递归遍历kAXChildrenAttribute展开每一级菜单,同时读取kAXTitleAttribute组装父菜单->子菜单的完整路径,最终生成可搜索的标题列表。

其中getMenuItemTitles的递归逻辑决定了标题的拼接规则:叶子菜单项(无子项)输出parentTitle->title,根级菜单输出裸标题;当标题为空时只传递父级路径。此外实现中会跳过名为Apple的菜单(即系统菜单),避免把系统级菜单混入搜索结果。

执行菜单动作时,performMenuAction再次遍历菜单树,找到完整路径与目标一致的菜单项后调用:

AXUIElementPerformAction(menuItem, kAXPressAction);

即向该菜单项发送"按下"动作,等效于用户真实点击该菜单。

从上述实现可以推断,该功能依赖 macOS 的 Accessibility 辅助功能接口,因此在系统设置中需要为 Wox 授予「辅助功能」权限;若读取失败,插件会记录错误日志(如Failed to get menu bar for app with pid ...)并返回空结果,这通常就是权限未授予或目标应用不暴露菜单栏的典型表现。

性能设计:一分钟菜单缓存

菜单栏解析是较为昂贵的操作(每次都要递归遍历整棵菜单树),因此插件引入了按 PID 隔离的缓存(见 menus_darwin.go):

var menusCacheTTL = time.Minute var menusCache = util.NewHashMap[int, menusCacheEntry]() type menusCacheEntry struct { titles []string expiresAt time.Time }

查询时先查缓存,命中且未过期则直接使用;否则调用menus.GetAppMenuTitles重新解析并写入缓存,TTL 为 1 分钟。getMenusFromCache在过期时会主动删除旧条目,避免内存中堆积已退出应用的菜单数据。这意味着:菜单变更后最多一分钟内即可看到新菜单项,同时重复查询不会重复触发昂贵的 AX 遍历。

使用提示与常见问题排查

  • 确认焦点:如果输入菜单名没有任何结果,先确认目标应用确实是当前焦点窗口(点击一下应用窗口再回到 Wox 输入)。
  • 使用更完整的路径片段:层级深的菜单(如文件->导出->导出为 PDF…)建议输入末级菜单名或中间片段,模糊匹配得分更高。
  • 通过menus关键词浏览:输入menus加空格后不输入其他内容,可以看到当前应用的全部菜单项列表。
  • 权限问题:如果插件完全无输出且日志出现 AX 相关错误,请检查系统「隐私与安全性 → 辅助功能」中是否已为 Wox 开启权限,并重启 Wox 使其生效。
  • 系统菜单排除Apple菜单被实现层主动跳过,这是设计行为,不属于缺陷。

小结

Wox 的 macOS 菜单插件以极小的交互成本,把"点菜单"变成了"打字搜索",其实现是 Wox 插件体系的一个典型范例:通过TriggerKeywordsMetadataFeatureQueryEnv声明触发方式和环境依赖,利用 cgo 桥接 Go 与 Objective-C,再借助 macOS Accessibility API 完成菜单树的读取与点击动作。如果你想深入阅读实现细节,推荐依次查看官方文档、插件主逻辑与Objective-C 实现三个文件。

  • 桌面应用
  • AI 应用
  • 插件系统

【免费下载链接】Wox

A cross-platform launcher that simply works

项目地址:https://gitcode.com/gh_mirrors/wo/Wox
点击查看免费下载

相关推荐

上一篇:终极指南:3步让老旧Mac焕新,用OpenCore Legacy Patcher重获新生
下一篇:TinyConsole快速入门指南:5分钟学会在iOS应用中集成微型控制台

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询