- csproj 静态链接 Skia/HarfBuzz/ANGLE(CoreUtils.*.Static),AOT 产出真单文件 - 发布后清理 .dll/.lib/.pdb,只留 notify.exe - bin/notify.cmd 与 notify.sh:首次运行从 Release 下载 notify.exe 原子下载(临时文件+改名)+ mkdir 锁,并发不重复下载,带陈旧锁恢复 - 修复 cmd 引导脚本管道 stdin 丢失:避免调 exe 前向前 goto,改用 call 子程序 - hooks.json 指向 notify.cmd,超时放宽以容纳首次下载 - .gitignore 放行两个脚本但忽略 notify.exe;.gitattributes 固定脚本换行符
0de6fe4064
·
2026-06-22 17:35:57 +08:00
10 Commits
Claude Code Notify (.NET / Avalonia)
为 Claude Code 提供原生 Windows 通知:Claude 干完活或需要你输入时,在屏幕角落弹出 toast,点击即可跳回原终端/编辑器窗口(并能切回正确的 Windows Terminal 标签页)。
这是原版 Rust 项目的 C# / .NET 10 + Avalonia 12 重写版。整体是一个瘦客户端 CLI + 常驻 Host 的进程模型,由 Claude Code 的 hook 驱动。
功能
| 功能 | 说明 |
|---|---|
| 原生 toast | 任务完成 / 需要输入时弹出,深色圆角、图标 + 标题 + 正文 + 关闭键 |
| 一键返回 | 左键点击 toast → 抢前台激活当初发起请求的窗口 |
| 标签页感知 | 目标是 Windows Terminal 时,激活后切回提问时所在的标签 |
| 调用方图标 | 沿进程树上溯识别 VSCode / Cursor / JetBrains / Windows Terminal 等,显示其图标 |
| 跨虚拟桌面 | 可选把通知钉到所有虚拟桌面(Win+Tab 切桌面也能看到) |
| 堆叠 / 排队 | 多条通知按方向堆叠,超出上限的排队 |
| 常驻 / 聚焦短显 | 输入类常驻不消失;完成类自动消失,且你正盯着目标窗口时只弹更短时间 |
| 工具窗口 | 不进任务栏、不进 Alt+Tab |
| 提示音 | 弹出时可选播放提示音(带防连环音) |
| 非阻塞投递 | 钩子毫秒级返回,绝不拖慢 Claude Code |
| 自由定位 | 水平(左/中/右) × 垂直(上/中/下) + 边缘留白,任意组合 |
架构一览
flowchart LR
CC[Claude Code] -->|UserPromptSubmit| SAVE["notify save"]
CC -->|Stop| NOTIFY["notify notify"]
CC -->|Notification / PreToolUse| INPUT["notify input"]
CC -->|SessionEnd| CLEAN["notify cleanup"]
SAVE --> ST[(状态文件<br/>%TEMP%/claude-notify-id.json)]
CLEAN -.删除.-> ST
NOTIFY --> SP[(spool 队列<br/>%TEMP%/claude-notify-spool)]
INPUT --> SP
SP --> HOST
ST -.读取.-> NOTIFY
ST -.读取.-> INPUT
subgraph HOST["notify host (Avalonia 常驻)"]
W[FileSystemWatcher] --> T1[Toast]
W --> T2[Toast]
end
T1 -->|点击| ACT[激活窗口 + 切 WT 标签]
- CLI 子命令(
save/notify/input/cleanup):纯 Win32 互操作 + 落盘,不加载 Avalonia,做完即退。 - Host(无参 /
host):Avalonia 单例常驻,仅托盘存在,监视 spool 目录弹通知。 - 详细时序见 docs/sequences.md。
安装与使用
当前用框架依赖型构建(依赖已安装的 .NET 10 运行时),AOT 单文件发布见 docs/build-and-install.md。
1. 构建
cd Notify
dotnet build -c Release
# 产物: Notify/bin/Release/net10.0-windows/notify.exe (含同目录依赖 DLL)
2A. 直连 settings.json(推荐,最省事)
把 exe 所在文件夹加入 PATH 后,往 ~/.claude/settings.json 的 hooks 里加:
{
"hooks": {
"UserPromptSubmit": [{ "matcher": "", "hooks": [{ "type": "command", "command": "notify save", "timeout": 5 }] }],
"Stop": [{ "matcher": "", "hooks": [{ "type": "command", "command": "notify notify", "timeout": 10 }] }],
"Notification": [{ "matcher": "", "hooks": [{ "type": "command", "command": "notify input", "timeout": 10 }] }],
"PreToolUse": [{ "matcher": "AskUserQuestion|ExitPlanMode", "hooks": [{ "type": "command", "command": "notify input", "timeout": 10 }] }],
"SessionEnd": [{ "matcher": "", "hooks": [{ "type": "command", "command": "notify cleanup", "timeout": 5 }] }]
}
}
不加 PATH 就把 notify 换成 exe 的绝对路径(用正斜杠)。配完重启 Claude Code。
2B. 作为插件安装(marketplace)
仓库已含合规的 .claude-plugin/ 与 hooks/hooks.json(命令指向 ${CLAUDE_PLUGIN_ROOT}/bin/notify.exe)。把构建产物放进插件根的 bin/ 后:
claude plugin marketplace add <仓库本地路径或 GitHub repo>
claude plugin install claude-code-notify@claude-code-notify
设置
- 托盘左键单击 → 打开设置窗口;右键 → 退出。
- 配置持久化到
%APPDATA%\ClaudeCodeNotify\settings.json。
| 项 | 默认 | 说明 |
|---|---|---|
| 停留时长(秒) | 4 | 完成类自动消失前的停留 |
| 聚焦时停留(秒) | 2 | 目标窗口已在前台时用更短的停留 |
| 水平方向 | Right | Left / Center / Right |
| 垂直方向 | Bottom | Top / Center / Bottom(决定堆叠方向) |
| 边缘留白(DIP) | 12 | 与屏幕边缘的距离 |
| 不透明度 | 0.96 | |
| 最多同时显示 | 5 | 超出排队 |
| 宽度(DIP) | 340 | |
| 淡入淡出(毫秒) | 300 | |
| 播放提示音 | 开 | |
| 跨所有桌面显示 | 开 | 未公开 API,失败自动退回单桌面 |
文档
| 文档 | 内容 |
|---|---|
| docs/architecture.md | 进程模型、组件、目录结构 |
| docs/sequences.md | 各路径的调用时序图(save / 投递 / 点击激活 / 切标签) |
| docs/hooks-and-cli.md | hook 事件、子命令、stdin JSON、状态文件 |
| docs/interop.md | 原生互操作(激活 / UIAutomation / 跨桌面 / 图标)与 AOT 说明 |
| docs/build-and-install.md | 构建、安装、AOT、排错 |
技术栈
.NET 10 · Avalonia 12 · CommunityToolkit.Mvvm(partial 属性源生成)· Semi.Avalonia · Irihi.Ursa · System.Text.Json 源生成 · 全程 LibraryImport / 源生成 COM(面向 NativeAOT)。