chuan b89223fa54 feat: 定位模型重做、聚焦短显、托盘交互与 host spawn 修复
- 定位改为 水平(左/中/右) × 垂直(上/中/下) + 边缘留白,取代四角枚举
- 完成类通知在目标窗口已聚焦时用更短停留(FocusedDurationSeconds)
- 托盘左键单击直接打开设置,右键菜单仅保留退出
- 修复 host 拉起用 UseShellExecute=true,脱离钩子 stdout 管道,避免 Claude 卡在 running stop hook
b89223fa54 · 2026-06-22 16:40:27 +08:00
8 Commits
2026-06-22 11:45:38 +08:00
2026-06-22 11:45:38 +08:00
2026-06-22 11:45:38 +08:00
2026-06-22 11:45:38 +08:00
2026-06-22 11:45:38 +08:00

Claude Code Notify — 功能与时序说明

为 Claude Code 提供 原生 Windows Toast 通知 的插件。当 Claude 完成任务或需要你输入时弹出系统通知,点击通知即可跳回原终端/编辑器窗口(并能切回正确的 Windows Terminal 标签页)。核心是一个用 Rust + Win32 API 编写的单一可执行程序 ToastWindow.exe,通过 Claude Code 的 hook 机制驱动。


一、实现的功能

功能 说明
原生 Toast 通知 任务完成 / 需要输入时弹出系统集成风格的通知
一键返回 左键点击通知 → 激活当初发起请求的窗口
标签页感知 在 Windows Terminal 中能切回提问时所在的标签页
自动取图标 沿进程树向上查找调用方 AppVSCode / Cursor / JetBrains / Windows Terminal …),显示其图标
会话隔离 每个 session_id 独立保存状态于 %TEMP%\claude-notify-{session_id}.txt,多开互不干扰
通知堆叠 多条通知纵向堆叠、平滑滑动、悬停暂停自动关闭
非侵入显示 `WS_EX_NOACTIVATE

exe 的 5 种运行模式

模式 触发 Hook 作用
--save UserPromptSubmit 保存当前窗口句柄、WT 标签 RuntimeId、调用方 exe 路径、prompt
--notify Stop 弹"任务完成"通知(橙色边框)
--input Notification / PreToolUse 弹"需要输入"通知(黄色边框),按类型区分标题
--cleanup SessionEnd 删除该 session 的状态文件
--notify-show (由 --notify/--input 内部分离启动) 实际加载状态、绘制并显示 Toast 窗口

二、整体生命周期时序

sequenceDiagram
    autonumber
    actor User as 用户
    participant CC as Claude Code
    participant Save as ToastWindow --save
    participant State as 状态文件<br/>%TEMP%\claude-notify-{id}.txt
    participant Notify as ToastWindow --notify/--input
    participant Toast as ToastWindow --notify-show
    participant Win as 目标窗口

    User->>CC: 发送消息 (UserPromptSubmit)
    activate CC
    CC->>Save: 调用 exestdin 传 session_id + prompt
    Note over Save: 启动瞬间立即<br/>GetForegroundWindow() 抓住窗口
    Save->>Save: 检测窗口类,若是 WT 则取标签 RuntimeId
    Save->>Save: 沿进程树查找调用方 exe(取图标用)
    Save->>State: 写入 HWND / RuntimeId / iconPath / prompt
    deactivate CC

    Note over CC: Claude 处理中…

    alt 任务完成 (Stop)
        CC->>Notify: ToastWindow --notify
    else 需要输入 (Notification / PreToolUse)
        CC->>Notify: ToastWindow --input(含 title/message
    end
    Notify->>Toast: 分离式 spawn --notify-show
    Note over Notify: 立即返回,不阻塞 hook

    Toast->>State: 读取状态
    Toast->>Toast: 取图标 / 载字体 / 播提示音
    Toast-->>User: 显示 Toast(堆叠 / 淡入)

    User->>Toast: 左键点击
    Toast->>Win: 激活窗口(夺前台 + 切回标签页)
    Win-->>User: 回到原窗口

    User->>CC: 结束会话 (SessionEnd)
    CC->>State: ToastWindow --cleanup 删除状态文件

三、关键时序点(为什么这样设计)

flowchart TD
    A[main 启动] -->|第一行代码| B[GetForegroundWindow<br/>立即抓前台窗口]
    B --> C[CoInitializeEx 初始化 COM]
    C --> D[解析参数 / 初始化日志]
    D --> E{模式分发}
    E -->|--save| F[校验 HWND 有效性<br/>无效则 fallback]
    E -->|--notify/--input| G[只解析 session<br/>分离 spawn 后立即退出]
    E -->|--notify-show| H[加载状态→绘制 Toast<br/>阻塞直到关闭]

    style B fill:#ffe9c7,stroke:#e08a00
    style G fill:#d7f0ff,stroke:#0078d6

要点:

  1. 最早抓窗口main() 第一行就 GetForegroundWindow(),避免后续 COM/参数初始化期间前台窗口变化导致句柄失真。
  2. hook 不阻塞--notify / --input 只做极少工作,随即 spawn_detached--notify-show 子进程后立即返回,保证 Claude Code 的 hook 超时(510s)内完成。
  3. 真正显示在子进程--notify-show 才阻塞绘制 Toast,与 hook 主进程解耦,通知存活不依赖 hook。
  4. 过滤无意义通知auth_success / elicitation_complete / elicitation_response 等类型直接跳过,不弹 toast。

四、点击通知的窗口激活时序

sequenceDiagram
    autonumber
    actor User as 用户
    participant Toast as Toast 窗口
    participant API as Win32 API
    participant WT as Windows Terminal
    participant Win as 目标窗口

    User->>Toast: 左键点击
    Toast->>API: AllowSetForegroundWindow(ASFW_ANY)
    Toast->>API: 模拟 ALT 键 + AttachThreadInput
    Toast->>API: SetWindowPos + BringWindowToTop<br/>SwitchToThisWindow + SetForegroundWindow
    API->>Win: 窗口拉回前台

    opt 目标是 Windows Terminal
        Toast->>WT: UIAutomation 枚举标签项
        Toast->>WT: 匹配保存的 RuntimeId
        Toast->>WT: SelectionItemPattern::Select() 切回标签
    end

    Win-->>User: 回到提问时的窗口与标签页

五、Hook 注册一览(hooks/hooks.json

graph LR
    UPS[UserPromptSubmit] --> S["--save"]
    NOT[Notification] --> I["--input"]
    PRE["PreToolUse<br/>AskUserQuestion|ExitPlanMode"] --> I
    STOP[Stop] --> N["--notify"]
    SE[SessionEnd] --> C["--cleanup"]
S
Description
No description provided
Readme MIT 343 KiB
v1.0.0 Latest
2026-06-22 18:05:27 +08:00
Languages
C# 91.4%
Batchfile 6%
Shell 2.6%