intools-cli 1.0.0
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/README.md +780 -0
- package/assets/default-icon.png +0 -0
- package/dist/commands/build.js +111 -0
- package/dist/commands/create.js +1038 -0
- package/dist/commands/dev.js +117 -0
- package/dist/commands/pack.js +91 -0
- package/dist/index.js +31 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
# InTools CLI
|
|
2
|
+
|
|
3
|
+
InTools 插件开发的命令行工具,帮助你快速创建、调试、构建和打包插件。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [安装](#安装)
|
|
8
|
+
- [快速开始](#快速开始)
|
|
9
|
+
- [命令参考](#命令参考)
|
|
10
|
+
- [插件开发指南](#插件开发指南)
|
|
11
|
+
- [项目结构](#项目结构)
|
|
12
|
+
- [manifest.json 配置](#manifestjson-配置)
|
|
13
|
+
- [开发模式](#开发模式)
|
|
14
|
+
- [使用第三方库](#使用第三方库)
|
|
15
|
+
- [插件 API](#插件-api)
|
|
16
|
+
- [构建与打包](#构建与打包)
|
|
17
|
+
- [完整示例](#完整示例)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 全局安装
|
|
25
|
+
npm install -g intools-cli
|
|
26
|
+
|
|
27
|
+
# 或在项目中作为开发依赖
|
|
28
|
+
npm install -D intools-cli
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 快速开始
|
|
34
|
+
|
|
35
|
+
### 5 分钟创建你的第一个插件
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 1. 创建插件项目
|
|
39
|
+
intools create my-plugin
|
|
40
|
+
|
|
41
|
+
# 2. 进入项目目录
|
|
42
|
+
cd my-plugin
|
|
43
|
+
|
|
44
|
+
# 3. 安装依赖
|
|
45
|
+
npm install
|
|
46
|
+
|
|
47
|
+
# 4. 启动开发模式
|
|
48
|
+
npm run dev
|
|
49
|
+
|
|
50
|
+
# 5. 构建插件
|
|
51
|
+
npm run build
|
|
52
|
+
|
|
53
|
+
# 6. 打包发布
|
|
54
|
+
npm run pack
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
执行完成后,你将得到一个 `my-plugin-1.0.0.inplugin` 文件,可以直接安装到 InTools 中。
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 命令参考
|
|
62
|
+
|
|
63
|
+
### `intools create <name>`
|
|
64
|
+
|
|
65
|
+
创建新的插件项目。
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# 创建 React 插件(默认,带 UI)
|
|
69
|
+
intools create my-plugin
|
|
70
|
+
|
|
71
|
+
# 创建基础插件(无 UI)
|
|
72
|
+
intools create my-plugin --template basic
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**选项:**
|
|
76
|
+
|
|
77
|
+
| 选项 | 说明 | 默认值 |
|
|
78
|
+
|------|------|--------|
|
|
79
|
+
| `-t, --template <type>` | 模板类型:`react` 或 `basic` | `react` |
|
|
80
|
+
|
|
81
|
+
### `intools dev`
|
|
82
|
+
|
|
83
|
+
启动开发模式,支持热重载。
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 在插件目录中运行
|
|
87
|
+
intools dev
|
|
88
|
+
|
|
89
|
+
# 或通过 npm
|
|
90
|
+
npm run dev
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**功能:**
|
|
94
|
+
- 后端代码变化时自动重新构建
|
|
95
|
+
- 有 UI 的插件会自动启动 Vite 开发服务器
|
|
96
|
+
- 支持 sourcemap 便于调试
|
|
97
|
+
|
|
98
|
+
### `intools build`
|
|
99
|
+
|
|
100
|
+
构建插件,生成生产环境代码。
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
intools build
|
|
104
|
+
|
|
105
|
+
# 或通过 npm
|
|
106
|
+
npm run build
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**输出:**
|
|
110
|
+
- 后端代码:`dist/main.js`(压缩后)
|
|
111
|
+
- UI 代码:`ui/` 目录
|
|
112
|
+
|
|
113
|
+
### `intools pack`
|
|
114
|
+
|
|
115
|
+
将插件打包成 `.inplugin` 文件,用于发布和安装。
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
intools pack
|
|
119
|
+
|
|
120
|
+
# 或通过 npm
|
|
121
|
+
npm run pack
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**输出:** `<插件名>-<版本号>.inplugin`
|
|
125
|
+
|
|
126
|
+
> ⚠️ **注意:** 打包前请先运行 `intools build` 或 `npm run build`
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 插件开发指南
|
|
131
|
+
|
|
132
|
+
### 项目结构
|
|
133
|
+
|
|
134
|
+
#### React 插件(默认模板)
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
my-plugin/
|
|
138
|
+
├── package.json # npm 配置
|
|
139
|
+
├── manifest.json # 插件配置(核心文件)
|
|
140
|
+
├── tsconfig.json # TypeScript 配置
|
|
141
|
+
├── vite.config.ts # Vite 配置
|
|
142
|
+
├── icon.png # 插件图标
|
|
143
|
+
├── src/
|
|
144
|
+
│ ├── main.ts # 后端逻辑(沙箱运行)
|
|
145
|
+
│ ├── types/
|
|
146
|
+
│ │ └── intools.d.ts # API 类型定义
|
|
147
|
+
│ └── ui/ # React UI 源码
|
|
148
|
+
│ ├── index.html
|
|
149
|
+
│ ├── main.tsx
|
|
150
|
+
│ ├── App.tsx
|
|
151
|
+
│ └── styles.css
|
|
152
|
+
├── dist/ # 后端构建输出
|
|
153
|
+
│ └── main.js
|
|
154
|
+
└── ui/ # UI 构建输出
|
|
155
|
+
├── index.html
|
|
156
|
+
└── assets/
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### 基础插件(无 UI)
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
my-plugin/
|
|
163
|
+
├── package.json
|
|
164
|
+
├── manifest.json
|
|
165
|
+
├── icon.png
|
|
166
|
+
└── src/
|
|
167
|
+
└── main.ts
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### manifest.json 配置
|
|
173
|
+
|
|
174
|
+
`manifest.json` 是插件的核心配置文件,定义了插件的基本信息和触发条件。
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"name": "my-plugin",
|
|
179
|
+
"version": "1.0.0",
|
|
180
|
+
"displayName": "我的插件",
|
|
181
|
+
"description": "插件功能描述",
|
|
182
|
+
"main": "dist/main.js",
|
|
183
|
+
"ui": "ui/index.html",
|
|
184
|
+
"icon": "icon.png",
|
|
185
|
+
"features": [
|
|
186
|
+
{
|
|
187
|
+
"code": "main",
|
|
188
|
+
"explain": "主功能",
|
|
189
|
+
"cmds": [
|
|
190
|
+
{ "type": "keyword", "value": "myplugin" }
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### 顶层字段说明
|
|
198
|
+
|
|
199
|
+
| 字段 | 类型 | 必需 | 说明 |
|
|
200
|
+
|------|------|------|------|
|
|
201
|
+
| `name` | string | ✅ | 插件唯一标识(小写字母、数字、连字符) |
|
|
202
|
+
| `version` | string | ✅ | 语义化版本 (x.y.z) |
|
|
203
|
+
| `displayName` | string | ✅ | 用户看到的名称 |
|
|
204
|
+
| `description` | string | ✅ | 功能描述 |
|
|
205
|
+
| `main` | string | ✅ | 后端入口文件路径 |
|
|
206
|
+
| `ui` | string | ❌ | UI 文件路径(有界面时必填) |
|
|
207
|
+
| `icon` | string/object | ❌ | 插件图标 |
|
|
208
|
+
| `features` | array | ✅ | 功能入口列表 |
|
|
209
|
+
| `author` | string | ❌ | 作者名 |
|
|
210
|
+
| `homepage` | string | ❌ | 项目主页 |
|
|
211
|
+
| `minAppVersion` | string | ❌ | 最低 InTools 版本要求 |
|
|
212
|
+
|
|
213
|
+
#### 功能入口 (features)
|
|
214
|
+
|
|
215
|
+
一个插件可以有多个功能入口,每个入口可以有不同的触发方式:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"features": [
|
|
220
|
+
{
|
|
221
|
+
"code": "format",
|
|
222
|
+
"explain": "格式化 JSON",
|
|
223
|
+
"cmds": [
|
|
224
|
+
{ "type": "keyword", "value": "json" },
|
|
225
|
+
{ "type": "keyword", "value": "格式化" },
|
|
226
|
+
{ "type": "regex", "match": "^\\s*[{\\[]", "explain": "检测到 JSON" }
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"code": "minify",
|
|
231
|
+
"explain": "压缩 JSON",
|
|
232
|
+
"cmds": [
|
|
233
|
+
{ "type": "keyword", "value": "json压缩" }
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| 字段 | 类型 | 必需 | 说明 |
|
|
241
|
+
|------|------|------|------|
|
|
242
|
+
| `code` | string | ✅ | 功能代码,传递给插件 |
|
|
243
|
+
| `explain` | string | ✅ | 功能说明,显示给用户 |
|
|
244
|
+
| `cmds` | array | ✅ | 触发命令列表 |
|
|
245
|
+
|
|
246
|
+
#### 触发命令类型 (cmds)
|
|
247
|
+
|
|
248
|
+
| type | 说明 | 额外字段 |
|
|
249
|
+
|------|------|----------|
|
|
250
|
+
| `keyword` | 关键词触发 | `value`: 关键词 |
|
|
251
|
+
| `regex` | 正则匹配 | `match`: 正则表达式, `explain`: 说明 |
|
|
252
|
+
| `files` | 文件类型 | `exts`: [".json", ".txt"] |
|
|
253
|
+
| `img` | 图片 | - |
|
|
254
|
+
| `over` | 选中文本 | - |
|
|
255
|
+
|
|
256
|
+
#### 图标配置
|
|
257
|
+
|
|
258
|
+
支持三种格式:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
// 1. 本地文件(推荐)
|
|
262
|
+
"icon": "icon.png"
|
|
263
|
+
|
|
264
|
+
// 2. URL
|
|
265
|
+
"icon": "https://example.com/icon.png"
|
|
266
|
+
|
|
267
|
+
// 3. 内联 SVG
|
|
268
|
+
"icon": "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"...\"/></svg>"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
> 💡 **提示:** 未设置 `icon` 时,会自动尝试加载插件目录下的 `icon.png`
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### 开发模式
|
|
276
|
+
|
|
277
|
+
#### 启动开发服务器
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
npm run dev
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
开发模式会:
|
|
284
|
+
1. **监听后端代码变化** - `src/main.ts` 及其依赖变化时自动重新构建
|
|
285
|
+
2. **启动 Vite 开发服务器** - 有 UI 时自动启动,支持 HMR(热模块替换)
|
|
286
|
+
|
|
287
|
+
#### 在 InTools 中预览
|
|
288
|
+
|
|
289
|
+
1. 打开 InTools 设置
|
|
290
|
+
2. 添加开发目录(插件项目路径)
|
|
291
|
+
3. 输入关键词触发插件
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
### 使用第三方库
|
|
296
|
+
|
|
297
|
+
InTools CLI 使用 **esbuild** 打包,会将所有依赖内联到 `dist/main.js` 中,因此你可以自由使用任何 npm 包。
|
|
298
|
+
|
|
299
|
+
#### 示例:使用 dayjs 处理日期
|
|
300
|
+
|
|
301
|
+
**1. 安装依赖**
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm install dayjs
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**2. 在代码中使用**
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// src/main.ts
|
|
311
|
+
import dayjs from 'dayjs'
|
|
312
|
+
|
|
313
|
+
module.exports = {
|
|
314
|
+
async run(context: any) {
|
|
315
|
+
const { clipboard, notification } = context.api
|
|
316
|
+
const text = context.input || await clipboard.readText()
|
|
317
|
+
|
|
318
|
+
// 使用 dayjs 格式化日期
|
|
319
|
+
const result = dayjs(text).format('YYYY-MM-DD HH:mm:ss')
|
|
320
|
+
|
|
321
|
+
await clipboard.writeText(result)
|
|
322
|
+
notification.show('日期格式化完成')
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**3. 构建**
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
npm run build
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
构建后,`dayjs` 的代码会被内联到 `dist/main.js` 中,无需用户单独安装。
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
### 插件 API
|
|
338
|
+
|
|
339
|
+
插件在沙箱中运行,通过 `context.api` 访问各种 API。
|
|
340
|
+
|
|
341
|
+
#### 剪贴板 API (clipboard)
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
const { clipboard } = context.api
|
|
345
|
+
|
|
346
|
+
// 读取文本
|
|
347
|
+
const text = clipboard.readText()
|
|
348
|
+
|
|
349
|
+
// 写入文本
|
|
350
|
+
await clipboard.writeText('Hello World')
|
|
351
|
+
|
|
352
|
+
// 读取图片(返回 PNG Buffer)
|
|
353
|
+
const imageBuffer = clipboard.readImage()
|
|
354
|
+
|
|
355
|
+
// 写入图片
|
|
356
|
+
clipboard.writeImage(imageData)
|
|
357
|
+
|
|
358
|
+
// 读取文件列表
|
|
359
|
+
const files = clipboard.readFiles()
|
|
360
|
+
// 返回: [{ path, name, size, isDirectory }]
|
|
361
|
+
|
|
362
|
+
// 获取剪贴板格式
|
|
363
|
+
const format = clipboard.getFormat()
|
|
364
|
+
// 返回: 'text' | 'image' | 'files' | 'empty'
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### 通知 API (notification)
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
const { notification } = context.api
|
|
371
|
+
|
|
372
|
+
notification.show('操作成功')
|
|
373
|
+
notification.show('发生错误', 'error')
|
|
374
|
+
// type: 'info' | 'success' | 'warning' | 'error'
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### 存储 API (storage)
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
const { storage } = context.api
|
|
381
|
+
|
|
382
|
+
// 存储数据
|
|
383
|
+
await storage.set('key', { foo: 'bar' })
|
|
384
|
+
|
|
385
|
+
// 读取数据
|
|
386
|
+
const data = await storage.get('key')
|
|
387
|
+
|
|
388
|
+
// 删除数据
|
|
389
|
+
await storage.remove('key')
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### 网络 API (http)
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
const { http } = context.api
|
|
396
|
+
|
|
397
|
+
// 完整请求
|
|
398
|
+
const response = await http.request({
|
|
399
|
+
url: 'https://api.example.com/data',
|
|
400
|
+
method: 'POST',
|
|
401
|
+
headers: { 'Authorization': 'Bearer token' },
|
|
402
|
+
body: { key: 'value' },
|
|
403
|
+
timeout: 5000
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
// 快捷方法
|
|
407
|
+
await http.get(url, headers?)
|
|
408
|
+
await http.post(url, body?, headers?)
|
|
409
|
+
await http.put(url, body?, headers?)
|
|
410
|
+
await http.delete(url, headers?)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### 窗口 API (window)
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
const { window } = context.api
|
|
417
|
+
|
|
418
|
+
// 设置窗口大小
|
|
419
|
+
await window.setSize(600, 400)
|
|
420
|
+
|
|
421
|
+
// 隐藏窗口
|
|
422
|
+
await window.hide()
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
#### 文件系统 API (filesystem)
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
const { filesystem } = context.api
|
|
429
|
+
|
|
430
|
+
// 读写文件
|
|
431
|
+
const buffer = filesystem.readFile('/path/to/file.png')
|
|
432
|
+
const text = filesystem.readFile('/path/to/file.txt', 'utf-8')
|
|
433
|
+
filesystem.writeFile('/path/to/output.txt', 'content', 'utf-8')
|
|
434
|
+
|
|
435
|
+
// 文件操作
|
|
436
|
+
filesystem.exists('/path')
|
|
437
|
+
filesystem.unlink('/path')
|
|
438
|
+
filesystem.mkdir('/path')
|
|
439
|
+
filesystem.readdir('/path')
|
|
440
|
+
filesystem.stat('/path')
|
|
441
|
+
filesystem.copy(src, dest)
|
|
442
|
+
filesystem.move(src, dest)
|
|
443
|
+
|
|
444
|
+
// 路径工具
|
|
445
|
+
filesystem.extname('/file.txt') // '.txt'
|
|
446
|
+
filesystem.dirname('/path/file') // '/path'
|
|
447
|
+
filesystem.basename('/path/file.txt') // 'file.txt'
|
|
448
|
+
filesystem.join('/path', 'to', 'file')
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
### 插件 UI
|
|
454
|
+
|
|
455
|
+
#### 在 UI 中访问 API
|
|
456
|
+
|
|
457
|
+
UI 通过 `window.intools` 全局对象访问 API:
|
|
458
|
+
|
|
459
|
+
```tsx
|
|
460
|
+
// src/ui/App.tsx
|
|
461
|
+
import { useEffect, useState } from 'react'
|
|
462
|
+
|
|
463
|
+
export default function App() {
|
|
464
|
+
const [input, setInput] = useState('')
|
|
465
|
+
|
|
466
|
+
// 接收插件初始化数据
|
|
467
|
+
useEffect(() => {
|
|
468
|
+
window.intools?.onPluginInit?.((data) => {
|
|
469
|
+
if (data.input) setInput(data.input)
|
|
470
|
+
})
|
|
471
|
+
}, [])
|
|
472
|
+
|
|
473
|
+
const handleProcess = async () => {
|
|
474
|
+
const result = input.toUpperCase()
|
|
475
|
+
// 复制到剪贴板
|
|
476
|
+
await window.intools?.clipboard?.writeText(result)
|
|
477
|
+
// 显示通知
|
|
478
|
+
window.intools?.notification?.show('已复制到剪贴板')
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return (
|
|
482
|
+
<div>
|
|
483
|
+
<textarea value={input} onChange={(e) => setInput(e.target.value)} />
|
|
484
|
+
<button onClick={handleProcess}>处理</button>
|
|
485
|
+
</div>
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### 主题适配
|
|
491
|
+
|
|
492
|
+
插件 UI 应跟随 InTools 主题:
|
|
493
|
+
|
|
494
|
+
```tsx
|
|
495
|
+
// 获取初始主题
|
|
496
|
+
function getInitialTheme(): 'light' | 'dark' {
|
|
497
|
+
const params = new URLSearchParams(window.location.search)
|
|
498
|
+
return (params.get('theme') as 'light' | 'dark') || 'light'
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// 监听主题变化
|
|
502
|
+
useEffect(() => {
|
|
503
|
+
window.intools?.onThemeChange?.((theme) => {
|
|
504
|
+
document.documentElement.classList.toggle('dark', theme === 'dark')
|
|
505
|
+
})
|
|
506
|
+
}, [])
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
### 生命周期钩子
|
|
512
|
+
|
|
513
|
+
插件支持生命周期钩子函数:
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
module.exports = {
|
|
517
|
+
// 插件加载时调用
|
|
518
|
+
onLoad() {
|
|
519
|
+
console.log('插件已加载')
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
// 插件卸载时调用
|
|
523
|
+
onUnload() {
|
|
524
|
+
console.log('插件即将卸载')
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
// 插件启用时调用
|
|
528
|
+
onEnable() {
|
|
529
|
+
console.log('插件已启用')
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
// 插件禁用时调用
|
|
533
|
+
onDisable() {
|
|
534
|
+
console.log('插件已禁用')
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
// 主执行函数
|
|
538
|
+
async run(context) {
|
|
539
|
+
// 插件主逻辑
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
| 钩子 | 触发时机 | 用途 |
|
|
545
|
+
|------|----------|------|
|
|
546
|
+
| `onLoad` | 插件加载时 | 初始化资源、注册服务 |
|
|
547
|
+
| `onUnload` | 插件卸载时 | 清理资源、保存状态 |
|
|
548
|
+
| `onEnable` | 插件启用时 | 恢复服务、重新注册 |
|
|
549
|
+
| `onDisable` | 插件禁用时 | 暂停服务、释放资源 |
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## 构建与打包
|
|
554
|
+
|
|
555
|
+
### 构建流程
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
# 构建生产版本
|
|
559
|
+
npm run build
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
构建命令会:
|
|
563
|
+
1. 使用 esbuild 打包后端代码到 `dist/main.js`(压缩)
|
|
564
|
+
2. 使用 Vite 构建 UI 到 `ui/` 目录(如果有 UI)
|
|
565
|
+
|
|
566
|
+
### 打包发布
|
|
567
|
+
|
|
568
|
+
```bash
|
|
569
|
+
# 打包成 .inplugin 文件
|
|
570
|
+
npm run pack
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
生成的 `.inplugin` 文件实际上是 ZIP 格式,包含:
|
|
574
|
+
|
|
575
|
+
```
|
|
576
|
+
my-plugin-1.0.0.inplugin
|
|
577
|
+
├── manifest.json # 插件配置
|
|
578
|
+
├── main.js # 打包后的后端代码
|
|
579
|
+
├── icon.png # 图标(可选)
|
|
580
|
+
└── ui/ # UI 资源(可选)
|
|
581
|
+
├── index.html
|
|
582
|
+
└── assets/
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### 安装插件
|
|
586
|
+
|
|
587
|
+
用户可以通过以下方式安装 `.inplugin` 文件:
|
|
588
|
+
|
|
589
|
+
1. **双击安装** - 直接双击 `.inplugin` 文件
|
|
590
|
+
2. **拖拽安装** - 将文件拖入 InTools 窗口
|
|
591
|
+
3. **命令安装** - `intools install ./my-plugin.inplugin`
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## 完整示例
|
|
596
|
+
|
|
597
|
+
### 示例 1:JSON 格式化插件(无 UI)
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
intools create json-formatter --template basic
|
|
601
|
+
cd json-formatter
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
**manifest.json:**
|
|
605
|
+
```json
|
|
606
|
+
{
|
|
607
|
+
"name": "json-formatter",
|
|
608
|
+
"version": "1.0.0",
|
|
609
|
+
"displayName": "JSON 格式化",
|
|
610
|
+
"description": "格式化或压缩 JSON 数据",
|
|
611
|
+
"main": "dist/main.js",
|
|
612
|
+
"icon": "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M5 3h2v2H5v5a2 2 0 01-2 2 2 2 0 012 2v5h2v2H5c-1.1 0-2-.9-2-2v-4a2 2 0 00-2-2H0v-2h1a2 2 0 002-2V5a2 2 0 012-2zm14 0a2 2 0 012 2v4a2 2 0 002 2h1v2h-1a2 2 0 00-2 2v4a2 2 0 01-2 2h-2v-2h2v-5a2 2 0 012-2 2 2 0 01-2-2V5h-2V3h2z\"/></svg>",
|
|
613
|
+
"features": [
|
|
614
|
+
{
|
|
615
|
+
"code": "format",
|
|
616
|
+
"explain": "格式化 JSON",
|
|
617
|
+
"cmds": [
|
|
618
|
+
{ "type": "keyword", "value": "json" },
|
|
619
|
+
{ "type": "regex", "match": "^\\s*[{\\[]", "explain": "检测到 JSON" }
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**src/main.ts:**
|
|
627
|
+
```typescript
|
|
628
|
+
module.exports = {
|
|
629
|
+
async run(context: any) {
|
|
630
|
+
const { clipboard, notification } = context.api
|
|
631
|
+
const text = context.input || await clipboard.readText()
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
const obj = JSON.parse(text)
|
|
635
|
+
const formatted = JSON.stringify(obj, null, 2)
|
|
636
|
+
await clipboard.writeText(formatted)
|
|
637
|
+
notification.show('JSON 格式化成功')
|
|
638
|
+
} catch (e) {
|
|
639
|
+
notification.show('无效的 JSON', 'error')
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### 示例 2:翻译插件(带 UI)
|
|
646
|
+
|
|
647
|
+
```bash
|
|
648
|
+
intools create translator
|
|
649
|
+
cd translator
|
|
650
|
+
npm install
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**manifest.json:**
|
|
654
|
+
```json
|
|
655
|
+
{
|
|
656
|
+
"name": "translator",
|
|
657
|
+
"version": "1.0.0",
|
|
658
|
+
"displayName": "快速翻译",
|
|
659
|
+
"description": "中英文互译",
|
|
660
|
+
"main": "dist/main.js",
|
|
661
|
+
"ui": "ui/index.html",
|
|
662
|
+
"features": [
|
|
663
|
+
{
|
|
664
|
+
"code": "translate",
|
|
665
|
+
"explain": "翻译文本",
|
|
666
|
+
"cmds": [
|
|
667
|
+
{ "type": "keyword", "value": "fy" },
|
|
668
|
+
{ "type": "keyword", "value": "翻译" }
|
|
669
|
+
]
|
|
670
|
+
}
|
|
671
|
+
]
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**src/main.ts:**
|
|
676
|
+
```typescript
|
|
677
|
+
module.exports = {
|
|
678
|
+
onLoad() {
|
|
679
|
+
console.log('翻译插件已加载')
|
|
680
|
+
},
|
|
681
|
+
|
|
682
|
+
async run(context: any) {
|
|
683
|
+
// UI 插件通常不需要在 run 中做太多事
|
|
684
|
+
// 主要逻辑在 UI 中处理
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
**src/ui/App.tsx:**
|
|
690
|
+
```tsx
|
|
691
|
+
import { useEffect, useState } from 'react'
|
|
692
|
+
|
|
693
|
+
export default function App() {
|
|
694
|
+
const [input, setInput] = useState('')
|
|
695
|
+
const [output, setOutput] = useState('')
|
|
696
|
+
const [loading, setLoading] = useState(false)
|
|
697
|
+
|
|
698
|
+
useEffect(() => {
|
|
699
|
+
window.intools?.onPluginInit?.((data) => {
|
|
700
|
+
if (data.input) setInput(data.input)
|
|
701
|
+
})
|
|
702
|
+
}, [])
|
|
703
|
+
|
|
704
|
+
const handleTranslate = async () => {
|
|
705
|
+
if (!input.trim()) return
|
|
706
|
+
|
|
707
|
+
setLoading(true)
|
|
708
|
+
try {
|
|
709
|
+
// 调用翻译 API
|
|
710
|
+
const response = await fetch('https://api.translate.com/v1/translate', {
|
|
711
|
+
method: 'POST',
|
|
712
|
+
headers: { 'Content-Type': 'application/json' },
|
|
713
|
+
body: JSON.stringify({ text: input, from: 'auto', to: 'en' })
|
|
714
|
+
})
|
|
715
|
+
const data = await response.json()
|
|
716
|
+
setOutput(data.translation)
|
|
717
|
+
} catch (error) {
|
|
718
|
+
window.intools?.notification?.show('翻译失败', 'error')
|
|
719
|
+
} finally {
|
|
720
|
+
setLoading(false)
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const handleCopy = async () => {
|
|
725
|
+
await window.intools?.clipboard?.writeText(output)
|
|
726
|
+
window.intools?.notification?.show('已复制')
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return (
|
|
730
|
+
<div className="app">
|
|
731
|
+
<div className="titlebar">快速翻译</div>
|
|
732
|
+
<div className="container">
|
|
733
|
+
<textarea
|
|
734
|
+
value={input}
|
|
735
|
+
onChange={(e) => setInput(e.target.value)}
|
|
736
|
+
placeholder="输入要翻译的文本..."
|
|
737
|
+
/>
|
|
738
|
+
<button onClick={handleTranslate} disabled={loading}>
|
|
739
|
+
{loading ? '翻译中...' : '翻译'}
|
|
740
|
+
</button>
|
|
741
|
+
<textarea
|
|
742
|
+
value={output}
|
|
743
|
+
readOnly
|
|
744
|
+
placeholder="翻译结果..."
|
|
745
|
+
/>
|
|
746
|
+
{output && <button onClick={handleCopy}>复制结果</button>}
|
|
747
|
+
</div>
|
|
748
|
+
</div>
|
|
749
|
+
)
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 常见问题
|
|
756
|
+
|
|
757
|
+
### Q: 如何调试插件?
|
|
758
|
+
|
|
759
|
+
A: 使用 `npm run dev` 启动开发模式,后端代码支持 sourcemap。可以在 InTools 的开发者工具中查看日志和调试。
|
|
760
|
+
|
|
761
|
+
### Q: 为什么我的第三方库不能用?
|
|
762
|
+
|
|
763
|
+
A: 确保依赖已安装(`npm install`)并且使用 `npm run build` 构建。CLI 会自动将依赖打包到 `dist/main.js` 中。
|
|
764
|
+
|
|
765
|
+
### Q: 如何更新已安装的插件?
|
|
766
|
+
|
|
767
|
+
A: 修改 `manifest.json` 中的 `version` 字段,重新打包后安装即可。InTools 会检测版本变化并更新。
|
|
768
|
+
|
|
769
|
+
### Q: 插件可以访问系统命令吗?
|
|
770
|
+
|
|
771
|
+
A: 出于安全考虑,默认不开放 `shell` 权限。如需使用,请在 `manifest.json` 的 `permissions` 中声明,并等待审核。
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## 相关文档
|
|
776
|
+
|
|
777
|
+
- [InTools 插件开发规范](../../docs/plugin-spec.md)
|
|
778
|
+
- [InTools API 接口参考](../../docs/api-reference.md)
|
|
779
|
+
- [Manifest 规范 v2](../../docs/manifest-v2.md)
|
|
780
|
+
- [插件打包说明](../../docs/plugin-packaging.md)
|