一个纯装饰性的 macOS 菜单栏 App(LSUIElement,不占 Dock、不进 Cmd-Tab)。每次按键——
包括退格删除——都会在当前文本插入点处迸发一小簇粒子。默认是多彩的粒子与爱心,
颜色每次按键换一个色系。
它和同类工具最大的差别在落点:不是画在鼠标处,而是真正跟住文本插入符。 这件事在 macOS 上比看起来难得多,见下方「插入点是怎么找到的」。
- 3 种特效风格:粒子 / 烟花 / 火焰
- 5 种粒子形状:圆点 / 爱心 / 星形 / 人民币 ¥ / 美元 $——任意风格都能切换形状
- 3 种配色:多彩(一次迸发共用一个色系)/ 彩虹(一次按键多种颜色)/ 固定色(可自选)
- 粒子发光:每颗粒子都套一层同色光晕,看起来「发光」而不是硬边色块
- 连击加成:连续打字时粒子数递增,封顶回落;空闲 1 秒归零
- 光标移动也触发:方向键 / 翻页挪光标时刷新连击;鼠标点击在小簇粒子反馈
- 跟住插入点:纯 AX 多层降级,能拿到插入点就画、拿不到就什么都不画(而不是错位)
- 纯 AX 方案:不读屏幕、不需要屏幕录制授权,只依赖「辅助功能」一项权限
- 不遮字:爱心以空心
♡为主,心形内部是透的;粒子不占排版位置 - 偏好自动保存:菜单里改一次就记住,重启不变;附赠「开机自启」开关(macOS 13+)
- 空闲零开销:按键触发、非轮询,粒子散尽后帧循环自停
菜单栏图标就是状态指示:
| 图标 | 含义 |
|---|---|
| 彩色爆裂图标(正常) | 已开启 |
| 同一个图标、35% 透明度 | 已关闭 |
| 感叹号三角 | 缺少「辅助功能」授权,特效不会触发 |
| 短暂出现的「✓」 | 用户刚授权成功,0.8 秒后自动恢复 |
点图标打开菜单:
- 开启打字特效 —— 总开关
- 特效风格 / 粒子形状 / 配色 —— 三个子菜单
- 辅助功能权限 —— 带勾选状态:已授权显示 ✓ 与「已授权」,未授权显示「未授权,点此开启」点一下跳转系统设置
- 重新检测权限 —— 系统设置里改完开关后点一下,让 App 立刻重试
- 版本 x.y.z(构建 N) —— 点开看版本、二进制路径、构建时间与授权状态
- 退出
没有预编译产物,从源码构建即可(需要 macOS 12+ 与 Xcode 命令行工具):
git clone <repo> && cd vscode-powermode-mac
./build.sh # 构建到 build/PowerMode.app
./build.sh --run # 构建后直接启动
./build.sh --dmg # 额外打包成 dmg也可以把 build/PowerMode.app 拖进「应用程序」目录。开机自启请自行到
系统设置 → 通用 → 登录项 里添加(App 不替你做这个决定)。
只需要一项授权:
| 权限 | 用途 | 缺少时 |
|---|---|---|
| 辅助功能 | 全局监听按键 + 读取文本插入点 | 完全没效果 |
重新编译之后必须重新授权。 App 用的是 ad-hoc 临时签名,编译一次签名就变了:系统设置里的
条目还在、开关也还亮着,但授权已经对不上号。这时菜单栏图标会变成感叹号,日志里也会写
权限不足:特效不会生效。处置办法是在列表里把它删掉再重新添加——只把开关关掉再打开
往往不生效,因为条目里存的还是旧签名。
调试期想省掉这一步,可以直接跑二进制,它会继承终端已有的授权:
build/PowerMode.app/Contents/MacOS/PowerMode| 风格 | 数值 | 手感 |
|---|---|---|
| 粒子 | 5 |
POWERMODE 原版手感,数值一一对应 |
| 烟花 | 26 |
外加 3 颗贴在插入符上的爆闪 |
| 火焰 | 10 |
向上喷射,轻微反重力 |
可以独立于特效风格选择,每种形状都对应一个粒子形态:
- 圆点 —— 经典圆点,最朴素
- 爱心 —— 全部爱心;尺寸不足 6px 的粒子会被抬到 6~8px(保留随机)
- 星形 —— 五角星贝塞尔路径,顶点朝上
- 人民币 ¥ —— 用系统字体字形轮廓绘制
- 美元 $ —— 用系统字体字形轮廓绘制
符号形状(爱心 / 星形 / ¥ / $)的尺寸都会自动抬到 6px 以上,否则小到一定程度就认不出形状。
6px 是实测的下限:再小一档,空心 ♡ 的笔画就不到 1px 了。
为什么不挡字:爱心用系统字体的 ♡ / ♥ 字形画。空心 ♡ 占七成、实心 ♥ 三成,
而实测 ♡ 的墨量只占中心区域的 9.8%,♥ 是 32.2%——心形内部是透的,压在文字上
文字仍然读得清。粒子尺寸取整量化,同一档尺寸与实心标记共用一份字形轮廓路径缓存。
- 多彩(默认)—— 每次按键随机一个基础色相,同一簇粒子共用这个色系、只在 ±10° 内微扰、
亮度在 50%~80% 浮动。观感是「一朵同色系的花火」,而不是一把打翻的调色盘。
这就是 POWERMODE 原版
colorful的写法(hsla(c(t−10,t+10), 100%, c(50,80)%, 1))。 - 彩虹 —— 每颗粒子独立随机一个色相,一次按键迸发出五颜六色的一簇,亮度 55%~75%。 观感像一把彩虹糖。和「多彩」的区别在于:多彩是「一簇一个色系」的聚, 彩虹是「每颗一个色相」的散。
- 固定色 —— 整簇用同一个颜色(默认
#ffcc00),可在菜单的色板里挑,也可以点「自定义颜色…」 打开 NS Color 拾色器。
原版 VS Code 扩展还有一个「跟随代码语法高亮取色」,macOS 版不做:菜单栏 App 不在编辑器里 跑,拿不到 token 颜色,硬做只能靠猜。
只通过系统无障碍(AX)通道。取不到插入点的 App 不显示特效(而不是错位画到鼠标上):
- AX 精确插入点(多层降级):系统级焦点元素 → App 级焦点元素 → 鼠标命中测试 →
从焦点窗口爬树找
AXFocused文本框。对 Chromium / Electron 会先设置AXEnhancedUser界面/AXManualAccessibility把无障碍树开出来。 - AX 近似值:只能保证落在文本框里。
曾经有过一条「像素取点」链路(读屏幕、按插入符的闪烁差分找光标),用来适配微信这类 完全没有文字无障碍通道的自绘 UI App。实测体验很差——输入法候选框上屏、选字、以及自绘 光标闪烁灰度差太小,都会让「读屏找光标」既不稳、又需要屏幕录制授权。已整体移除: 拿不到插入点的 App 里什么都不显示——宁可没有特效,也不给一个错位位置。
空闲为 0:按键触发、非轮询,粒子散尽后帧循环自停。取点只走 AX,从不截屏。
装上没反应? 看菜单栏图标是不是感叹号——那是缺「辅助功能」授权。点「重新检测权限」。 重新编译过的话,必须把系统设置里的条目删掉再加回来。
授权成功后是否有提示? 菜单栏图标会短暂变成 ✓ 大概 0.8 秒,让你知道这一步成了。
微信、企业微信、QQ、钉钉、飞书里没有特效? 这些 App 不向系统提供文本插入点通道, 本 App 读不到光标位置,于是不显示特效(而非错位)。这是有意为之的取舍—— 不打算用「读屏幕」的方式去硬猜,那样既需要屏幕录制授权、体验又不稳。
日志在哪? ~/Library/Logs/PowerMode.log(运行与取点来源)。
开发、调试与打包流程见 CONTRIBUTING.md。
A purely decorative macOS menu bar app (an LSUIElement accessory — no Dock icon, not in Cmd-Tab).
Every keystroke — including backspace — bursts a small cluster of particles at the current
text insertion point. The default is colourful particles and hearts, with a new colour
family on every keystroke.
What sets it apart from similar tools is where the particles land: not at the mouse, but genuinely tracking the text caret. On macOS that is far harder than it sounds — see "How the caret is located" below.
- 3 presets: particles / fireworks / flames
- 5 shapes: dot / heart / star / ¥ / $ — switch independently of the preset
- 3 colour modes: colourful (one hue family per burst) / rainbow (many colours per burst) / fixed colour
- Glowing particles: every particle gets a soft same-colour halo, so it "glows" instead of looking like a hard-edged chip
- Combo bonus: particle count grows as you type continuously, capped then reset; idle for 1s resets it
- Caret movement feedback: arrow / page keys refresh the combo; mouse clicks fire a small cluster
- Caret tracking: pure AX. If the caret is reachable the effect follows it; if not, nothing fires
- Pure AX: no screen capture, no Screen Recording permission — Accessibility only
- Never obscures text: hearts are mostly hollow
♡, so the inside of each heart is transparent - Settings persist: change it once in the menu, it sticks across launches; plus a "launch at login" toggle (macOS 13+)
- Zero idle cost: event-driven, not polling; the frame loop stops when particles die out
The menu bar icon doubles as a status indicator:
| Icon | Meaning |
|---|---|
| Colourful burst icon | Enabled |
| Same icon at 35% opacity | Disabled |
| Exclamation triangle | Accessibility permission missing — effects will not fire |
| Brief ✓ | Just authorised, auto-reverts after 0.8s |
The menu contains: the master toggle, submenus for preset / shape / colour, an Accessibility permission item with a live checkmark reflecting the current status, "recheck permissions", a version line (click it for build info and live permission status), and Quit.
There is no prebuilt binary — build from source (macOS 12+ and Xcode command line tools):
git clone <repo> && cd vscode-powermode-mac
./build.sh # builds build/PowerMode.app
./build.sh --run # build and launch
./build.sh --dmg # also produce a dmgFor launch at login, add the app manually under System Settings → General → Login Items.
Only one permission is needed:
| Permission | Used for | When missing |
|---|---|---|
| Accessibility | Global key monitoring + reading the text caret | No effects at all |
Re-authorise after every rebuild. The app is ad-hoc signed, so its signature changes on each
compile: the entry in System Settings is still there and the switch is still on, but it no longer
matches. The menu bar icon turns into an exclamation triangle and the log says
权限不足:特效不会生效. Remove the entry and add it back — simply toggling the switch
usually does not work, because the entry still stores the old signature.
While developing you can skip this by running the binary directly, which inherits the terminal's existing authorisation:
build/PowerMode.app/Contents/MacOS/PowerMode| Preset | Numbers | Feel |
|---|---|---|
| Particles | 5–15 / 2–3px / vy −3.5–−1.5 / g 0.075 | Matches POWERMODE's original feel exactly |
| Fireworks | 26–52 / 2–4px, radial | Plus 3 flash particles pinned to the caret |
| Flames | 10–22 / 2–5px / g −0.02 | Shooting upward, slight anti-gravity |
Picked independently of the preset:
- Dot — plain circles
- Heart — all hearts; particles below 6px are lifted to 6–8px (with randomness preserved)
- Star — five-pointed star drawn from a Bezier path
- ¥ — Chinese yuan, drawn from the system glyph outline
- $ — US dollar, drawn from the system glyph outline
Glyph shapes (heart / star / ¥ / $) are all lifted to 6px and above; below that they stop
looking like their shape. 6px is the measured floor: one step smaller and the stroke of a
hollow ♡ drops below 1px.
Why it never obscures text: hearts are drawn from the system font's ♡ / ♥ glyphs.
Hollow ♡ makes up 70% and solid ♥ 30%, and hollow ♡ covers only 9.8% of its centre area
versus 32.2% for ♥ — the inside of each heart is transparent, so text underneath stays
readable. Glyph outline paths are cached per (shape, size) bucket.
- Colourful (default) — one random base hue per keystroke, perturbed within ±10°, lightness
50–80%. You get "a firework in one colour family" rather than a spilled paint box. This is
POWERMODE's original
colorfulformula:hsla(c(t−10,t+10), 100%, c(50,80)%, 1). - Rainbow — each particle gets its own random hue, so a single keystroke bursts into a multicoloured cluster (lightness 55–75%). Unlike colourful — which is one hue family "together" — rainbow is one hue per particle "spread out", like a handful of Skittles.
- Fixed colour — the whole cluster uses one colour (default
#ffcc00). Pick from the swatch in the menu or choose "Custom Colour…" to open the NSColor picker.
The original VS Code extension also had "follow syntax highlighting", which this app deliberately does not: a menu bar app runs outside the editor and cannot get token colours.
Accessibility (AX) only. If the caret cannot be reached, nothing fires — never the mouse:
- Exact AX caret (with layered fallbacks): system-wide focused element → app-level focused
element → hit-test at the mouse → crawl the focused window for an
AXFocusedtext control. For Chromium / Electron apps the tree is switched on first viaAXEnhancedUserInterface/AXManualAccessibility. - AX approximation: only guarantees the hit is inside the text control.
There was once a "pixel caret" channel that read the screen and located the caret by its blink, for self-drawn UIs with no text AX channel (WeChat and friends). In practice it was a poor experience — IME candidate windows, character composition, and the tiny blink contrast of self-drawn carets all made it unstable, and it required Screen Recording permission. It has been removed entirely: in apps that expose no caret, nothing fires. That is the deliberate price of keeping the design simple and consistent.
Idle cost is zero: event-driven, not polling, and the frame loop stops when particles die. Caret resolution is AX-only — the screen is never captured.
Nothing happens. Check whether the menu bar icon is an exclamation triangle — that means the Accessibility permission is missing. Click "recheck permissions". If you rebuilt the app, remove the entry in System Settings and add it back.
How do I know the authorisation worked? The menu bar icon briefly turns into a ✓ for about 0.8 seconds when authorisation is granted, then reverts to its normal state.
No effect in WeChat / WeCom / QQ / DingTalk / Lark. These apps do not expose a text caret to the system, so the app cannot read the cursor position and displays nothing. This is deliberate — rather than reading the screen (which needs Screen Recording and is unreliable), the app keeps things simple and consistent.
Where are the logs? ~/Library/Logs/PowerMode.log (runtime + caret source).
See CONTRIBUTING.md.