diff --git a/plugins/screenshot-annotate/.gitignore b/plugins/screenshot-annotate/.gitignore new file mode 100644 index 00000000..f00c30a7 --- /dev/null +++ b/plugins/screenshot-annotate/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +release +*.zip +*.log diff --git a/plugins/screenshot-annotate/CHANGELOG.md b/plugins/screenshot-annotate/CHANGELOG.md new file mode 100644 index 00000000..c4138dce --- /dev/null +++ b/plugins/screenshot-annotate/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +## 1.3.12 + +- author 显示为 zyy,新增 homepage 跳转仓库 +- 插件配置路径规范化为相对无 `./` 前缀 + +## 1.3.11 + +- 版本号与配置同步调整 + +## 1.3.10 + +- 保存时弹出系统「另存为」对话框 +- 矩形等选区支持 8 个缩放手柄(4 角 + 4 边可单独拖拽) +- 文字框按内容真实测量扩展(多行、字号准确) +- 多次使用快捷键截图不会跳到插件详情页 + +## 1.3.9 + +- 选中文字样式编辑:字号 / 粗体 / 颜色 \ No newline at end of file diff --git a/plugins/screenshot-annotate/README.md b/plugins/screenshot-annotate/README.md new file mode 100644 index 00000000..f1333f9d --- /dev/null +++ b/plugins/screenshot-annotate/README.md @@ -0,0 +1,121 @@ +# screenshot-annotate + +一个运行在 **ZTools**(uTools 的开源 fork,Electron 41 + TypeScript)平台的**截图标注插件**。 + +流程一句话:**框选截图 → 自由标注 → 复制 / 保存 / 钉到桌面**。 + +## 这是干什么的 + +按快捷键(或输入命令)唤起原生框选,选中屏幕任意区域后进入标注编辑器,用各类图形标记重点,最后复制到剪贴板、保存为图片文件、或作为置顶窗口钉在桌面上持续引用。 + +## 已实现的功能 + +### 截图 +- 调用平台原生**交互式框选**(`ztools.screenCapture`),无需桌面录屏权限,支持多显示器。 +- 隐藏能力:不剪切屏幕,只拿选区,隐私友好。 + +### 标注编辑器 +9 种工具(沿右侧工具条切换): + +| 工具 | 说明 | +| --- | --- | +| 选择 / 移动 | 拖动已有标注、移窗(选择工具下拖动空白处移动整个窗口) | +| 矩形 | 边框 / 填充可切换 | +| 圆形 / 椭圆 | 边框 / 填充可切换 | +| 箭头 | 带箭头的直线 | +| 画笔 | 自由手绘 | +| 文字 | 多行文本,支持字号 / 粗体 / 颜色 | +| 马赛克 | 覆盖隐私区域,支持**方格 / 球状**两种颗粒 + 格尺寸 | +| 直线 | 两端点 | +| 高亮 | 半透明覆盖层 | + +关键交互能力: + +- **8 个缩放手柄**:矩形 / 椭圆 / 高亮 / 马赛克选区有 4 角 + 4 边共 8 个手柄,可单独拖动任一条边或角(不必只靠 4 角)。 +- **文字框按内容扩展**:选中文字后,选中框按文字**真实测量尺寸**包裹内容(多行、字号变化都准确)。 +- **微信截图式**:不切换工具也能直接点中已有标注拖动 / 收紧手柄缩放。 +- **文字样式回显**:选中文字时工具栏自动读取其字号 / 粗体 / 颜色,改动实时应用到该文字。 +- 画布**滚轮缩放**、空白区拖动平移。 +- **撤销 / 重做**(`Ctrl+Z` / `Ctrl+Y`)、**删除**选中标注(`Del` / `Backspace`,选择工具下)。 +- **Esc 关闭**编辑窗口。 + +### 输出 +- **复制**:`Ctrl+C` 或按钮,把标注结果以 PNG 位图写入系统剪贴板。 +- **保存**:`Ctrl+S` 或按钮,弹出系统「另存为」对话框,按偏好设置默认 PNG / JPG 及保存目录。 +- **钉到桌面**:钉图按钮,把结果开成一个**置顶、免框、可拖动缩放**的窗口,随时参考。 + +### 偏好设置 +默认颜色、默认线宽、保存格式(PNG / JPG)、保存目录(留空走系统图片目录 Screenshots)。设置持久化于 dbStorage。 + +## 技术栈与结构 + +- **Vue 3 + TypeScript**(` + + \ No newline at end of file diff --git a/plugins/screenshot-annotate/index.html b/plugins/screenshot-annotate/index.html new file mode 100644 index 00000000..10782238 --- /dev/null +++ b/plugins/screenshot-annotate/index.html @@ -0,0 +1,12 @@ + + + + + + 截图标注 + + +
+ + + \ No newline at end of file diff --git a/plugins/screenshot-annotate/package-lock.json b/plugins/screenshot-annotate/package-lock.json new file mode 100644 index 00000000..ab26b7ce --- /dev/null +++ b/plugins/screenshot-annotate/package-lock.json @@ -0,0 +1,1524 @@ +{ + "name": "screenshot-annotate", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "screenshot-annotate", + "version": "1.0.0", + "dependencies": { + "vue": "^3.5.13" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "@ztools-center/ztools-api-types": "^1.0.1", + "typescript": "^5.3.0", + "vite": "^6.0.11", + "vue-tsc": "^2.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", + "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", + "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", + "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", + "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", + "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", + "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", + "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", + "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", + "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", + "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", + "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", + "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", + "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", + "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", + "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", + "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", + "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", + "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", + "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", + "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", + "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", + "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", + "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", + "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", + "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz", + "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.42", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz", + "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz", + "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.42", + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-ssr": "3.5.42", + "@vue/shared": "3.5.42", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz", + "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.42.tgz", + "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.42.tgz", + "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz", + "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/runtime-core": "3.5.42", + "@vue/shared": "3.5.42", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.42.tgz", + "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz", + "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==", + "license": "MIT" + }, + "node_modules/@ztools-center/ztools-api-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ztools-center/ztools-api-types/-/ztools-api-types-1.0.3.tgz", + "integrity": "sha512-dv1eOAIasAupqKaQL/gESk1i2+RebdM/1gvZhrvH2D/bo3enCUsAGJ8nrHnlCLBSOGB81eC/SU0IH8BNsUlmvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.0.tgz", + "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.0", + "@rollup/rollup-android-arm64": "4.63.0", + "@rollup/rollup-darwin-arm64": "4.63.0", + "@rollup/rollup-darwin-x64": "4.63.0", + "@rollup/rollup-freebsd-arm64": "4.63.0", + "@rollup/rollup-freebsd-x64": "4.63.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", + "@rollup/rollup-linux-arm-musleabihf": "4.63.0", + "@rollup/rollup-linux-arm64-gnu": "4.63.0", + "@rollup/rollup-linux-arm64-musl": "4.63.0", + "@rollup/rollup-linux-loong64-gnu": "4.63.0", + "@rollup/rollup-linux-loong64-musl": "4.63.0", + "@rollup/rollup-linux-ppc64-gnu": "4.63.0", + "@rollup/rollup-linux-ppc64-musl": "4.63.0", + "@rollup/rollup-linux-riscv64-gnu": "4.63.0", + "@rollup/rollup-linux-riscv64-musl": "4.63.0", + "@rollup/rollup-linux-s390x-gnu": "4.63.0", + "@rollup/rollup-linux-x64-gnu": "4.63.0", + "@rollup/rollup-linux-x64-musl": "4.63.0", + "@rollup/rollup-openbsd-x64": "4.63.0", + "@rollup/rollup-openharmony-arm64": "4.63.0", + "@rollup/rollup-win32-arm64-msvc": "4.63.0", + "@rollup/rollup-win32-ia32-msvc": "4.63.0", + "@rollup/rollup-win32-x64-gnu": "4.63.0", + "@rollup/rollup-win32-x64-msvc": "4.63.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.42.tgz", + "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-sfc": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/server-renderer": "3.5.42", + "@vue/shared": "3.5.42" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/plugins/screenshot-annotate/package.json b/plugins/screenshot-annotate/package.json new file mode 100644 index 00000000..8afd8688 --- /dev/null +++ b/plugins/screenshot-annotate/package.json @@ -0,0 +1,21 @@ +{ + "name": "screenshot-annotate", + "version": "1.3.0", + "description": "截图 → 标注 → 复制 / 保存 / 钉到桌面", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "typecheck": "vue-tsc --noEmit" + }, + "dependencies": { + "vue": "^3.5.13" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "@ztools-center/ztools-api-types": "^1.0.1", + "typescript": "^5.3.0", + "vite": "^6.0.11", + "vue-tsc": "^2.0.0" + } +} \ No newline at end of file diff --git a/plugins/screenshot-annotate/pin.html b/plugins/screenshot-annotate/pin.html new file mode 100644 index 00000000..d9cb850d --- /dev/null +++ b/plugins/screenshot-annotate/pin.html @@ -0,0 +1,12 @@ + + + + + + 钉图 + + +
+ + + \ No newline at end of file diff --git a/plugins/screenshot-annotate/plugin.json b/plugins/screenshot-annotate/plugin.json new file mode 100644 index 00000000..5c811f0d --- /dev/null +++ b/plugins/screenshot-annotate/plugin.json @@ -0,0 +1,35 @@ +{ + "$schema": "node_modules/@ztools-center/ztools-api-types/resource/ztools.schema.json", + "name": "screenshot-annotate", + "title": "截图标注", + "description": "截图 → 标注 → 复制 / 保存 / 钉到桌面", + "version": "1.3.12", + "author": "zyy", + "homepage": "https://github.com/zhaoyuyang8520/screenshot-annotate", + "logo": "logo.png", + "main": "index.html", + "preload": "preload/services.js", + "development": { + "main": "http://localhost:5173" + }, + "features": [ + { + "code": "ui.capture", + "explain": "进入截图标注", + "icon": "./logo.png", + "cmds": ["截图标注", "截图", "截屏"] + }, + { + "code": "function.capture-copy", + "explain": "截图标注并复制到剪贴板", + "icon": "./logo.png", + "cmds": ["截图标注并复制"] + }, + { + "code": "function.capture-pin", + "explain": "截图直接钉到桌面", + "icon": "./logo.png", + "cmds": ["截图并钉图", "钉图"] + } + ] +} \ No newline at end of file diff --git a/plugins/screenshot-annotate/public/logo.png b/plugins/screenshot-annotate/public/logo.png new file mode 100644 index 00000000..293d2c1c Binary files /dev/null and b/plugins/screenshot-annotate/public/logo.png differ diff --git a/plugins/screenshot-annotate/public/plugin.json b/plugins/screenshot-annotate/public/plugin.json new file mode 100644 index 00000000..5c811f0d --- /dev/null +++ b/plugins/screenshot-annotate/public/plugin.json @@ -0,0 +1,35 @@ +{ + "$schema": "node_modules/@ztools-center/ztools-api-types/resource/ztools.schema.json", + "name": "screenshot-annotate", + "title": "截图标注", + "description": "截图 → 标注 → 复制 / 保存 / 钉到桌面", + "version": "1.3.12", + "author": "zyy", + "homepage": "https://github.com/zhaoyuyang8520/screenshot-annotate", + "logo": "logo.png", + "main": "index.html", + "preload": "preload/services.js", + "development": { + "main": "http://localhost:5173" + }, + "features": [ + { + "code": "ui.capture", + "explain": "进入截图标注", + "icon": "./logo.png", + "cmds": ["截图标注", "截图", "截屏"] + }, + { + "code": "function.capture-copy", + "explain": "截图标注并复制到剪贴板", + "icon": "./logo.png", + "cmds": ["截图标注并复制"] + }, + { + "code": "function.capture-pin", + "explain": "截图直接钉到桌面", + "icon": "./logo.png", + "cmds": ["截图并钉图", "钉图"] + } + ] +} \ No newline at end of file diff --git a/plugins/screenshot-annotate/public/preload/services.js b/plugins/screenshot-annotate/public/preload/services.js new file mode 100644 index 00000000..abe0e545 --- /dev/null +++ b/plugins/screenshot-annotate/public/preload/services.js @@ -0,0 +1,496 @@ +// ZTools「截图标注」插件 —— 渲染进程 Node 能力桥 +// 运行于 preload 环境:可访问 window.ztools(平铺 API)与 Node fs/path。 +// 所有跨进程的磁盘、剪贴板、建窗操作都集中在这里。 + +const fs = require('node:fs') +const path = require('node:path') +const { ipcRenderer } = require('electron') + +/** 确保目录存在 */ +function ensureDir(dir) { + fs.mkdirSync(dir, { recursive: true }) + return dir +} + +/** 把 dataURL 写入文件(按 MIME 判断扩展名由调用方控制,这里按传入数据字节写) */ +function dataURLToBuffer(dataURL) { + const m = /^data:image\/[a-z+]+;base64,(.+)$/.exec(dataURL) + if (!m) throw new Error('dataURL 格式不合法') + return { buffer: Buffer.from(m[1], 'base64') } +} + +/** + * 从 PNG buffer 解析像素宽高(IHDR 固定偏移 16/20)。非 PNG 或解析失败返回 null。 + * 用于选区尺寸缺失时让编辑器/钉图窗口贴合图片真实尺寸,避免留白。 + */ +function resolvePngSize(buffer) { + try { + if ( + buffer[0] === 0x89 && + buffer[1] === 0x50 && + buffer[2] === 0x4e && + buffer[3] === 0x47 && + buffer[12] === 0x49 + ) { + return { w: buffer.readUInt32BE(16), h: buffer.readUInt32BE(20) } + } + } catch { + /* ignore */ + } + return null +} + +// ── 跨窗口大图传递(临时文件)────────────────────────────── +// 全屏大图 dataURL 动辄数 MB,塞进 URL query 会超 Chromium 长度限制导致建窗/加载失败。 +// 因此 dataURL 写到临时文件,URL 只传短文件路径,子窗口读回 dataURL。 +const TEMP_DIR = path.join(window.ztools.getPath('temp'), 'screenshot-annotate') + +// 固定默认保存格式(无可视化设置界面,默认 png) +const DEFAULT_FORMAT = 'png' + +/** 写 dataURL 到临时文件,返回短文件路径(供 URL query 传递) */ +function writeTempImage(dataURL) { + const { buffer } = dataURLToBuffer(dataURL) + const stamp = Date.now().toString(36) + const filePath = path.join(TEMP_DIR, `shot_${stamp}.png`) + fs.mkdirSync(TEMP_DIR, { recursive: true }) + fs.writeFileSync(filePath, buffer) + return filePath +} + +/** 读临时图片文件 → dataURL;读后删除防堆积 */ +function readTempImage(filePath) { + const buf = fs.readFileSync(filePath) + try { + fs.unlinkSync(filePath) + } catch { + /* 删除失败不影响使用 */ + } + return `data:image/png;base64,${buf.toString('base64')}` +} + +/** 给 Promise 加超时,超时后 reject,避免永久挂起 */ +function withTimeout(promise, ms, msg) { + return new Promise((resolve, reject) => { + const t = setTimeout(() => reject(new Error(msg)), ms) + promise.then( + (v) => { + clearTimeout(t) + resolve(v) + }, + (e) => { + clearTimeout(t) + reject(e) + } + ) + }) +} + +/** + * 抓所有显示器的物理原图 + DIP 边界信息,供渲染层拼虚拟桌面。 + * @returns {Promise>} + */ +async function captureDisplays() { + const displays = window.ztools.getAllDisplays() + const sources = await withTimeout( + window.ztools.desktopCaptureSources({ + types: ['screen'], + thumbnailSize: { width: 0, height: 0 }, + fetchWindowIcons: false + }), + 8000, + 'desktopCaptureSources 超时:可能缺少屏幕录制权限,请检查 Windows「设置→隐私→屏幕捕获」' + ) + + // sources 顺序与 displays 不一定一一对应,用 display_id 关联; + // 兜底按 index 对齐(desktopCapturer 通常按屏幕顺序返回)。 + return displays.map((d, idx) => { + const thumb = (sources.find((s) => s.display_id === d.id) || sources[idx])?.thumbnail + const dataURL = thumb && !thumb.isEmpty() ? thumb.toDataURL() : null + return { + id: d.id, + bounds: d.bounds, + scaleFactor: d.scaleFactor, + dataURL + } + }) +} + +/** 打开标注编辑窗口(承载 EditorPanel)。dataURL 经临时文件传给渲染层(query 只放短路径)。 */ +function openCaptureWindow({ dataURL, x, y, width, height }) { + const winId = `edit${++editorSeq}` + const sz = resolvePngSize(dataURLToBuffer(dataURL).buffer) + const w = width || sz?.w || 960 + const h = height || sz?.h || 640 + const px = x ?? 80 + const py = y ?? 80 + const imgPath = writeTempImage(dataURL) + const url = `capture.html?img=${encodeURIComponent(imgPath)}&win=${winId}` + const win = window.ztools.createBrowserWindow( + url, + { + x: px, + y: py, + width: w, + height: h, + transparent: false, + frame: false, + title: '截图标注', + alwaysOnTop: true, + skipTaskbar: true, + resizable: true, + hasShadow: false, + fullscreenable: false, + webPreferences: { + nodeIntegration: true, + webSecurity: false, + preload: path.join(__dirname, 'services.js') + } + }, + () => {} + ) + // 窗口被关闭时自我清理 + const t = setInterval(() => { + if (win.isDestroyed && win.isDestroyed()) { + clearInterval(t) + editors.delete(winId) + } + }, 2000) + editors.set(winId, win) + return win +} + +/** 虚拟桌面 DIP 包围盒(主视图据此定 capture 窗口尺寸,不抓原图) */ +function getVirtualBounds() { + const displays = window.ztools.getAllDisplays() + let minX = Infinity + let minY = Infinity + let maxX = -Infinity + let maxY = -Infinity + for (const d of displays) { + minX = Math.min(minX, d.bounds.x) + minY = Math.min(minY, d.bounds.y) + maxX = Math.max(maxX, d.bounds.x + d.bounds.width) + maxY = Math.max(maxY, d.bounds.y + d.bounds.height) + } + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY } +} + +/** 复制 DIP 合成图到系统剪贴板(写 png 位图) */ +function copyImageDataURL(dataURL) { + const { buffer } = dataURLToBuffer(dataURL) + return window.ztools.copyImage(new Uint8Array(buffer)) +} + +/** + * 弹出系统"另存为"对话框保存合成图;取消时返回 { path: null, canceled: true }。 + * @param dataURL 图像 dataURL(png 或 jpeg) + * @param options { format?: 'png'|'jpg' }——省略时读持久化设置的默认值 + * @returns { path: string|null, canceled: boolean } + */ +function saveImageDataURL(dataURL, options = {}) { + const { buffer } = dataURLToBuffer(dataURL) + const format = options.format || DEFAULT_FORMAT + const ext = format === 'jpg' ? 'jpg' : 'png' + const d = new Date() + const pad = (n) => String(n).padStart(2, '0') + const stamp = + `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_` + + `${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}` + const baseDir = path.join(window.ztools.getPath('pictures'), 'Screenshots') + const defaultPath = path.join(baseDir, `Screenshot_${stamp}.${ext}`) + const chosen = window.ztools.showSaveDialog({ + title: '保存截图', + defaultPath, + filters: [{ name: '图片', extensions: [ext] }] + }) + // undefined 表示用户取消 + if (!chosen) return { path: null, canceled: true } + ensureDir(path.dirname(chosen)) + fs.writeFileSync(chosen, buffer) + return { path: chosen, canceled: false } +} + +const services = { + captureDisplays, + openCaptureWindow, + getVirtualBounds, + copyImageDataURL, + saveImageDataURL, + writeTempImage, + readTempImage, + openPinWindow, + pinGetBounds, + pinSetBounds, + pinMoveBy, + pinResize, + pinSetAlwaysOnTop, + pinClose, + pathJoin: (...p) => path.join(...p) +} + +window.services = services + +// ── 钉图窗口管理 ─────────────────────────────────────────── +// 每张钉图一个独立置顶窗口;preload 记注册表,关闭即销毁防泄漏。 + +/** @type {Map} winId → WindowInstance */ +const pins = new Map() +let pinSeq = 0 + +/** 编辑器窗口注册表(供 JS 移窗中转) */ +const editors = new Map() +let editorSeq = 0 + +/** + * 窗口位置缓存(winId → [x, y],绝对坐标)。 + * 拖动时若每帧 getPosition/getBounds 回读再叠加增量,连续 setPosition 会回读到 + * 合成前的旧值,窗口在邻近位置往返 → 整窗震动。这里首次惰性读一次作基准, + * 之后纯本地累加 + setPosition 绝对坐标(单调不回读),消除该竞态。 + */ +const winPos = new Map() +function posOf(winId, getter) { + let p = winPos.get(winId) + if (!p) { + const b = getter ? getter() : null + if (!b) return null + p = [Math.round(b.x === undefined ? b[0] : b.x), Math.round(b.y === undefined ? b[1] : b.y)] + winPos.set(winId, p) + } + return p +} + +/** + * 打开一张钉图窗口。 + * @param {{dataURL:string, x?:number, y?:number, width?:number, height?:number}} opts + * @returns {{winId:string, x:number, y:number, width:number, height:number}} + */ +function openPinWindow({ dataURL, x, y, width, height }) { + const winId = `pin${++pinSeq}` + // 默认按图原始尺寸;图片 dataURL 无法在 preload 取宽高,PinApp 会自适。窗口先给个合理默认。 + const w = width || Math.min(480 * 2, 1200) + const h = height || Math.min(320 * 2, 900) + const px = x ?? 80 + const py = y ?? 80 + const imgPath = writeTempImage(dataURL) + const q = encodeURIComponent(imgPath) + + const url = `pin.html?win=${winId}&img=${q}` + const win = window.ztools.createBrowserWindow( + url, + { + x: px, + y: py, + width: w, + height: h, + frame: false, + transparent: false, + backgroundColor: '#1c1c20', + alwaysOnTop: true, + skipTaskbar: true, + resizable: true, + hasShadow: true, + fullscreenable: false, + webPreferences: { + nodeIntegration: true, + webSecurity: false, + preload: path.join(__dirname, 'services.js') + } + }, + () => {} + ) + // 窗口被关闭时自我清理 + const t = setInterval(() => { + if (win.isDestroyed && win.isDestroyed()) { + clearInterval(t) + pins.delete(winId) + } + }, 2000) + pins.set(winId, win) + return { winId, x: px, y: py, width: w, height: h } +} + +function mustPin(winId) { + const w = pins.get(winId) + if (!w) throw new Error('钉图窗口不存在') + return w +} +function pinGetBounds(winId) { + const w = mustPin(winId) + return w.getBounds ? w.getBounds() : null +} +function pinSetBounds(winId, b) { + const w = mustPin(winId) + if (w.setBounds) { + w.setBounds(b) + // 同步位置缓存,避免后续拖动从旧基准累加导致错位 + if (b && b.x !== undefined && b.y !== undefined) winPos.set(winId, [Math.round(b.x), Math.round(b.y)]) + } +} +/** 设置窗口绝对位置(并同步位置缓存,供后续拖动累加) */ +function pinSetPosition(winId, x, y) { + const w = mustPin(winId) + const nx = Math.round(x) + const ny = Math.round(y) + winPos.set(winId, [nx, ny]) + if (w.setPosition) w.setPosition(nx, ny) +} +/** 拖动:按增量移动。主窗口用缓存里的绝对坐标累加(不回读 getBounds,避免整窗震动) */ +function pinMoveBy(winId, dx, dy) { + const w = mustPin(winId) + const p = posOf(winId, () => w.getBounds()) + if (!p || !w.setPosition) return + p[0] += dx + p[1] += dy + w.setPosition(Math.round(p[0]), Math.round(p[1])) +} +/** 缩放:以窗口左上为锚改变尺寸(phase4 简化:左上锚,滚轮缩放够用) */ +function pinResize(winId, dw, dh) { + const w = mustPin(winId) + const p = posOf(winId, () => w.getBounds()) + const b = w.getBounds ? w.getBounds() : null + if (!b || !w.setBounds) return + const nw = Math.max(80, Math.round(b.width + dw)) + const nh = Math.max(60, Math.round(b.height + dh)) + w.setBounds({ x: p[0], y: p[1], width: nw, height: nh }) +} +function pinSetAlwaysOnTop(winId, flag) { + const w = mustPin(winId) + if (w.setAlwaysOnTop) w.setAlwaysOnTop(!!flag) +} +function pinClose(winId) { + const w = pins.get(winId) + if (!w) return + if (w.close) w.close() + pins.delete(winId) +} + +// ── 子窗口 → 主窗口钉图中转 ─────────────────────────────── +// 只有主插件窗口能 createBrowserWindow(子窗口的 webContents 不被识别为插件 → "plugin not found")。 +// 编辑子窗口用 ztools.sendToParent 把钉图请求送回主窗口;主进程只把 __ipc_sendto_relay__ 发给发送者的 +// 父窗口,因此这里的监听只会在主窗口触发一次(子窗口自身不会收到自己的转发),无需按类型过滤。 +const PIN_CHANNEL = 'screenshot-annotate:open-pin' + +ipcRenderer.on(PIN_CHANNEL, (_event, dataURL) => { + try { + openPinWindow({ dataURL }) + } catch (err) { + const msg = err instanceof Error ? err.message : String(err) + try { + window.ztools.showToast?.(`钉图失败: ${msg}`) + } catch { + /* 无 toast 通道时忽略 */ + } + } +}) + +// 钉图窗口操作中转:钉图窗口是子窗口,其 preload 里 pins map 为空,无法直接操作 +// 主窗口创建的窗口实例(move/scale/top/bounds 全失效)。因此钉图窗口把这些操作 +// 经 sendToParent 送回主窗口执行(与钉图创建同一条已验证可靠的中转链路)。 +const PIN_CMD_CHANNEL = 'screenshot-annotate:pin-cmd' + +ipcRenderer.on(PIN_CMD_CHANNEL, (_event, payload) => { + const { winId, action } = payload || {} + if (!winId) return + try { + switch (action) { + case 'move': + // 绝对屏幕坐标(渲染层已算好窗口左上角 = screenX - clientX),直接落位并同步缓存 + pinSetPosition(winId, payload.x || 0, payload.y || 0) + break + case 'resize': + pinResize(winId, payload.dw || 0, payload.dh || 0) + break + case 'setTop': + pinSetAlwaysOnTop(winId, !!payload.flag) + break + case 'setBounds': + pinSetBounds(winId, payload.bounds) + break + default: + break + } + } catch { + /* 窗口可能已关闭,忽略 */ + } +}) + +// 编辑器窗口移动:编辑器子窗口经 sendToParent 请求主窗口移动自己窗口 +// (子窗口 preload 无法操作主窗口创建的窗口 instance)。 +// 采用**绝对屏幕坐标**(渲染层已算好窗口左上角目标 = screenX - clientX), +// setPosition 直接落位并同步 winPos 缓存,不再做增量回读,彻底避免整窗震动。 +function editorMoveTo(winId, x, y) { + const w = editors.get(winId) + if (!w || !w.setPosition) return + const nx = Math.round(x) + const ny = Math.round(y) + winPos.set(winId, [nx, ny]) + w.setPosition(nx, ny) +} + +const EDITOR_MOVE_CHANNEL = 'screenshot-annotate:editor-move' + +ipcRenderer.on(EDITOR_MOVE_CHANNEL, (_event, payload) => { + const { winId, x, y } = payload || {} + if (!winId) return + try { + editorMoveTo(winId, x || 0, y || 0) + } catch { + /* 忽略 */ + } +}) + +// ── 无页面命令协议(window.exports + mode:none)─────────── +// 触发任一 feature 时 ZTools 不渲染 main 页面,直接进入这里截图。 +// 参考市场插件 shortcut-capture 的同款协议。 +function startCapture(mode) { + const anyZtools = window.ztools + // 框选前先隐藏搜索主窗口:否则主窗口停留在屏幕上,被拍进截图画面里 + anyZtools.hideMainWindow(true) + anyZtools.screenCapture((image, bounds) => { + if (!image) { + // 用户取消了框选,恢复主窗口让交互不中断 + anyZtools.showMainWindow() + return + } + const b = bounds ?? { x: 80, y: 80, width: 0, height: 0 } + if (mode === 'pin') { + openPinWindow({ + dataURL: image, + x: b.x ?? undefined, + y: b.y ?? undefined, + width: b.width || undefined, + height: b.height || undefined + }) + } else { + openCaptureWindow({ + dataURL: image, + x: b.x, + y: b.y, + width: b.width || undefined, + height: b.height || undefined + }) + } + }) +} + +window.exports = { + 'ui.capture': { + mode: 'none', + args: { + enter: () => startCapture('edit') + } + }, + 'function.capture-copy': { + mode: 'none', + args: { + enter: () => startCapture('edit') + } + }, + 'function.capture-pin': { + mode: 'none', + args: { + enter: () => startCapture('pin') + } + } +} \ No newline at end of file diff --git a/plugins/screenshot-annotate/src/App.vue b/plugins/screenshot-annotate/src/App.vue new file mode 100644 index 00000000..976db312 --- /dev/null +++ b/plugins/screenshot-annotate/src/App.vue @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/plugins/screenshot-annotate/src/capture.css b/plugins/screenshot-annotate/src/capture.css new file mode 100644 index 00000000..31efc739 --- /dev/null +++ b/plugins/screenshot-annotate/src/capture.css @@ -0,0 +1,82 @@ +html, +body, +#capture-app { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + background: transparent; + font-family: system-ui, "PingFang SC", "Microsoft YaHei", sans-serif; +} + +.cap-root { + width: 100%; + height: 100%; + position: relative; + background: transparent; +} + +.capture-stage { + width: 100%; + height: 100%; + position: relative; + cursor: crosshair; + background: rgba(0, 0, 0, 0.28); +} + +.preview-canvas { + display: block; + position: absolute; + inset: 0; + /* 背景图半透明,透出下方"实际桌面"的裁切效果 */ + opacity: 0.92; +} + +.sel-rect { + position: absolute; + border: 1.5px solid #4c8dff; + background: rgba(76, 141, 255, 0.12); + box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.35); + pointer-events: none; +} + +.sel-size { + position: absolute; + left: 0; + top: -24px; + padding: 2px 6px; + font-size: 12px; + color: #fff; + background: #4c8dff; + border-radius: 4px; + white-space: nowrap; +} + +.hint { + position: absolute; + left: 50%; + bottom: 24px; + transform: translateX(-50%); + padding: 8px 16px; + font-size: 13px; + color: #fff; + background: rgba(0, 0, 0, 0.6); + border-radius: 8px; + white-space: nowrap; + pointer-events: none; +} + +.loading { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 15px; +} + +.err { + color: #ff6b6b; +} \ No newline at end of file diff --git a/plugins/screenshot-annotate/src/capture.ts b/plugins/screenshot-annotate/src/capture.ts new file mode 100644 index 00000000..85b4a23b --- /dev/null +++ b/plugins/screenshot-annotate/src/capture.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './capture.css' +import CaptureApp from './capture/CaptureApp.vue' + +createApp(CaptureApp).mount('#capture-app') \ No newline at end of file diff --git a/plugins/screenshot-annotate/src/capture/CaptureApp.vue b/plugins/screenshot-annotate/src/capture/CaptureApp.vue new file mode 100644 index 00000000..a25b0e38 --- /dev/null +++ b/plugins/screenshot-annotate/src/capture/CaptureApp.vue @@ -0,0 +1,100 @@ + + + + + \ No newline at end of file diff --git a/plugins/screenshot-annotate/src/editor/EditorPanel.vue b/plugins/screenshot-annotate/src/editor/EditorPanel.vue new file mode 100644 index 00000000..1d241226 --- /dev/null +++ b/plugins/screenshot-annotate/src/editor/EditorPanel.vue @@ -0,0 +1,821 @@ + + +