# 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<boolean>('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<string, number> = 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

