# IC Coder 系统通知功能实现方案 ## 目录 - [1. 需求背景](#1-需求背景) - [2. 技术方案对比](#2-技术方案对比) - [3. 推荐方案详解](#3-推荐方案详解) - [4. 实现步骤](#4-实现步骤) - [5. API 设计](#5-api-设计) - [6. 配置选项](#6-配置选项) - [7. 测试方案](#7-测试方案) - [8. 注意事项](#8-注意事项) - [9. 常见问题](#9-常见问题) --- ## 1. 需求背景 ### 1.1 问题描述 当前 IC Coder 插件使用 VS Code 内置的通知 API (`vscode.window.showInformationMessage`) 来提示用户任务完成。这种方式存在以下问题: - **可见性问题**: 用户切换到其他应用时,无法看到 VS Code 内部的通知 - **错过通知**: 长时间运行的任务(如 iverilog 仿真)完成时,用户可能已经离开 VS Code - **用户体验**: 需要用户主动回到 VS Code 才能知道任务状态 ### 1.2 目标 实现系统级通知功能,使得: 1. 用户在任何应用中都能收到任务完成通知 2. 通知显示在操作系统的通知中心(Windows Action Center / macOS Notification Center / Linux notify-send) 3. 支持自定义通知内容、图标、声音 4. 用户可以配置是否启用系统通知 --- ## 2. 技术方案对比 ### 2.1 方案一:node-notifier(推荐) **描述**: 使用 `node-notifier` 库,封装了各平台的原生通知 API **优点**: - ✅ 跨平台支持(Windows/macOS/Linux) - ✅ API 简单易用 - ✅ 支持自定义图标、声音、操作按钮 - ✅ 活跃维护,社区支持良好 - ✅ 支持通知点击回调 **缺点**: - ❌ 需要添加额外依赖(~500KB) - ❌ 首次使用需要用户授权 **适用场景**: 需要跨平台支持的生产环境 --- ### 2.2 方案二:Windows PowerShell Toast 通知 **描述**: 使用 PowerShell 脚本调用 Windows 10/11 的 Toast 通知 API **优点**: - ✅ 无需额外依赖 - ✅ 支持丰富的 Toast 样式(按钮、输入框等) - ✅ 与 Windows 系统深度集成 **缺点**: - ❌ 仅支持 Windows 10/11 - ❌ 需要执行 PowerShell 脚本,可能有安全限制 - ❌ 实现复杂度较高 **适用场景**: 仅针对 Windows 平台的专用功能 --- ### 2.3 方案三:Electron Notification API **描述**: 使用 Electron 的 `Notification` API(VS Code 基于 Electron) **优点**: - ✅ 无需额外依赖 - ✅ 跨平台支持 - ✅ API 简洁 **缺点**: - ❌ VS Code 扩展 API 未直接暴露 Electron API - ❌ 需要通过 `@vscode/webview-ui-toolkit` 或其他方式间接调用 - ❌ 可能存在兼容性问题 **适用场景**: 理论可行,但实际受限于 VS Code 扩展沙箱 --- ### 2.4 方案四:结合 VS Code 通知 + 系统通知 **描述**: 同时使用 VS Code 内置通知和系统通知 **优点**: - ✅ 双重保障,覆盖所有场景 - ✅ 用户在 VS Code 内外都能看到 **缺点**: - ❌ 可能显得冗余 - ❌ 需要处理两种通知的协调逻辑 **适用场景**: 对通知可靠性要求极高的场景 --- ### 2.5 方案对比表 | 方案 | 跨平台 | 依赖大小 | 实现难度 | 用户体验 | 推荐度 | |------|--------|----------|----------|----------|--------| | node-notifier | ✅ | ~500KB | ⭐ 低 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | PowerShell Toast | ❌ Windows Only | 0 | ⭐⭐⭐ 高 | ⭐⭐⭐⭐ | ⭐⭐ | | Electron API | ✅ | 0 | ⭐⭐⭐⭐ 很高 | ⭐⭐⭐ | ⭐ | | 双重通知 | ✅ | ~500KB | ⭐⭐ 中 | ⭐⭐⭐⭐ | ⭐⭐⭐ | --- ## 3. 推荐方案详解 ### 3.1 选择 node-notifier 的理由 1. **成熟稳定**: 被广泛使用(npm 周下载量 > 200 万) 2. **跨平台**: 自动适配不同操作系统的通知机制 3. **功能丰富**: 支持图标、声音、操作按钮、回调 4. **易于集成**: 与 VS Code 扩展开发无缝集成 ### 3.2 node-notifier 工作原理 ``` ┌─────────────────────────────────────────────────────────────┐ │ IC Coder Extension │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ notificationService.ts │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ │ │ sendSystemNotification(title, message, options) │ │ │ │ │ └──────────────────┬───────────────────────────────┘ │ │ │ └────────────────────┼──────────────────────────────────┘ │ └────────────────────────┼─────────────────────────────────────┘ │ ▼ ┌──────────────────────┐ │ node-notifier │ │ (跨平台适配层) │ └──────────┬───────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ Windows │ │ macOS │ │ Linux │ │ Toast │ │ NSUser │ │ notify- │ │ Notif. │ │ Notif. │ │ send │ └─────────┘ └──────────┘ └──────────┘ ``` ### 3.3 各平台通知效果 #### Windows 10/11 - 显示在右下角 Action Center - 支持应用图标、标题、消息、操作按钮 - 可以播放系统声音 - 通知历史保存在通知中心 #### macOS - 显示在右上角 Notification Center - 支持应用图标、标题、副标题、消息 - 可以播放系统声音 - 支持回复和操作按钮 #### Linux - 使用 `notify-send` 或 `libnotify` - 显示位置取决于桌面环境(GNOME/KDE/XFCE) - 支持图标、标题、消息、紧急程度 --- ## 4. 实现步骤 ### 4.1 安装依赖 ```bash # 安装 node-notifier pnpm add node-notifier # 安装类型定义 pnpm add -D @types/node-notifier ``` ### 4.2 创建通知服务模块 创建 `src/services/notificationService.ts` ```typescript import * as notifier from 'node-notifier'; import * as path from 'path'; import * as vscode from 'vscode'; /** * 通知类型枚举 */ export enum NotificationType { INFO = 'info', SUCCESS = 'success', WARNING = 'warning', ERROR = 'error' } /** * 通知选项接口 */ export interface NotificationOptions { /** 通知标题 */ title: string; /** 通知消息 */ message: string; /** 通知类型 */ type?: NotificationType; /** 是否播放声音 */ sound?: boolean; /** 超时时间(秒),0 表示不自动消失 */ timeout?: number; /** 自定义图标路径 */ icon?: string; /** 点击通知时的回调 */ onClick?: () => void; } /** * 系统通知服务类 */ export class NotificationService { private static instance: NotificationService; private readonly extensionPath: string; private readonly iconPath: string; private constructor(context: vscode.ExtensionContext) { this.extensionPath = context.extensionPath; this.iconPath = path.join(this.extensionPath, 'resources', 'icon.png'); } /** * 获取单例实例 */ public static getInstance(context?: vscode.ExtensionContext): NotificationService { if (!NotificationService.instance && context) { NotificationService.instance = new NotificationService(context); } return NotificationService.instance; } /** * 检查是否启用系统通知 */ private isSystemNotificationEnabled(): boolean { const config = vscode.workspace.getConfiguration('ic-coder'); return config.get('enableSystemNotification', true); } /** * 发送系统通知 */ public sendNotification(options: NotificationOptions): void { // 检查用户配置 if (!this.isSystemNotificationEnabled()) { console.log('[NotificationService] 系统通知已禁用'); return; } const { title, message, type = NotificationType.INFO, sound = true, timeout = 10, icon, onClick } = options; // 准备通知参数 const notificationConfig: notifier.Notification = { title: title, message: message, icon: icon || this.iconPath, sound: sound, wait: false, timeout: timeout, appID: 'IC Coder' // Windows 10/11 需要 }; // 发送通知 notifier.notify(notificationConfig, (err, response, metadata) => { if (err) { console.error('[NotificationService] 通知发送失败:', err); // 降级到 VS Code 内置通知 this.fallbackToVSCodeNotification(title, message, type); return; } console.log('[NotificationService] 通知已发送:', response, metadata); }); // 监听通知点击事件 if (onClick) { notifier.on('click', (notifierObject, options, event) => { onClick(); }); } } /** * 降级到 VS Code 内置通知 */ private fallbackToVSCodeNotification( title: string, message: string, type: NotificationType ): void { const fullMessage = `${title}: ${message}`; switch (type) { case NotificationType.ERROR: vscode.window.showErrorMessage(fullMessage); break; case NotificationType.WARNING: vscode.window.showWarningMessage(fullMessage); break; case NotificationType.SUCCESS: case NotificationType.INFO: default: vscode.window.showInformationMessage(fullMessage); break; } } /** * 发送成功通知 */ public success(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.SUCCESS, sound: true, timeout: 10, onClick }); } /** * 发送错误通知 */ public error(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.ERROR, sound: true, timeout: 15, onClick }); } /** * 发送警告通知 */ public warning(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.WARNING, sound: true, timeout: 10, onClick }); } /** * 发送信息通知 */ public info(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.INFO, sound: false, timeout: 8, onClick }); } } ``` ### 4.3 在扩展入口初始化服务 修改 `src/extension.ts` ```typescript import { NotificationService } from './services/notificationService'; export function activate(context: vscode.ExtensionContext) { // 初始化通知服务 const notificationService = NotificationService.getInstance(context); // ... 其他初始化代码 } ``` ### 4.4 在消息处理器中使用 修改 `src/utils/messageHandler.ts` ```typescript import { NotificationService } from '../services/notificationService'; // 在适当的位置添加通知 export async function handleMessage(message: any, panel: vscode.WebviewPanel) { const notificationService = NotificationService.getInstance(); // 示例:iverilog 仿真完成 if (message.type === 'simulationComplete') { notificationService.success( 'IC Coder - 仿真完成', 'iverilog 仿真已成功完成,VCD 文件已生成', () => { // 点击通知时聚焦到 VS Code vscode.window.showTextDocument(vscode.window.activeTextEditor!.document); } ); } // 示例:仿真失败 if (message.type === 'simulationError') { notificationService.error( 'IC Coder - 仿真失败', `仿真过程中发生错误: ${message.error}`, () => { // 点击通知时打开输出面板 panel.reveal(); } ); } } ``` ### 4.5 添加配置项 修改 `package.json` ```json { "contributes": { "configuration": { "title": "IC Coder", "properties": { "ic-coder.enableSystemNotification": { "type": "boolean", "default": true, "description": "启用系统级通知(任务完成时显示操作系统通知)" }, "ic-coder.notificationSound": { "type": "boolean", "default": true, "description": "通知时播放系统声音" }, "ic-coder.notificationTimeout": { "type": "number", "default": 10, "minimum": 0, "maximum": 60, "description": "通知自动消失时间(秒),0 表示不自动消失" } } } } } ``` --- ## 5. API 设计 ### 5.1 核心 API #### `NotificationService.getInstance(context?)` 获取通知服务单例实例 **参数**: - `context` (可选): `vscode.ExtensionContext` - 扩展上下文,首次调用时必须提供 **返回**: `NotificationService` 实例 **示例**: ```typescript const notificationService = NotificationService.getInstance(context); ``` --- #### `sendNotification(options)` 发送自定义通知 **参数**: - `options`: `NotificationOptions` - 通知选项对象 **返回**: `void` **示例**: ```typescript notificationService.sendNotification({ title: 'IC Coder', message: '任务已完成', type: NotificationType.SUCCESS, sound: true, timeout: 10, onClick: () => { console.log('用户点击了通知'); } }); ``` --- #### `success(title, message, onClick?)` 发送成功通知(快捷方法) **参数**: - `title`: `string` - 通知标题 - `message`: `string` - 通知消息 - `onClick` (可选): `() => void` - 点击回调 **示例**: ```typescript notificationService.success( 'IC Coder', 'VCD 文件生成成功', () => panel.reveal() ); ``` --- #### `error(title, message, onClick?)` 发送错误通知(快捷方法) **参数**: - `title`: `string` - 通知标题 - `message`: `string` - 通知消息 - `onClick` (可选): `() => void` - 点击回调 **示例**: ```typescript notificationService.error( 'IC Coder', '编译失败: 语法错误', () => vscode.commands.executeCommand('workbench.action.showErrorsWarnings') ); ``` --- #### `warning(title, message, onClick?)` 发送警告通知(快捷方法) --- #### `info(title, message, onClick?)` 发送信息通知(快捷方法) --- ### 5.2 类型定义 ```typescript enum NotificationType { INFO = 'info', SUCCESS = 'success', WARNING = 'warning', ERROR = 'error' } interface NotificationOptions { title: string; message: string; type?: NotificationType; sound?: boolean; timeout?: number; icon?: string; onClick?: () => void; } ``` --- ## 6. 配置选项 ### 6.1 用户配置项 | 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | `ic-coder.enableSystemNotification` | `boolean` | `true` | 是否启用系统通知 | | `ic-coder.notificationSound` | `boolean` | `true` | 是否播放通知声音 | | `ic-coder.notificationTimeout` | `number` | `10` | 通知自动消失时间(秒) | ### 6.2 配置方式 #### 方式 1: VS Code 设置界面 1. 打开 VS Code 设置 (`Ctrl+,` / `Cmd+,`) 2. 搜索 "IC Coder" 3. 找到 "Enable System Notification" 选项 4. 勾选或取消勾选 #### 方式 2: settings.json ```json { "ic-coder.enableSystemNotification": true, "ic-coder.notificationSound": true, "ic-coder.notificationTimeout": 10 } ``` --- ## 7. 测试方案 ### 7.1 单元测试 创建 `src/test/suite/notificationService.test.ts` ```typescript import * as assert from 'assert'; import * as vscode from 'vscode'; import { NotificationService, NotificationType } from '../../services/notificationService'; suite('NotificationService Test Suite', () => { let notificationService: NotificationService; suiteSetup(() => { const context = { extensionPath: __dirname } as vscode.ExtensionContext; notificationService = NotificationService.getInstance(context); }); test('应该成功创建单例实例', () => { const instance1 = NotificationService.getInstance(); const instance2 = NotificationService.getInstance(); assert.strictEqual(instance1, instance2); }); test('应该发送成功通知', (done) => { notificationService.success('测试标题', '测试消息'); setTimeout(() => done(), 1000); }); }); ``` ### 7.2 手动测试清单 #### Windows 测试 - [ ] 通知显示在 Action Center - [ ] 点击通知能够聚焦到 VS Code - [ ] 通知声音正常播放 - [ ] 通知图标正确显示 - [ ] 通知在设定时间后自动消失 - [ ] 禁用系统通知后不再显示 --- ## 8. 注意事项 ### 8.1 权限问题 **Windows**: - 首次使用时,Windows 可能会弹出权限请求 - 用户需要在"设置 > 系统 > 通知和操作"中允许应用通知 **macOS**: - 需要在"系统偏好设置 > 通知"中允许 VS Code 发送通知 **Linux**: - 需要安装 `libnotify-bin` 包 - 不同桌面环境的通知样式可能不同 ### 8.2 通知频率控制 为避免通知轰炸,建议实现防抖机制: ```typescript export class NotificationService { private lastNotificationTime: Map = new Map(); private readonly DEBOUNCE_INTERVAL = 3000; // 3 秒 private shouldSendNotification(key: string): boolean { const now = Date.now(); const lastTime = this.lastNotificationTime.get(key) || 0; if (now - lastTime < this.DEBOUNCE_INTERVAL) { return false; } this.lastNotificationTime.set(key, now); return true; } } ``` ### 8.3 错误处理 通知发送失败时,自动降级到 VS Code 内置通知。 ### 8.4 安全考虑 - **不要在通知中显示敏感信息**(如 token、密码) - **验证通知内容**,防止 XSS 攻击 - **限制通知频率**,防止滥用 --- ## 9. 常见问题 ### 9.1 通知不显示 **问题**: 调用通知 API 后,系统没有显示通知 **可能原因**: 1. 用户禁用了系统通知权限 2. 操作系统的"勿扰模式"已启用 3. `node-notifier` 安装失败或版本不兼容 **解决方案**: ```typescript // 添加调试日志 notifier.notify(notificationConfig, (err, response, metadata) => { if (err) { console.error('[NotificationService] 错误:', err); } else { console.log('[NotificationService] 响应:', response); } }); ``` ### 9.2 通知点击回调不触发 **问题**: 点击通知后,`onClick` 回调没有执行 **解决方案**: ```typescript // 设置 wait: true const notificationConfig: notifier.Notification = { title: title, message: message, wait: true, // 等待用户交互 }; ``` ### 9.3 通知图标不显示 **问题**: 通知显示时没有自定义图标 **解决方案**: ```typescript import * as fs from 'fs'; // 检查图标是否存在 if (!fs.existsSync(this.iconPath)) { console.warn(`图标文件不存在: ${this.iconPath}`); this.iconPath = ''; // 使用系统默认图标 } ``` ### 9.4 Linux 上通知不工作 **问题**: 在 Linux 系统上通知无法显示 **解决方案**: ```bash # Ubuntu/Debian sudo apt-get install libnotify-bin # Fedora/RHEL sudo dnf install libnotify ``` --- ## 10. 最佳实践 ### 10.1 通知时机 **推荐发送通知的场景**: - ✅ 长时间运行的任务完成(> 10 秒) - ✅ 后台任务完成(用户可能已切换到其他应用) - ✅ 发生错误需要用户关注 - ✅ 重要状态变更 **不推荐发送通知的场景**: - ❌ 即时完成的操作(< 3 秒) - ❌ 用户主动触发且立即完成的操作 - ❌ 频繁发生的事件(如自动保存) - ❌ 调试信息或日志 ### 10.2 通知内容 **标题**: - 简洁明了,不超过 20 个字符 - 包含应用名称(如 "IC Coder - 仿真完成") - 使用动作完成时态("已完成" 而不是 "完成中") **消息**: - 提供具体信息,不超过 100 个字符 - 包含关键细节(如文件名、错误类型) - 避免技术术语,使用用户友好的语言 **示例**: ```typescript // ✅ 好的通知 notificationService.success( 'IC Coder - 仿真完成', 'testbench.v 仿真成功,VCD 文件已生成' ); // ❌ 不好的通知 notificationService.success('完成', '操作已完成'); ``` ### 10.3 通知优先级 根据重要性设置不同的通知类型和超时时间: ```typescript // 高优先级:错误(15 秒) notificationService.error( 'IC Coder - 编译失败', '发现 3 个语法错误,请检查代码' ); // 中优先级:警告(10 秒) notificationService.warning( 'IC Coder - 警告', '仿真时间过长,可能存在死循环' ); // 低优先级:信息(8 秒,无声音) notificationService.info( 'IC Coder - 提示', '已自动保存工作区' ); ``` --- ## 11. 性能指标 ### 11.1 预期性能 | 指标 | 目标值 | 说明 | |------|--------|------| | 通知发送延迟 | < 100ms | 从调用到系统显示 | | 内存占用 | < 5MB | 通知服务常驻内存 | | CPU 占用 | < 1% | 空闲时 CPU 使用率 | | 包体积增加 | ~500KB | node-notifier 依赖 | --- ## 12. 参考资料 ### 12.1 官方文档 - [node-notifier GitHub](https://github.com/mikaelbr/node-notifier) - [VS Code Extension API](https://code.visualstudio.com/api) - [Windows Toast Notifications](https://docs.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-notifications-overview) ### 12.2 相关文章 - [Best Practices for Desktop Notifications](https://web.dev/notifications/) - [Designing Better Notifications](https://uxdesign.cc/designing-better-notifications-36ba9c0b3e0e) --- ## 13. 总结 本文档详细介绍了在 IC Coder 插件中实现系统级通知功能的完整方案,包括: ✅ **技术选型**: 选择 `node-notifier` 作为跨平台通知解决方案 ✅ **架构设计**: 单例模式的通知服务类,支持多种通知类型 ✅ **实现细节**: 完整的代码示例和配置说明 ✅ **测试方案**: 单元测试、集成测试和手动测试清单 ✅ **最佳实践**: 通知时机、内容设计和用户体验优化 ✅ **故障排查**: 常见问题和解决方案 通过实现系统级通知,IC Coder 插件能够在用户切换到其他应用时仍然及时通知任务状态,显著提升用户体验。 --- **文档版本**: v1.0 **最后更新**: 2026-01-26 **作者**: IC Coder Team **许可**: MIT License