上岸的鱼

心中有光,便可使整个世界升起太阳

Aether - 开启 macOS 的空间交互时代 / Entering the Era of Spatial Interaction for macOS

Aether Interaction

中文版本 | English Version

[CN] 🚀 开发者自白:当传感器遇上交互灵感

最初写 Aether,其实是因为我戴着 AirPods Pro 在写代码时产生的一个脑洞:既然耳机里塞满了加速计和陀螺仪,既然系统已经能追踪我的空间音频,为什么不能用它们来做点更酷的事?

我想探索一种更具“未来感”的交互——离开键盘和鼠标,也能用 Mac。这不仅仅是为了炫技,更是为了在我的双手正忙(或是单纯想换个姿态)时,能用一种更自然、更“空间化”的方式与设备交流。

这是一款极客味很重的作品,也是我对“下一代交互”的一次小规模实验。

Aether 是一款挖掘 macOS 隐藏交互潜力的工具。它让你用声音完成输入和常见电脑操作,不用每一个小动作都再回到键盘和鼠标。这不只是针对 AirPods 用户的增强,更是为了实现 “离开键盘鼠标,也能用 Mac” 的完整愿景。


✨ 核心逻辑与功能

1. 听写输入 (Dictation)

回消息、改名字、写笔记。最直接的效率提升:说出来,它就出现在你要的地方。建议开启“按住说话 (Push to Talk)”来获得最稳定的体验。

2. 语音控制 (Voice Control)

点击、右键、双击、撤销、滚动。把那些总把你拉回触控板的小动作交给 Aether。它让键盘鼠标变成可选,而不是必须。

3. 头控指针 (Head Pointer)

利用 AirPods Pro / Max 的传感器,像移动头部一样自然地控制光标。结合姿态引导和重新校准,提供了丝滑的指向体验。


🛠️ 这样开始最稳妥

由于空间感知交互需要一定的适应期,我们建议按照以下顺序循序渐进:

  1. 先安装,再授权:先把 Aether 移到 /Applications,再授予权限。这样系统会记住正确路径。
  2. 先试语音输入:确认听写流程稳定,输入框能正常出字。
  3. 再试语音控制:尝试简单的点击、滚动命令。
  4. 最后开头控指针:头控是补充层,不是先决条件,放在最后开启最轻松。

更多信息请访问官网:aether.7caifei.com


[EN] 🚀 Project Introduction

If you are wearing AirPods, why move your hands back and forth between the keyboard and trackpad just to switch music, flip documents, or control the cursor?

Aether is a tool that unlocks the hidden interaction potential of macOS. It allows you to use your voice for input and common Mac actions, eliminating the need to revert to the keyboard and mouse for every small task. This is more than just an enhancement for AirPods users—it’s a step toward the vision of “Using Mac without keyboard & mouse.”


✨ Core Features

1. Voice Input (Dictation)

Reply to messages, rename files, and take notes. The most direct efficiency boost: say it, and it appears where you need it. We recommend starting with “Push to Talk” for the most stable experience.

2. Voice Control

Clicks, right-clicks, double-clicks, undo, scroll. Offload the small actions that keep pulling you back to the trackpad. It makes the keyboard and mouse optional, not required.

3. Head Pointer

Utilizing sensors in AirPods Pro / Max to control the cursor as naturally as moving your head. Combined with posture guides and re-centering, it provides a smooth pointing experience.


🛠️ Getting Started the Right Way

As spatial interaction requires an adaptation period, we suggest following this sequence:

  1. Install First, Then Grant Permissions: Move Aether into /Applications before granting permissions. This ensures macOS binds accessibility, microphone, and speech permissions to the final path.
  2. Start with Voice Input: Confirm the dictation workflow is stable and text appears correctly in input fields.
  3. Try Voice Control Next: Test simple commands like click and scroll.
  4. Turn on Head Tracking Last: Head tracking is an optional layer, not a prerequisite. Enabling it last makes the transition much smoother.

🔒 Privacy & Local Logic

Aether is designed to keep interaction logic as local to the Mac as possible. Your dictation text and documents are not uploaded to our servers as part of normal use.


For more information, visit the official website: aether.7caifei.com

Aion (灵汐) - 重新定义 macOS 后台自动化 / Redefining macOS Background Automation

Aion Automation

Aion Logo

中文版本 | English Version

[CN] 🚀 开发者自白:为什么有了灵汐 (Aion)?

作为一名重度 Mac 用户,我一直对“自动清理”类工具又爱又恨。大多数工具在后台杀进程时都太“暴力”了:下载到一半的应用、正在开会的 Zoom,甚至是在后台跑着 AI 任务的浏览器,常常因为几分钟没被点过就被强行收割。

我想要的是一个能感知上下文、能跟我建立信任感的助手。它应该像一个懂行的管家:当你真在忙正事时(哪怕应用在后台),它保持安静;只有当一个应用彻底变成“僵尸”状态时,它才出面收拾。

这就是 Aion (灵汐) 的由来——它不仅是清理工具,更是一场关于“自动化中‘信任感’边界”的编程实践。

核心理念

信任感是自动化的基石。 Aion 的核心目标不是多关几个后台,而是让你放心地把“关闭后台”这个决定权交给它,而不用担心它会在你下载 99% 时突然断掉你的浏览器。


✨ 核心功能详解

🎯 智能感知与守护

1. Smart Guards (智能护卫)

灵汐默认是偏保守的:会先判断应用当前在做什么,再决定是否退出。

  • 自动保护正在下载的应用:网络活动会让应用处于保护状态,而非过早退出。
  • 自动保护音乐与视频播放:正在播放媒体的应用受保护。
  • 自动保护麦克风和摄像头占用:你正在开会、通话或录音时,相关应用受保护。

2. AI App Protection (AI 应用保护)

当本地 AI / Agent 工作流正在使用某个应用时,灵汐可以把它保护起来,而不是把它当成普通后台闲置应用来清理。

📊 信任链路建设

1. 活动记录 (Activity Log)

这是 Aion 最近几个版本中最重要的更新。它公开了自动化的“黑盒”,你可以清晰地查看到:刚才哪个应用被尝试关闭了,以及为什么它被保留了下来。

Activity Log

2. 瞬时任务 (Instant Task)

针对计算器、词典等小工具,灵汐可以让这类短时工具用完即走,不留痕迹。

为什么灵汐比纯计时型工具更稳妥?

很多工具只看“闲置了多久”,灵汐在决定前还会判断“它还在做正事吗”:

  • 浏览器下载:超时后计时型工具仍可能清理,灵汐则会识别下载活动。
  • 系统清理:灵汐可以自动清理残留安装盘 (DMG) 和关闭无窗口的 Dock 应用。

更多信息请访问官网:aion.7caifei.com


[EN] 🚀 Project Introduction

On macOS, we often face a dilemma: to keep the system smooth, we want to clear redundant background processes; but to avoid interrupting the workflow, we fear that tools might “mistakenly kill” critical tasks running in the background (such as large file downloads, video rendering, or meetings).

Aion is a background automation assistant designed specifically to solve this pain point. It no longer simply relies on “idle time” to decide whether to close an app, but instead judges whether an app is “truly idle” through a deep context-aware system.

Core Philosophy

Trust is the foundation of automation. Aion’s core goal is not just to close more background apps, but to let you confidently hand over the decision of “closing background apps” to it, without worrying that it will suddenly cut off your browser while your download is at 99%.


✨ Core Features

🎯 Smart Guards & Context-Awareness

Aion is conservative by design. It checks what the app is doing before it quits anything.

  • Protects Active Downloads: Network activity keeps the app protected instead of being quit too early.
  • Protects Music and Video Playback: Apps currently playing media are protected.
  • Protects Mic and Camera Activity: If you are on a call or recording, the relevant app stays protected.

2. AI App Protection

Aion can protect an app while a local AI or agent workflow is actively using it, instead of treating that app like ordinary background idle time.

📊 Building the Trust Loop

1. Activity Log

This is the most important update in recent versions. It opens up the “black box” of automation, allowing you to clearly see: which app was attempted to be closed just now, and why it was retained.

2. Instant Task Mode

For small utilities like calculators or dictionaries, Instant Task mode lets short-lived apps disappear right after use.

Why Aion feels safer than timer-based tools?

Most tools only look at “how long it has been idle”. Aion looks at “whether it is still doing productive work” before deciding:

  • Browser Downloads: Timer-based tools might still clean them up after expiration; Aion recognizes the download activity.
  • System Cleanup: Aion can automatically clean up leftover installer DMGs and close Dock apps with no windows.

🌟 Creator’s Note

A background tool itself should not become a new background burden.

We’ve optimized Aion’s resource usage to ensure it remains nearly invisible:

  • Visibility Driven: Interface logic only refreshes when the menu bar or HUD is visible.
  • Minimal Resource Footprint: All judgments are completed locally on your Mac, ensuring privacy and performance.

For more information, visit the official website: aion.7caifei.com

本文包含中文与英文两个版本。
This post includes both Chinese and English versions.

中文版

现象

这次遇到的问题是:Antigravity 聊天框里消息可以发出去,但一直没有回复。

最开始最显眼的报错是:

1
2
3
4
5
[Error] Server initialization failed.
Message: Pending response rejected since connection got disposed

[Error] antigravity client: couldn't create connection to server.
Error: connect ECONNREFUSED 127.0.0.1:60557

另外还有一条容易让人分心的错误:

1
Error generating commit message: [unknown] core.repositoryformatversion does not support extension: worktreeconfig

但排查到最后,这两类信息都不是根因。

先排掉假线索

第一条假线索是 worktreeconfig。
它确实会影响 Git 相关能力,比如自动生成 commit message,但它和聊天框“不回复”不是一条链路。

第二条假线索是 ECONNREFUSED。
继续追日志后会发现,language server 后面其实已经成功拉起:

1
2
3
4
LS lspClient started successfully
Language server started
loadCodeAssist
fetchAvailableModels

所以端口拒绝连接更像是启动阶段的一次短暂失败,而不是最后把聊天功能彻底卡死的根因。

真正的问题在 renderer

真正关键的报错出现在 renderer 日志里:

1
[createInstance] ... depends on UNKNOWN service agentSessions

同时还能看到类似提示:

1
command 'workbench.action.chat.newChat' not found

这说明问题已经不只是 language server,而是聊天工作台本身依赖的会话服务没有被正常注册。
也就是说,输入框虽然还在,但聊天的前端主链路已经坏了。

为什么重装当前版本没用

我后面做了一个关键对比:不仅看本机 /Applications/Antigravity.app,还去下载并比对了当时官方 releases 提供的当前版本安装包。

结论是:

  1. 本机当前版本和官方当前版本是一致的。
  2. 所以不是“这台机器装坏了”。
  3. 至少在这次命中的链路里,当时的当前官方包本身就有问题。

也正因为如此,单纯删掉再重装最新版没有意义。

为什么没有直接热补丁 App 包

排查过程中,我一度已经定位到工作台主包里可疑的注册缺失点,也做过本地补丁尝试。

但在 macOS 上很快就会撞到系统安全策略:

1
2
Security policy would not allow process
The file is adhoc signed or signed by an unknown certificate chain

意思很直接:

  1. 技术上能改。
  2. 但改完的包不适合长期稳定运行。
  3. 因为只要动了已签名 App 的资源,后面就会遇到签名链、AMFI 或系统策略的问题。

所以“直接热补丁现有安装包”不是一个稳妥方案。

最后可用的修法

最后真正可用的修法不是继续硬修当前版本,而是:

  1. 换成一份官方签名的旧版 Antigravity 1.20.6。
  2. 把 language server 路径一起切到这份旧版自带的二进制。
  3. 关闭自动更新,避免它再次跳回坏版本。

关键配置在:

1
$HOME/Library/Application Support/Antigravity/User/settings.json

核心字段如下:

1
2
3
4
5
{
"codeiumDev.languageServerBinaryPath": "$HOME/Applications/Antigravity 1.20.6.app/Contents/Resources/app/extensions/antigravity/bin/language_server_macos_arm",
"antigravity.persistentLanguageServer": false,
"update.mode": "none"
}

其中:

  • codeiumDev.languageServerBinaryPath:强制指向旧版 1.20.6 自带的 language server。
  • antigravity.persistentLanguageServer: false:避免继续黏住旧的异常状态。
  • update.mode: none:防止自动更新把运行链路再次切回坏版本。

这条路更稳的原因在于:它仍然使用的是官方签名包,没有继续破坏应用完整性。

这次排查最值得记住的几点

  1. 不要被第一条报错带偏。
  2. 要先区分“噪音错误”和“致命错误”。
  3. 不要默认“重装最新版”一定有用。
  4. 在 macOS 上,热补丁已签名 App 通常不是长期修法。
  5. 如果问题确实出在某个发布版本里,官方签名旧版 + 锁版本 往往比自行魔改更现实。

结语

这次问题最难的地方,不是没有日志,而是日志太多,而且有不少误导项。

表面看像 language server,像 Git,像本地缓存,最后真正影响聊天恢复的,却是工作台聊天链路和版本选择。

最后真正把它拉回正常的,不是继续修当前安装,而是:

回退到官方签名的 1.20.6,切换 language server 路径,并锁住更新。


English Version

Symptom

The problem looked simple at first: messages could be sent in Antigravity chat, but no reply ever came back.

The first errors that stood out were:

1
2
3
4
5
[Error] Server initialization failed.
Message: Pending response rejected since connection got disposed

[Error] antigravity client: couldn't create connection to server.
Error: connect ECONNREFUSED 127.0.0.1:60557

There was also another distracting error:

1
Error generating commit message: [unknown] core.repositoryformatversion does not support extension: worktreeconfig

In the end, neither of them turned out to be the real root cause.

Removing the false leads first

The worktreeconfig error was the first false lead.
It can affect Git-related features such as commit message generation, but it is not on the same path as “chat sends but never replies”.

The second false lead was ECONNREFUSED.
Once I kept reading the logs, it became clear that the language server was eventually starting successfully:

1
2
3
4
LS lspClient started successfully
Language server started
loadCodeAssist
fetchAvailableModels

So the refused localhost port looked more like a startup race or transient failure, not the final blocker.

The real failure was in the renderer

The truly important error showed up in the renderer log:

1
[createInstance] ... depends on UNKNOWN service agentSessions

There was also a related message like:

1
command 'workbench.action.chat.newChat' not found

That changes the diagnosis completely.
At that point, the issue is no longer just the language server. It means the chat workbench itself is missing a required session service registration.

In other words, the input box may still be visible, but the front-end chat pipeline is already broken.

Why reinstalling the current version did not help

I compared not only the local /Applications/Antigravity.app, but also the then-current official installer downloaded from the official releases feed.

The result was:

  1. The local current version matched the official current version.
  2. So this was not simply “a corrupted install on this Mac”.
  3. On the code path I hit during this debugging session, the current official build itself appeared to be problematic.

That is why deleting and reinstalling the latest version did not meaningfully help.

Why I did not hot-patch the app bundle

At one point I had already narrowed the issue down to a suspicious missing registration in the workbench bundle and even tried building a local patch.

On macOS, that quickly runs into system policy:

1
2
Security policy would not allow process
The file is adhoc signed or signed by an unknown certificate chain

That means:

  1. You can patch it technically.
  2. But the patched app is not a good long-term runtime target.
  3. Once you modify resources inside a signed app, you start fighting code signing, AMFI, and system policy.

So “hot-patching the installed app” is not a reliable long-term fix.

The fix that actually worked

The working solution was not to keep patching the broken current version, but to:

  1. Switch to an officially signed older build, Antigravity 1.20.6.
  2. Repoint the language server path to the binary shipped inside that build.
  3. Disable auto-update so it would not jump back to the broken version.

The key configuration lives in:

1
$HOME/Library/Application Support/Antigravity/User/settings.json

The important fields were:

1
2
3
4
5
{
"codeiumDev.languageServerBinaryPath": "$HOME/Applications/Antigravity 1.20.6.app/Contents/Resources/app/extensions/antigravity/bin/language_server_macos_arm",
"antigravity.persistentLanguageServer": false,
"update.mode": "none"
}

Why this is safer:

  • It keeps using an officially signed build.
  • It avoids breaking the integrity of the installed app bundle.
  • It pins the runtime to a version that still behaves normally.

The most useful lessons from this debugging session

  1. Do not let the first visible error dictate the whole investigation.
  2. Separate noisy errors from fatal ones.
  3. Do not assume reinstalling the latest build will always help.
  4. On macOS, patching a signed app is rarely the best long-term answer.
  5. If a release itself is the problem, an officially signed older version plus update pinning is often much more practical.

Closing note

The hardest part of this issue was not the lack of logs.
It was the opposite: there were too many logs, and several of them were misleading.

At first glance it looked like a language server problem, or a Git problem, or a cache problem.
In the end, the real issue was the chat workbench path itself and the version being used.

What finally brought the app back to a usable state was:

rolling back to the officially signed 1.20.6 build, repointing the language server path, and locking updates.

UE5 插件开发:C++ Tooltip 本地化失效的避坑指南

在进行 UE 插件开发时,我们通常会为 UPROPERTY 编写注释,期望这些注释能作为 Tooltip 在编辑器中显示,并支持多语言本地化。但在实际操作中,经常会遇到翻译失效的问题。

问题现象

当你完成了以下步骤:

  1. 代码注释: 编写了 /** My Tooltip with \n Newline */。
  2. 文本收集: 成功 Gather 到了对应的 SourceString。
  3. 完成翻译: 在 PO 文件中填好了对应的中文并编译为 LocRes。

却发现编辑器里悬停显示的依然是旧的英文,甚至当你删除了代码中的注释,编辑器里依然顽固地显示着之前的旧文本。

这种情况通常不是缓存的问题,而是掉进了 UE 本地化中最隐蔽的陷阱:UHT 元数据持久化 (Metadata Persistence)。


原因分析

UE 的 Tooltip 并非在运行时直接读取代码注释,其真实流程如下:

  1. 编译期 (UHT): Unreal Header Tool 解析 .h 文件。它读取注释并将其转换为一个字符串常量,作为 MetaData 烧录进生成的 .gen.cpp 文件中,最终编译进二进制。
  2. 运行期 (Editor): 编辑器通过反射系统 (FProperty::GetToolTipText) 获取这个硬编码在二进制里的 SourceString。
  3. 查字典: 编辑器拿着这个 SourceString 去 LocRes 里寻找匹配的翻译。

核心症结:SourceString 的不确定性

UHT 在处理注释时非常不透明:

  • 它可能会将 /** A \n B */ 解析为 "A\nB",也可能解析为 "A B"。
  • 解析结果受操作系统换行符(CRLF/LF)和缩进的影响。

导致失效的典型场景:

  1. 最初写的注释生成的 Key A 被烧录进了二进制。
  2. 你发现翻译不对,修改了注释,UHT 生成了 Key B。
  3. 由于增量编译或构建缓存的原因,二进制文件没有彻底更新,或者 UHT 认为该 .h 无需重新处理。
  4. 结果是:LocRes 里存的是 Key B 的翻译,但二进制里残留的依然是 Key A。 由于 SourceString 不匹配,翻译永远无法生效。

解决方案:显式元数据 (Explicit Meta)

与其去猜测 UHT 会如何解析注释,不如直接接管这个过程。

最佳实践:对于必须本地化的编辑器属性,不要依赖注释,直接使用显式元数据。

修改方式

1
2
3
4
5
6
7
8
9
10
11
// ❌ 依赖注释(不推荐)
/**
* The Enhanced Input Action.
* Triggers the attack.
*/
UPROPERTY(EditAnywhere)
UInputAction* InputAction;

// ✅ 显式覆盖(推荐)
UPROPERTY(EditAnywhere, meta=(ToolTip="The Enhanced Input Action. Triggers the attack."))
UInputAction* InputAction;

为什么这样做有效?

  1. 跳过不确定解析: meta=(ToolTip="...") 是直接赋值。UHT 不会对其进行任何“智能化”处理,你写什么,二进制里就是什么。
  2. 跨平台一致性: 无论什么操作系统或 IDE,这个字符串在二进制层面上是绝对一致的。
  3. 强制 UHT 更新: 修改 meta 会明确告知 UHT 文件已变更,从而确保重新编译并刷新二进制中的元数据。

结语

如果你遇到了“代码改了、翻译更新了,但编辑器死活不认”的本地化难题,请尝试以下操作:

  1. 放弃依赖注释: 在对应的 UPROPERTY 中添加 meta=(ToolTip="你的文本")。
  2. 手动强制更新: 如果可能,清理并重新编译项目,确保二进制中的 SourceString 被刷新。
  3. 重新收集文本: 运行一次新的 Gather Text 流程。

这通常是解决 UE Tooltip 本地化顽疾最稳妥、最高效的方法。

0%