intools-cli 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLUGIN_API.md +140 -24
- package/package.json +1 -1
package/PLUGIN_API.md
CHANGED
|
@@ -141,56 +141,172 @@
|
|
|
141
141
|
"icon": { "type": "svg", "value": "<svg>...</svg>" }
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
## Preload
|
|
144
|
+
## Preload 预加载脚本 ⭐ 核心概念
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
> [!IMPORTANT]
|
|
147
|
+
> **Preload 是 InTools 插件访问 Node.js 能力的核心机制。** 对于需要在渲染进程(UI)中使用 Node.js API、第三方 npm 模块或 Electron 渲染进程 API 的插件来说,Preload 是**必不可少**的。
|
|
148
|
+
|
|
149
|
+
### 什么是 Preload?
|
|
150
|
+
|
|
151
|
+
Preload 脚本是一个特殊的 JavaScript 文件,在**渲染进程加载之前**执行,具有以下特点:
|
|
152
|
+
|
|
153
|
+
| 特性 | 说明 |
|
|
154
|
+
|------|------|
|
|
155
|
+
| 🔧 **Node.js 完整支持** | 可以使用 `require()` 导入任何 Node.js 原生模块和 npm 包 |
|
|
156
|
+
| 🖥️ **Electron API 访问** | 可以调用 Electron 渲染进程 API |
|
|
157
|
+
| 🌉 **桥接能力** | 通过 `window.xxx` 将原生能力暴露给前端 React/Vue 组件 |
|
|
158
|
+
| ⚡ **同步执行** | 在页面 DOM 加载前执行,确保 API 可用 |
|
|
159
|
+
|
|
160
|
+
### 适用场景
|
|
161
|
+
|
|
162
|
+
以下场景**需要使用** Preload:
|
|
163
|
+
|
|
164
|
+
- 📂 使用 `pdf-lib`、`sharp`、`ffmpeg` 等需要 Node.js 环境的 npm 包
|
|
165
|
+
- 🔐 调用 Node.js 加密模块 (`crypto`)、子进程 (`child_process`)
|
|
166
|
+
- 📁 需要比 `window.intools.filesystem` 更底层的文件操作
|
|
167
|
+
- 🔗 与本地数据库交互 (SQLite、LevelDB 等)
|
|
168
|
+
- 🎯 任何需要原生能力但又想在前端统一调用的场景
|
|
169
|
+
|
|
170
|
+
---
|
|
147
171
|
|
|
148
172
|
### 配置方式
|
|
149
173
|
|
|
174
|
+
在 `manifest.json` 中添加 `preload` 字段,指定预加载脚本路径:
|
|
175
|
+
|
|
150
176
|
```json
|
|
151
177
|
{
|
|
152
|
-
"
|
|
178
|
+
"name": "my-plugin",
|
|
179
|
+
"version": "1.0.0",
|
|
180
|
+
"displayName": "我的插件",
|
|
181
|
+
"main": "dist/main.js",
|
|
182
|
+
"ui": "ui/index.html",
|
|
183
|
+
"preload": "preload.js", // 👈 指定预加载脚本
|
|
184
|
+
"features": [...]
|
|
153
185
|
}
|
|
154
186
|
```
|
|
155
187
|
|
|
156
|
-
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### preload.js 编写规范
|
|
157
191
|
|
|
158
192
|
```javascript
|
|
159
|
-
// preload.js -
|
|
193
|
+
// preload.js - 必须使用 CommonJS 规范
|
|
160
194
|
const fs = require('fs')
|
|
161
195
|
const os = require('os')
|
|
162
196
|
const path = require('path')
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
197
|
+
const { PDFDocument } = require('pdf-lib') // 可使用 npm 包
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 通过 window 对象暴露 API 给前端
|
|
201
|
+
* 命名建议:window.{插件名}Api 或 window.{功能名}Api
|
|
202
|
+
*/
|
|
203
|
+
window.myPluginApi = {
|
|
204
|
+
// 同步方法
|
|
166
205
|
getHomeDir: () => os.homedir(),
|
|
167
|
-
|
|
168
|
-
|
|
206
|
+
getPlatform: () => process.platform,
|
|
207
|
+
|
|
208
|
+
// 异步方法
|
|
209
|
+
readFile: async (filePath) => {
|
|
210
|
+
return fs.promises.readFile(filePath, 'utf-8')
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// 复杂功能封装
|
|
214
|
+
mergePDFs: async (pdfPaths, outputPath) => {
|
|
215
|
+
const mergedPdf = await PDFDocument.create()
|
|
216
|
+
for (const pdfPath of pdfPaths) {
|
|
217
|
+
const pdfBytes = fs.readFileSync(pdfPath)
|
|
218
|
+
const pdf = await PDFDocument.load(pdfBytes)
|
|
219
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices())
|
|
220
|
+
pages.forEach(page => mergedPdf.addPage(page))
|
|
221
|
+
}
|
|
222
|
+
const bytes = await mergedPdf.save()
|
|
223
|
+
fs.writeFileSync(outputPath, bytes)
|
|
224
|
+
return outputPath
|
|
225
|
+
}
|
|
169
226
|
}
|
|
227
|
+
|
|
228
|
+
console.log('[Preload] API 已挂载到 window.myPluginApi')
|
|
170
229
|
```
|
|
171
230
|
|
|
172
|
-
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### 前端调用示例
|
|
173
234
|
|
|
174
|
-
```
|
|
175
|
-
// 在
|
|
176
|
-
|
|
177
|
-
const content = window.myApi?.readFile('/path/to/file.txt')
|
|
235
|
+
```tsx
|
|
236
|
+
// 在 React 组件中调用
|
|
237
|
+
import { useEffect, useState } from 'react'
|
|
178
238
|
|
|
179
|
-
//
|
|
180
|
-
|
|
239
|
+
// 类型声明(推荐单独放在 types.d.ts)
|
|
240
|
+
declare global {
|
|
241
|
+
interface Window {
|
|
242
|
+
myPluginApi?: {
|
|
243
|
+
getHomeDir: () => string
|
|
244
|
+
getPlatform: () => string
|
|
245
|
+
readFile: (path: string) => Promise<string>
|
|
246
|
+
mergePDFs: (paths: string[], output: string) => Promise<string>
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function MyComponent() {
|
|
252
|
+
const [homeDir, setHomeDir] = useState('')
|
|
253
|
+
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
// 使用可选链确保安全访问
|
|
256
|
+
if (window.myPluginApi) {
|
|
257
|
+
setHomeDir(window.myPluginApi.getHomeDir())
|
|
258
|
+
}
|
|
259
|
+
}, [])
|
|
260
|
+
|
|
261
|
+
const handleMerge = async () => {
|
|
262
|
+
const result = await window.myPluginApi?.mergePDFs(
|
|
263
|
+
['/path/to/1.pdf', '/path/to/2.pdf'],
|
|
264
|
+
'/path/to/merged.pdf'
|
|
265
|
+
)
|
|
266
|
+
console.log('合并完成:', result)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 核心 API 仍然可用
|
|
270
|
+
const handleCopy = async () => {
|
|
271
|
+
const text = await window.intools.clipboard.readText()
|
|
272
|
+
console.log('剪贴板内容:', text)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return <div>Home: {homeDir}</div>
|
|
276
|
+
}
|
|
181
277
|
```
|
|
182
278
|
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### 与 Main 后端的区别
|
|
282
|
+
|
|
283
|
+
| 对比项 | Preload 脚本 | Main 后端 (main.js) |
|
|
284
|
+
|--------|--------------|---------------------|
|
|
285
|
+
| 执行环境 | 渲染进程(带 Node.js 权限) | 独立 Worker 进程 |
|
|
286
|
+
| 调用方式 | `window.xxxApi.method()` | `window.intools.host.invoke()` |
|
|
287
|
+
| 适合场景 | 同步操作、UI 紧密相关的原生功能 | 后台任务、长时间运行的操作 |
|
|
288
|
+
| 进程通信 | 无需 IPC,直接调用 | 需要 IPC,异步调用 |
|
|
289
|
+
| 生命周期 | 随 UI 窗口创建/销毁 | 独立管理,可持久化 |
|
|
290
|
+
|
|
291
|
+
> [!TIP]
|
|
292
|
+
> **选择建议**:如果功能与 UI 紧密相关且需要快速响应,使用 **Preload**;如果是后台任务或需要在无 UI 时运行,使用 **Main 后端**。
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
183
296
|
### 注意事项
|
|
184
297
|
|
|
185
298
|
| 项目 | 说明 |
|
|
186
299
|
|------|------|
|
|
187
|
-
| 文件格式 | CommonJS 格式,使用 `require()` 导入模块 |
|
|
188
|
-
| 代码规范 |
|
|
189
|
-
| 可用模块 | Node.js 原生模块 +
|
|
190
|
-
| API 暴露 | 通过 `window.xxx`
|
|
191
|
-
| 核心 API | `window.intools` 核心 API
|
|
192
|
-
| 安全性 |
|
|
193
|
-
| 打包 |
|
|
300
|
+
| 📝 文件格式 | **必须是 CommonJS** 格式,使用 `require()` 导入模块 |
|
|
301
|
+
| 🔍 代码规范 | 必须是清晰可读的源码,**禁止压缩/混淆**(安全审查需要) |
|
|
302
|
+
| 📦 可用模块 | Node.js 原生模块 + 已安装的 npm 包 |
|
|
303
|
+
| 🌐 API 暴露 | 通过 `window.xxx` 暴露,建议使用 `window.{插件名}Api` 命名 |
|
|
304
|
+
| 🔧 核心 API | `window.intools` 核心 API 在 Preload 环境中**仍然可用** |
|
|
305
|
+
| ⚠️ 安全性 | 拥有完整 Node.js 权限,**请谨慎处理用户输入** |
|
|
306
|
+
| 📦 打包 | `intools pack` 会自动包含 preload 及其依赖 |
|
|
307
|
+
|
|
308
|
+
> [!CAUTION]
|
|
309
|
+
> Preload 脚本拥有完整的 Node.js 权限,可以访问文件系统、网络等敏感资源。请确保代码安全,避免执行不可信的用户输入。
|
|
194
310
|
|
|
195
311
|
---
|
|
196
312
|
|