# 调用时序 本文用时序图说明各条执行路径,帮助理解项目的运行方式。 ## 1. 整体生命周期 ```mermaid sequenceDiagram autonumber actor User as 用户 participant CC as Claude Code participant Cli as notify (CLI) participant State as 状态文件 participant Spool as spool 队列 participant Host as notify host participant Win as 目标窗口 User->>CC: 发送消息 CC->>Cli: notify save (stdin: session_id + prompt) Cli->>Cli: GetForegroundWindow / 进程树取图标 / WT 标签 Cli->>State: 写入状态 Cli-->>CC: 立即退出 Note over CC: Claude 处理中… alt 任务完成 CC->>Cli: notify notify else 需要输入 / 提问 / 出 Plan CC->>Cli: notify input end Cli->>State: 读取状态(hwnd / 图标 / WT 标签) Cli->>Spool: 原子写入 NotifyMessage Cli->>Host: 不在则拉起(不等待) Cli-->>CC: 立即退出(~100ms) Host->>Spool: FileSystemWatcher 消费 Host-->>User: 弹出 toast User->>Host: 左键点击 toast Host->>Win: 抢前台激活 (+ 切回 WT 标签) Win-->>User: 回到原窗口 User->>CC: 结束会话 CC->>State: notify cleanup 删除状态 ``` ## 2. 非阻塞投递(关键设计) CLI 绝不能等 Host,否则会拖住 Claude Code。 ```mermaid sequenceDiagram autonumber participant Cli as notify notify/input participant Spool as spool 目录 participant Mtx as 单例互斥量 participant Host as notify host Cli->>Spool: 写 .tmp → 原子改名 .json Cli->>Mtx: TryOpenExisting? alt Host 已在跑 Mtx-->>Cli: 存在 → 不拉起 else Host 未运行 Cli->>Host: Process.Start("host", UseShellExecute=true) Note over Cli,Host: UseShellExecute=true 让 Host 脱离
本进程标准句柄,否则会攥住钩子
stdout 管道导致 Claude 卡在 running hook end Cli-->>Cli: 立即返回(~100ms) Host->>Spool: 启动时 DrainExisting + Watcher 增量 Host->>Host: 读取并删除文件 → UI 线程弹 toast ``` 要点: 1. **原子写入**:先 `.tmp` 再改名 `.json`,Watcher 只认 `.json`,避免读到半截文件。 2. **拉起不等待**:`Process.Start` 后立刻退出;通知由 Host 起来后从 spool 补弹。 3. **`UseShellExecute=true` 是必须的**:否则常驻 Host 继承钩子的 stdout 管道、一直不关闭,Claude Code 等不到 EOF 会卡在 "running stop hook"。 ## 3. 点击 toast → 激活原窗口 ```mermaid sequenceDiagram autonumber actor User as 用户 participant Toast as ToastWindow participant Act as WindowActivator participant WT as WinTerminalTabs participant Win as 目标窗口 User->>Toast: 左键点击主体 Toast->>Act: Activate(targetHwnd) Act->>Win: ALT 模拟 + AttachThreadInput +
SetForegroundWindow 组合技 Win-->>User: 窗口回到前台 opt 目标是 Windows Terminal 且有 RuntimeId Toast->>WT: SelectTab(hwnd, runtimeId) WT->>Win: UIAutomation 找到标签 → Select() end Toast->>Toast: 淡出关闭 ``` ## 4. save 捕获(状态从哪来) ```mermaid flowchart TD A[notify save] --> B[读 stdin JSON: session_id + prompt] B --> C[GetForegroundWindow → hwnd] C --> D{窗口类是 Windows Terminal?} D -->|是| E[UIAutomation 取当前标签 RuntimeId] D -->|否| F[跳过] E --> G[进程树上溯找调用方 App exe] F --> G G --> H[写状态文件: hwnd / prompt / wtRuntimeId / callerExePath] ``` ## 5. Host 内:弹窗与堆叠 ```mermaid sequenceDiagram autonumber participant Watch as SpoolWatcher participant App as App.OnNotify participant Mgr as ToastManager participant Win as ToastWindow Watch->>App: NotifyMessage(线程池线程) App->>App: 若 PlaySound 播提示音 App->>App: 目标已是前台?→ 用更短停留 App->>Mgr: UIThread.Post(Show(request)) alt 可见数 < 上限 Mgr->>Win: 新建并显示 Win->>Win: 工具窗口化 / 跨桌面 pin / 取图标 Mgr->>Mgr: Arrange(按 水平×垂直+margin 堆叠) else 超出上限 Mgr->>Mgr: 入队,等有空位再弹 end ```