{"owner":"Hiram-Wong","repo":"zyfun","hasSkills":true,"totalSkillsCount":5,"totalTokensCount":10691,"categories":["root-instruction","claude-rule","plugin-manifest","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md","packages/crypto/README.md","packages/vlc/AGENTS.md","packages/vlc/docs/api.md"],"skills":{"AGENTS.md":"CLAUDE.md","CLAUDE.md":"# AI Assistant Guide\n\nThis file provides guidance to AI coding assistants when working with code in this repository. Adherence to these guidelines is crucial for maintaining code quality and consistency.\n\n## Guiding Principles (MUST FOLLOW)\n\n- **Keep it clear**: Write code that is easy to read, maintain, and explain.\n- **Start simple**: Begin with the simplest design — no extra abstraction layers, wrapper classes, or separate services unless explicitly requested. Prefer flat, minimal designs.\n- **Fix upstream, don't hack downstream**: When a new feature hits an existing module's limitation, flag the upstream improvement for the user's decision before proposing a downstream workaround.\n- **Read local READMEs first**: Before editing code in a directory, check for a `README.md` in that directory (and its parents) and read it — these files capture local conventions, invariants, and entry points that aren't obvious from the code alone.\n- **Library-first, custom-last**: Before writing custom code, check library/framework docs for built-in options or existing solutions. Write custom code only when no adequate alternative exists.\n- **Match the house style**: Reuse existing patterns, naming, and conventions.\n- **Research via subagent**: Lean on `subagent` for external docs, APIs, news, and references.\n- **Log centrally**: Using the correct context, the main-process logs via `loggerService`, renderer-process often uses `console.log`.\n- **Access paths centrally**: Use `application.getPath('namespace.key', filename?)` for all main-process filesystem paths—never call `app.getPath()`, `os.homedir()`, or construct paths ad-hoc. Import the singleton via `import { application } from '@application'`.\n- **Always propose before executing**: Before making any changes, clearly explain your planned approach and wait for explicit user approval to ensure alignment and prevent unwanted modifications.\n- **Lint, test, and format before completion**: Coding tasks are only complete after running `pnpm lint`, `pnpm test`, and `pnpm format` successfully.\n- **Write conventional commits**: Commit small, focused changes using Conventional Commit messages (e.g., `feat:`, `fix:`, `refactor:`, `docs:`).- **Keep history linear**: On shared branches, never use plain `git pull` — it creates merge commits. Always `git pull --rebase` (or `git fetch && git rebase origin/<branch>`). Before `git push`, run `git fetch`; if `origin/<branch>` has advanced, rebase your local commits onto it first. If you notice a merge commit in local history that hasn't been pushed yet, rebase it away — cleaning one up after it's public requires a risky force-push on a shared branch.\n- **Sign commits**: Use `git commit --signoff` as required by contributor guidelines.\n\n## Development\n\n### Commands\n\nRun `pnpm install` first (requires Node ≥22, pnpm 10.27.0). For every other script, read `package.json` — the ones you must know:\n\n- `pnpm lint` — run eslint auto-fix, typeScript type checking, and prettier format check\n- `pnpm stylelint` - run stylelint fix for stylesheets\n- `pnpm test` — run all Vitest tests\n- `pnpm format` — format files with prettier and run lint in write/fix mode.\n- `pnpm build:check` — **REQUIRED before commits** (`pnpm lint && pnpm test`).\n\n### Testing\n\n- Tests run with Vitest 3 (see `vitest.config.*` for project setup).\n<!-- - **Features without tests are not considered complete** -->\n\n### Patched Dependencies\n\nBefore upgrading any dependency, check `patches/` for custom patches.\n\n## Conventions\n\n### TypeScript\n\n- Strict mode enabled; use `tsgo` (native TypeScript compiler preview) for typechecking\n- Separate configs: `tsconfig.node.json` (main), `tsconfig.web.json` (renderer)\n- Type definitions centralized in `src/renderer/src/types/` and `src/shared/`\n\n### File Naming\n\n- Vue components: `PascalCase.tsx`\n- Services, hooks, utilities: `camelCase.ts`\n- Test files: `*.test.ts` or `*.spec.ts` alongside source or in `__tests__/` subdirectory\n\n### Logging\n\n```typescript\nimport { loggerService } from '@logger';\nconst logger = loggerService.withContext('moduleName');\n// Renderer only: loggerService.initWindowSource('windowName') first\nlogger.info('message', CONTEXT);\nlogger.warn('message');\nlogger.error('message', error);\n```\n\n- Backend: Winston with daily log rotation\n- Log files in `userData/log/`\n- Never use `console.log` — always use `loggerService`\n\n### Paths\n\n```typescript\nimport { getSystemPath, getUserPath } from '@main/utils/path';\n\ngetUserPath('typeName'); // get user data path\ngetUserPath('typeName'); // get system data path\n```\n\n### UI Design\n\n- TDesign\n\n## Security\n\n- Never expose Node.js APIs directly to renderer; use `contextBridge` in preload\n- Validate all IPC inputs in main process handlers\n- URL sanitization via `strict-url-sanitise`\n","packages/crypto/README.md":"# @zy/crypto\n\nCrypto for Node.js and Web — hash, hmac, symmetric/asymmetric encryption, encoding, and more.\n\n## Install\n\n`@zy/crypto` is not published to npm yet.\n\nAdd it as a workspace dependency in your package:\n\n```json\n{\n  \"dependencies\": {\n    \"@zy/crypto\": \"workspace:*\"\n  }\n}\n```\n\n```bash\npnpm install\n```\n\n## API\n\n### Hash\n\n```ts\nimport { hash } from '@zy/crypto';\n\nhash['md5-32']({ src: 'hello' });\nhash.sha256({ src: 'hello', outputEncode: 'hex' });\nhash.sm3({ src: 'hello' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### HMAC\n\n```ts\nimport { hmac } from '@zy/crypto';\n\nhmac.sha256({ src: 'hello', key: 'secret' });\nhmac.sm3({ src: 'hello', key: 'secret' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### Encrypt\n\n#### AES (node-forge, supports GCM)\n\n```ts\nimport { aes } from '@zy/crypto';\n\nconst encrypted = aes.encode({\n  src: 'hello',\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  outputEncode: 'hex',\n});\n\nconst decrypted = aes.decode({\n  src: encrypted,\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  inputEncode: 'hex',\n  outputEncode: 'utf8',\n});\n```\n\n#### DES / 3DES / RC4 / Rabbit\n\n```ts\nimport { des, tripleDes, rc4, rabbit } from '@zy/crypto';\n\ndes.encode({ src: 'hello', key: '12345678', iv: '12345678', mode: 'cbc' });\n```\n\n#### SM4\n\n```ts\nimport { sm4 } from '@zy/crypto';\n\nsm4.encode({ src: 'hello', key: '1234567890123456', iv: '1234567890123456', mode: 'cbc' });\n```\n\n#### RSA\n\n```ts\nimport { rsa } from '@zy/crypto';\n\nrsa.encode({\n  src: 'hello',\n  key: '-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----',\n  pad: 'rsaes-pkcs1-v1_5',\n  outputEncode: 'hex',\n});\n```\n\n### Encode\n\n```ts\nimport { base64, unicode, html, gzip, url, hex } from '@zy/crypto';\n\nbase64.encode({ src: 'hello' });\nbase64.decode({ src: 'aGVsbG8=' });\n\nunicode.encode({ src: 'hello' });\nhtml.encode({ src: '<b>bold</b>' });\ngzip.encode({ src: 'hello' });\nurl.encode({ src: 'https://example.com?q=hello' });\nhex.encode({ src: 'hello' });\n```\n\n## Build\n\n```bash\npnpm build\n```\n\n## Modules\n\n| Module                    | Description                                     |\n| ------------------------- | ----------------------------------------------- |\n| `hash`                    | Hash algorithms (MD5, SHA, SM3, RIPEMD160)      |\n| `hmac`                    | HMAC algorithms                                 |\n| `aes`                     | AES encrypt/decrypt (node-forge, GCM supported) |\n| `des` / `tripleDes`       | DES/3DES encrypt/decrypt (CryptoJS)             |\n| `rc4` / `rc4Drop`         | RC4 encrypt/decrypt                             |\n| `rabbit` / `rabbitLegacy` | Rabbit encrypt/decrypt                          |\n| `rsa`                     | RSA encrypt/decrypt (node-forge)                |\n| `sm4`                     | SM4 encrypt/decrypt (sm-crypto-v2)              |\n| `base64`                  | Base64 encode/decode                            |\n| `unicode`                 | Unicode encode/decode                           |\n| `html`                    | HTML entity encode/decode                       |\n| `gzip`                    | Gzip compress/decompress                        |\n| `url`                     | URL encode/decode                               |\n| `hex`                     | Hex encode/decode                               |\n","packages/vlc/AGENTS.md":"# AGENTS.md\n\n## 项目概述\n\n这是一个基于 **libVLC** 的 Node.js 原生插件项目，专为 Electron 应用提供视频播放能力。项目采用 **TypeScript + Rust (NAPI)** 混合架构。\n\n## 技术栈\n\n- **前端**: TypeScript (ESM/CJS)\n- **原生层**: Rust (通过 `napi-rs` 框架)\n- **构建工具**: `tsdown` (打包), `napi build` (原生编译)\n\n## 项目结构\n\n```\npackages/vlc/\n├── src/                         # TypeScript 源码\n│   ├── index.ts                 # 主入口，默认导出\n│   ├── types.ts                 # TypeScript 类型定义\n│   ├── constants/               # 常量定义\n│   │   ├── ipc.ts               # IPC 通讯通道定义\n│   │   └── index.ts             # 默认入口\n│   ├── control/                 # 主进程控制层 (API/IPC/加载器)\n│   │   ├── api.ts               # VLC API 封装\n│   │   ├── ipc.ts               # IPC 处理\n│   │   ├── native-loader.ts     # 原生模块加载器\n│   │   ├── utils.ts             # 工具函数\n│   │   └── index.ts             # 默认入口\n│   └── renderer/                # 渲染进程组件\n│       ├── icons/               # SVG 图标资源\n│       ├── lang/                # 国际化\n│       │   └── locales.ts       # 语言配置\n│       ├── player/              # 播放器 mixin 组件\n│       │   ├── playMix.ts       # 播放控制\n│       │   ├── pauseMix.ts      # 暂停控制\n│       │   ├── seekMix.ts       # 进度跳转\n│       │   ├── volumeMix.ts     # 音量控制\n│       │   ├── fullscreenMix.ts # 全屏控制\n│       │   ├── pipMix.ts        # 画中画\n│       │   └── ...              # 其他 mixin\n│       ├── plugins/             # 插件\n│       │   ├── canvas-renderer.ts # Canvas 渲染器\n│       │   └── runtime.ts       # 运行时\n│       ├── style/               # 样式 (Less)\n│       │   └── index.less\n│       ├── utils/               # 工具函数\n│       │   ├── dom.ts           # DOM 操作\n│       │   ├── emitter.ts       # 事件发射器\n│       │   ├── format.ts        # 格式化\n│       │   └── property.ts      # 属性工具\n│       ├── adapter.ts           # 适配器\n│       ├── bridge.ts            # 桥接通信\n│       ├── index.ts             # 默认入口\n│       ├── raw.d.ts             # 原生模块类型声明\n│       ├── storage.ts           # 存储\n│       ├── template.ts          # 播放器模板\n│       └── vlc-player.ts        # VLC 播放器组件\n├── native/                      # Rust 原生代码\n│   ├── src/\n│   │   ├── platform/            # 平台特定实现\n│   │   │   ├── macos.rs         # macOS (NSView)\n│   │   │   └── mod.rs\n│   │   ├── api.rs               # libVLC FFI 封装\n│   │   ├── event.rs             # 事件处理\n│   │   ├── ffi.rs               # FFI 类型定义\n│   │   ├── lib.rs               # NAPI 绑定实现\n│   │   ├── state.rs             # 状态管理\n│   │   ├── types.rs             # 类型定义\n│   │   └── util.rs              # 辅助工具\n│   ├── build.rs                 # 构建脚本\n│   └── Cargo.toml\n├── example/                     # Electron 示例应用\n│   ├── electron.cjs             # 主进程逻辑\n│   ├── preload.cjs              # 接口暴露逻辑\n│   ├── renderer.js              # 渲染进程逻辑\n│   └── index.html\n├── lib/                         # 编译输出 (ESM/CJS)\n└── build/                       # 原生二进制文件\n```\n\n## 核心架构\n\n### 双层设计\n\n1. **TypeScript 层** (`src/`): 提供 Promise 化的异步 API，处理跨线程调用\n2. **Rust 原生层** (`native/`): 通过 NAPI 调用 libVLC C API，管理播放器状态和事件\n\n### 状态管理\n\n- 使用 `once_cell::sync::Lazy` 实现线程安全的全局状态\n- `PlayerContext` 包含 `instance` 和 `player` 句柄\n- 事件回调通过 `ThreadsafeFunction` 在 Rust 和 JS 间传递\n\n### 平台特定实现\n\n- **macOS**: 使用 `NSView` 进行视频渲染 (`platform/macos.rs`)\n- **Windows**: 待实现 (`HWND`)\n- **Linux**: 待实现 (`XWindow`)\n\n## 开发命令\n\n```bash\n# 完整构建流程 (clean -> native -> ts -> css)\nnpm run build\n\n# 分步构建\nnpm run build:native  # napi build (Release 模式)\nnpm run build:ts      # tsdown 打包 TypeScript\nnpm run build:css     # less 编译样式\n\n# 类型检查\nnpm run typecheck     # tsc --noEmit\n\n# 运行示例应用\nnpm run dev           # 启动 Electron 示例应用\n```\n\n## 主要 API\n\n完整 API 文档见 [docs/api.md](docs/api.md)，包含类型定义、Control API（主进程）、Renderer API（渲染进程）的详细说明。\n\n快速概览：\n\n| 层级     | 入口                                            | 核心                                            |\n| -------- | ----------------------------------------------- | ----------------------------------------------- |\n| 主进程   | `import { VlcApi, ipc } from '@zy/vlc/control'` | `VlcApi` 类（30+ 方法）+ `ipc()` 注册处理器     |\n| 渲染进程 | `import { VlcPlayer } from '@zy/vlc/renderer'`  | `new VlcPlayer()` → `IVlcRuntime` + Canvas 渲染 |\n\n## 关键实现细节\n\n### 1. 线程安全\n\n- Rust 代码使用 `lock_state()` 确保状态访问的线程安全\n- 事件回调通过 `ThreadsafeFunction` 在后台线程触发，避免阻塞主线程\n\n### 2. 内存管理\n\n- `play_media` 在创建新播放器前会安全释放旧播放器\n- 使用 `CString` 处理字符串以避免内存泄漏\n\n### 3. 事件系统\n\n- 支持所有 libVLC 事件类型 (播放、暂停、停止、结束、错误等)\n- 事件数据通过 `VlcEventPayload` 结构传递\n\n### 4. 路径处理\n\n- HTTP 流媒体使用 `libvlc_media_new_location`\n- 本地文件使用 `libvlc_media_new_path` 并进行路径规范化\n\n## 注意事项\n\n1. **VLC 库依赖**: 需要系统中安装 VLC 应用程序（包含 `libvlc` 动态库）\n2. **动态库路径**: macOS 默认 `/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib`\n3. **平台限制**: 当前仅实现 macOS，Windows/Linux 需补充 `platform/` 实现\n4. **窗口句柄**: macOS 需传递 `NSView` 指针（通过 `BigInt` 类型）\n5. **双入口导出**: 包导出 `./control`（主进程）和 `./renderer`（渲染进程）两个入口\n\n## 构建输出\n\n编译后生成：\n\n- `lib/control.mjs` / `lib/control.cjs` - 主进程控制层\n- `lib/renderer.mjs` / `lib/renderer.cjs` - 渲染进程组件\n- `lib/*.d.mts` - TypeScript 类型定义\n- `build/vlc_native.node` - 原生二进制文件\n\n## 示例用法\n\n**主进程** (`main.ts`):\n\n```typescript\nimport { app } from 'electron';\nimport { ipc } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  ipc(); // 注册所有 IPC 处理器\n});\n```\n\n**渲染进程** (`renderer.ts`):\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n  },\n);\n\n// 销毁\n// runtime.destroy();\n```\n\n更多示例见 [example/](example/) 目录。\n","packages/vlc/docs/api.md":"# @zy/vlc API 文档\n\n基于 libVLC 的 Electron 原生视频播放器插件。采用 TypeScript + Rust (NAPI) 混合架构，通过 Electron IPC 实现主进程与渲染进程通信，Canvas 渲染视频帧。\n\n## 安装前提\n\n- 需要系统已安装 VLC 应用（加载 `libvlc.dylib` / 共享库）\n- VLC 下载: https://get.videolan.org/vlc/last/\n\n## 包导出\n\n| 导入路径               | 说明                                 |\n| ---------------------- | ------------------------------------ |\n| `@zy/vlc/control`      | 主进程 API（`VlcApi` 类 + IPC 注册） |\n| `@zy/vlc/renderer`     | 渲染进程 API（`VlcPlayer` 入口）     |\n| `@zy/vlc/renderer.css` | 渲染器样式                           |\n\n## 架构概览\n\n```\n┌─────────────────────────────────────────────────────┐\n│  Renderer Process                                    │\n│  new VlcPlayer() → IVlcRuntime                    │\n│       ↓ IPC Bridge (ipcRenderer.invoke)              │\n├─────────────────────────────────────────────────────┤\n│  Main Process                                        │\n│  ipc() → VlcApi → Rust NAPI addon (libVLC FFI)      │\n└─────────────────────────────────────────────────────┘\n```\n\n- **主进程** (`@zy/vlc/control`): `VlcApi` 封装 Rust 原生模块，`ipc()` 注册所有 `ipcMain.handle` 处理器\n- **渲染进程** (`@zy/vlc/renderer`): `new VlcPlayer()` 创建完整播放器 UI，通过 IPC Bridge 与主进程通信\n- **视频渲染**: VLC 在 Rust 内部渲染帧 → `getFrameRgba()` 返回 RGBA `Uint8Array` → Canvas 渲染器绘制到 HTML Canvas\n\n---\n\n## 类型定义\n\n所有共享类型从 `@zy/vlc/types` 导出。\n\n### IVlcPlayerState\n\n播放器状态枚举：\n\n```typescript\ntype IVlcPlayerState =\n  | 'nothing-special'\n  | 'opening'\n  | 'buffering'\n  | 'playing'\n  | 'paused'\n  | 'stopped'\n  | 'ended'\n  | 'error'\n  | 'unknown';\n```\n\n### IVlcTrack\n\n轨道信息：\n\n```typescript\ninterface IVlcTrack {\n  id: number;\n  name: string;\n  isActive: boolean;\n}\n```\n\n### IVlcEventPayload\n\n事件回调载荷：\n\n```typescript\ninterface IVlcEventPayload {\n  instanceId?: string;\n  eventType: string;\n  value: number;\n  additionalInfo: string;\n}\n```\n\n### IVlcInitPath\n\nlibVLC 路径配置：\n\n```typescript\ninterface IVlcInitPath {\n  libPath: string; // libVLC 动态库路径（必填）\n  pluginPath?: string; // VLC 插件目录路径（可选）\n}\n```\n\n### IVlcInitOptions\n\n播放器初始化选项：\n\n```typescript\ninterface IVlcInitOptions {\n  el: string; // 挂载容器 CSS 选择器\n  url: string; // 播放地址（本地路径或在线 URL）\n  headers?: Record<string, string>; // 在线资源请求头\n  debug?: boolean; // 是否输出 Rust 层 [vlc-native] 日志，默认 false\n  seekStep?: number; // 快进/快退步进（毫秒），默认 5000\n  volumeStep?: number; // 音量步进，默认 0.05\n  autoplay?: boolean; // 是否自动播放，默认 true\n  volume?: number; // 初始音量（0~1），默认 0.7\n  muted?: boolean; // 是否静音，默认 false\n  loop?: boolean; // 是否循环播放\n  startTime?: number; // 初始起播时间（毫秒）\n  playbackRate?: number; // 初始播放倍率，默认 1\n  playbackRates?: number[]; // 可选倍速列表，默认 [0.5, 0.75, 1, 1.25, 1.5, 2]\n  locale?: IVlcLocale; // 语言 ('zh-CN' | 'zh-TW' | 'en-US')\n  i18n?: IVlcI18n; // 自定义国际化文案覆盖\n  playNext?: () => void; // 播放下一个回调\n  playPrev?: () => void; // 播放上一个回调\n}\n```\n\n### IVlcI18n\n\n国际化文案键值对（所有键均为可选）：\n\n```typescript\ntype IVlcI18nKey =\n  | 'playerTitle'\n  | 'placeholderLoading'\n  | 'placeholderStopped'\n  | 'placeholderError'\n  | 'placeholderInitError'\n  | 'statusIdle'\n  | 'statusReady'\n  | 'statusOpening'\n  | 'statusPlaying'\n  | 'statusPaused'\n  | 'statusStopped'\n  | 'statusEnded'\n  | 'statusError'\n  | 'statusPipUnsupported'\n  | 'statusPipError'\n  | 'statusBuffering'\n  | 'labelVolume'\n  | 'labelPlaybackRate'\n  | 'actionPlay'\n  | 'actionPause'\n  | 'actionMute'\n  | 'actionUnmute'\n  | 'actionPictureInPicture'\n  | 'actionExitPictureInPicture'\n  | 'actionFullscreen'\n  | 'actionExitFullscreen'\n  | 'actionNext'\n  | 'actionPrev'\n  | 'actionForward'\n  | 'actionBackward';\n\ntype IVlcI18n = Partial<Record<IVlcI18nKey, string>>;\n```\n\n---\n\n## Control API（主进程）\n\n```typescript\nimport { VlcApi, ipc } from '@zy/vlc/control';\n```\n\n### ipc()\n\n在主进程中注册所有 IPC 处理器。必须在 Electron `app.whenReady()` 之后调用。\n\n```typescript\nfunction ipc(): void;\n```\n\n调用后，渲染进程可通过 `ipcRenderer.invoke('vlc:*', ...)` 访问所有播放器方法。内部维护 `Map<string, VlcApi>` 实例映射，通过 `instanceId` 区分多播放器。\n\n---\n\n### VlcApi 类\n\n```typescript\nclass VlcApi implements IVlcApiContract {\n  constructor(instanceId?: string); // 默认 'default'\n}\n```\n\n每个实例绑定一个 `instanceId`，对应一个 libVLC 播放器实例。\n\n#### 方法列表\n\n| 方法               | 参数                                                               | 返回值            | 说明                                |\n| ------------------ | ------------------------------------------------------------------ | ----------------- | ----------------------------------- |\n| `create`           | `path: IVlcInitPath, options: IVlcInitOptions`                     | `string`          | 初始化 libVLC，返回实例 ID          |\n| `play`             | 无                                                                 | `void`            | 开始播放                            |\n| `pause`            | 无                                                                 | `void`            | 暂停播放                            |\n| `toggle`           | 无                                                                 | `void`            | 切换播放/暂停                       |\n| `stop`             | 无                                                                 | `void`            | 停止播放并释放资源                  |\n| `attach`           | `handle: bigint`                                                   | `void`            | 设置原生窗口句柄（macOS NSView 等） |\n| `setFrameFormat`   | `width: number, height: number`                                    | `void`            | 设置帧渲染尺寸                      |\n| `getFrameRgba`     | 无                                                                 | `Uint8Array`      | 获取当前帧 RGBA 数据                |\n| `getState`         | 无                                                                 | `IVlcPlayerState` | 获取播放器状态                      |\n| `getPlaying`       | 无                                                                 | `boolean`         | 是否正在播放                        |\n| `getEnded`         | 无                                                                 | `boolean`         | 是否播放结束                        |\n| `setVolume`        | `volume: number`                                                   | `void`            | 设置音量（0~1）                     |\n| `getVolume`        | 无                                                                 | `number`          | 获取音量                            |\n| `setMuted`         | `muted: boolean`                                                   | `void`            | 设置静音                            |\n| `getMuted`         | 无                                                                 | `boolean`         | 获取静音状态                        |\n| `seek`             | `time: number`                                                     | `void`            | 跳转到指定时间（毫秒）              |\n| `setProgress`      | `progress: number`                                                 | `void`            | 设置播放进度（0~1）                 |\n| `getProgress`      | 无                                                                 | `number`          | 获取播放进度（0~1）                 |\n| `getDuration`      | 无                                                                 | `number`          | 获取总时长（毫秒）                  |\n| `getPlayed`        | 无                                                                 | `number`          | 获取已播放时长（毫秒）              |\n| `getBuffered`      | 无                                                                 | `number`          | 获取缓冲进度                        |\n| `setPlaybackRate`  | `rate: number`                                                     | `void`            | 设置播放速率                        |\n| `getPlaybackRate`  | 无                                                                 | `number`          | 获取播放速率                        |\n| `getSubtitleTrack` | 无                                                                 | `IVlcTrack[]`     | 获取字幕轨道列表                    |\n| `setSubtitleTrack` | `id: number`                                                       | `void`            | 激活指定字幕轨道                    |\n| `addSubtitleFile`  | `subtitlePath: string`                                             | `void`            | 添加外部字幕文件                    |\n| `getAudioTrack`    | 无                                                                 | `IVlcTrack[]`     | 获取音频轨道列表                    |\n| `setAudioTrack`    | `id: number`                                                       | `void`            | 激活指定音频轨道                    |\n| `onEvent`          | `eventName: string, callback: (payload: IVlcEventPayload) => void` | `void`            | 注册事件回调                        |\n| `destroy`          | 无                                                                 | `void`            | 销毁播放器释放资源                  |\n\n#### 使用示例\n\n```typescript\nimport { app } from 'electron';\nimport { ipc, VlcApi } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  // 注册 IPC 处理器\n  ipc();\n\n  // 直接使用 API（非 IPC 模式）\n  const player = new VlcApi('my-player');\n  player.create(\n    {\n      libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n      pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n    },\n    {\n      el: '',\n      url: 'https://example.com/video.mp4',\n      autoplay: false,\n      volume: 0.8,\n      playbackRate: 1,\n    },\n  );\n\n  player.onEvent('Playing', (payload) => {\n    console.log('事件:', payload.eventType, payload.value);\n  });\n\n  player.play();\n});\n```\n\n---\n\n## Renderer API（渲染进程）\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n```\n\n### VlcPlayer\n\n渲染进程入口类，创建完整播放器实例。\n\n```typescript\nclass VlcPlayer implements IVlcRuntime {\n  constructor(path: IVlcInitPath, options: IVlcInitOptions);\n  adapter: VlcAdapter;\n  destroy(): void;\n}\n```\n\n**参数：**\n\n- `path`: libVLC 路径配置\n- `options`: 播放器选项（`el` 为挂载容器的 CSS 选择器）\n\n**实例结构：** `IVlcRuntime`\n\n```typescript\ninterface IVlcRuntime extends IVlcPlayer {\n  adapter: VlcAdapter;\n  destroy: () => void;\n}\n```\n\n#### 使用示例\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player-container',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n    locale: 'zh-CN',\n  },\n);\n\n// 销毁\nruntime.destroy();\n```\n\n---\n\n### IVlcRuntime 对象\n\n由 `new VlcPlayer()` 直接创建的播放器对象，基于 mixin 模式组合了播放控制、UI 模板和事件系统，并额外提供 `destroy()`。\n\n#### 状态属性\n\n| 属性             | 类型                         | 说明               |\n| ---------------- | ---------------------------- | ------------------ |\n| `state`          | `string`                     | 当前播放器状态     |\n| `isReady`        | `boolean`                    | 是否就绪           |\n| `playing`        | `boolean`                    | 是否正在播放       |\n| `currentTime`    | `number`                     | 当前播放时间（秒） |\n| `duration`       | `number`                     | 总时长（秒）       |\n| `volume`         | `number`                     | 音量（0~1）        |\n| `muted`          | `boolean`                    | 是否静音           |\n| `playbackRate`   | `number`                     | 播放速率           |\n| `fullscreenWeb`  | `boolean`                    | 是否网页全屏       |\n| `pip`            | `boolean`                    | 是否画中画         |\n| `pipEnabled`     | `boolean`                    | 画中画是否可用     |\n| `adapter`        | `VlcAdapter`                 | 底层适配器实例     |\n| `canvasRenderer` | `IVlcCanvasRenderer \\| null` | Canvas 渲染器      |\n\n#### 播放控制\n\n| 方法     | 参数 | 返回值          | 说明          |\n| -------- | ---- | --------------- | ------------- |\n| `play`   | 无   | `Promise<void>` | 播放          |\n| `pause`  | 无   | `void`          | 暂停          |\n| `toggle` | 无   | `void`          | 切换播放/暂停 |\n\n#### 可写属性（setter）\n\n| 属性       | 类型     | 说明                      |\n| ---------- | -------- | ------------------------- |\n| `seek`     | `number` | 跳转到指定时间（秒）      |\n| `forward`  | `number` | 快进（秒）                |\n| `backward` | `number` | 快退（秒）                |\n| `played`   | `number` | 跳转到指定播放进度（0~1） |\n\n#### 事件系统\n\n```typescript\nplayer.on(name: string, fn: (...args: any[]) => void): void;\nplayer.off(name: string, fn?: (...args: any[]) => void): void;\nplayer.once(name: string, fn: (...args: any[]) => void): void;\nplayer.emit(name: string, ...args: any[]): void;\nplayer.proxy(el: HTMLElement | Window, event: string, fn: (e: any) => void): () => void;\n```\n\n#### 内部服务\n\n| 属性       | 类型                                         | 说明         |\n| ---------- | -------------------------------------------- | ------------ |\n| `template` | `Template`                                   | DOM 模板     |\n| `notice`   | `{ show: string \\| Error; silent: boolean }` | 通知         |\n| `i18n`     | `{ get: (key: string) => string }`           | 国际化       |\n| `storage`  | `Storage`                                    | 本地存储     |\n| `cssVar`   | `(key: string, value?: string) => any`       | CSS 变量操作 |\n\n---\n\n### VlcAdapter 类\n\n底层播放器适配器，封装 IPC Bridge 通信和播放状态管理。\n\n```typescript\nimport type { VlcAdapter } from '@zy/vlc/renderer';\n```\n\n#### 属性\n\n| 属性           | 类型                                        | 读写 | 说明                           |\n| -------------- | ------------------------------------------- | ---- | ------------------------------ |\n| `instance`     | `IVlcBridge \\| null`                        | 只读 | IPC Bridge 实例                |\n| `playing`      | `boolean`                                   | 只读 | 是否正在播放                   |\n| `muted`        | `boolean`                                   | 读写 | 静音状态（持久化到 Storage）   |\n| `volume`       | `number`                                    | 读写 | 音量 0~1（自动 clamp，持久化） |\n| `playbackRate` | `number`                                    | 读写 | 播放速率（持久化）             |\n| `currentTime`  | `number`                                    | 只读 | 当前时间（毫秒）               |\n| `duration`     | `number`                                    | 只读 | 总时长（毫秒）                 |\n| `progress`     | `number`                                    | 只读 | 播放进度（0~1）                |\n| `time`         | `{ currentTime: number; duration: number }` | 只读 | 时间快照                       |\n\n#### 生命周期\n\n| 方法      | 参数                                              | 返回值          | 说明                                                     |\n| --------- | ------------------------------------------------- | --------------- | -------------------------------------------------------- |\n| `create`  | `path: IVlcInitPath, options: IVlcAdapterOptions` | `void`          | 创建 IPC Bridge 并初始化配置                             |\n| `init`    | 无                                                | `Promise<void>` | 初始化播放器、设置帧格式（1280x720）、启动轮询、开始播放 |\n| `destroy` | 无                                                | `void`          | 停止轮询、清除监听器、销毁 Bridge                        |\n\n#### 播放控制\n\n| 方法          | 参数               | 返回值 | 说明                                        |\n| ------------- | ------------------ | ------ | ------------------------------------------- |\n| `play`        | 无                 | `void` | 播放                                        |\n| `pause`       | 无                 | `void` | 暂停                                        |\n| `togglePlay`  | 无                 | `void` | 切换播放/暂停                               |\n| `toggleMuted` | 无                 | `void` | 切换静音                                    |\n| `seek`        | `time: number`     | `void` | 跳转到指定时间（毫秒），内部转换为 progress |\n| `setProgress` | `progress: number` | `void` | 设置进度（0~1）                             |\n\n#### 帧访问\n\n| 方法             | 参数                            | 返回值       | 说明                 |\n| ---------------- | ------------------------------- | ------------ | -------------------- |\n| `getFrameRgba`   | 无                              | `Uint8Array` | 获取当前帧 RGBA 数据 |\n| `setFrameFormat` | `width: number, height: number` | `void`       | 设置帧渲染尺寸       |\n\n#### 时间更新轮询\n\n```typescript\nadapter.onTimeUpdate(callback: (args: { currentTime: number; duration: number }) => void): void;\nadapter.offTimeUpdate(): void;\n```\n\n内部以 500ms 间隔轮询 `getPlayed()` 和 `getDuration()`。\n\n#### 度量查询\n\n| 方法                | 返回值    | 说明               |\n| ------------------- | --------- | ------------------ |\n| `getPlayed()`       | `number`  | 已播放时长（毫秒） |\n| `getDuration()`     | `number`  | 总时长（毫秒）     |\n| `getProgress()`     | `number`  | 播放进度（0~1）    |\n| `getVolume()`       | `number`  | 音量               |\n| `getMuted()`        | `boolean` | 静音状态           |\n| `getPlaybackRate()` | `number`  | 播放速率           |\n\n#### 事件\n\n```typescript\nadapter.on(event: string, handler: (...args: any[]) => void): void;\nadapter.off(event: string, handler?: (...args: any[]) => void): void;\nadapter.onEvent(callback: (payload: IVlcEventPayload) => void): () => void; // 返回取消订阅函数\n```\n\n---\n\n### IVlcBridge 接口\n\nIPC 通信桥接层，由 `createBridge()` 创建。渲染进程通过 `ipcRenderer.invoke` 调用主进程 VLC 操作。\n\n```typescript\ninterface IVlcBridge {\n  create: (mountSelector: string) => void | Promise<void>;\n  play: () => void;\n  pause: () => void;\n  setVolume: (vol: number) => void;\n  getVolume: () => number;\n  setMuted: (muted: boolean) => void;\n  getMuted: () => boolean;\n  setProgress: (progress: number) => void;\n  getProgress: () => number;\n  getDuration: () => number;\n  getPlayed: () => number;\n  setPlaybackRate: (rate: number) => void;\n  getPlaybackRate: () => number;\n  setFrameFormat: (width: number, height: number) => void;\n  getFrameRgba: () => Uint8Array;\n  onEvent: (callback: (payload: IVlcEventPayload) => void) => () => void;\n  destroy: () => void;\n}\n\nfunction createBridge(path: IVlcInitPath, options: IVlcInitOptions, instanceId?: string): IVlcBridge;\n```\n\nBridge 内部通过 250ms 轮询缓存 metrics（volume, progress, duration, played, playbackRate, muted），getter 直接返回缓存值，setter 异步调用 IPC 后立即更新缓存。\n\n---\n\n### Template 类\n\n播放器 DOM 模板，负责创建和管理播放器 UI 元素。\n\n```typescript\nclass Template {\n  constructor(vlc: any, t?: I18nFn);\n  static html(t: I18nFn): string;\n  query<T extends HTMLElement>(selector: string): T | null;\n  queryMust<T extends HTMLElement>(selector: string): T;\n  init(): void;\n  destroy(removeHtml?: boolean): void;\n}\n```\n\n#### DOM 元素引用\n\n| 属性             | 类型                | 说明            |\n| ---------------- | ------------------- | --------------- |\n| `$container`     | `HTMLElement`       | 最外层容器      |\n| `$player`        | `HTMLElement`       | 播放器容器      |\n| `$videoStage`    | `HTMLElement`       | 视频舞台        |\n| `$videoCanvas`   | `HTMLCanvasElement` | 视频渲染 Canvas |\n| `$placeholder`   | `HTMLElement`       | 占位符          |\n| `$topbar`        | `HTMLElement`       | 顶部栏          |\n| `$notice`        | `HTMLElement`       | 通知栏          |\n| `$controlsStage` | `HTMLElement`       | 控制栏          |\n| `$progress`      | `HTMLInputElement`  | 进度条          |\n| `$timeLeft`      | `HTMLElement`       | 左侧时间        |\n| `$timeRight`     | `HTMLElement`       | 右侧时间        |\n| `$btnPlay`       | `HTMLElement`       | 播放按钮        |\n| `$btnMute`       | `HTMLElement`       | 静音按钮        |\n| `$volume`        | `HTMLInputElement`  | 音量滑块        |\n| `$rateBtn`       | `HTMLElement`       | 倍率按钮        |\n| `$rateOptions`   | `HTMLElement`       | 倍率选项        |\n| `$btnPip`        | `HTMLElement`       | 画中画按钮      |\n| `$btnFullscreen` | `HTMLElement`       | 全屏按钮        |\n| `$btnNext`       | `HTMLElement`       | 下一个按钮      |\n| `$btnPrev`       | `HTMLElement`       | 上一个按钮      |\n\n---\n\n### IVlcCanvasRenderer 接口\n\nCanvas 帧渲染器，将 VLC RGBA 帧数据绘制到 HTML Canvas。\n\n```typescript\ninterface IVlcCanvasRenderer {\n  renderSourceToDisplay: () => void;\n  renderToCanvas: (targetCanvas: HTMLCanvasElement, targetCtx: CanvasRenderingContext2D) => void;\n  resizeCanvasToStage: () => void;\n  clearDisplayCanvas: () => void;\n  scheduleCanvasResize: () => void;\n  startFramePump: () => void;\n  stopFramePump: () => void;\n  setPipActive: (active: boolean) => void;\n  initFrameData: () => void;\n  onFrame: (callback: () => void) => void;\n  destroy: () => void;\n}\n\nfunction createCanvasRenderer(\n  refs: { videoCanvas: HTMLCanvasElement; videoStage: HTMLElement },\n  adapter: VlcAdapter,\n): IVlcCanvasRenderer;\n```\n\n---\n\n### Storage 类\n\n基于 localStorage 的播放器配置持久化。\n\n```typescript\nclass Storage {\n  constructor();\n  get(): Record<string, any>; // 获取全部\n  get(key: string): any; // 获取指定键\n  set(key: string, value: any): void;\n  del(key: string): void;\n  clear(): void;\n}\n```\n\n持久化键：`volume`, `muted`, `playrate`。\n\n---\n\n### 图标工具\n\n```typescript\nfunction resolveDemoIcon(name: IVlcIconKey): string;\nfunction populateSvgIcons(root: ParentNode): void;\nfunction setIVlcIconResolver(resolver: IVlcIconResolver | null | undefined): void;\nfunction resetIVlcIconResolver(): void;\n```\n\n`IVlcIconKey`: `'play'` | `'pause'` | `'volume'` | `'volumeSmall'` | `'muted'` | `'pip'` | `'pipExit'` | `'fullscreen'` | `'fullscreenExit'` | `'mnext'` | `'mprev'`\n\n---\n\n## 注意事项\n\n- **多实例支持**: 通过 `instanceId` 区分多个播放器实例，IPC 自动路由到对应实例\n- **帧渲染**: 默认 1280x720，通过 `requestAnimationFrame` 驱动帧泵，Bridge 内部防并发请求\n- **状态轮询**: Bridge 以 250ms 间隔轮询 metrics，Adapter 以 500ms 间隔触发 `onTimeUpdate` 回调\n- **平台限制**: `attach` 方法传入原生窗口句柄（macOS 为 NSView 指针的 `bigint`），仅用于原生窗口渲染场景\n- **本地字幕**: `addSubtitleFile` 本地文件需要 `file:///` 前缀\n- **Storage 持久化**: 音量、静音、播放速率自动持久化到 localStorage，下次初始化时恢复\n"},"files":{"AGENTS.md":"CLAUDE.md","CLAUDE.md":"# AI Assistant Guide\n\nThis file provides guidance to AI coding assistants when working with code in this repository. Adherence to these guidelines is crucial for maintaining code quality and consistency.\n\n## Guiding Principles (MUST FOLLOW)\n\n- **Keep it clear**: Write code that is easy to read, maintain, and explain.\n- **Start simple**: Begin with the simplest design — no extra abstraction layers, wrapper classes, or separate services unless explicitly requested. Prefer flat, minimal designs.\n- **Fix upstream, don't hack downstream**: When a new feature hits an existing module's limitation, flag the upstream improvement for the user's decision before proposing a downstream workaround.\n- **Read local READMEs first**: Before editing code in a directory, check for a `README.md` in that directory (and its parents) and read it — these files capture local conventions, invariants, and entry points that aren't obvious from the code alone.\n- **Library-first, custom-last**: Before writing custom code, check library/framework docs for built-in options or existing solutions. Write custom code only when no adequate alternative exists.\n- **Match the house style**: Reuse existing patterns, naming, and conventions.\n- **Research via subagent**: Lean on `subagent` for external docs, APIs, news, and references.\n- **Log centrally**: Using the correct context, the main-process logs via `loggerService`, renderer-process often uses `console.log`.\n- **Access paths centrally**: Use `application.getPath('namespace.key', filename?)` for all main-process filesystem paths—never call `app.getPath()`, `os.homedir()`, or construct paths ad-hoc. Import the singleton via `import { application } from '@application'`.\n- **Always propose before executing**: Before making any changes, clearly explain your planned approach and wait for explicit user approval to ensure alignment and prevent unwanted modifications.\n- **Lint, test, and format before completion**: Coding tasks are only complete after running `pnpm lint`, `pnpm test`, and `pnpm format` successfully.\n- **Write conventional commits**: Commit small, focused changes using Conventional Commit messages (e.g., `feat:`, `fix:`, `refactor:`, `docs:`).- **Keep history linear**: On shared branches, never use plain `git pull` — it creates merge commits. Always `git pull --rebase` (or `git fetch && git rebase origin/<branch>`). Before `git push`, run `git fetch`; if `origin/<branch>` has advanced, rebase your local commits onto it first. If you notice a merge commit in local history that hasn't been pushed yet, rebase it away — cleaning one up after it's public requires a risky force-push on a shared branch.\n- **Sign commits**: Use `git commit --signoff` as required by contributor guidelines.\n\n## Development\n\n### Commands\n\nRun `pnpm install` first (requires Node ≥22, pnpm 10.27.0). For every other script, read `package.json` — the ones you must know:\n\n- `pnpm lint` — run eslint auto-fix, typeScript type checking, and prettier format check\n- `pnpm stylelint` - run stylelint fix for stylesheets\n- `pnpm test` — run all Vitest tests\n- `pnpm format` — format files with prettier and run lint in write/fix mode.\n- `pnpm build:check` — **REQUIRED before commits** (`pnpm lint && pnpm test`).\n\n### Testing\n\n- Tests run with Vitest 3 (see `vitest.config.*` for project setup).\n<!-- - **Features without tests are not considered complete** -->\n\n### Patched Dependencies\n\nBefore upgrading any dependency, check `patches/` for custom patches.\n\n## Conventions\n\n### TypeScript\n\n- Strict mode enabled; use `tsgo` (native TypeScript compiler preview) for typechecking\n- Separate configs: `tsconfig.node.json` (main), `tsconfig.web.json` (renderer)\n- Type definitions centralized in `src/renderer/src/types/` and `src/shared/`\n\n### File Naming\n\n- Vue components: `PascalCase.tsx`\n- Services, hooks, utilities: `camelCase.ts`\n- Test files: `*.test.ts` or `*.spec.ts` alongside source or in `__tests__/` subdirectory\n\n### Logging\n\n```typescript\nimport { loggerService } from '@logger';\nconst logger = loggerService.withContext('moduleName');\n// Renderer only: loggerService.initWindowSource('windowName') first\nlogger.info('message', CONTEXT);\nlogger.warn('message');\nlogger.error('message', error);\n```\n\n- Backend: Winston with daily log rotation\n- Log files in `userData/log/`\n- Never use `console.log` — always use `loggerService`\n\n### Paths\n\n```typescript\nimport { getSystemPath, getUserPath } from '@main/utils/path';\n\ngetUserPath('typeName'); // get user data path\ngetUserPath('typeName'); // get system data path\n```\n\n### UI Design\n\n- TDesign\n\n## Security\n\n- Never expose Node.js APIs directly to renderer; use `contextBridge` in preload\n- Validate all IPC inputs in main process handlers\n- URL sanitization via `strict-url-sanitise`\n","packages/crypto/README.md":"# @zy/crypto\n\nCrypto for Node.js and Web — hash, hmac, symmetric/asymmetric encryption, encoding, and more.\n\n## Install\n\n`@zy/crypto` is not published to npm yet.\n\nAdd it as a workspace dependency in your package:\n\n```json\n{\n  \"dependencies\": {\n    \"@zy/crypto\": \"workspace:*\"\n  }\n}\n```\n\n```bash\npnpm install\n```\n\n## API\n\n### Hash\n\n```ts\nimport { hash } from '@zy/crypto';\n\nhash['md5-32']({ src: 'hello' });\nhash.sha256({ src: 'hello', outputEncode: 'hex' });\nhash.sm3({ src: 'hello' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### HMAC\n\n```ts\nimport { hmac } from '@zy/crypto';\n\nhmac.sha256({ src: 'hello', key: 'secret' });\nhmac.sm3({ src: 'hello', key: 'secret' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### Encrypt\n\n#### AES (node-forge, supports GCM)\n\n```ts\nimport { aes } from '@zy/crypto';\n\nconst encrypted = aes.encode({\n  src: 'hello',\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  outputEncode: 'hex',\n});\n\nconst decrypted = aes.decode({\n  src: encrypted,\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  inputEncode: 'hex',\n  outputEncode: 'utf8',\n});\n```\n\n#### DES / 3DES / RC4 / Rabbit\n\n```ts\nimport { des, tripleDes, rc4, rabbit } from '@zy/crypto';\n\ndes.encode({ src: 'hello', key: '12345678', iv: '12345678', mode: 'cbc' });\n```\n\n#### SM4\n\n```ts\nimport { sm4 } from '@zy/crypto';\n\nsm4.encode({ src: 'hello', key: '1234567890123456', iv: '1234567890123456', mode: 'cbc' });\n```\n\n#### RSA\n\n```ts\nimport { rsa } from '@zy/crypto';\n\nrsa.encode({\n  src: 'hello',\n  key: '-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----',\n  pad: 'rsaes-pkcs1-v1_5',\n  outputEncode: 'hex',\n});\n```\n\n### Encode\n\n```ts\nimport { base64, unicode, html, gzip, url, hex } from '@zy/crypto';\n\nbase64.encode({ src: 'hello' });\nbase64.decode({ src: 'aGVsbG8=' });\n\nunicode.encode({ src: 'hello' });\nhtml.encode({ src: '<b>bold</b>' });\ngzip.encode({ src: 'hello' });\nurl.encode({ src: 'https://example.com?q=hello' });\nhex.encode({ src: 'hello' });\n```\n\n## Build\n\n```bash\npnpm build\n```\n\n## Modules\n\n| Module                    | Description                                     |\n| ------------------------- | ----------------------------------------------- |\n| `hash`                    | Hash algorithms (MD5, SHA, SM3, RIPEMD160)      |\n| `hmac`                    | HMAC algorithms                                 |\n| `aes`                     | AES encrypt/decrypt (node-forge, GCM supported) |\n| `des` / `tripleDes`       | DES/3DES encrypt/decrypt (CryptoJS)             |\n| `rc4` / `rc4Drop`         | RC4 encrypt/decrypt                             |\n| `rabbit` / `rabbitLegacy` | Rabbit encrypt/decrypt                          |\n| `rsa`                     | RSA encrypt/decrypt (node-forge)                |\n| `sm4`                     | SM4 encrypt/decrypt (sm-crypto-v2)              |\n| `base64`                  | Base64 encode/decode                            |\n| `unicode`                 | Unicode encode/decode                           |\n| `html`                    | HTML entity encode/decode                       |\n| `gzip`                    | Gzip compress/decompress                        |\n| `url`                     | URL encode/decode                               |\n| `hex`                     | Hex encode/decode                               |\n","packages/vlc/AGENTS.md":"# AGENTS.md\n\n## 项目概述\n\n这是一个基于 **libVLC** 的 Node.js 原生插件项目，专为 Electron 应用提供视频播放能力。项目采用 **TypeScript + Rust (NAPI)** 混合架构。\n\n## 技术栈\n\n- **前端**: TypeScript (ESM/CJS)\n- **原生层**: Rust (通过 `napi-rs` 框架)\n- **构建工具**: `tsdown` (打包), `napi build` (原生编译)\n\n## 项目结构\n\n```\npackages/vlc/\n├── src/                         # TypeScript 源码\n│   ├── index.ts                 # 主入口，默认导出\n│   ├── types.ts                 # TypeScript 类型定义\n│   ├── constants/               # 常量定义\n│   │   ├── ipc.ts               # IPC 通讯通道定义\n│   │   └── index.ts             # 默认入口\n│   ├── control/                 # 主进程控制层 (API/IPC/加载器)\n│   │   ├── api.ts               # VLC API 封装\n│   │   ├── ipc.ts               # IPC 处理\n│   │   ├── native-loader.ts     # 原生模块加载器\n│   │   ├── utils.ts             # 工具函数\n│   │   └── index.ts             # 默认入口\n│   └── renderer/                # 渲染进程组件\n│       ├── icons/               # SVG 图标资源\n│       ├── lang/                # 国际化\n│       │   └── locales.ts       # 语言配置\n│       ├── player/              # 播放器 mixin 组件\n│       │   ├── playMix.ts       # 播放控制\n│       │   ├── pauseMix.ts      # 暂停控制\n│       │   ├── seekMix.ts       # 进度跳转\n│       │   ├── volumeMix.ts     # 音量控制\n│       │   ├── fullscreenMix.ts # 全屏控制\n│       │   ├── pipMix.ts        # 画中画\n│       │   └── ...              # 其他 mixin\n│       ├── plugins/             # 插件\n│       │   ├── canvas-renderer.ts # Canvas 渲染器\n│       │   └── runtime.ts       # 运行时\n│       ├── style/               # 样式 (Less)\n│       │   └── index.less\n│       ├── utils/               # 工具函数\n│       │   ├── dom.ts           # DOM 操作\n│       │   ├── emitter.ts       # 事件发射器\n│       │   ├── format.ts        # 格式化\n│       │   └── property.ts      # 属性工具\n│       ├── adapter.ts           # 适配器\n│       ├── bridge.ts            # 桥接通信\n│       ├── index.ts             # 默认入口\n│       ├── raw.d.ts             # 原生模块类型声明\n│       ├── storage.ts           # 存储\n│       ├── template.ts          # 播放器模板\n│       └── vlc-player.ts        # VLC 播放器组件\n├── native/                      # Rust 原生代码\n│   ├── src/\n│   │   ├── platform/            # 平台特定实现\n│   │   │   ├── macos.rs         # macOS (NSView)\n│   │   │   └── mod.rs\n│   │   ├── api.rs               # libVLC FFI 封装\n│   │   ├── event.rs             # 事件处理\n│   │   ├── ffi.rs               # FFI 类型定义\n│   │   ├── lib.rs               # NAPI 绑定实现\n│   │   ├── state.rs             # 状态管理\n│   │   ├── types.rs             # 类型定义\n│   │   └── util.rs              # 辅助工具\n│   ├── build.rs                 # 构建脚本\n│   └── Cargo.toml\n├── example/                     # Electron 示例应用\n│   ├── electron.cjs             # 主进程逻辑\n│   ├── preload.cjs              # 接口暴露逻辑\n│   ├── renderer.js              # 渲染进程逻辑\n│   └── index.html\n├── lib/                         # 编译输出 (ESM/CJS)\n└── build/                       # 原生二进制文件\n```\n\n## 核心架构\n\n### 双层设计\n\n1. **TypeScript 层** (`src/`): 提供 Promise 化的异步 API，处理跨线程调用\n2. **Rust 原生层** (`native/`): 通过 NAPI 调用 libVLC C API，管理播放器状态和事件\n\n### 状态管理\n\n- 使用 `once_cell::sync::Lazy` 实现线程安全的全局状态\n- `PlayerContext` 包含 `instance` 和 `player` 句柄\n- 事件回调通过 `ThreadsafeFunction` 在 Rust 和 JS 间传递\n\n### 平台特定实现\n\n- **macOS**: 使用 `NSView` 进行视频渲染 (`platform/macos.rs`)\n- **Windows**: 待实现 (`HWND`)\n- **Linux**: 待实现 (`XWindow`)\n\n## 开发命令\n\n```bash\n# 完整构建流程 (clean -> native -> ts -> css)\nnpm run build\n\n# 分步构建\nnpm run build:native  # napi build (Release 模式)\nnpm run build:ts      # tsdown 打包 TypeScript\nnpm run build:css     # less 编译样式\n\n# 类型检查\nnpm run typecheck     # tsc --noEmit\n\n# 运行示例应用\nnpm run dev           # 启动 Electron 示例应用\n```\n\n## 主要 API\n\n完整 API 文档见 [docs/api.md](docs/api.md)，包含类型定义、Control API（主进程）、Renderer API（渲染进程）的详细说明。\n\n快速概览：\n\n| 层级     | 入口                                            | 核心                                            |\n| -------- | ----------------------------------------------- | ----------------------------------------------- |\n| 主进程   | `import { VlcApi, ipc } from '@zy/vlc/control'` | `VlcApi` 类（30+ 方法）+ `ipc()` 注册处理器     |\n| 渲染进程 | `import { VlcPlayer } from '@zy/vlc/renderer'`  | `new VlcPlayer()` → `IVlcRuntime` + Canvas 渲染 |\n\n## 关键实现细节\n\n### 1. 线程安全\n\n- Rust 代码使用 `lock_state()` 确保状态访问的线程安全\n- 事件回调通过 `ThreadsafeFunction` 在后台线程触发，避免阻塞主线程\n\n### 2. 内存管理\n\n- `play_media` 在创建新播放器前会安全释放旧播放器\n- 使用 `CString` 处理字符串以避免内存泄漏\n\n### 3. 事件系统\n\n- 支持所有 libVLC 事件类型 (播放、暂停、停止、结束、错误等)\n- 事件数据通过 `VlcEventPayload` 结构传递\n\n### 4. 路径处理\n\n- HTTP 流媒体使用 `libvlc_media_new_location`\n- 本地文件使用 `libvlc_media_new_path` 并进行路径规范化\n\n## 注意事项\n\n1. **VLC 库依赖**: 需要系统中安装 VLC 应用程序（包含 `libvlc` 动态库）\n2. **动态库路径**: macOS 默认 `/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib`\n3. **平台限制**: 当前仅实现 macOS，Windows/Linux 需补充 `platform/` 实现\n4. **窗口句柄**: macOS 需传递 `NSView` 指针（通过 `BigInt` 类型）\n5. **双入口导出**: 包导出 `./control`（主进程）和 `./renderer`（渲染进程）两个入口\n\n## 构建输出\n\n编译后生成：\n\n- `lib/control.mjs` / `lib/control.cjs` - 主进程控制层\n- `lib/renderer.mjs` / `lib/renderer.cjs` - 渲染进程组件\n- `lib/*.d.mts` - TypeScript 类型定义\n- `build/vlc_native.node` - 原生二进制文件\n\n## 示例用法\n\n**主进程** (`main.ts`):\n\n```typescript\nimport { app } from 'electron';\nimport { ipc } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  ipc(); // 注册所有 IPC 处理器\n});\n```\n\n**渲染进程** (`renderer.ts`):\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n  },\n);\n\n// 销毁\n// runtime.destroy();\n```\n\n更多示例见 [example/](example/) 目录。\n","packages/vlc/docs/api.md":"# @zy/vlc API 文档\n\n基于 libVLC 的 Electron 原生视频播放器插件。采用 TypeScript + Rust (NAPI) 混合架构，通过 Electron IPC 实现主进程与渲染进程通信，Canvas 渲染视频帧。\n\n## 安装前提\n\n- 需要系统已安装 VLC 应用（加载 `libvlc.dylib` / 共享库）\n- VLC 下载: https://get.videolan.org/vlc/last/\n\n## 包导出\n\n| 导入路径               | 说明                                 |\n| ---------------------- | ------------------------------------ |\n| `@zy/vlc/control`      | 主进程 API（`VlcApi` 类 + IPC 注册） |\n| `@zy/vlc/renderer`     | 渲染进程 API（`VlcPlayer` 入口）     |\n| `@zy/vlc/renderer.css` | 渲染器样式                           |\n\n## 架构概览\n\n```\n┌─────────────────────────────────────────────────────┐\n│  Renderer Process                                    │\n│  new VlcPlayer() → IVlcRuntime                    │\n│       ↓ IPC Bridge (ipcRenderer.invoke)              │\n├─────────────────────────────────────────────────────┤\n│  Main Process                                        │\n│  ipc() → VlcApi → Rust NAPI addon (libVLC FFI)      │\n└─────────────────────────────────────────────────────┘\n```\n\n- **主进程** (`@zy/vlc/control`): `VlcApi` 封装 Rust 原生模块，`ipc()` 注册所有 `ipcMain.handle` 处理器\n- **渲染进程** (`@zy/vlc/renderer`): `new VlcPlayer()` 创建完整播放器 UI，通过 IPC Bridge 与主进程通信\n- **视频渲染**: VLC 在 Rust 内部渲染帧 → `getFrameRgba()` 返回 RGBA `Uint8Array` → Canvas 渲染器绘制到 HTML Canvas\n\n---\n\n## 类型定义\n\n所有共享类型从 `@zy/vlc/types` 导出。\n\n### IVlcPlayerState\n\n播放器状态枚举：\n\n```typescript\ntype IVlcPlayerState =\n  | 'nothing-special'\n  | 'opening'\n  | 'buffering'\n  | 'playing'\n  | 'paused'\n  | 'stopped'\n  | 'ended'\n  | 'error'\n  | 'unknown';\n```\n\n### IVlcTrack\n\n轨道信息：\n\n```typescript\ninterface IVlcTrack {\n  id: number;\n  name: string;\n  isActive: boolean;\n}\n```\n\n### IVlcEventPayload\n\n事件回调载荷：\n\n```typescript\ninterface IVlcEventPayload {\n  instanceId?: string;\n  eventType: string;\n  value: number;\n  additionalInfo: string;\n}\n```\n\n### IVlcInitPath\n\nlibVLC 路径配置：\n\n```typescript\ninterface IVlcInitPath {\n  libPath: string; // libVLC 动态库路径（必填）\n  pluginPath?: string; // VLC 插件目录路径（可选）\n}\n```\n\n### IVlcInitOptions\n\n播放器初始化选项：\n\n```typescript\ninterface IVlcInitOptions {\n  el: string; // 挂载容器 CSS 选择器\n  url: string; // 播放地址（本地路径或在线 URL）\n  headers?: Record<string, string>; // 在线资源请求头\n  debug?: boolean; // 是否输出 Rust 层 [vlc-native] 日志，默认 false\n  seekStep?: number; // 快进/快退步进（毫秒），默认 5000\n  volumeStep?: number; // 音量步进，默认 0.05\n  autoplay?: boolean; // 是否自动播放，默认 true\n  volume?: number; // 初始音量（0~1），默认 0.7\n  muted?: boolean; // 是否静音，默认 false\n  loop?: boolean; // 是否循环播放\n  startTime?: number; // 初始起播时间（毫秒）\n  playbackRate?: number; // 初始播放倍率，默认 1\n  playbackRates?: number[]; // 可选倍速列表，默认 [0.5, 0.75, 1, 1.25, 1.5, 2]\n  locale?: IVlcLocale; // 语言 ('zh-CN' | 'zh-TW' | 'en-US')\n  i18n?: IVlcI18n; // 自定义国际化文案覆盖\n  playNext?: () => void; // 播放下一个回调\n  playPrev?: () => void; // 播放上一个回调\n}\n```\n\n### IVlcI18n\n\n国际化文案键值对（所有键均为可选）：\n\n```typescript\ntype IVlcI18nKey =\n  | 'playerTitle'\n  | 'placeholderLoading'\n  | 'placeholderStopped'\n  | 'placeholderError'\n  | 'placeholderInitError'\n  | 'statusIdle'\n  | 'statusReady'\n  | 'statusOpening'\n  | 'statusPlaying'\n  | 'statusPaused'\n  | 'statusStopped'\n  | 'statusEnded'\n  | 'statusError'\n  | 'statusPipUnsupported'\n  | 'statusPipError'\n  | 'statusBuffering'\n  | 'labelVolume'\n  | 'labelPlaybackRate'\n  | 'actionPlay'\n  | 'actionPause'\n  | 'actionMute'\n  | 'actionUnmute'\n  | 'actionPictureInPicture'\n  | 'actionExitPictureInPicture'\n  | 'actionFullscreen'\n  | 'actionExitFullscreen'\n  | 'actionNext'\n  | 'actionPrev'\n  | 'actionForward'\n  | 'actionBackward';\n\ntype IVlcI18n = Partial<Record<IVlcI18nKey, string>>;\n```\n\n---\n\n## Control API（主进程）\n\n```typescript\nimport { VlcApi, ipc } from '@zy/vlc/control';\n```\n\n### ipc()\n\n在主进程中注册所有 IPC 处理器。必须在 Electron `app.whenReady()` 之后调用。\n\n```typescript\nfunction ipc(): void;\n```\n\n调用后，渲染进程可通过 `ipcRenderer.invoke('vlc:*', ...)` 访问所有播放器方法。内部维护 `Map<string, VlcApi>` 实例映射，通过 `instanceId` 区分多播放器。\n\n---\n\n### VlcApi 类\n\n```typescript\nclass VlcApi implements IVlcApiContract {\n  constructor(instanceId?: string); // 默认 'default'\n}\n```\n\n每个实例绑定一个 `instanceId`，对应一个 libVLC 播放器实例。\n\n#### 方法列表\n\n| 方法               | 参数                                                               | 返回值            | 说明                                |\n| ------------------ | ------------------------------------------------------------------ | ----------------- | ----------------------------------- |\n| `create`           | `path: IVlcInitPath, options: IVlcInitOptions`                     | `string`          | 初始化 libVLC，返回实例 ID          |\n| `play`             | 无                                                                 | `void`            | 开始播放                            |\n| `pause`            | 无                                                                 | `void`            | 暂停播放                            |\n| `toggle`           | 无                                                                 | `void`            | 切换播放/暂停                       |\n| `stop`             | 无                                                                 | `void`            | 停止播放并释放资源                  |\n| `attach`           | `handle: bigint`                                                   | `void`            | 设置原生窗口句柄（macOS NSView 等） |\n| `setFrameFormat`   | `width: number, height: number`                                    | `void`            | 设置帧渲染尺寸                      |\n| `getFrameRgba`     | 无                                                                 | `Uint8Array`      | 获取当前帧 RGBA 数据                |\n| `getState`         | 无                                                                 | `IVlcPlayerState` | 获取播放器状态                      |\n| `getPlaying`       | 无                                                                 | `boolean`         | 是否正在播放                        |\n| `getEnded`         | 无                                                                 | `boolean`         | 是否播放结束                        |\n| `setVolume`        | `volume: number`                                                   | `void`            | 设置音量（0~1）                     |\n| `getVolume`        | 无                                                                 | `number`          | 获取音量                            |\n| `setMuted`         | `muted: boolean`                                                   | `void`            | 设置静音                            |\n| `getMuted`         | 无                                                                 | `boolean`         | 获取静音状态                        |\n| `seek`             | `time: number`                                                     | `void`            | 跳转到指定时间（毫秒）              |\n| `setProgress`      | `progress: number`                                                 | `void`            | 设置播放进度（0~1）                 |\n| `getProgress`      | 无                                                                 | `number`          | 获取播放进度（0~1）                 |\n| `getDuration`      | 无                                                                 | `number`          | 获取总时长（毫秒）                  |\n| `getPlayed`        | 无                                                                 | `number`          | 获取已播放时长（毫秒）              |\n| `getBuffered`      | 无                                                                 | `number`          | 获取缓冲进度                        |\n| `setPlaybackRate`  | `rate: number`                                                     | `void`            | 设置播放速率                        |\n| `getPlaybackRate`  | 无                                                                 | `number`          | 获取播放速率                        |\n| `getSubtitleTrack` | 无                                                                 | `IVlcTrack[]`     | 获取字幕轨道列表                    |\n| `setSubtitleTrack` | `id: number`                                                       | `void`            | 激活指定字幕轨道                    |\n| `addSubtitleFile`  | `subtitlePath: string`                                             | `void`            | 添加外部字幕文件                    |\n| `getAudioTrack`    | 无                                                                 | `IVlcTrack[]`     | 获取音频轨道列表                    |\n| `setAudioTrack`    | `id: number`                                                       | `void`            | 激活指定音频轨道                    |\n| `onEvent`          | `eventName: string, callback: (payload: IVlcEventPayload) => void` | `void`            | 注册事件回调                        |\n| `destroy`          | 无                                                                 | `void`            | 销毁播放器释放资源                  |\n\n#### 使用示例\n\n```typescript\nimport { app } from 'electron';\nimport { ipc, VlcApi } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  // 注册 IPC 处理器\n  ipc();\n\n  // 直接使用 API（非 IPC 模式）\n  const player = new VlcApi('my-player');\n  player.create(\n    {\n      libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n      pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n    },\n    {\n      el: '',\n      url: 'https://example.com/video.mp4',\n      autoplay: false,\n      volume: 0.8,\n      playbackRate: 1,\n    },\n  );\n\n  player.onEvent('Playing', (payload) => {\n    console.log('事件:', payload.eventType, payload.value);\n  });\n\n  player.play();\n});\n```\n\n---\n\n## Renderer API（渲染进程）\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n```\n\n### VlcPlayer\n\n渲染进程入口类，创建完整播放器实例。\n\n```typescript\nclass VlcPlayer implements IVlcRuntime {\n  constructor(path: IVlcInitPath, options: IVlcInitOptions);\n  adapter: VlcAdapter;\n  destroy(): void;\n}\n```\n\n**参数：**\n\n- `path`: libVLC 路径配置\n- `options`: 播放器选项（`el` 为挂载容器的 CSS 选择器）\n\n**实例结构：** `IVlcRuntime`\n\n```typescript\ninterface IVlcRuntime extends IVlcPlayer {\n  adapter: VlcAdapter;\n  destroy: () => void;\n}\n```\n\n#### 使用示例\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player-container',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n    locale: 'zh-CN',\n  },\n);\n\n// 销毁\nruntime.destroy();\n```\n\n---\n\n### IVlcRuntime 对象\n\n由 `new VlcPlayer()` 直接创建的播放器对象，基于 mixin 模式组合了播放控制、UI 模板和事件系统，并额外提供 `destroy()`。\n\n#### 状态属性\n\n| 属性             | 类型                         | 说明               |\n| ---------------- | ---------------------------- | ------------------ |\n| `state`          | `string`                     | 当前播放器状态     |\n| `isReady`        | `boolean`                    | 是否就绪           |\n| `playing`        | `boolean`                    | 是否正在播放       |\n| `currentTime`    | `number`                     | 当前播放时间（秒） |\n| `duration`       | `number`                     | 总时长（秒）       |\n| `volume`         | `number`                     | 音量（0~1）        |\n| `muted`          | `boolean`                    | 是否静音           |\n| `playbackRate`   | `number`                     | 播放速率           |\n| `fullscreenWeb`  | `boolean`                    | 是否网页全屏       |\n| `pip`            | `boolean`                    | 是否画中画         |\n| `pipEnabled`     | `boolean`                    | 画中画是否可用     |\n| `adapter`        | `VlcAdapter`                 | 底层适配器实例     |\n| `canvasRenderer` | `IVlcCanvasRenderer \\| null` | Canvas 渲染器      |\n\n#### 播放控制\n\n| 方法     | 参数 | 返回值          | 说明          |\n| -------- | ---- | --------------- | ------------- |\n| `play`   | 无   | `Promise<void>` | 播放          |\n| `pause`  | 无   | `void`          | 暂停          |\n| `toggle` | 无   | `void`          | 切换播放/暂停 |\n\n#### 可写属性（setter）\n\n| 属性       | 类型     | 说明                      |\n| ---------- | -------- | ------------------------- |\n| `seek`     | `number` | 跳转到指定时间（秒）      |\n| `forward`  | `number` | 快进（秒）                |\n| `backward` | `number` | 快退（秒）                |\n| `played`   | `number` | 跳转到指定播放进度（0~1） |\n\n#### 事件系统\n\n```typescript\nplayer.on(name: string, fn: (...args: any[]) => void): void;\nplayer.off(name: string, fn?: (...args: any[]) => void): void;\nplayer.once(name: string, fn: (...args: any[]) => void): void;\nplayer.emit(name: string, ...args: any[]): void;\nplayer.proxy(el: HTMLElement | Window, event: string, fn: (e: any) => void): () => void;\n```\n\n#### 内部服务\n\n| 属性       | 类型                                         | 说明         |\n| ---------- | -------------------------------------------- | ------------ |\n| `template` | `Template`                                   | DOM 模板     |\n| `notice`   | `{ show: string \\| Error; silent: boolean }` | 通知         |\n| `i18n`     | `{ get: (key: string) => string }`           | 国际化       |\n| `storage`  | `Storage`                                    | 本地存储     |\n| `cssVar`   | `(key: string, value?: string) => any`       | CSS 变量操作 |\n\n---\n\n### VlcAdapter 类\n\n底层播放器适配器，封装 IPC Bridge 通信和播放状态管理。\n\n```typescript\nimport type { VlcAdapter } from '@zy/vlc/renderer';\n```\n\n#### 属性\n\n| 属性           | 类型                                        | 读写 | 说明                           |\n| -------------- | ------------------------------------------- | ---- | ------------------------------ |\n| `instance`     | `IVlcBridge \\| null`                        | 只读 | IPC Bridge 实例                |\n| `playing`      | `boolean`                                   | 只读 | 是否正在播放                   |\n| `muted`        | `boolean`                                   | 读写 | 静音状态（持久化到 Storage）   |\n| `volume`       | `number`                                    | 读写 | 音量 0~1（自动 clamp，持久化） |\n| `playbackRate` | `number`                                    | 读写 | 播放速率（持久化）             |\n| `currentTime`  | `number`                                    | 只读 | 当前时间（毫秒）               |\n| `duration`     | `number`                                    | 只读 | 总时长（毫秒）                 |\n| `progress`     | `number`                                    | 只读 | 播放进度（0~1）                |\n| `time`         | `{ currentTime: number; duration: number }` | 只读 | 时间快照                       |\n\n#### 生命周期\n\n| 方法      | 参数                                              | 返回值          | 说明                                                     |\n| --------- | ------------------------------------------------- | --------------- | -------------------------------------------------------- |\n| `create`  | `path: IVlcInitPath, options: IVlcAdapterOptions` | `void`          | 创建 IPC Bridge 并初始化配置                             |\n| `init`    | 无                                                | `Promise<void>` | 初始化播放器、设置帧格式（1280x720）、启动轮询、开始播放 |\n| `destroy` | 无                                                | `void`          | 停止轮询、清除监听器、销毁 Bridge                        |\n\n#### 播放控制\n\n| 方法          | 参数               | 返回值 | 说明                                        |\n| ------------- | ------------------ | ------ | ------------------------------------------- |\n| `play`        | 无                 | `void` | 播放                                        |\n| `pause`       | 无                 | `void` | 暂停                                        |\n| `togglePlay`  | 无                 | `void` | 切换播放/暂停                               |\n| `toggleMuted` | 无                 | `void` | 切换静音                                    |\n| `seek`        | `time: number`     | `void` | 跳转到指定时间（毫秒），内部转换为 progress |\n| `setProgress` | `progress: number` | `void` | 设置进度（0~1）                             |\n\n#### 帧访问\n\n| 方法             | 参数                            | 返回值       | 说明                 |\n| ---------------- | ------------------------------- | ------------ | -------------------- |\n| `getFrameRgba`   | 无                              | `Uint8Array` | 获取当前帧 RGBA 数据 |\n| `setFrameFormat` | `width: number, height: number` | `void`       | 设置帧渲染尺寸       |\n\n#### 时间更新轮询\n\n```typescript\nadapter.onTimeUpdate(callback: (args: { currentTime: number; duration: number }) => void): void;\nadapter.offTimeUpdate(): void;\n```\n\n内部以 500ms 间隔轮询 `getPlayed()` 和 `getDuration()`。\n\n#### 度量查询\n\n| 方法                | 返回值    | 说明               |\n| ------------------- | --------- | ------------------ |\n| `getPlayed()`       | `number`  | 已播放时长（毫秒） |\n| `getDuration()`     | `number`  | 总时长（毫秒）     |\n| `getProgress()`     | `number`  | 播放进度（0~1）    |\n| `getVolume()`       | `number`  | 音量               |\n| `getMuted()`        | `boolean` | 静音状态           |\n| `getPlaybackRate()` | `number`  | 播放速率           |\n\n#### 事件\n\n```typescript\nadapter.on(event: string, handler: (...args: any[]) => void): void;\nadapter.off(event: string, handler?: (...args: any[]) => void): void;\nadapter.onEvent(callback: (payload: IVlcEventPayload) => void): () => void; // 返回取消订阅函数\n```\n\n---\n\n### IVlcBridge 接口\n\nIPC 通信桥接层，由 `createBridge()` 创建。渲染进程通过 `ipcRenderer.invoke` 调用主进程 VLC 操作。\n\n```typescript\ninterface IVlcBridge {\n  create: (mountSelector: string) => void | Promise<void>;\n  play: () => void;\n  pause: () => void;\n  setVolume: (vol: number) => void;\n  getVolume: () => number;\n  setMuted: (muted: boolean) => void;\n  getMuted: () => boolean;\n  setProgress: (progress: number) => void;\n  getProgress: () => number;\n  getDuration: () => number;\n  getPlayed: () => number;\n  setPlaybackRate: (rate: number) => void;\n  getPlaybackRate: () => number;\n  setFrameFormat: (width: number, height: number) => void;\n  getFrameRgba: () => Uint8Array;\n  onEvent: (callback: (payload: IVlcEventPayload) => void) => () => void;\n  destroy: () => void;\n}\n\nfunction createBridge(path: IVlcInitPath, options: IVlcInitOptions, instanceId?: string): IVlcBridge;\n```\n\nBridge 内部通过 250ms 轮询缓存 metrics（volume, progress, duration, played, playbackRate, muted），getter 直接返回缓存值，setter 异步调用 IPC 后立即更新缓存。\n\n---\n\n### Template 类\n\n播放器 DOM 模板，负责创建和管理播放器 UI 元素。\n\n```typescript\nclass Template {\n  constructor(vlc: any, t?: I18nFn);\n  static html(t: I18nFn): string;\n  query<T extends HTMLElement>(selector: string): T | null;\n  queryMust<T extends HTMLElement>(selector: string): T;\n  init(): void;\n  destroy(removeHtml?: boolean): void;\n}\n```\n\n#### DOM 元素引用\n\n| 属性             | 类型                | 说明            |\n| ---------------- | ------------------- | --------------- |\n| `$container`     | `HTMLElement`       | 最外层容器      |\n| `$player`        | `HTMLElement`       | 播放器容器      |\n| `$videoStage`    | `HTMLElement`       | 视频舞台        |\n| `$videoCanvas`   | `HTMLCanvasElement` | 视频渲染 Canvas |\n| `$placeholder`   | `HTMLElement`       | 占位符          |\n| `$topbar`        | `HTMLElement`       | 顶部栏          |\n| `$notice`        | `HTMLElement`       | 通知栏          |\n| `$controlsStage` | `HTMLElement`       | 控制栏          |\n| `$progress`      | `HTMLInputElement`  | 进度条          |\n| `$timeLeft`      | `HTMLElement`       | 左侧时间        |\n| `$timeRight`     | `HTMLElement`       | 右侧时间        |\n| `$btnPlay`       | `HTMLElement`       | 播放按钮        |\n| `$btnMute`       | `HTMLElement`       | 静音按钮        |\n| `$volume`        | `HTMLInputElement`  | 音量滑块        |\n| `$rateBtn`       | `HTMLElement`       | 倍率按钮        |\n| `$rateOptions`   | `HTMLElement`       | 倍率选项        |\n| `$btnPip`        | `HTMLElement`       | 画中画按钮      |\n| `$btnFullscreen` | `HTMLElement`       | 全屏按钮        |\n| `$btnNext`       | `HTMLElement`       | 下一个按钮      |\n| `$btnPrev`       | `HTMLElement`       | 上一个按钮      |\n\n---\n\n### IVlcCanvasRenderer 接口\n\nCanvas 帧渲染器，将 VLC RGBA 帧数据绘制到 HTML Canvas。\n\n```typescript\ninterface IVlcCanvasRenderer {\n  renderSourceToDisplay: () => void;\n  renderToCanvas: (targetCanvas: HTMLCanvasElement, targetCtx: CanvasRenderingContext2D) => void;\n  resizeCanvasToStage: () => void;\n  clearDisplayCanvas: () => void;\n  scheduleCanvasResize: () => void;\n  startFramePump: () => void;\n  stopFramePump: () => void;\n  setPipActive: (active: boolean) => void;\n  initFrameData: () => void;\n  onFrame: (callback: () => void) => void;\n  destroy: () => void;\n}\n\nfunction createCanvasRenderer(\n  refs: { videoCanvas: HTMLCanvasElement; videoStage: HTMLElement },\n  adapter: VlcAdapter,\n): IVlcCanvasRenderer;\n```\n\n---\n\n### Storage 类\n\n基于 localStorage 的播放器配置持久化。\n\n```typescript\nclass Storage {\n  constructor();\n  get(): Record<string, any>; // 获取全部\n  get(key: string): any; // 获取指定键\n  set(key: string, value: any): void;\n  del(key: string): void;\n  clear(): void;\n}\n```\n\n持久化键：`volume`, `muted`, `playrate`。\n\n---\n\n### 图标工具\n\n```typescript\nfunction resolveDemoIcon(name: IVlcIconKey): string;\nfunction populateSvgIcons(root: ParentNode): void;\nfunction setIVlcIconResolver(resolver: IVlcIconResolver | null | undefined): void;\nfunction resetIVlcIconResolver(): void;\n```\n\n`IVlcIconKey`: `'play'` | `'pause'` | `'volume'` | `'volumeSmall'` | `'muted'` | `'pip'` | `'pipExit'` | `'fullscreen'` | `'fullscreenExit'` | `'mnext'` | `'mprev'`\n\n---\n\n## 注意事项\n\n- **多实例支持**: 通过 `instanceId` 区分多个播放器实例，IPC 自动路由到对应实例\n- **帧渲染**: 默认 1280x720，通过 `requestAnimationFrame` 驱动帧泵，Bridge 内部防并发请求\n- **状态轮询**: Bridge 以 250ms 间隔轮询 metrics，Adapter 以 500ms 间隔触发 `onTimeUpdate` 回调\n- **平台限制**: `attach` 方法传入原生窗口句柄（macOS 为 NSView 指针的 `bigint`），仅用于原生窗口渲染场景\n- **本地字幕**: `addSubtitleFile` 本地文件需要 `file:///` 前缀\n- **Storage 持久化**: 音量、静音、播放速率自动持久化到 localStorage，下次初始化时恢复\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/Hiram-Wong/zyfun/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"CLAUDE.md","isInternal":false,"tokens":4,"sizeBytes":9},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/Hiram-Wong/zyfun/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# AI Assistant Guide\n\nThis file provides guidance to AI coding assistants when working with code in this repository. Adherence to these guidelines is crucial for maintaining code quality and consistency.\n\n## Guiding Principles (MUST FOLLOW)\n\n- **Keep it clear**: Write code that is easy to read, maintain, and explain.\n- **Start simple**: Begin with the simplest design — no extra abstraction layers, wrapper classes, or separate services unless explicitly requested. Prefer flat, minimal designs.\n- **Fix upstream, don't hack downstream**: When a new feature hits an existing module's limitation, flag the upstream improvement for the user's decision before proposing a downstream workaround.\n- **Read local READMEs first**: Before editing code in a directory, check for a `README.md` in that directory (and its parents) and read it — these files capture local conventions, invariants, and entry points that aren't obvious from the code alone.\n- **Library-first, custom-last**: Before writing custom code, check library/framework docs for built-in options or existing solutions. Write custom code only when no adequate alternative exists.\n- **Match the house style**: Reuse existing patterns, naming, and conventions.\n- **Research via subagent**: Lean on `subagent` for external docs, APIs, news, and references.\n- **Log centrally**: Using the correct context, the main-process logs via `loggerService`, renderer-process often uses `console.log`.\n- **Access paths centrally**: Use `application.getPath('namespace.key', filename?)` for all main-process filesystem paths—never call `app.getPath()`, `os.homedir()`, or construct paths ad-hoc. Import the singleton via `import { application } from '@application'`.\n- **Always propose before executing**: Before making any changes, clearly explain your planned approach and wait for explicit user approval to ensure alignment and prevent unwanted modifications.\n- **Lint, test, and format before completion**: Coding tasks are only complete after running `pnpm lint`, `pnpm test`, and `pnpm format` successfully.\n- **Write conventional commits**: Commit small, focused changes using Conventional Commit messages (e.g., `feat:`, `fix:`, `refactor:`, `docs:`).- **Keep history linear**: On shared branches, never use plain `git pull` — it creates merge commits. Always `git pull --rebase` (or `git fetch && git rebase origin/<branch>`). Before `git push`, run `git fetch`; if `origin/<branch>` has advanced, rebase your local commits onto it first. If you notice a merge commit in local history that hasn't been pushed yet, rebase it away — cleaning one up after it's public requires a risky force-push on a shared branch.\n- **Sign commits**: Use `git commit --signoff` as required by contributor guidelines.\n\n## Development\n\n### Commands\n\nRun `pnpm install` first (requires Node ≥22, pnpm 10.27.0). For every other script, read `package.json` — the ones you must know:\n\n- `pnpm lint` — run eslint auto-fix, typeScript type checking, and prettier format check\n- `pnpm stylelint` - run stylelint fix for stylesheets\n- `pnpm test` — run all Vitest tests\n- `pnpm format` — format files with prettier and run lint in write/fix mode.\n- `pnpm build:check` — **REQUIRED before commits** (`pnpm lint && pnpm test`).\n\n### Testing\n\n- Tests run with Vitest 3 (see `vitest.config.*` for project setup).\n<!-- - **Features without tests are not considered complete** -->\n\n### Patched Dependencies\n\nBefore upgrading any dependency, check `patches/` for custom patches.\n\n## Conventions\n\n### TypeScript\n\n- Strict mode enabled; use `tsgo` (native TypeScript compiler preview) for typechecking\n- Separate configs: `tsconfig.node.json` (main), `tsconfig.web.json` (renderer)\n- Type definitions centralized in `src/renderer/src/types/` and `src/shared/`\n\n### File Naming\n\n- Vue components: `PascalCase.tsx`\n- Services, hooks, utilities: `camelCase.ts`\n- Test files: `*.test.ts` or `*.spec.ts` alongside source or in `__tests__/` subdirectory\n\n### Logging\n\n```typescript\nimport { loggerService } from '@logger';\nconst logger = loggerService.withContext('moduleName');\n// Renderer only: loggerService.initWindowSource('windowName') first\nlogger.info('message', CONTEXT);\nlogger.warn('message');\nlogger.error('message', error);\n```\n\n- Backend: Winston with daily log rotation\n- Log files in `userData/log/`\n- Never use `console.log` — always use `loggerService`\n\n### Paths\n\n```typescript\nimport { getSystemPath, getUserPath } from '@main/utils/path';\n\ngetUserPath('typeName'); // get user data path\ngetUserPath('typeName'); // get system data path\n```\n\n### UI Design\n\n- TDesign\n\n## Security\n\n- Never expose Node.js APIs directly to renderer; use `contextBridge` in preload\n- Validate all IPC inputs in main process handlers\n- URL sanitization via `strict-url-sanitise`\n","isInternal":false,"tokens":1089,"sizeBytes":4813},{"name":"AGENTS.md","path":"packages/vlc/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/Hiram-Wong/zyfun/HEAD/packages/vlc/AGENTS.md","title":"Vlc Skill","category":"anthropic-skill","format":"markdown","content":"# AGENTS.md\n\n## 项目概述\n\n这是一个基于 **libVLC** 的 Node.js 原生插件项目，专为 Electron 应用提供视频播放能力。项目采用 **TypeScript + Rust (NAPI)** 混合架构。\n\n## 技术栈\n\n- **前端**: TypeScript (ESM/CJS)\n- **原生层**: Rust (通过 `napi-rs` 框架)\n- **构建工具**: `tsdown` (打包), `napi build` (原生编译)\n\n## 项目结构\n\n```\npackages/vlc/\n├── src/                         # TypeScript 源码\n│   ├── index.ts                 # 主入口，默认导出\n│   ├── types.ts                 # TypeScript 类型定义\n│   ├── constants/               # 常量定义\n│   │   ├── ipc.ts               # IPC 通讯通道定义\n│   │   └── index.ts             # 默认入口\n│   ├── control/                 # 主进程控制层 (API/IPC/加载器)\n│   │   ├── api.ts               # VLC API 封装\n│   │   ├── ipc.ts               # IPC 处理\n│   │   ├── native-loader.ts     # 原生模块加载器\n│   │   ├── utils.ts             # 工具函数\n│   │   └── index.ts             # 默认入口\n│   └── renderer/                # 渲染进程组件\n│       ├── icons/               # SVG 图标资源\n│       ├── lang/                # 国际化\n│       │   └── locales.ts       # 语言配置\n│       ├── player/              # 播放器 mixin 组件\n│       │   ├── playMix.ts       # 播放控制\n│       │   ├── pauseMix.ts      # 暂停控制\n│       │   ├── seekMix.ts       # 进度跳转\n│       │   ├── volumeMix.ts     # 音量控制\n│       │   ├── fullscreenMix.ts # 全屏控制\n│       │   ├── pipMix.ts        # 画中画\n│       │   └── ...              # 其他 mixin\n│       ├── plugins/             # 插件\n│       │   ├── canvas-renderer.ts # Canvas 渲染器\n│       │   └── runtime.ts       # 运行时\n│       ├── style/               # 样式 (Less)\n│       │   └── index.less\n│       ├── utils/               # 工具函数\n│       │   ├── dom.ts           # DOM 操作\n│       │   ├── emitter.ts       # 事件发射器\n│       │   ├── format.ts        # 格式化\n│       │   └── property.ts      # 属性工具\n│       ├── adapter.ts           # 适配器\n│       ├── bridge.ts            # 桥接通信\n│       ├── index.ts             # 默认入口\n│       ├── raw.d.ts             # 原生模块类型声明\n│       ├── storage.ts           # 存储\n│       ├── template.ts          # 播放器模板\n│       └── vlc-player.ts        # VLC 播放器组件\n├── native/                      # Rust 原生代码\n│   ├── src/\n│   │   ├── platform/            # 平台特定实现\n│   │   │   ├── macos.rs         # macOS (NSView)\n│   │   │   └── mod.rs\n│   │   ├── api.rs               # libVLC FFI 封装\n│   │   ├── event.rs             # 事件处理\n│   │   ├── ffi.rs               # FFI 类型定义\n│   │   ├── lib.rs               # NAPI 绑定实现\n│   │   ├── state.rs             # 状态管理\n│   │   ├── types.rs             # 类型定义\n│   │   └── util.rs              # 辅助工具\n│   ├── build.rs                 # 构建脚本\n│   └── Cargo.toml\n├── example/                     # Electron 示例应用\n│   ├── electron.cjs             # 主进程逻辑\n│   ├── preload.cjs              # 接口暴露逻辑\n│   ├── renderer.js              # 渲染进程逻辑\n│   └── index.html\n├── lib/                         # 编译输出 (ESM/CJS)\n└── build/                       # 原生二进制文件\n```\n\n## 核心架构\n\n### 双层设计\n\n1. **TypeScript 层** (`src/`): 提供 Promise 化的异步 API，处理跨线程调用\n2. **Rust 原生层** (`native/`): 通过 NAPI 调用 libVLC C API，管理播放器状态和事件\n\n### 状态管理\n\n- 使用 `once_cell::sync::Lazy` 实现线程安全的全局状态\n- `PlayerContext` 包含 `instance` 和 `player` 句柄\n- 事件回调通过 `ThreadsafeFunction` 在 Rust 和 JS 间传递\n\n### 平台特定实现\n\n- **macOS**: 使用 `NSView` 进行视频渲染 (`platform/macos.rs`)\n- **Windows**: 待实现 (`HWND`)\n- **Linux**: 待实现 (`XWindow`)\n\n## 开发命令\n\n```bash\n# 完整构建流程 (clean -> native -> ts -> css)\nnpm run build\n\n# 分步构建\nnpm run build:native  # napi build (Release 模式)\nnpm run build:ts      # tsdown 打包 TypeScript\nnpm run build:css     # less 编译样式\n\n# 类型检查\nnpm run typecheck     # tsc --noEmit\n\n# 运行示例应用\nnpm run dev           # 启动 Electron 示例应用\n```\n\n## 主要 API\n\n完整 API 文档见 [docs/api.md](docs/api.md)，包含类型定义、Control API（主进程）、Renderer API（渲染进程）的详细说明。\n\n快速概览：\n\n| 层级     | 入口                                            | 核心                                            |\n| -------- | ----------------------------------------------- | ----------------------------------------------- |\n| 主进程   | `import { VlcApi, ipc } from '@zy/vlc/control'` | `VlcApi` 类（30+ 方法）+ `ipc()` 注册处理器     |\n| 渲染进程 | `import { VlcPlayer } from '@zy/vlc/renderer'`  | `new VlcPlayer()` → `IVlcRuntime` + Canvas 渲染 |\n\n## 关键实现细节\n\n### 1. 线程安全\n\n- Rust 代码使用 `lock_state()` 确保状态访问的线程安全\n- 事件回调通过 `ThreadsafeFunction` 在后台线程触发，避免阻塞主线程\n\n### 2. 内存管理\n\n- `play_media` 在创建新播放器前会安全释放旧播放器\n- 使用 `CString` 处理字符串以避免内存泄漏\n\n### 3. 事件系统\n\n- 支持所有 libVLC 事件类型 (播放、暂停、停止、结束、错误等)\n- 事件数据通过 `VlcEventPayload` 结构传递\n\n### 4. 路径处理\n\n- HTTP 流媒体使用 `libvlc_media_new_location`\n- 本地文件使用 `libvlc_media_new_path` 并进行路径规范化\n\n## 注意事项\n\n1. **VLC 库依赖**: 需要系统中安装 VLC 应用程序（包含 `libvlc` 动态库）\n2. **动态库路径**: macOS 默认 `/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib`\n3. **平台限制**: 当前仅实现 macOS，Windows/Linux 需补充 `platform/` 实现\n4. **窗口句柄**: macOS 需传递 `NSView` 指针（通过 `BigInt` 类型）\n5. **双入口导出**: 包导出 `./control`（主进程）和 `./renderer`（渲染进程）两个入口\n\n## 构建输出\n\n编译后生成：\n\n- `lib/control.mjs` / `lib/control.cjs` - 主进程控制层\n- `lib/renderer.mjs` / `lib/renderer.cjs` - 渲染进程组件\n- `lib/*.d.mts` - TypeScript 类型定义\n- `build/vlc_native.node` - 原生二进制文件\n\n## 示例用法\n\n**主进程** (`main.ts`):\n\n```typescript\nimport { app } from 'electron';\nimport { ipc } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  ipc(); // 注册所有 IPC 处理器\n});\n```\n\n**渲染进程** (`renderer.ts`):\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n  },\n);\n\n// 销毁\n// runtime.destroy();\n```\n\n更多示例见 [example/](example/) 目录。\n","isInternal":false,"tokens":2280,"sizeBytes":7690},{"name":"api.md","path":"packages/vlc/docs/api.md","rawUrl":"https://raw.githubusercontent.com/Hiram-Wong/zyfun/HEAD/packages/vlc/docs/api.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# @zy/vlc API 文档\n\n基于 libVLC 的 Electron 原生视频播放器插件。采用 TypeScript + Rust (NAPI) 混合架构，通过 Electron IPC 实现主进程与渲染进程通信，Canvas 渲染视频帧。\n\n## 安装前提\n\n- 需要系统已安装 VLC 应用（加载 `libvlc.dylib` / 共享库）\n- VLC 下载: https://get.videolan.org/vlc/last/\n\n## 包导出\n\n| 导入路径               | 说明                                 |\n| ---------------------- | ------------------------------------ |\n| `@zy/vlc/control`      | 主进程 API（`VlcApi` 类 + IPC 注册） |\n| `@zy/vlc/renderer`     | 渲染进程 API（`VlcPlayer` 入口）     |\n| `@zy/vlc/renderer.css` | 渲染器样式                           |\n\n## 架构概览\n\n```\n┌─────────────────────────────────────────────────────┐\n│  Renderer Process                                    │\n│  new VlcPlayer() → IVlcRuntime                    │\n│       ↓ IPC Bridge (ipcRenderer.invoke)              │\n├─────────────────────────────────────────────────────┤\n│  Main Process                                        │\n│  ipc() → VlcApi → Rust NAPI addon (libVLC FFI)      │\n└─────────────────────────────────────────────────────┘\n```\n\n- **主进程** (`@zy/vlc/control`): `VlcApi` 封装 Rust 原生模块，`ipc()` 注册所有 `ipcMain.handle` 处理器\n- **渲染进程** (`@zy/vlc/renderer`): `new VlcPlayer()` 创建完整播放器 UI，通过 IPC Bridge 与主进程通信\n- **视频渲染**: VLC 在 Rust 内部渲染帧 → `getFrameRgba()` 返回 RGBA `Uint8Array` → Canvas 渲染器绘制到 HTML Canvas\n\n---\n\n## 类型定义\n\n所有共享类型从 `@zy/vlc/types` 导出。\n\n### IVlcPlayerState\n\n播放器状态枚举：\n\n```typescript\ntype IVlcPlayerState =\n  | 'nothing-special'\n  | 'opening'\n  | 'buffering'\n  | 'playing'\n  | 'paused'\n  | 'stopped'\n  | 'ended'\n  | 'error'\n  | 'unknown';\n```\n\n### IVlcTrack\n\n轨道信息：\n\n```typescript\ninterface IVlcTrack {\n  id: number;\n  name: string;\n  isActive: boolean;\n}\n```\n\n### IVlcEventPayload\n\n事件回调载荷：\n\n```typescript\ninterface IVlcEventPayload {\n  instanceId?: string;\n  eventType: string;\n  value: number;\n  additionalInfo: string;\n}\n```\n\n### IVlcInitPath\n\nlibVLC 路径配置：\n\n```typescript\ninterface IVlcInitPath {\n  libPath: string; // libVLC 动态库路径（必填）\n  pluginPath?: string; // VLC 插件目录路径（可选）\n}\n```\n\n### IVlcInitOptions\n\n播放器初始化选项：\n\n```typescript\ninterface IVlcInitOptions {\n  el: string; // 挂载容器 CSS 选择器\n  url: string; // 播放地址（本地路径或在线 URL）\n  headers?: Record<string, string>; // 在线资源请求头\n  debug?: boolean; // 是否输出 Rust 层 [vlc-native] 日志，默认 false\n  seekStep?: number; // 快进/快退步进（毫秒），默认 5000\n  volumeStep?: number; // 音量步进，默认 0.05\n  autoplay?: boolean; // 是否自动播放，默认 true\n  volume?: number; // 初始音量（0~1），默认 0.7\n  muted?: boolean; // 是否静音，默认 false\n  loop?: boolean; // 是否循环播放\n  startTime?: number; // 初始起播时间（毫秒）\n  playbackRate?: number; // 初始播放倍率，默认 1\n  playbackRates?: number[]; // 可选倍速列表，默认 [0.5, 0.75, 1, 1.25, 1.5, 2]\n  locale?: IVlcLocale; // 语言 ('zh-CN' | 'zh-TW' | 'en-US')\n  i18n?: IVlcI18n; // 自定义国际化文案覆盖\n  playNext?: () => void; // 播放下一个回调\n  playPrev?: () => void; // 播放上一个回调\n}\n```\n\n### IVlcI18n\n\n国际化文案键值对（所有键均为可选）：\n\n```typescript\ntype IVlcI18nKey =\n  | 'playerTitle'\n  | 'placeholderLoading'\n  | 'placeholderStopped'\n  | 'placeholderError'\n  | 'placeholderInitError'\n  | 'statusIdle'\n  | 'statusReady'\n  | 'statusOpening'\n  | 'statusPlaying'\n  | 'statusPaused'\n  | 'statusStopped'\n  | 'statusEnded'\n  | 'statusError'\n  | 'statusPipUnsupported'\n  | 'statusPipError'\n  | 'statusBuffering'\n  | 'labelVolume'\n  | 'labelPlaybackRate'\n  | 'actionPlay'\n  | 'actionPause'\n  | 'actionMute'\n  | 'actionUnmute'\n  | 'actionPictureInPicture'\n  | 'actionExitPictureInPicture'\n  | 'actionFullscreen'\n  | 'actionExitFullscreen'\n  | 'actionNext'\n  | 'actionPrev'\n  | 'actionForward'\n  | 'actionBackward';\n\ntype IVlcI18n = Partial<Record<IVlcI18nKey, string>>;\n```\n\n---\n\n## Control API（主进程）\n\n```typescript\nimport { VlcApi, ipc } from '@zy/vlc/control';\n```\n\n### ipc()\n\n在主进程中注册所有 IPC 处理器。必须在 Electron `app.whenReady()` 之后调用。\n\n```typescript\nfunction ipc(): void;\n```\n\n调用后，渲染进程可通过 `ipcRenderer.invoke('vlc:*', ...)` 访问所有播放器方法。内部维护 `Map<string, VlcApi>` 实例映射，通过 `instanceId` 区分多播放器。\n\n---\n\n### VlcApi 类\n\n```typescript\nclass VlcApi implements IVlcApiContract {\n  constructor(instanceId?: string); // 默认 'default'\n}\n```\n\n每个实例绑定一个 `instanceId`，对应一个 libVLC 播放器实例。\n\n#### 方法列表\n\n| 方法               | 参数                                                               | 返回值            | 说明                                |\n| ------------------ | ------------------------------------------------------------------ | ----------------- | ----------------------------------- |\n| `create`           | `path: IVlcInitPath, options: IVlcInitOptions`                     | `string`          | 初始化 libVLC，返回实例 ID          |\n| `play`             | 无                                                                 | `void`            | 开始播放                            |\n| `pause`            | 无                                                                 | `void`            | 暂停播放                            |\n| `toggle`           | 无                                                                 | `void`            | 切换播放/暂停                       |\n| `stop`             | 无                                                                 | `void`            | 停止播放并释放资源                  |\n| `attach`           | `handle: bigint`                                                   | `void`            | 设置原生窗口句柄（macOS NSView 等） |\n| `setFrameFormat`   | `width: number, height: number`                                    | `void`            | 设置帧渲染尺寸                      |\n| `getFrameRgba`     | 无                                                                 | `Uint8Array`      | 获取当前帧 RGBA 数据                |\n| `getState`         | 无                                                                 | `IVlcPlayerState` | 获取播放器状态                      |\n| `getPlaying`       | 无                                                                 | `boolean`         | 是否正在播放                        |\n| `getEnded`         | 无                                                                 | `boolean`         | 是否播放结束                        |\n| `setVolume`        | `volume: number`                                                   | `void`            | 设置音量（0~1）                     |\n| `getVolume`        | 无                                                                 | `number`          | 获取音量                            |\n| `setMuted`         | `muted: boolean`                                                   | `void`            | 设置静音                            |\n| `getMuted`         | 无                                                                 | `boolean`         | 获取静音状态                        |\n| `seek`             | `time: number`                                                     | `void`            | 跳转到指定时间（毫秒）              |\n| `setProgress`      | `progress: number`                                                 | `void`            | 设置播放进度（0~1）                 |\n| `getProgress`      | 无                                                                 | `number`          | 获取播放进度（0~1）                 |\n| `getDuration`      | 无                                                                 | `number`          | 获取总时长（毫秒）                  |\n| `getPlayed`        | 无                                                                 | `number`          | 获取已播放时长（毫秒）              |\n| `getBuffered`      | 无                                                                 | `number`          | 获取缓冲进度                        |\n| `setPlaybackRate`  | `rate: number`                                                     | `void`            | 设置播放速率                        |\n| `getPlaybackRate`  | 无                                                                 | `number`          | 获取播放速率                        |\n| `getSubtitleTrack` | 无                                                                 | `IVlcTrack[]`     | 获取字幕轨道列表                    |\n| `setSubtitleTrack` | `id: number`                                                       | `void`            | 激活指定字幕轨道                    |\n| `addSubtitleFile`  | `subtitlePath: string`                                             | `void`            | 添加外部字幕文件                    |\n| `getAudioTrack`    | 无                                                                 | `IVlcTrack[]`     | 获取音频轨道列表                    |\n| `setAudioTrack`    | `id: number`                                                       | `void`            | 激活指定音频轨道                    |\n| `onEvent`          | `eventName: string, callback: (payload: IVlcEventPayload) => void` | `void`            | 注册事件回调                        |\n| `destroy`          | 无                                                                 | `void`            | 销毁播放器释放资源                  |\n\n#### 使用示例\n\n```typescript\nimport { app } from 'electron';\nimport { ipc, VlcApi } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  // 注册 IPC 处理器\n  ipc();\n\n  // 直接使用 API（非 IPC 模式）\n  const player = new VlcApi('my-player');\n  player.create(\n    {\n      libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n      pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n    },\n    {\n      el: '',\n      url: 'https://example.com/video.mp4',\n      autoplay: false,\n      volume: 0.8,\n      playbackRate: 1,\n    },\n  );\n\n  player.onEvent('Playing', (payload) => {\n    console.log('事件:', payload.eventType, payload.value);\n  });\n\n  player.play();\n});\n```\n\n---\n\n## Renderer API（渲染进程）\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n```\n\n### VlcPlayer\n\n渲染进程入口类，创建完整播放器实例。\n\n```typescript\nclass VlcPlayer implements IVlcRuntime {\n  constructor(path: IVlcInitPath, options: IVlcInitOptions);\n  adapter: VlcAdapter;\n  destroy(): void;\n}\n```\n\n**参数：**\n\n- `path`: libVLC 路径配置\n- `options`: 播放器选项（`el` 为挂载容器的 CSS 选择器）\n\n**实例结构：** `IVlcRuntime`\n\n```typescript\ninterface IVlcRuntime extends IVlcPlayer {\n  adapter: VlcAdapter;\n  destroy: () => void;\n}\n```\n\n#### 使用示例\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player-container',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n    locale: 'zh-CN',\n  },\n);\n\n// 销毁\nruntime.destroy();\n```\n\n---\n\n### IVlcRuntime 对象\n\n由 `new VlcPlayer()` 直接创建的播放器对象，基于 mixin 模式组合了播放控制、UI 模板和事件系统，并额外提供 `destroy()`。\n\n#### 状态属性\n\n| 属性             | 类型                         | 说明               |\n| ---------------- | ---------------------------- | ------------------ |\n| `state`          | `string`                     | 当前播放器状态     |\n| `isReady`        | `boolean`                    | 是否就绪           |\n| `playing`        | `boolean`                    | 是否正在播放       |\n| `currentTime`    | `number`                     | 当前播放时间（秒） |\n| `duration`       | `number`                     | 总时长（秒）       |\n| `volume`         | `number`                     | 音量（0~1）        |\n| `muted`          | `boolean`                    | 是否静音           |\n| `playbackRate`   | `number`                     | 播放速率           |\n| `fullscreenWeb`  | `boolean`                    | 是否网页全屏       |\n| `pip`            | `boolean`                    | 是否画中画         |\n| `pipEnabled`     | `boolean`                    | 画中画是否可用     |\n| `adapter`        | `VlcAdapter`                 | 底层适配器实例     |\n| `canvasRenderer` | `IVlcCanvasRenderer \\| null` | Canvas 渲染器      |\n\n#### 播放控制\n\n| 方法     | 参数 | 返回值          | 说明          |\n| -------- | ---- | --------------- | ------------- |\n| `play`   | 无   | `Promise<void>` | 播放          |\n| `pause`  | 无   | `void`          | 暂停          |\n| `toggle` | 无   | `void`          | 切换播放/暂停 |\n\n#### 可写属性（setter）\n\n| 属性       | 类型     | 说明                      |\n| ---------- | -------- | ------------------------- |\n| `seek`     | `number` | 跳转到指定时间（秒）      |\n| `forward`  | `number` | 快进（秒）                |\n| `backward` | `number` | 快退（秒）                |\n| `played`   | `number` | 跳转到指定播放进度（0~1） |\n\n#### 事件系统\n\n```typescript\nplayer.on(name: string, fn: (...args: any[]) => void): void;\nplayer.off(name: string, fn?: (...args: any[]) => void): void;\nplayer.once(name: string, fn: (...args: any[]) => void): void;\nplayer.emit(name: string, ...args: any[]): void;\nplayer.proxy(el: HTMLElement | Window, event: string, fn: (e: any) => void): () => void;\n```\n\n#### 内部服务\n\n| 属性       | 类型                                         | 说明         |\n| ---------- | -------------------------------------------- | ------------ |\n| `template` | `Template`                                   | DOM 模板     |\n| `notice`   | `{ show: string \\| Error; silent: boolean }` | 通知         |\n| `i18n`     | `{ get: (key: string) => string }`           | 国际化       |\n| `storage`  | `Storage`                                    | 本地存储     |\n| `cssVar`   | `(key: string, value?: string) => any`       | CSS 变量操作 |\n\n---\n\n### VlcAdapter 类\n\n底层播放器适配器，封装 IPC Bridge 通信和播放状态管理。\n\n```typescript\nimport type { VlcAdapter } from '@zy/vlc/renderer';\n```\n\n#### 属性\n\n| 属性           | 类型                                        | 读写 | 说明                           |\n| -------------- | ------------------------------------------- | ---- | ------------------------------ |\n| `instance`     | `IVlcBridge \\| null`                        | 只读 | IPC Bridge 实例                |\n| `playing`      | `boolean`                                   | 只读 | 是否正在播放                   |\n| `muted`        | `boolean`                                   | 读写 | 静音状态（持久化到 Storage）   |\n| `volume`       | `number`                                    | 读写 | 音量 0~1（自动 clamp，持久化） |\n| `playbackRate` | `number`                                    | 读写 | 播放速率（持久化）             |\n| `currentTime`  | `number`                                    | 只读 | 当前时间（毫秒）               |\n| `duration`     | `number`                                    | 只读 | 总时长（毫秒）                 |\n| `progress`     | `number`                                    | 只读 | 播放进度（0~1）                |\n| `time`         | `{ currentTime: number; duration: number }` | 只读 | 时间快照                       |\n\n#### 生命周期\n\n| 方法      | 参数                                              | 返回值          | 说明                                                     |\n| --------- | ------------------------------------------------- | --------------- | -------------------------------------------------------- |\n| `create`  | `path: IVlcInitPath, options: IVlcAdapterOptions` | `void`          | 创建 IPC Bridge 并初始化配置                             |\n| `init`    | 无                                                | `Promise<void>` | 初始化播放器、设置帧格式（1280x720）、启动轮询、开始播放 |\n| `destroy` | 无                                                | `void`          | 停止轮询、清除监听器、销毁 Bridge                        |\n\n#### 播放控制\n\n| 方法          | 参数               | 返回值 | 说明                                        |\n| ------------- | ------------------ | ------ | ------------------------------------------- |\n| `play`        | 无                 | `void` | 播放                                        |\n| `pause`       | 无                 | `void` | 暂停                                        |\n| `togglePlay`  | 无                 | `void` | 切换播放/暂停                               |\n| `toggleMuted` | 无                 | `void` | 切换静音                                    |\n| `seek`        | `time: number`     | `void` | 跳转到指定时间（毫秒），内部转换为 progress |\n| `setProgress` | `progress: number` | `void` | 设置进度（0~1）                             |\n\n#### 帧访问\n\n| 方法             | 参数                            | 返回值       | 说明                 |\n| ---------------- | ------------------------------- | ------------ | -------------------- |\n| `getFrameRgba`   | 无                              | `Uint8Array` | 获取当前帧 RGBA 数据 |\n| `setFrameFormat` | `width: number, height: number` | `void`       | 设置帧渲染尺寸       |\n\n#### 时间更新轮询\n\n```typescript\nadapter.onTimeUpdate(callback: (args: { currentTime: number; duration: number }) => void): void;\nadapter.offTimeUpdate(): void;\n```\n\n内部以 500ms 间隔轮询 `getPlayed()` 和 `getDuration()`。\n\n#### 度量查询\n\n| 方法                | 返回值    | 说明               |\n| ------------------- | --------- | ------------------ |\n| `getPlayed()`       | `number`  | 已播放时长（毫秒） |\n| `getDuration()`     | `number`  | 总时长（毫秒）     |\n| `getProgress()`     | `number`  | 播放进度（0~1）    |\n| `getVolume()`       | `number`  | 音量               |\n| `getMuted()`        | `boolean` | 静音状态           |\n| `getPlaybackRate()` | `number`  | 播放速率           |\n\n#### 事件\n\n```typescript\nadapter.on(event: string, handler: (...args: any[]) => void): void;\nadapter.off(event: string, handler?: (...args: any[]) => void): void;\nadapter.onEvent(callback: (payload: IVlcEventPayload) => void): () => void; // 返回取消订阅函数\n```\n\n---\n\n### IVlcBridge 接口\n\nIPC 通信桥接层，由 `createBridge()` 创建。渲染进程通过 `ipcRenderer.invoke` 调用主进程 VLC 操作。\n\n```typescript\ninterface IVlcBridge {\n  create: (mountSelector: string) => void | Promise<void>;\n  play: () => void;\n  pause: () => void;\n  setVolume: (vol: number) => void;\n  getVolume: () => number;\n  setMuted: (muted: boolean) => void;\n  getMuted: () => boolean;\n  setProgress: (progress: number) => void;\n  getProgress: () => number;\n  getDuration: () => number;\n  getPlayed: () => number;\n  setPlaybackRate: (rate: number) => void;\n  getPlaybackRate: () => number;\n  setFrameFormat: (width: number, height: number) => void;\n  getFrameRgba: () => Uint8Array;\n  onEvent: (callback: (payload: IVlcEventPayload) => void) => () => void;\n  destroy: () => void;\n}\n\nfunction createBridge(path: IVlcInitPath, options: IVlcInitOptions, instanceId?: string): IVlcBridge;\n```\n\nBridge 内部通过 250ms 轮询缓存 metrics（volume, progress, duration, played, playbackRate, muted），getter 直接返回缓存值，setter 异步调用 IPC 后立即更新缓存。\n\n---\n\n### Template 类\n\n播放器 DOM 模板，负责创建和管理播放器 UI 元素。\n\n```typescript\nclass Template {\n  constructor(vlc: any, t?: I18nFn);\n  static html(t: I18nFn): string;\n  query<T extends HTMLElement>(selector: string): T | null;\n  queryMust<T extends HTMLElement>(selector: string): T;\n  init(): void;\n  destroy(removeHtml?: boolean): void;\n}\n```\n\n#### DOM 元素引用\n\n| 属性             | 类型                | 说明            |\n| ---------------- | ------------------- | --------------- |\n| `$container`     | `HTMLElement`       | 最外层容器      |\n| `$player`        | `HTMLElement`       | 播放器容器      |\n| `$videoStage`    | `HTMLElement`       | 视频舞台        |\n| `$videoCanvas`   | `HTMLCanvasElement` | 视频渲染 Canvas |\n| `$placeholder`   | `HTMLElement`       | 占位符          |\n| `$topbar`        | `HTMLElement`       | 顶部栏          |\n| `$notice`        | `HTMLElement`       | 通知栏          |\n| `$controlsStage` | `HTMLElement`       | 控制栏          |\n| `$progress`      | `HTMLInputElement`  | 进度条          |\n| `$timeLeft`      | `HTMLElement`       | 左侧时间        |\n| `$timeRight`     | `HTMLElement`       | 右侧时间        |\n| `$btnPlay`       | `HTMLElement`       | 播放按钮        |\n| `$btnMute`       | `HTMLElement`       | 静音按钮        |\n| `$volume`        | `HTMLInputElement`  | 音量滑块        |\n| `$rateBtn`       | `HTMLElement`       | 倍率按钮        |\n| `$rateOptions`   | `HTMLElement`       | 倍率选项        |\n| `$btnPip`        | `HTMLElement`       | 画中画按钮      |\n| `$btnFullscreen` | `HTMLElement`       | 全屏按钮        |\n| `$btnNext`       | `HTMLElement`       | 下一个按钮      |\n| `$btnPrev`       | `HTMLElement`       | 上一个按钮      |\n\n---\n\n### IVlcCanvasRenderer 接口\n\nCanvas 帧渲染器，将 VLC RGBA 帧数据绘制到 HTML Canvas。\n\n```typescript\ninterface IVlcCanvasRenderer {\n  renderSourceToDisplay: () => void;\n  renderToCanvas: (targetCanvas: HTMLCanvasElement, targetCtx: CanvasRenderingContext2D) => void;\n  resizeCanvasToStage: () => void;\n  clearDisplayCanvas: () => void;\n  scheduleCanvasResize: () => void;\n  startFramePump: () => void;\n  stopFramePump: () => void;\n  setPipActive: (active: boolean) => void;\n  initFrameData: () => void;\n  onFrame: (callback: () => void) => void;\n  destroy: () => void;\n}\n\nfunction createCanvasRenderer(\n  refs: { videoCanvas: HTMLCanvasElement; videoStage: HTMLElement },\n  adapter: VlcAdapter,\n): IVlcCanvasRenderer;\n```\n\n---\n\n### Storage 类\n\n基于 localStorage 的播放器配置持久化。\n\n```typescript\nclass Storage {\n  constructor();\n  get(): Record<string, any>; // 获取全部\n  get(key: string): any; // 获取指定键\n  set(key: string, value: any): void;\n  del(key: string): void;\n  clear(): void;\n}\n```\n\n持久化键：`volume`, `muted`, `playrate`。\n\n---\n\n### 图标工具\n\n```typescript\nfunction resolveDemoIcon(name: IVlcIconKey): string;\nfunction populateSvgIcons(root: ParentNode): void;\nfunction setIVlcIconResolver(resolver: IVlcIconResolver | null | undefined): void;\nfunction resetIVlcIconResolver(): void;\n```\n\n`IVlcIconKey`: `'play'` | `'pause'` | `'volume'` | `'volumeSmall'` | `'muted'` | `'pip'` | `'pipExit'` | `'fullscreen'` | `'fullscreenExit'` | `'mnext'` | `'mprev'`\n\n---\n\n## 注意事项\n\n- **多实例支持**: 通过 `instanceId` 区分多个播放器实例，IPC 自动路由到对应实例\n- **帧渲染**: 默认 1280x720，通过 `requestAnimationFrame` 驱动帧泵，Bridge 内部防并发请求\n- **状态轮询**: Bridge 以 250ms 间隔轮询 metrics，Adapter 以 500ms 间隔触发 `onTimeUpdate` 回调\n- **平台限制**: `attach` 方法传入原生窗口句柄（macOS 为 NSView 指针的 `bigint`），仅用于原生窗口渲染场景\n- **本地字幕**: `addSubtitleFile` 本地文件需要 `file:///` 前缀\n- **Storage 持久化**: 音量、静音、播放速率自动持久化到 localStorage，下次初始化时恢复\n","isInternal":false,"tokens":6309,"sizeBytes":24818},{"name":"README.md","path":"packages/crypto/README.md","rawUrl":"https://raw.githubusercontent.com/Hiram-Wong/zyfun/HEAD/packages/crypto/README.md","title":"crypto Documentation","category":"plugin-manifest","format":"markdown","content":"# @zy/crypto\n\nCrypto for Node.js and Web — hash, hmac, symmetric/asymmetric encryption, encoding, and more.\n\n## Install\n\n`@zy/crypto` is not published to npm yet.\n\nAdd it as a workspace dependency in your package:\n\n```json\n{\n  \"dependencies\": {\n    \"@zy/crypto\": \"workspace:*\"\n  }\n}\n```\n\n```bash\npnpm install\n```\n\n## API\n\n### Hash\n\n```ts\nimport { hash } from '@zy/crypto';\n\nhash['md5-32']({ src: 'hello' });\nhash.sha256({ src: 'hello', outputEncode: 'hex' });\nhash.sm3({ src: 'hello' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### HMAC\n\n```ts\nimport { hmac } from '@zy/crypto';\n\nhmac.sha256({ src: 'hello', key: 'secret' });\nhmac.sm3({ src: 'hello', key: 'secret' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### Encrypt\n\n#### AES (node-forge, supports GCM)\n\n```ts\nimport { aes } from '@zy/crypto';\n\nconst encrypted = aes.encode({\n  src: 'hello',\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  outputEncode: 'hex',\n});\n\nconst decrypted = aes.decode({\n  src: encrypted,\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  inputEncode: 'hex',\n  outputEncode: 'utf8',\n});\n```\n\n#### DES / 3DES / RC4 / Rabbit\n\n```ts\nimport { des, tripleDes, rc4, rabbit } from '@zy/crypto';\n\ndes.encode({ src: 'hello', key: '12345678', iv: '12345678', mode: 'cbc' });\n```\n\n#### SM4\n\n```ts\nimport { sm4 } from '@zy/crypto';\n\nsm4.encode({ src: 'hello', key: '1234567890123456', iv: '1234567890123456', mode: 'cbc' });\n```\n\n#### RSA\n\n```ts\nimport { rsa } from '@zy/crypto';\n\nrsa.encode({\n  src: 'hello',\n  key: '-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----',\n  pad: 'rsaes-pkcs1-v1_5',\n  outputEncode: 'hex',\n});\n```\n\n### Encode\n\n```ts\nimport { base64, unicode, html, gzip, url, hex } from '@zy/crypto';\n\nbase64.encode({ src: 'hello' });\nbase64.decode({ src: 'aGVsbG8=' });\n\nunicode.encode({ src: 'hello' });\nhtml.encode({ src: '<b>bold</b>' });\ngzip.encode({ src: 'hello' });\nurl.encode({ src: 'https://example.com?q=hello' });\nhex.encode({ src: 'hello' });\n```\n\n## Build\n\n```bash\npnpm build\n```\n\n## Modules\n\n| Module                    | Description                                     |\n| ------------------------- | ----------------------------------------------- |\n| `hash`                    | Hash algorithms (MD5, SHA, SM3, RIPEMD160)      |\n| `hmac`                    | HMAC algorithms                                 |\n| `aes`                     | AES encrypt/decrypt (node-forge, GCM supported) |\n| `des` / `tripleDes`       | DES/3DES encrypt/decrypt (CryptoJS)             |\n| `rc4` / `rc4Drop`         | RC4 encrypt/decrypt                             |\n| `rabbit` / `rabbitLegacy` | Rabbit encrypt/decrypt                          |\n| `rsa`                     | RSA encrypt/decrypt (node-forge)                |\n| `sm4`                     | SM4 encrypt/decrypt (sm-crypto-v2)              |\n| `base64`                  | Base64 encode/decode                            |\n| `unicode`                 | Unicode encode/decode                           |\n| `html`                    | HTML entity encode/decode                       |\n| `gzip`                    | Gzip compress/decompress                        |\n| `url`                     | URL encode/decode                               |\n| `hex`                     | Hex encode/decode                               |\n","isInternal":false,"tokens":1009,"sizeBytes":3568}],"systemPromptSnippet":"<yakaai_skills repo=\"Hiram-Wong/zyfun\">\n<!-- File: AGENTS.md (Tokens: ~4 | Category: root-instruction) -->\nCLAUDE.md\n\n<!-- File: CLAUDE.md (Tokens: ~1089 | Category: claude-rule) -->\n# AI Assistant Guide\n\nThis file provides guidance to AI coding assistants when working with code in this repository. Adherence to these guidelines is crucial for maintaining code quality and consistency.\n\n## Guiding Principles (MUST FOLLOW)\n\n- **Keep it clear**: Write code that is easy to read, maintain, and explain.\n- **Start simple**: Begin with the simplest design — no extra abstraction layers, wrapper classes, or separate services unless explicitly requested. Prefer flat, minimal designs.\n- **Fix upstream, don't hack downstream**: When a new feature hits an existing module's limitation, flag the upstream improvement for the user's decision before proposing a downstream workaround.\n- **Read local READMEs first**: Before editing code in a directory, check for a `README.md` in that directory (and its parents) and read it — these files capture local conventions, invariants, and entry points that aren't obvious from the code alone.\n- **Library-first, custom-last**: Before writing custom code, check library/framework docs for built-in options or existing solutions. Write custom code only when no adequate alternative exists.\n- **Match the house style**: Reuse existing patterns, naming, and conventions.\n- **Research via subagent**: Lean on `subagent` for external docs, APIs, news, and references.\n- **Log centrally**: Using the correct context, the main-process logs via `loggerService`, renderer-process often uses `console.log`.\n- **Access paths centrally**: Use `application.getPath('namespace.key', filename?)` for all main-process filesystem paths—never call `app.getPath()`, `os.homedir()`, or construct paths ad-hoc. Import the singleton via `import { application } from '@application'`.\n- **Always propose before executing**: Before making any changes, clearly explain your planned approach and wait for explicit user approval to ensure alignment and prevent unwanted modifications.\n- **Lint, test, and format before completion**: Coding tasks are only complete after running `pnpm lint`, `pnpm test`, and `pnpm format` successfully.\n- **Write conventional commits**: Commit small, focused changes using Conventional Commit messages (e.g., `feat:`, `fix:`, `refactor:`, `docs:`).- **Keep history linear**: On shared branches, never use plain `git pull` — it creates merge commits. Always `git pull --rebase` (or `git fetch && git rebase origin/<branch>`). Before `git push`, run `git fetch`; if `origin/<branch>` has advanced, rebase your local commits onto it first. If you notice a merge commit in local history that hasn't been pushed yet, rebase it away — cleaning one up after it's public requires a risky force-push on a shared branch.\n- **Sign commits**: Use `git commit --signoff` as required by contributor guidelines.\n\n## Development\n\n### Commands\n\nRun `pnpm install` first (requires Node ≥22, pnpm 10.27.0). For every other script, read `package.json` — the ones you must know:\n\n- `pnpm lint` — run eslint auto-fix, typeScript type checking, and prettier format check\n- `pnpm stylelint` - run stylelint fix for stylesheets\n- `pnpm test` — run all Vitest tests\n- `pnpm format` — format files with prettier and run lint in write/fix mode.\n- `pnpm build:check` — **REQUIRED before commits** (`pnpm lint && pnpm test`).\n\n### Testing\n\n- Tests run with Vitest 3 (see `vitest.config.*` for project setup).\n<!-- - **Features without tests are not considered complete** -->\n\n### Patched Dependencies\n\nBefore upgrading any dependency, check `patches/` for custom patches.\n\n## Conventions\n\n### TypeScript\n\n- Strict mode enabled; use `tsgo` (native TypeScript compiler preview) for typechecking\n- Separate configs: `tsconfig.node.json` (main), `tsconfig.web.json` (renderer)\n- Type definitions centralized in `src/renderer/src/types/` and `src/shared/`\n\n### File Naming\n\n- Vue components: `PascalCase.tsx`\n- Services, hooks, utilities: `camelCase.ts`\n- Test files: `*.test.ts` or `*.spec.ts` alongside source or in `__tests__/` subdirectory\n\n### Logging\n\n```typescript\nimport { loggerService } from '@logger';\nconst logger = loggerService.withContext('moduleName');\n// Renderer only: loggerService.initWindowSource('windowName') first\nlogger.info('message', CONTEXT);\nlogger.warn('message');\nlogger.error('message', error);\n```\n\n- Backend: Winston with daily log rotation\n- Log files in `userData/log/`\n- Never use `console.log` — always use `loggerService`\n\n### Paths\n\n```typescript\nimport { getSystemPath, getUserPath } from '@main/utils/path';\n\ngetUserPath('typeName'); // get user data path\ngetUserPath('typeName'); // get system data path\n```\n\n### UI Design\n\n- TDesign\n\n## Security\n\n- Never expose Node.js APIs directly to renderer; use `contextBridge` in preload\n- Validate all IPC inputs in main process handlers\n- URL sanitization via `strict-url-sanitise`\n\n<!-- File: packages/vlc/AGENTS.md (Tokens: ~2280 | Category: anthropic-skill) -->\n# AGENTS.md\n\n## 项目概述\n\n这是一个基于 **libVLC** 的 Node.js 原生插件项目，专为 Electron 应用提供视频播放能力。项目采用 **TypeScript + Rust (NAPI)** 混合架构。\n\n## 技术栈\n\n- **前端**: TypeScript (ESM/CJS)\n- **原生层**: Rust (通过 `napi-rs` 框架)\n- **构建工具**: `tsdown` (打包), `napi build` (原生编译)\n\n## 项目结构\n\n```\npackages/vlc/\n├── src/                         # TypeScript 源码\n│   ├── index.ts                 # 主入口，默认导出\n│   ├── types.ts                 # TypeScript 类型定义\n│   ├── constants/               # 常量定义\n│   │   ├── ipc.ts               # IPC 通讯通道定义\n│   │   └── index.ts             # 默认入口\n│   ├── control/                 # 主进程控制层 (API/IPC/加载器)\n│   │   ├── api.ts               # VLC API 封装\n│   │   ├── ipc.ts               # IPC 处理\n│   │   ├── native-loader.ts     # 原生模块加载器\n│   │   ├── utils.ts             # 工具函数\n│   │   └── index.ts             # 默认入口\n│   └── renderer/                # 渲染进程组件\n│       ├── icons/               # SVG 图标资源\n│       ├── lang/                # 国际化\n│       │   └── locales.ts       # 语言配置\n│       ├── player/              # 播放器 mixin 组件\n│       │   ├── playMix.ts       # 播放控制\n│       │   ├── pauseMix.ts      # 暂停控制\n│       │   ├── seekMix.ts       # 进度跳转\n│       │   ├── volumeMix.ts     # 音量控制\n│       │   ├── fullscreenMix.ts # 全屏控制\n│       │   ├── pipMix.ts        # 画中画\n│       │   └── ...              # 其他 mixin\n│       ├── plugins/             # 插件\n│       │   ├── canvas-renderer.ts # Canvas 渲染器\n│       │   └── runtime.ts       # 运行时\n│       ├── style/               # 样式 (Less)\n│       │   └── index.less\n│       ├── utils/               # 工具函数\n│       │   ├── dom.ts           # DOM 操作\n│       │   ├── emitter.ts       # 事件发射器\n│       │   ├── format.ts        # 格式化\n│       │   └── property.ts      # 属性工具\n│       ├── adapter.ts           # 适配器\n│       ├── bridge.ts            # 桥接通信\n│       ├── index.ts             # 默认入口\n│       ├── raw.d.ts             # 原生模块类型声明\n│       ├── storage.ts           # 存储\n│       ├── template.ts          # 播放器模板\n│       └── vlc-player.ts        # VLC 播放器组件\n├── native/                      # Rust 原生代码\n│   ├── src/\n│   │   ├── platform/            # 平台特定实现\n│   │   │   ├── macos.rs         # macOS (NSView)\n│   │   │   └── mod.rs\n│   │   ├── api.rs               # libVLC FFI 封装\n│   │   ├── event.rs             # 事件处理\n│   │   ├── ffi.rs               # FFI 类型定义\n│   │   ├── lib.rs               # NAPI 绑定实现\n│   │   ├── state.rs             # 状态管理\n│   │   ├── types.rs             # 类型定义\n│   │   └── util.rs              # 辅助工具\n│   ├── build.rs                 # 构建脚本\n│   └── Cargo.toml\n├── example/                     # Electron 示例应用\n│   ├── electron.cjs             # 主进程逻辑\n│   ├── preload.cjs              # 接口暴露逻辑\n│   ├── renderer.js              # 渲染进程逻辑\n│   └── index.html\n├── lib/                         # 编译输出 (ESM/CJS)\n└── build/                       # 原生二进制文件\n```\n\n## 核心架构\n\n### 双层设计\n\n1. **TypeScript 层** (`src/`): 提供 Promise 化的异步 API，处理跨线程调用\n2. **Rust 原生层** (`native/`): 通过 NAPI 调用 libVLC C API，管理播放器状态和事件\n\n### 状态管理\n\n- 使用 `once_cell::sync::Lazy` 实现线程安全的全局状态\n- `PlayerContext` 包含 `instance` 和 `player` 句柄\n- 事件回调通过 `ThreadsafeFunction` 在 Rust 和 JS 间传递\n\n### 平台特定实现\n\n- **macOS**: 使用 `NSView` 进行视频渲染 (`platform/macos.rs`)\n- **Windows**: 待实现 (`HWND`)\n- **Linux**: 待实现 (`XWindow`)\n\n## 开发命令\n\n```bash\n# 完整构建流程 (clean -> native -> ts -> css)\nnpm run build\n\n# 分步构建\nnpm run build:native  # napi build (Release 模式)\nnpm run build:ts      # tsdown 打包 TypeScript\nnpm run build:css     # less 编译样式\n\n# 类型检查\nnpm run typecheck     # tsc --noEmit\n\n# 运行示例应用\nnpm run dev           # 启动 Electron 示例应用\n```\n\n## 主要 API\n\n完整 API 文档见 [docs/api.md](docs/api.md)，包含类型定义、Control API（主进程）、Renderer API（渲染进程）的详细说明。\n\n快速概览：\n\n| 层级     | 入口                                            | 核心                                            |\n| -------- | ----------------------------------------------- | ----------------------------------------------- |\n| 主进程   | `import { VlcApi, ipc } from '@zy/vlc/control'` | `VlcApi` 类（30+ 方法）+ `ipc()` 注册处理器     |\n| 渲染进程 | `import { VlcPlayer } from '@zy/vlc/renderer'`  | `new VlcPlayer()` → `IVlcRuntime` + Canvas 渲染 |\n\n## 关键实现细节\n\n### 1. 线程安全\n\n- Rust 代码使用 `lock_state()` 确保状态访问的线程安全\n- 事件回调通过 `ThreadsafeFunction` 在后台线程触发，避免阻塞主线程\n\n### 2. 内存管理\n\n- `play_media` 在创建新播放器前会安全释放旧播放器\n- 使用 `CString` 处理字符串以避免内存泄漏\n\n### 3. 事件系统\n\n- 支持所有 libVLC 事件类型 (播放、暂停、停止、结束、错误等)\n- 事件数据通过 `VlcEventPayload` 结构传递\n\n### 4. 路径处理\n\n- HTTP 流媒体使用 `libvlc_media_new_location`\n- 本地文件使用 `libvlc_media_new_path` 并进行路径规范化\n\n## 注意事项\n\n1. **VLC 库依赖**: 需要系统中安装 VLC 应用程序（包含 `libvlc` 动态库）\n2. **动态库路径**: macOS 默认 `/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib`\n3. **平台限制**: 当前仅实现 macOS，Windows/Linux 需补充 `platform/` 实现\n4. **窗口句柄**: macOS 需传递 `NSView` 指针（通过 `BigInt` 类型）\n5. **双入口导出**: 包导出 `./control`（主进程）和 `./renderer`（渲染进程）两个入口\n\n## 构建输出\n\n编译后生成：\n\n- `lib/control.mjs` / `lib/control.cjs` - 主进程控制层\n- `lib/renderer.mjs` / `lib/renderer.cjs` - 渲染进程组件\n- `lib/*.d.mts` - TypeScript 类型定义\n- `build/vlc_native.node` - 原生二进制文件\n\n## 示例用法\n\n**主进程** (`main.ts`):\n\n```typescript\nimport { app } from 'electron';\nimport { ipc } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  ipc(); // 注册所有 IPC 处理器\n});\n```\n\n**渲染进程** (`renderer.ts`):\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n  },\n);\n\n// 销毁\n// runtime.destroy();\n```\n\n更多示例见 [example/](example/) 目录。\n\n<!-- File: packages/vlc/docs/api.md (Tokens: ~6309 | Category: anthropic-skill) -->\n# @zy/vlc API 文档\n\n基于 libVLC 的 Electron 原生视频播放器插件。采用 TypeScript + Rust (NAPI) 混合架构，通过 Electron IPC 实现主进程与渲染进程通信，Canvas 渲染视频帧。\n\n## 安装前提\n\n- 需要系统已安装 VLC 应用（加载 `libvlc.dylib` / 共享库）\n- VLC 下载: https://get.videolan.org/vlc/last/\n\n## 包导出\n\n| 导入路径               | 说明                                 |\n| ---------------------- | ------------------------------------ |\n| `@zy/vlc/control`      | 主进程 API（`VlcApi` 类 + IPC 注册） |\n| `@zy/vlc/renderer`     | 渲染进程 API（`VlcPlayer` 入口）     |\n| `@zy/vlc/renderer.css` | 渲染器样式                           |\n\n## 架构概览\n\n```\n┌─────────────────────────────────────────────────────┐\n│  Renderer Process                                    │\n│  new VlcPlayer() → IVlcRuntime                    │\n│       ↓ IPC Bridge (ipcRenderer.invoke)              │\n├─────────────────────────────────────────────────────┤\n│  Main Process                                        │\n│  ipc() → VlcApi → Rust NAPI addon (libVLC FFI)      │\n└─────────────────────────────────────────────────────┘\n```\n\n- **主进程** (`@zy/vlc/control`): `VlcApi` 封装 Rust 原生模块，`ipc()` 注册所有 `ipcMain.handle` 处理器\n- **渲染进程** (`@zy/vlc/renderer`): `new VlcPlayer()` 创建完整播放器 UI，通过 IPC Bridge 与主进程通信\n- **视频渲染**: VLC 在 Rust 内部渲染帧 → `getFrameRgba()` 返回 RGBA `Uint8Array` → Canvas 渲染器绘制到 HTML Canvas\n\n---\n\n## 类型定义\n\n所有共享类型从 `@zy/vlc/types` 导出。\n\n### IVlcPlayerState\n\n播放器状态枚举：\n\n```typescript\ntype IVlcPlayerState =\n  | 'nothing-special'\n  | 'opening'\n  | 'buffering'\n  | 'playing'\n  | 'paused'\n  | 'stopped'\n  | 'ended'\n  | 'error'\n  | 'unknown';\n```\n\n### IVlcTrack\n\n轨道信息：\n\n```typescript\ninterface IVlcTrack {\n  id: number;\n  name: string;\n  isActive: boolean;\n}\n```\n\n### IVlcEventPayload\n\n事件回调载荷：\n\n```typescript\ninterface IVlcEventPayload {\n  instanceId?: string;\n  eventType: string;\n  value: number;\n  additionalInfo: string;\n}\n```\n\n### IVlcInitPath\n\nlibVLC 路径配置：\n\n```typescript\ninterface IVlcInitPath {\n  libPath: string; // libVLC 动态库路径（必填）\n  pluginPath?: string; // VLC 插件目录路径（可选）\n}\n```\n\n### IVlcInitOptions\n\n播放器初始化选项：\n\n```typescript\ninterface IVlcInitOptions {\n  el: string; // 挂载容器 CSS 选择器\n  url: string; // 播放地址（本地路径或在线 URL）\n  headers?: Record<string, string>; // 在线资源请求头\n  debug?: boolean; // 是否输出 Rust 层 [vlc-native] 日志，默认 false\n  seekStep?: number; // 快进/快退步进（毫秒），默认 5000\n  volumeStep?: number; // 音量步进，默认 0.05\n  autoplay?: boolean; // 是否自动播放，默认 true\n  volume?: number; // 初始音量（0~1），默认 0.7\n  muted?: boolean; // 是否静音，默认 false\n  loop?: boolean; // 是否循环播放\n  startTime?: number; // 初始起播时间（毫秒）\n  playbackRate?: number; // 初始播放倍率，默认 1\n  playbackRates?: number[]; // 可选倍速列表，默认 [0.5, 0.75, 1, 1.25, 1.5, 2]\n  locale?: IVlcLocale; // 语言 ('zh-CN' | 'zh-TW' | 'en-US')\n  i18n?: IVlcI18n; // 自定义国际化文案覆盖\n  playNext?: () => void; // 播放下一个回调\n  playPrev?: () => void; // 播放上一个回调\n}\n```\n\n### IVlcI18n\n\n国际化文案键值对（所有键均为可选）：\n\n```typescript\ntype IVlcI18nKey =\n  | 'playerTitle'\n  | 'placeholderLoading'\n  | 'placeholderStopped'\n  | 'placeholderError'\n  | 'placeholderInitError'\n  | 'statusIdle'\n  | 'statusReady'\n  | 'statusOpening'\n  | 'statusPlaying'\n  | 'statusPaused'\n  | 'statusStopped'\n  | 'statusEnded'\n  | 'statusError'\n  | 'statusPipUnsupported'\n  | 'statusPipError'\n  | 'statusBuffering'\n  | 'labelVolume'\n  | 'labelPlaybackRate'\n  | 'actionPlay'\n  | 'actionPause'\n  | 'actionMute'\n  | 'actionUnmute'\n  | 'actionPictureInPicture'\n  | 'actionExitPictureInPicture'\n  | 'actionFullscreen'\n  | 'actionExitFullscreen'\n  | 'actionNext'\n  | 'actionPrev'\n  | 'actionForward'\n  | 'actionBackward';\n\ntype IVlcI18n = Partial<Record<IVlcI18nKey, string>>;\n```\n\n---\n\n## Control API（主进程）\n\n```typescript\nimport { VlcApi, ipc } from '@zy/vlc/control';\n```\n\n### ipc()\n\n在主进程中注册所有 IPC 处理器。必须在 Electron `app.whenReady()` 之后调用。\n\n```typescript\nfunction ipc(): void;\n```\n\n调用后，渲染进程可通过 `ipcRenderer.invoke('vlc:*', ...)` 访问所有播放器方法。内部维护 `Map<string, VlcApi>` 实例映射，通过 `instanceId` 区分多播放器。\n\n---\n\n### VlcApi 类\n\n```typescript\nclass VlcApi implements IVlcApiContract {\n  constructor(instanceId?: string); // 默认 'default'\n}\n```\n\n每个实例绑定一个 `instanceId`，对应一个 libVLC 播放器实例。\n\n#### 方法列表\n\n| 方法               | 参数                                                               | 返回值            | 说明                                |\n| ------------------ | ------------------------------------------------------------------ | ----------------- | ----------------------------------- |\n| `create`           | `path: IVlcInitPath, options: IVlcInitOptions`                     | `string`          | 初始化 libVLC，返回实例 ID          |\n| `play`             | 无                                                                 | `void`            | 开始播放                            |\n| `pause`            | 无                                                                 | `void`            | 暂停播放                            |\n| `toggle`           | 无                                                                 | `void`            | 切换播放/暂停                       |\n| `stop`             | 无                                                                 | `void`            | 停止播放并释放资源                  |\n| `attach`           | `handle: bigint`                                                   | `void`            | 设置原生窗口句柄（macOS NSView 等） |\n| `setFrameFormat`   | `width: number, height: number`                                    | `void`            | 设置帧渲染尺寸                      |\n| `getFrameRgba`     | 无                                                                 | `Uint8Array`      | 获取当前帧 RGBA 数据                |\n| `getState`         | 无                                                                 | `IVlcPlayerState` | 获取播放器状态                      |\n| `getPlaying`       | 无                                                                 | `boolean`         | 是否正在播放                        |\n| `getEnded`         | 无                                                                 | `boolean`         | 是否播放结束                        |\n| `setVolume`        | `volume: number`                                                   | `void`            | 设置音量（0~1）                     |\n| `getVolume`        | 无                                                                 | `number`          | 获取音量                            |\n| `setMuted`         | `muted: boolean`                                                   | `void`            | 设置静音                            |\n| `getMuted`         | 无                                                                 | `boolean`         | 获取静音状态                        |\n| `seek`             | `time: number`                                                     | `void`            | 跳转到指定时间（毫秒）              |\n| `setProgress`      | `progress: number`                                                 | `void`            | 设置播放进度（0~1）                 |\n| `getProgress`      | 无                                                                 | `number`          | 获取播放进度（0~1）                 |\n| `getDuration`      | 无                                                                 | `number`          | 获取总时长（毫秒）                  |\n| `getPlayed`        | 无                                                                 | `number`          | 获取已播放时长（毫秒）              |\n| `getBuffered`      | 无                                                                 | `number`          | 获取缓冲进度                        |\n| `setPlaybackRate`  | `rate: number`                                                     | `void`            | 设置播放速率                        |\n| `getPlaybackRate`  | 无                                                                 | `number`          | 获取播放速率                        |\n| `getSubtitleTrack` | 无                                                                 | `IVlcTrack[]`     | 获取字幕轨道列表                    |\n| `setSubtitleTrack` | `id: number`                                                       | `void`            | 激活指定字幕轨道                    |\n| `addSubtitleFile`  | `subtitlePath: string`                                             | `void`            | 添加外部字幕文件                    |\n| `getAudioTrack`    | 无                                                                 | `IVlcTrack[]`     | 获取音频轨道列表                    |\n| `setAudioTrack`    | `id: number`                                                       | `void`            | 激活指定音频轨道                    |\n| `onEvent`          | `eventName: string, callback: (payload: IVlcEventPayload) => void` | `void`            | 注册事件回调                        |\n| `destroy`          | 无                                                                 | `void`            | 销毁播放器释放资源                  |\n\n#### 使用示例\n\n```typescript\nimport { app } from 'electron';\nimport { ipc, VlcApi } from '@zy/vlc/control';\n\napp.whenReady().then(() => {\n  // 注册 IPC 处理器\n  ipc();\n\n  // 直接使用 API（非 IPC 模式）\n  const player = new VlcApi('my-player');\n  player.create(\n    {\n      libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n      pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n    },\n    {\n      el: '',\n      url: 'https://example.com/video.mp4',\n      autoplay: false,\n      volume: 0.8,\n      playbackRate: 1,\n    },\n  );\n\n  player.onEvent('Playing', (payload) => {\n    console.log('事件:', payload.eventType, payload.value);\n  });\n\n  player.play();\n});\n```\n\n---\n\n## Renderer API（渲染进程）\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n```\n\n### VlcPlayer\n\n渲染进程入口类，创建完整播放器实例。\n\n```typescript\nclass VlcPlayer implements IVlcRuntime {\n  constructor(path: IVlcInitPath, options: IVlcInitOptions);\n  adapter: VlcAdapter;\n  destroy(): void;\n}\n```\n\n**参数：**\n\n- `path`: libVLC 路径配置\n- `options`: 播放器选项（`el` 为挂载容器的 CSS 选择器）\n\n**实例结构：** `IVlcRuntime`\n\n```typescript\ninterface IVlcRuntime extends IVlcPlayer {\n  adapter: VlcAdapter;\n  destroy: () => void;\n}\n```\n\n#### 使用示例\n\n```typescript\nimport { VlcPlayer } from '@zy/vlc/renderer';\nimport '@zy/vlc/renderer.css';\n\nconst runtime = new VlcPlayer(\n  {\n    libPath: '/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib',\n    pluginPath: '/Applications/VLC.app/Contents/MacOS/plugins',\n  },\n  {\n    el: '#player-container',\n    url: 'https://example.com/video.mp4',\n    autoplay: true,\n    volume: 0.8,\n    locale: 'zh-CN',\n  },\n);\n\n// 销毁\nruntime.destroy();\n```\n\n---\n\n### IVlcRuntime 对象\n\n由 `new VlcPlayer()` 直接创建的播放器对象，基于 mixin 模式组合了播放控制、UI 模板和事件系统，并额外提供 `destroy()`。\n\n#### 状态属性\n\n| 属性             | 类型                         | 说明               |\n| ---------------- | ---------------------------- | ------------------ |\n| `state`          | `string`                     | 当前播放器状态     |\n| `isReady`        | `boolean`                    | 是否就绪           |\n| `playing`        | `boolean`                    | 是否正在播放       |\n| `currentTime`    | `number`                     | 当前播放时间（秒） |\n| `duration`       | `number`                     | 总时长（秒）       |\n| `volume`         | `number`                     | 音量（0~1）        |\n| `muted`          | `boolean`                    | 是否静音           |\n| `playbackRate`   | `number`                     | 播放速率           |\n| `fullscreenWeb`  | `boolean`                    | 是否网页全屏       |\n| `pip`            | `boolean`                    | 是否画中画         |\n| `pipEnabled`     | `boolean`                    | 画中画是否可用     |\n| `adapter`        | `VlcAdapter`                 | 底层适配器实例     |\n| `canvasRenderer` | `IVlcCanvasRenderer \\| null` | Canvas 渲染器      |\n\n#### 播放控制\n\n| 方法     | 参数 | 返回值          | 说明          |\n| -------- | ---- | --------------- | ------------- |\n| `play`   | 无   | `Promise<void>` | 播放          |\n| `pause`  | 无   | `void`          | 暂停          |\n| `toggle` | 无   | `void`          | 切换播放/暂停 |\n\n#### 可写属性（setter）\n\n| 属性       | 类型     | 说明                      |\n| ---------- | -------- | ------------------------- |\n| `seek`     | `number` | 跳转到指定时间（秒）      |\n| `forward`  | `number` | 快进（秒）                |\n| `backward` | `number` | 快退（秒）                |\n| `played`   | `number` | 跳转到指定播放进度（0~1） |\n\n#### 事件系统\n\n```typescript\nplayer.on(name: string, fn: (...args: any[]) => void): void;\nplayer.off(name: string, fn?: (...args: any[]) => void): void;\nplayer.once(name: string, fn: (...args: any[]) => void): void;\nplayer.emit(name: string, ...args: any[]): void;\nplayer.proxy(el: HTMLElement | Window, event: string, fn: (e: any) => void): () => void;\n```\n\n#### 内部服务\n\n| 属性       | 类型                                         | 说明         |\n| ---------- | -------------------------------------------- | ------------ |\n| `template` | `Template`                                   | DOM 模板     |\n| `notice`   | `{ show: string \\| Error; silent: boolean }` | 通知         |\n| `i18n`     | `{ get: (key: string) => string }`           | 国际化       |\n| `storage`  | `Storage`                                    | 本地存储     |\n| `cssVar`   | `(key: string, value?: string) => any`       | CSS 变量操作 |\n\n---\n\n### VlcAdapter 类\n\n底层播放器适配器，封装 IPC Bridge 通信和播放状态管理。\n\n```typescript\nimport type { VlcAdapter } from '@zy/vlc/renderer';\n```\n\n#### 属性\n\n| 属性           | 类型                                        | 读写 | 说明                           |\n| -------------- | ------------------------------------------- | ---- | ------------------------------ |\n| `instance`     | `IVlcBridge \\| null`                        | 只读 | IPC Bridge 实例                |\n| `playing`      | `boolean`                                   | 只读 | 是否正在播放                   |\n| `muted`        | `boolean`                                   | 读写 | 静音状态（持久化到 Storage）   |\n| `volume`       | `number`                                    | 读写 | 音量 0~1（自动 clamp，持久化） |\n| `playbackRate` | `number`                                    | 读写 | 播放速率（持久化）             |\n| `currentTime`  | `number`                                    | 只读 | 当前时间（毫秒）               |\n| `duration`     | `number`                                    | 只读 | 总时长（毫秒）                 |\n| `progress`     | `number`                                    | 只读 | 播放进度（0~1）                |\n| `time`         | `{ currentTime: number; duration: number }` | 只读 | 时间快照                       |\n\n#### 生命周期\n\n| 方法      | 参数                                              | 返回值          | 说明                                                     |\n| --------- | ------------------------------------------------- | --------------- | -------------------------------------------------------- |\n| `create`  | `path: IVlcInitPath, options: IVlcAdapterOptions` | `void`          | 创建 IPC Bridge 并初始化配置                             |\n| `init`    | 无                                                | `Promise<void>` | 初始化播放器、设置帧格式（1280x720）、启动轮询、开始播放 |\n| `destroy` | 无                                                | `void`          | 停止轮询、清除监听器、销毁 Bridge                        |\n\n#### 播放控制\n\n| 方法          | 参数               | 返回值 | 说明                                        |\n| ------------- | ------------------ | ------ | ------------------------------------------- |\n| `play`        | 无                 | `void` | 播放                                        |\n| `pause`       | 无                 | `void` | 暂停                                        |\n| `togglePlay`  | 无                 | `void` | 切换播放/暂停                               |\n| `toggleMuted` | 无                 | `void` | 切换静音                                    |\n| `seek`        | `time: number`     | `void` | 跳转到指定时间（毫秒），内部转换为 progress |\n| `setProgress` | `progress: number` | `void` | 设置进度（0~1）                             |\n\n#### 帧访问\n\n| 方法             | 参数                            | 返回值       | 说明                 |\n| ---------------- | ------------------------------- | ------------ | -------------------- |\n| `getFrameRgba`   | 无                              | `Uint8Array` | 获取当前帧 RGBA 数据 |\n| `setFrameFormat` | `width: number, height: number` | `void`       | 设置帧渲染尺寸       |\n\n#### 时间更新轮询\n\n```typescript\nadapter.onTimeUpdate(callback: (args: { currentTime: number; duration: number }) => void): void;\nadapter.offTimeUpdate(): void;\n```\n\n内部以 500ms 间隔轮询 `getPlayed()` 和 `getDuration()`。\n\n#### 度量查询\n\n| 方法                | 返回值    | 说明               |\n| ------------------- | --------- | ------------------ |\n| `getPlayed()`       | `number`  | 已播放时长（毫秒） |\n| `getDuration()`     | `number`  | 总时长（毫秒）     |\n| `getProgress()`     | `number`  | 播放进度（0~1）    |\n| `getVolume()`       | `number`  | 音量               |\n| `getMuted()`        | `boolean` | 静音状态           |\n| `getPlaybackRate()` | `number`  | 播放速率           |\n\n#### 事件\n\n```typescript\nadapter.on(event: string, handler: (...args: any[]) => void): void;\nadapter.off(event: string, handler?: (...args: any[]) => void): void;\nadapter.onEvent(callback: (payload: IVlcEventPayload) => void): () => void; // 返回取消订阅函数\n```\n\n---\n\n### IVlcBridge 接口\n\nIPC 通信桥接层，由 `createBridge()` 创建。渲染进程通过 `ipcRenderer.invoke` 调用主进程 VLC 操作。\n\n```typescript\ninterface IVlcBridge {\n  create: (mountSelector: string) => void | Promise<void>;\n  play: () => void;\n  pause: () => void;\n  setVolume: (vol: number) => void;\n  getVolume: () => number;\n  setMuted: (muted: boolean) => void;\n  getMuted: () => boolean;\n  setProgress: (progress: number) => void;\n  getProgress: () => number;\n  getDuration: () => number;\n  getPlayed: () => number;\n  setPlaybackRate: (rate: number) => void;\n  getPlaybackRate: () => number;\n  setFrameFormat: (width: number, height: number) => void;\n  getFrameRgba: () => Uint8Array;\n  onEvent: (callback: (payload: IVlcEventPayload) => void) => () => void;\n  destroy: () => void;\n}\n\nfunction createBridge(path: IVlcInitPath, options: IVlcInitOptions, instanceId?: string): IVlcBridge;\n```\n\nBridge 内部通过 250ms 轮询缓存 metrics（volume, progress, duration, played, playbackRate, muted），getter 直接返回缓存值，setter 异步调用 IPC 后立即更新缓存。\n\n---\n\n### Template 类\n\n播放器 DOM 模板，负责创建和管理播放器 UI 元素。\n\n```typescript\nclass Template {\n  constructor(vlc: any, t?: I18nFn);\n  static html(t: I18nFn): string;\n  query<T extends HTMLElement>(selector: string): T | null;\n  queryMust<T extends HTMLElement>(selector: string): T;\n  init(): void;\n  destroy(removeHtml?: boolean): void;\n}\n```\n\n#### DOM 元素引用\n\n| 属性             | 类型                | 说明            |\n| ---------------- | ------------------- | --------------- |\n| `$container`     | `HTMLElement`       | 最外层容器      |\n| `$player`        | `HTMLElement`       | 播放器容器      |\n| `$videoStage`    | `HTMLElement`       | 视频舞台        |\n| `$videoCanvas`   | `HTMLCanvasElement` | 视频渲染 Canvas |\n| `$placeholder`   | `HTMLElement`       | 占位符          |\n| `$topbar`        | `HTMLElement`       | 顶部栏          |\n| `$notice`        | `HTMLElement`       | 通知栏          |\n| `$controlsStage` | `HTMLElement`       | 控制栏          |\n| `$progress`      | `HTMLInputElement`  | 进度条          |\n| `$timeLeft`      | `HTMLElement`       | 左侧时间        |\n| `$timeRight`     | `HTMLElement`       | 右侧时间        |\n| `$btnPlay`       | `HTMLElement`       | 播放按钮        |\n| `$btnMute`       | `HTMLElement`       | 静音按钮        |\n| `$volume`        | `HTMLInputElement`  | 音量滑块        |\n| `$rateBtn`       | `HTMLElement`       | 倍率按钮        |\n| `$rateOptions`   | `HTMLElement`       | 倍率选项        |\n| `$btnPip`        | `HTMLElement`       | 画中画按钮      |\n| `$btnFullscreen` | `HTMLElement`       | 全屏按钮        |\n| `$btnNext`       | `HTMLElement`       | 下一个按钮      |\n| `$btnPrev`       | `HTMLElement`       | 上一个按钮      |\n\n---\n\n### IVlcCanvasRenderer 接口\n\nCanvas 帧渲染器，将 VLC RGBA 帧数据绘制到 HTML Canvas。\n\n```typescript\ninterface IVlcCanvasRenderer {\n  renderSourceToDisplay: () => void;\n  renderToCanvas: (targetCanvas: HTMLCanvasElement, targetCtx: CanvasRenderingContext2D) => void;\n  resizeCanvasToStage: () => void;\n  clearDisplayCanvas: () => void;\n  scheduleCanvasResize: () => void;\n  startFramePump: () => void;\n  stopFramePump: () => void;\n  setPipActive: (active: boolean) => void;\n  initFrameData: () => void;\n  onFrame: (callback: () => void) => void;\n  destroy: () => void;\n}\n\nfunction createCanvasRenderer(\n  refs: { videoCanvas: HTMLCanvasElement; videoStage: HTMLElement },\n  adapter: VlcAdapter,\n): IVlcCanvasRenderer;\n```\n\n---\n\n### Storage 类\n\n基于 localStorage 的播放器配置持久化。\n\n```typescript\nclass Storage {\n  constructor();\n  get(): Record<string, any>; // 获取全部\n  get(key: string): any; // 获取指定键\n  set(key: string, value: any): void;\n  del(key: string): void;\n  clear(): void;\n}\n```\n\n持久化键：`volume`, `muted`, `playrate`。\n\n---\n\n### 图标工具\n\n```typescript\nfunction resolveDemoIcon(name: IVlcIconKey): string;\nfunction populateSvgIcons(root: ParentNode): void;\nfunction setIVlcIconResolver(resolver: IVlcIconResolver | null | undefined): void;\nfunction resetIVlcIconResolver(): void;\n```\n\n`IVlcIconKey`: `'play'` | `'pause'` | `'volume'` | `'volumeSmall'` | `'muted'` | `'pip'` | `'pipExit'` | `'fullscreen'` | `'fullscreenExit'` | `'mnext'` | `'mprev'`\n\n---\n\n## 注意事项\n\n- **多实例支持**: 通过 `instanceId` 区分多个播放器实例，IPC 自动路由到对应实例\n- **帧渲染**: 默认 1280x720，通过 `requestAnimationFrame` 驱动帧泵，Bridge 内部防并发请求\n- **状态轮询**: Bridge 以 250ms 间隔轮询 metrics，Adapter 以 500ms 间隔触发 `onTimeUpdate` 回调\n- **平台限制**: `attach` 方法传入原生窗口句柄（macOS 为 NSView 指针的 `bigint`），仅用于原生窗口渲染场景\n- **本地字幕**: `addSubtitleFile` 本地文件需要 `file:///` 前缀\n- **Storage 持久化**: 音量、静音、播放速率自动持久化到 localStorage，下次初始化时恢复\n\n<!-- File: packages/crypto/README.md (Tokens: ~1009 | Category: plugin-manifest) -->\n# @zy/crypto\n\nCrypto for Node.js and Web — hash, hmac, symmetric/asymmetric encryption, encoding, and more.\n\n## Install\n\n`@zy/crypto` is not published to npm yet.\n\nAdd it as a workspace dependency in your package:\n\n```json\n{\n  \"dependencies\": {\n    \"@zy/crypto\": \"workspace:*\"\n  }\n}\n```\n\n```bash\npnpm install\n```\n\n## API\n\n### Hash\n\n```ts\nimport { hash } from '@zy/crypto';\n\nhash['md5-32']({ src: 'hello' });\nhash.sha256({ src: 'hello', outputEncode: 'hex' });\nhash.sm3({ src: 'hello' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### HMAC\n\n```ts\nimport { hmac } from '@zy/crypto';\n\nhmac.sha256({ src: 'hello', key: 'secret' });\nhmac.sm3({ src: 'hello', key: 'secret' });\n```\n\nSupported: `md5-16`, `md5-32`, `sha1`, `sha224`, `sha256`, `sha3`, `sha384`, `sha512`, `sha512-224`, `sha512-256`, `ripemd160`, `sm3`\n\n### Encrypt\n\n#### AES (node-forge, supports GCM)\n\n```ts\nimport { aes } from '@zy/crypto';\n\nconst encrypted = aes.encode({\n  src: 'hello',\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  outputEncode: 'hex',\n});\n\nconst decrypted = aes.decode({\n  src: encrypted,\n  key: '1234567890123456',\n  iv: '1234567890123456',\n  mode: 'cbc',\n  pad: 'pkcs7padding',\n  inputEncode: 'hex',\n  outputEncode: 'utf8',\n});\n```\n\n#### DES / 3DES / RC4 / Rabbit\n\n```ts\nimport { des, tripleDes, rc4, rabbit } from '@zy/crypto';\n\ndes.encode({ src: 'hello', key: '12345678', iv: '12345678', mode: 'cbc' });\n```\n\n#### SM4\n\n```ts\nimport { sm4 } from '@zy/crypto';\n\nsm4.encode({ src: 'hello', key: '1234567890123456', iv: '1234567890123456', mode: 'cbc' });\n```\n\n#### RSA\n\n```ts\nimport { rsa } from '@zy/crypto';\n\nrsa.encode({\n  src: 'hello',\n  key: '-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----',\n  pad: 'rsaes-pkcs1-v1_5',\n  outputEncode: 'hex',\n});\n```\n\n### Encode\n\n```ts\nimport { base64, unicode, html, gzip, url, hex } from '@zy/crypto';\n\nbase64.encode({ src: 'hello' });\nbase64.decode({ src: 'aGVsbG8=' });\n\nunicode.encode({ src: 'hello' });\nhtml.encode({ src: '<b>bold</b>' });\ngzip.encode({ src: 'hello' });\nurl.encode({ src: 'https://example.com?q=hello' });\nhex.encode({ src: 'hello' });\n```\n\n## Build\n\n```bash\npnpm build\n```\n\n## Modules\n\n| Module                    | Description                                     |\n| ------------------------- | ----------------------------------------------- |\n| `hash`                    | Hash algorithms (MD5, SHA, SM3, RIPEMD160)      |\n| `hmac`                    | HMAC algorithms                                 |\n| `aes`                     | AES encrypt/decrypt (node-forge, GCM supported) |\n| `des` / `tripleDes`       | DES/3DES encrypt/decrypt (CryptoJS)             |\n| `rc4` / `rc4Drop`         | RC4 encrypt/decrypt                             |\n| `rabbit` / `rabbitLegacy` | Rabbit encrypt/decrypt                          |\n| `rsa`                     | RSA encrypt/decrypt (node-forge)                |\n| `sm4`                     | SM4 encrypt/decrypt (sm-crypto-v2)              |\n| `base64`                  | Base64 encode/decode                            |\n| `unicode`                 | Unicode encode/decode                           |\n| `html`                    | HTML entity encode/decode                       |\n| `gzip`                    | Gzip compress/decompress                        |\n| `url`                     | URL encode/decode                               |\n| `hex`                     | Hex encode/decode                               |\n</yakaai_skills>"}