mulby-cli 1.1.5

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.
Files changed (59) hide show
  1. package/PLUGIN_DEVELOP_PROMPT.md +1164 -0
  2. package/README.md +852 -0
  3. package/assets/default-icon.png +0 -0
  4. package/dist/commands/ai-session.js +44 -0
  5. package/dist/commands/build.js +111 -0
  6. package/dist/commands/config-ai.js +291 -0
  7. package/dist/commands/config.js +53 -0
  8. package/dist/commands/create/ai-create.js +183 -0
  9. package/dist/commands/create/assets.js +53 -0
  10. package/dist/commands/create/basic.js +72 -0
  11. package/dist/commands/create/index.js +73 -0
  12. package/dist/commands/create/react.js +136 -0
  13. package/dist/commands/create/templates/basic.js +383 -0
  14. package/dist/commands/create/templates/react/backend.js +72 -0
  15. package/dist/commands/create/templates/react/config.js +166 -0
  16. package/dist/commands/create/templates/react/docs.js +78 -0
  17. package/dist/commands/create/templates/react/hooks.js +469 -0
  18. package/dist/commands/create/templates/react/index.js +41 -0
  19. package/dist/commands/create/templates/react/types.js +1228 -0
  20. package/dist/commands/create/templates/react/ui.js +528 -0
  21. package/dist/commands/create/templates/react.js +1888 -0
  22. package/dist/commands/dev.js +141 -0
  23. package/dist/commands/pack.js +160 -0
  24. package/dist/commands/resume.js +97 -0
  25. package/dist/commands/test-ui.js +50 -0
  26. package/dist/index.js +71 -0
  27. package/dist/services/ai/PLUGIN_API.md +1102 -0
  28. package/dist/services/ai/PLUGIN_DEVELOP_PROMPT.md +1164 -0
  29. package/dist/services/ai/context-manager.js +639 -0
  30. package/dist/services/ai/index.js +88 -0
  31. package/dist/services/ai/knowledge.js +52 -0
  32. package/dist/services/ai/prompts.js +114 -0
  33. package/dist/services/ai/providers/base.js +38 -0
  34. package/dist/services/ai/providers/claude.js +284 -0
  35. package/dist/services/ai/providers/deepseek.js +28 -0
  36. package/dist/services/ai/providers/gemini.js +191 -0
  37. package/dist/services/ai/providers/glm.js +31 -0
  38. package/dist/services/ai/providers/minimax.js +27 -0
  39. package/dist/services/ai/providers/openai.js +177 -0
  40. package/dist/services/ai/tools.js +204 -0
  41. package/dist/services/ai-generator.js +968 -0
  42. package/dist/services/config-manager.js +117 -0
  43. package/dist/services/dependency-manager.js +236 -0
  44. package/dist/services/file-writer.js +66 -0
  45. package/dist/services/plan-adapter.js +244 -0
  46. package/dist/services/plan-command-handler.js +172 -0
  47. package/dist/services/plan-manager.js +502 -0
  48. package/dist/services/session-manager.js +113 -0
  49. package/dist/services/task-analyzer.js +136 -0
  50. package/dist/services/tui/index.js +57 -0
  51. package/dist/services/tui/store.js +123 -0
  52. package/dist/types/ai.js +172 -0
  53. package/dist/types/plan.js +2 -0
  54. package/dist/ui/Terminal.js +56 -0
  55. package/dist/ui/components/InputArea.js +176 -0
  56. package/dist/ui/components/LogArea.js +19 -0
  57. package/dist/ui/components/PlanPanel.js +69 -0
  58. package/dist/ui/components/SelectArea.js +13 -0
  59. package/package.json +45 -0
@@ -0,0 +1,1888 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildReactManifest = buildReactManifest;
4
+ exports.buildReactPackageJson = buildReactPackageJson;
5
+ exports.buildTsConfig = buildTsConfig;
6
+ exports.buildViteConfig = buildViteConfig;
7
+ exports.buildPostcssConfig = buildPostcssConfig;
8
+ exports.buildTailwindConfig = buildTailwindConfig;
9
+ exports.buildBackendMain = buildBackendMain;
10
+ exports.buildIndexHtml = buildIndexHtml;
11
+ exports.buildMainTsx = buildMainTsx;
12
+ exports.buildAppTsx = buildAppTsx;
13
+ exports.buildStylesCss = buildStylesCss;
14
+ exports.buildUseIntools = buildUseIntools;
15
+ exports.buildGitignore = buildGitignore;
16
+ exports.buildReactReadme = buildReactReadme;
17
+ exports.buildIntoolsTypes = buildIntoolsTypes;
18
+ function buildReactManifest(name) {
19
+ return {
20
+ id: name,
21
+ name,
22
+ version: '1.0.0',
23
+ author: 'intools',
24
+ displayName: name,
25
+ description: '插件描述',
26
+ main: 'dist/main.js',
27
+ ui: 'ui/index.html',
28
+ icon: 'icon.png',
29
+ window: {
30
+ width: 800, // 默认宽度
31
+ height: 600, // 默认高度
32
+ minWidth: 400, // 最小宽度
33
+ minHeight: 300, // 最小高度
34
+ maxWidth: 1200, // 最大宽度
35
+ maxHeight: 900 // 最大高度
36
+ },
37
+ features: [
38
+ {
39
+ code: 'main',
40
+ explain: '主功能',
41
+ cmds: [{ type: 'keyword', value: name }]
42
+ }
43
+ ]
44
+ };
45
+ }
46
+ function buildReactPackageJson(name) {
47
+ return {
48
+ name,
49
+ version: '1.0.0',
50
+ type: 'module',
51
+ scripts: {
52
+ dev: 'intools dev',
53
+ build: 'npm run build:backend && npm run build:ui',
54
+ 'build:backend': 'esbuild src/main.ts --bundle --platform=node --outfile=dist/main.js',
55
+ 'build:ui': 'vite build',
56
+ pack: 'intools pack'
57
+ },
58
+ dependencies: {
59
+ react: '^18.3.1',
60
+ 'react-dom': '^18.3.1',
61
+ 'lucide-react': '^0.562.0'
62
+ },
63
+ devDependencies: {
64
+ '@types/react': '^18.3.3',
65
+ '@types/react-dom': '^18.3.0',
66
+ '@vitejs/plugin-react': '^4.3.1',
67
+ autoprefixer: '^10.4.19',
68
+ postcss: '^8.4.38',
69
+ tailwindcss: '^3.4.4',
70
+ typescript: '^5.2.2',
71
+ vite: '^5.3.1'
72
+ }
73
+ };
74
+ }
75
+ function buildTsConfig() {
76
+ return {
77
+ compilerOptions: {
78
+ target: 'ES2020',
79
+ useDefineForClassFields: true,
80
+ lib: ['ES2020', 'DOM', 'DOM.Iterable'],
81
+ module: 'ESNext',
82
+ skipLibCheck: true,
83
+ moduleResolution: 'bundler',
84
+ allowImportingTsExtensions: true,
85
+ resolveJsonModule: true,
86
+ isolatedModules: true,
87
+ noEmit: true,
88
+ jsx: 'react-jsx',
89
+ strict: true,
90
+ noUnusedLocals: true,
91
+ noUnusedParameters: true,
92
+ noFallthroughCasesInSwitch: true
93
+ },
94
+ include: ['src']
95
+ };
96
+ }
97
+ function buildViteConfig() {
98
+ return `import { defineConfig } from 'vite'
99
+ import react from '@vitejs/plugin-react'
100
+ import path from 'path'
101
+
102
+ export default defineConfig({
103
+ plugins: [react()],
104
+ root: 'src/ui',
105
+ base: './',
106
+ build: {
107
+ outDir: '../../ui',
108
+ emptyOutDir: true
109
+ },
110
+ resolve: {
111
+ alias: {
112
+ '@': path.resolve(__dirname, 'src')
113
+ }
114
+ }
115
+ })
116
+ `;
117
+ }
118
+ function buildPostcssConfig() {
119
+ return `export default {
120
+ plugins: {
121
+ tailwindcss: {},
122
+ autoprefixer: {},
123
+ },
124
+ }
125
+ `;
126
+ }
127
+ function buildTailwindConfig() {
128
+ return `/** @type {import('tailwindcss').Config} */
129
+ export default {
130
+ content: [
131
+ "./src/ui/index.html",
132
+ "./src/ui/**/*.{js,ts,jsx,tsx}",
133
+ ],
134
+ theme: {
135
+ extend: {},
136
+ },
137
+ plugins: [],
138
+ }
139
+ `;
140
+ }
141
+ function buildBackendMain(name) {
142
+ return `interface PluginContext {
143
+ api: {
144
+ clipboard: {
145
+ readText: () => string
146
+ writeText: (text: string) => Promise<void>
147
+ readImage: () => ArrayBuffer | null
148
+ getFormat: () => string
149
+ }
150
+ clipboardHistory: {
151
+ query: (options?: {
152
+ type?: 'text' | 'image' | 'files'
153
+ search?: string
154
+ favorite?: boolean
155
+ limit?: number
156
+ offset?: number
157
+ }) => Promise<any[]>
158
+ get: (id: string) => Promise<any>
159
+ copy: (id: string) => Promise<{ success: boolean; error?: string }>
160
+ toggleFavorite: (id: string) => Promise<{ success: boolean }>
161
+ delete: (id: string) => Promise<{ success: boolean }>
162
+ clear: () => Promise<{ success: boolean }>
163
+ stats: () => Promise<{ total: number; text: number; image: number; files: number; favorite: number }>
164
+ }
165
+ notification: {
166
+ show: (message: string, type?: string) => void
167
+ }
168
+ messaging: {
169
+ send: (targetPluginId: string, type: string, payload: unknown) => Promise<void>
170
+ broadcast: (type: string, payload: unknown) => Promise<void>
171
+ on: (handler: (message: { id: string; from: string; to?: string; type: string; payload: unknown; timestamp: number }) => void | Promise<void>) => void
172
+ off: (handler?: (message: any) => void) => void
173
+ }
174
+ scheduler: {
175
+ schedule: (task: {
176
+ name: string
177
+ type: 'once' | 'repeat' | 'delay'
178
+ callback: string
179
+ time?: number
180
+ cron?: string
181
+ delay?: number
182
+ payload?: any
183
+ maxRetries?: number
184
+ retryDelay?: number
185
+ timeout?: number
186
+ }) => Promise<any>
187
+ cancelTask: (taskId: string) => Promise<void>
188
+ pauseTask: (taskId: string) => Promise<void>
189
+ resumeTask: (taskId: string) => Promise<void>
190
+ listTasks: (filter?: { status?: string; type?: string; limit?: number; offset?: number }) => Promise<any[]>
191
+ getTaskCount: (filter?: { status?: string; type?: string }) => Promise<number>
192
+ getTask: (taskId: string) => Promise<any>
193
+ deleteTasks: (taskIds: string[]) => Promise<{ success: boolean; deletedCount: number }>
194
+ cleanupTasks: (olderThan?: number) => Promise<{ success: boolean; deletedCount: number }>
195
+ getExecutions: (taskId: string, limit?: number) => Promise<any[]>
196
+ validateCron: (expression: string) => boolean
197
+ getNextCronTime: (expression: string, after?: Date) => Date
198
+ describeCron: (expression: string) => string
199
+ }
200
+ ai: {
201
+ call: (option: {
202
+ model?: string
203
+ messages: Array<{ role: 'system' | 'user' | 'assistant'; content?: string | Array<any> }>
204
+ tools?: Array<{ type: 'function'; function: { name: string; description?: string; parameters?: object } }>
205
+ params?: any
206
+ }, onChunk?: (chunk: any) => void) => Promise<{ role: 'assistant'; content?: string }>
207
+ allModels: () => Promise<any[]>
208
+ tokens: {
209
+ estimate: (input: { model?: string; messages: Array<any> }) => Promise<{ inputTokens: number; outputTokens: number }>
210
+ }
211
+ attachments: {
212
+ upload: (input: { filePath?: string; buffer?: ArrayBuffer; mimeType: string; purpose?: string }) => Promise<any>
213
+ get: (attachmentId: string) => Promise<any>
214
+ delete: (attachmentId: string) => Promise<void>
215
+ }
216
+ images: {
217
+ generate: (input: { model: string; prompt: string; size?: string; count?: number }) => Promise<{ images: string[] }>
218
+ edit: (input: { model: string; imageAttachmentId: string; prompt: string }) => Promise<{ images: string[] }>
219
+ }
220
+ videos: {
221
+ generate: (input: { model: string; prompt: string; duration?: number; size?: string }) => Promise<void>
222
+ }
223
+ }
224
+ features?: {
225
+ getFeatures: (codes?: string[]) => Array<{ code: string }>
226
+ setFeature: (feature: {
227
+ code: string
228
+ explain?: string
229
+ icon?: string
230
+ platform?: string | string[]
231
+ mode?: 'ui' | 'silent' | 'detached'
232
+ route?: string
233
+ mainHide?: boolean
234
+ mainPush?: boolean
235
+ cmds: Array<
236
+ | string
237
+ | { type: 'keyword'; value: string; explain?: string }
238
+ | { type: 'regex'; match: string; explain?: string; label?: string; minLength?: number; maxLength?: number }
239
+ | { type: 'files'; exts?: string[]; fileType?: 'file' | 'directory' | 'any'; match?: string; minLength?: number; maxLength?: number }
240
+ | { type: 'img'; exts?: string[] }
241
+ | { type: 'over'; label?: string; exclude?: string; minLength?: number; maxLength?: number }
242
+ >
243
+ }) => void
244
+ removeFeature: (code: string) => boolean
245
+ redirectHotKeySetting: (cmdLabel: string, autocopy?: boolean) => void
246
+ redirectAiModelsSetting: () => void
247
+ }
248
+ }
249
+ input?: string
250
+ featureCode?: string
251
+ }
252
+
253
+ export function onLoad() {
254
+ console.log('[${name}] 插件已加载')
255
+ }
256
+
257
+ export function onUnload() {
258
+ console.log('[${name}] 插件已卸载')
259
+ }
260
+
261
+ export function onEnable() {
262
+ console.log('[${name}] 插件已启用')
263
+ }
264
+
265
+ export function onDisable() {
266
+ console.log('[${name}] 插件已禁用')
267
+ }
268
+
269
+ export async function run(context: PluginContext) {
270
+ const { notification } = context.api
271
+ notification.show('插件已启动')
272
+ }
273
+
274
+ // 导出 host 方法供 UI 调用
275
+ // 支持三种导出方式(按优先级):
276
+ // 1. 直接导出函数: export async function myMethod(context, ...args) {}
277
+ // 2. host 对象(推荐): export const host = { myMethod(context, ...args) {} }
278
+ // 3. 其他对象: export const api = { myMethod(context, ...args) {} }
279
+
280
+ export const host = {
281
+ // 示例方法:处理数据
282
+ async processData(context: PluginContext, data: any) {
283
+ const { notification } = context.api
284
+ notification.show('处理数据中...')
285
+
286
+ // 处理逻辑
287
+ const result = {
288
+ ...data,
289
+ processed: true,
290
+ timestamp: Date.now()
291
+ }
292
+
293
+ return result
294
+ },
295
+
296
+ // 示例方法:获取配置
297
+ async getConfig(context: PluginContext) {
298
+ // 可以使用 context.api 中的所有 API
299
+ return {
300
+ version: '1.0.0',
301
+ settings: {}
302
+ }
303
+ }
304
+ }
305
+
306
+ const plugin = { onLoad, onUnload, onEnable, onDisable, run, host }
307
+ export default plugin
308
+ `;
309
+ }
310
+ function buildIndexHtml(name) {
311
+ return `<!DOCTYPE html>
312
+ <html lang="zh-CN">
313
+ <head>
314
+ <meta charset="UTF-8">
315
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
316
+ <title>${name}</title>
317
+ </head>
318
+ <body>
319
+ <div id="root"></div>
320
+ <script type="module" src="./main.tsx"></script>
321
+ </body>
322
+ </html>
323
+ `;
324
+ }
325
+ function buildMainTsx() {
326
+ return `import { StrictMode } from 'react'
327
+ import { createRoot } from 'react-dom/client'
328
+ import App from './App'
329
+ import './styles.css'
330
+
331
+ createRoot(document.getElementById('root')!).render(
332
+ <StrictMode>
333
+ <App />
334
+ </StrictMode>
335
+ )
336
+ `;
337
+ }
338
+ function buildAppTsx(name) {
339
+ return `import { useEffect, useState } from 'react'
340
+ import { FileText, Image } from 'lucide-react'
341
+ import { useIntools } from './hooks/useIntools'
342
+
343
+ // 附件类型定义
344
+ interface Attachment {
345
+ id: string
346
+ name: string
347
+ size: number
348
+ kind: 'file' | 'image'
349
+ mime?: string
350
+ ext?: string
351
+ path?: string
352
+ dataUrl?: string
353
+ }
354
+
355
+ interface PluginInitData {
356
+ pluginName: string
357
+ featureCode: string
358
+ input: string
359
+ mode?: string
360
+ route?: string
361
+ attachments?: Attachment[]
362
+ }
363
+
364
+ export default function App() {
365
+ const [input, setInput] = useState('')
366
+ const [output, setOutput] = useState('')
367
+ const [theme, setTheme] = useState<'light' | 'dark'>('light')
368
+ const [attachments, setAttachments] = useState<Attachment[]>([])
369
+ const { clipboard, notification, host } = useIntools('${name}')
370
+
371
+ useEffect(() => {
372
+ // 获取初始主题(从 URL 参数)
373
+ const params = new URLSearchParams(window.location.search)
374
+ const initialTheme = (params.get('theme') as 'light' | 'dark') || 'light'
375
+ setTheme(initialTheme)
376
+ document.documentElement.classList.toggle('dark', initialTheme === 'dark')
377
+
378
+ // 监听主题变化
379
+ window.intools?.onThemeChange?.((newTheme: 'light' | 'dark') => {
380
+ setTheme(newTheme)
381
+ document.documentElement.classList.toggle('dark', newTheme === 'dark')
382
+ })
383
+
384
+ // 接收插件初始化数据
385
+ window.intools?.onPluginInit?.((data: PluginInitData) => {
386
+ if (data.input) {
387
+ setInput(data.input)
388
+ }
389
+ // 接收附件数据
390
+ if (data.attachments) {
391
+ setAttachments(data.attachments)
392
+ }
393
+ })
394
+ }, [])
395
+
396
+ const handleProcess = async () => {
397
+ // 示例:将输入转为大写
398
+ const result = input.toUpperCase()
399
+ setOutput(result)
400
+
401
+ // 复制到剪贴板并通知
402
+ await clipboard.writeText(result)
403
+ notification.show('已复制到剪贴板')
404
+ }
405
+
406
+ // 示例:调用后端 host 方法
407
+ const handleCallHost = async () => {
408
+ try {
409
+ const result = await host.call('processData', { value: input })
410
+ console.log('Host返回:', result.data)
411
+ notification.show('后端处理成功')
412
+ } catch (err: any) {
413
+ notification.show(\`错误: \${err.message}\`, 'error')
414
+ }
415
+ }
416
+
417
+ // 格式化文件大小
418
+ const formatSize = (bytes: number) => {
419
+ if (bytes < 1024) return \`\${bytes} B\`
420
+ if (bytes < 1024 * 1024) return \`\${(bytes / 1024).toFixed(1)} KB\`
421
+ return \`\${(bytes / 1024 / 1024).toFixed(1)} MB\`
422
+ }
423
+
424
+ return (
425
+ <div className="app">
426
+
427
+ <div className="container">
428
+ {/* 附件展示区域 */}
429
+ {attachments.length > 0 && (
430
+ <div className="field">
431
+ <label>附件 ({attachments.length})</label>
432
+ <div className="attachments-list">
433
+ {attachments.map((item, index) => (
434
+ <div key={item.id || index} className="attachment-item">
435
+ <span className="attachment-icon">
436
+ {item.kind === 'image' ? <Image size={20} /> : <FileText size={20} />}
437
+ </span>
438
+ <div className="attachment-info">
439
+ <div className="attachment-name">{item.name}</div>
440
+ <div className="attachment-meta">{formatSize(item.size)}</div>
441
+ </div>
442
+ {item.kind === 'image' && (item.dataUrl || item.path) && (
443
+ <img
444
+ src={item.dataUrl || \`file://\${item.path}\`}
445
+ alt={item.name}
446
+ className="attachment-preview"
447
+ />
448
+ )}
449
+ </div>
450
+ ))}
451
+ </div>
452
+ </div>
453
+ )}
454
+
455
+ <div className="field">
456
+ <label>输入</label>
457
+ <textarea
458
+ value={input}
459
+ onChange={(e) => setInput(e.target.value)}
460
+ placeholder="请输入内容..."
461
+ />
462
+ </div>
463
+ <div className="actions">
464
+ <button className="btn-primary" onClick={handleProcess}>
465
+ 处理
466
+ </button>
467
+ <button className="btn-secondary" onClick={handleCallHost}>
468
+ 调用后端
469
+ </button>
470
+ </div>
471
+ <div className="field">
472
+ <label>输出</label>
473
+ <textarea
474
+ value={output}
475
+ readOnly
476
+ placeholder="结果将显示在这里..."
477
+ />
478
+ </div>
479
+ </div>
480
+ </div>
481
+ )
482
+ }
483
+ `;
484
+ }
485
+ function buildStylesCss() {
486
+ return `@tailwind base;
487
+ @tailwind components;
488
+ @tailwind utilities;
489
+
490
+ /* CSS 变量 - 亮色主题 */
491
+ :root {
492
+ --bg-primary: #ffffff;
493
+ --bg-secondary: #f5f5f5;
494
+ --bg-tertiary: #ebebeb;
495
+ --text-primary: #1e1e1e;
496
+ --text-secondary: #666666;
497
+ --text-tertiary: #999999;
498
+ --border-color: #e0e0e0;
499
+ --accent-color: #0078d4;
500
+ --accent-hover: #1084d8;
501
+ }
502
+
503
+ /* CSS 变量 - 暗色主题 */
504
+ :root.dark {
505
+ --bg-primary: #1e1e1e;
506
+ --bg-secondary: #2d2d2d;
507
+ --bg-tertiary: #3d3d3d;
508
+ --text-primary: #e0e0e0;
509
+ --text-secondary: #999999;
510
+ --text-tertiary: #666666;
511
+ --border-color: #3d3d3d;
512
+ --accent-color: #0078d4;
513
+ --accent-hover: #1084d8;
514
+ }
515
+
516
+ * {
517
+ margin: 0;
518
+ padding: 0;
519
+ box-sizing: border-box;
520
+ }
521
+
522
+ body {
523
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
524
+ background: var(--bg-primary);
525
+ color: var(--text-primary);
526
+ min-height: 100vh;
527
+ transition: background-color 0.2s, color 0.2s;
528
+ }
529
+
530
+ .app {
531
+ display: flex;
532
+ flex-direction: column;
533
+ height: 100vh;
534
+ }
535
+
536
+
537
+
538
+ .container {
539
+ flex: 1;
540
+ padding: 16px;
541
+ display: flex;
542
+ flex-direction: column;
543
+ gap: 12px;
544
+ overflow: auto;
545
+ }
546
+
547
+ .field {
548
+ display: flex;
549
+ flex-direction: column;
550
+ gap: 6px;
551
+ flex: 1;
552
+ min-height: 0;
553
+ }
554
+
555
+ .field label {
556
+ font-size: 12px;
557
+ color: var(--text-secondary);
558
+ }
559
+
560
+ .field textarea {
561
+ flex: 1;
562
+ background: var(--bg-secondary);
563
+ border: 1px solid var(--border-color);
564
+ border-radius: 6px;
565
+ padding: 12px;
566
+ color: var(--text-primary);
567
+ font-family: 'Monaco', 'Consolas', monospace;
568
+ font-size: 13px;
569
+ resize: none;
570
+ outline: none;
571
+ min-height: 80px;
572
+ transition: background-color 0.2s, border-color 0.2s, color 0.2s;
573
+ }
574
+
575
+ .field textarea:focus {
576
+ border-color: var(--accent-color);
577
+ }
578
+
579
+ .field textarea::placeholder {
580
+ color: var(--text-tertiary);
581
+ }
582
+
583
+ .actions {
584
+ display: flex;
585
+ gap: 12px;
586
+ justify-content: center;
587
+ }
588
+
589
+ button {
590
+ padding: 8px 24px;
591
+ border: none;
592
+ border-radius: 4px;
593
+ font-size: 14px;
594
+ cursor: pointer;
595
+ transition: background 0.2s;
596
+ }
597
+
598
+ .btn-primary {
599
+ background: var(--accent-color);
600
+ color: #fff;
601
+ }
602
+
603
+ .btn-primary:hover {
604
+ background: var(--accent-hover);
605
+ }
606
+
607
+ .btn-secondary {
608
+ background: var(--bg-tertiary);
609
+ color: var(--text-primary);
610
+ }
611
+
612
+ .btn-secondary:hover {
613
+ background: var(--bg-secondary);
614
+ }
615
+
616
+ /* 附件列表样式 */
617
+ .attachments-list {
618
+ display: flex;
619
+ flex-direction: column;
620
+ gap: 8px;
621
+ max-height: 200px;
622
+ overflow-y: auto;
623
+ }
624
+
625
+ .attachment-item {
626
+ display: flex;
627
+ align-items: center;
628
+ padding: 10px 12px;
629
+ background: var(--bg-secondary);
630
+ border: 1px solid var(--border-color);
631
+ border-radius: 6px;
632
+ gap: 10px;
633
+ }
634
+
635
+ .attachment-icon {
636
+ font-size: 20px;
637
+ }
638
+
639
+ .attachment-info {
640
+ flex: 1;
641
+ min-width: 0;
642
+ }
643
+
644
+ .attachment-name {
645
+ font-size: 13px;
646
+ font-weight: 500;
647
+ white-space: nowrap;
648
+ overflow: hidden;
649
+ text-overflow: ellipsis;
650
+ }
651
+
652
+ .attachment-meta {
653
+ font-size: 11px;
654
+ color: var(--text-tertiary);
655
+ margin-top: 2px;
656
+ }
657
+
658
+ .attachment-preview {
659
+ width: 40px;
660
+ height: 40px;
661
+ border-radius: 4px;
662
+ object-fit: cover;
663
+ }
664
+ `;
665
+ }
666
+ function buildUseIntools() {
667
+ return `import { useMemo } from 'react'
668
+
669
+ export function useIntools(pluginId?: string) {
670
+ return useMemo(() => ({
671
+ // Clipboard API
672
+ clipboard: {
673
+ readText: () => window.intools?.clipboard?.readText(),
674
+ writeText: (text: string) => window.intools?.clipboard?.writeText(text),
675
+ readImage: () => window.intools?.clipboard?.readImage(),
676
+ writeImage: (image: string | ArrayBuffer) => window.intools?.clipboard?.writeImage(image),
677
+ readFiles: () => window.intools?.clipboard?.readFiles(),
678
+ writeFiles: (files: string | string[]) => window.intools?.clipboard?.writeFiles(files),
679
+ getFormat: () => window.intools?.clipboard?.getFormat(),
680
+ },
681
+
682
+ // Clipboard History API
683
+ clipboardHistory: {
684
+ query: (options?: {
685
+ type?: 'text' | 'image' | 'files'
686
+ search?: string
687
+ favorite?: boolean
688
+ limit?: number
689
+ offset?: number
690
+ }) => window.intools?.clipboardHistory?.query(options),
691
+ get: (id: string) => window.intools?.clipboardHistory?.get(id),
692
+ copy: (id: string) => window.intools?.clipboardHistory?.copy(id),
693
+ toggleFavorite: (id: string) => window.intools?.clipboardHistory?.toggleFavorite(id),
694
+ delete: (id: string) => window.intools?.clipboardHistory?.delete(id),
695
+ clear: () => window.intools?.clipboardHistory?.clear(),
696
+ stats: () => window.intools?.clipboardHistory?.stats(),
697
+ },
698
+
699
+ // Input API
700
+ input: {
701
+ hideMainWindowPasteText: (text: string) => window.intools?.input?.hideMainWindowPasteText(text),
702
+ hideMainWindowPasteImage: (image: string | ArrayBuffer) => window.intools?.input?.hideMainWindowPasteImage(image),
703
+ hideMainWindowPasteFile: (filePaths: string | string[]) => window.intools?.input?.hideMainWindowPasteFile(filePaths),
704
+ hideMainWindowTypeString: (text: string) => window.intools?.input?.hideMainWindowTypeString(text),
705
+ restoreWindows: () => window.intools?.input?.restoreWindows(),
706
+ simulateKeyboardTap: (key: string, ...modifiers: string[]) =>
707
+ window.intools?.input?.simulateKeyboardTap(key, ...modifiers),
708
+ simulateMouseMove: (x: number, y: number) => window.intools?.input?.simulateMouseMove(x, y),
709
+ simulateMouseClick: (x: number, y: number) => window.intools?.input?.simulateMouseClick(x, y),
710
+ simulateMouseDoubleClick: (x: number, y: number) => window.intools?.input?.simulateMouseDoubleClick(x, y),
711
+ simulateMouseRightClick: (x: number, y: number) => window.intools?.input?.simulateMouseRightClick(x, y),
712
+ },
713
+
714
+ // Storage API
715
+ storage: {
716
+ get: (key: string) => window.intools?.storage?.get(key, pluginId),
717
+ set: (key: string, value: unknown) => window.intools?.storage?.set(key, value, pluginId),
718
+ remove: (key: string) => window.intools?.storage?.remove(key, pluginId),
719
+ },
720
+
721
+ // AI API
722
+ ai: {
723
+ call: (option: any, onChunk?: (chunk: any) => void) => window.intools?.ai?.call(option, onChunk),
724
+ allModels: () => window.intools?.ai?.allModels?.(),
725
+ tokens: {
726
+ estimate: (input: any) => window.intools?.ai?.tokens?.estimate(input),
727
+ },
728
+ attachments: {
729
+ upload: (input: any) => window.intools?.ai?.attachments?.upload(input),
730
+ get: (attachmentId: string) => window.intools?.ai?.attachments?.get(attachmentId),
731
+ delete: (attachmentId: string) => window.intools?.ai?.attachments?.delete(attachmentId),
732
+ },
733
+ images: {
734
+ generate: (input: any) => window.intools?.ai?.images?.generate(input),
735
+ edit: (input: any) => window.intools?.ai?.images?.edit(input),
736
+ },
737
+ videos: {
738
+ generate: (input: any) => window.intools?.ai?.videos?.generate(input),
739
+ },
740
+ models: {
741
+ fetch: (input: any) => window.intools?.ai?.models?.fetch(input),
742
+ },
743
+ testConnection: (input?: any) => window.intools?.ai?.testConnection?.(input),
744
+ testConnectionStream: (input: any, onChunk: (chunk: any) => void) =>
745
+ window.intools?.ai?.testConnectionStream?.(input, onChunk),
746
+ settings: {
747
+ get: () => window.intools?.ai?.settings?.get(),
748
+ update: (next: any) => window.intools?.ai?.settings?.update(next),
749
+ },
750
+ },
751
+
752
+ // Messaging API
753
+ messaging: {
754
+ send: (targetPluginId: string, type: string, payload: unknown) =>
755
+ window.intools?.messaging?.send(targetPluginId, type, payload),
756
+ broadcast: (type: string, payload: unknown) =>
757
+ window.intools?.messaging?.broadcast(type, payload),
758
+ on: (callback: (message: {
759
+ id: string
760
+ from: string
761
+ to?: string
762
+ type: string
763
+ payload: unknown
764
+ timestamp: number
765
+ }) => void | Promise<void>) => window.intools?.messaging?.on(callback),
766
+ off: (callback?: (message: any) => void) => window.intools?.messaging?.off(callback),
767
+ },
768
+
769
+ // Scheduler API
770
+ scheduler: {
771
+ schedule: (task: {
772
+ name: string
773
+ type: 'once' | 'repeat' | 'delay'
774
+ callback: string
775
+ time?: number
776
+ cron?: string
777
+ delay?: number
778
+ payload?: any
779
+ maxRetries?: number
780
+ retryDelay?: number
781
+ timeout?: number
782
+ }) => window.intools?.scheduler?.schedule(task),
783
+ cancelTask: (taskId: string) => window.intools?.scheduler?.cancelTask(taskId),
784
+ pauseTask: (taskId: string) => window.intools?.scheduler?.pauseTask(taskId),
785
+ resumeTask: (taskId: string) => window.intools?.scheduler?.resumeTask(taskId),
786
+ listTasks: (filter?: { status?: string; type?: string; limit?: number; offset?: number }) => window.intools?.scheduler?.listTasks(filter),
787
+ getTaskCount: (filter?: { status?: string; type?: string }) => window.intools?.scheduler?.getTaskCount(filter),
788
+ getTask: (taskId: string) => window.intools?.scheduler?.getTask(taskId),
789
+ deleteTasks: (taskIds: string[]) => window.intools?.scheduler?.deleteTasks(taskIds),
790
+ cleanupTasks: (olderThan?: number) => window.intools?.scheduler?.cleanupTasks(olderThan),
791
+ getExecutions: (taskId: string, limit?: number) => window.intools?.scheduler?.getExecutions(taskId, limit),
792
+ validateCron: (expression: string) => window.intools?.scheduler?.validateCron(expression),
793
+ getNextCronTime: (expression: string, after?: Date) => window.intools?.scheduler?.getNextCronTime(expression, after),
794
+ describeCron: (expression: string) => window.intools?.scheduler?.describeCron(expression),
795
+ },
796
+
797
+ // Notification API
798
+ notification: {
799
+ show: (message: string, type?: 'info' | 'success' | 'warning' | 'error') =>
800
+ window.intools?.notification?.show(message, type),
801
+ },
802
+
803
+ // Window API
804
+ window: {
805
+ setSize: (width: number, height: number) => window.intools?.window?.setSize(width, height),
806
+ setExpendHeight: (height: number) => window.intools?.window?.setExpendHeight?.(height),
807
+ center: () => window.intools?.window?.center?.(),
808
+ hide: (isRestorePreWindow?: boolean) => window.intools?.window?.hide?.(isRestorePreWindow),
809
+ show: () => window.intools?.window?.show(),
810
+ close: () => window.intools?.window?.close(),
811
+ create: (url: string, options?: { width?: number; height?: number; title?: string }) =>
812
+ window.intools?.window?.create(url, options),
813
+ detach: () => window.intools?.window?.detach?.(),
814
+ setAlwaysOnTop: (flag: boolean) => window.intools?.window?.setAlwaysOnTop?.(flag),
815
+ getMode: () => window.intools?.window?.getMode?.(),
816
+ getWindowType: () => window.intools?.window?.getWindowType?.(),
817
+ minimize: () => window.intools?.window?.minimize?.(),
818
+ maximize: () => window.intools?.window?.maximize?.(),
819
+ getState: () => window.intools?.window?.getState?.(),
820
+ reload: () => window.intools?.window?.reload?.(),
821
+ sendToParent: (channel: string, ...args: unknown[]) =>
822
+ window.intools?.window?.sendToParent?.(channel, ...args),
823
+ onChildMessage: (callback: (channel: string, ...args: unknown[]) => void) =>
824
+ window.intools?.window?.onChildMessage?.(callback),
825
+ findInPage: (text: string, options?: { forward?: boolean; findNext?: boolean; matchCase?: boolean }) =>
826
+ window.intools?.window?.findInPage?.(text, options),
827
+ stopFindInPage: (action?: 'clearSelection' | 'keepSelection' | 'activateSelection') =>
828
+ window.intools?.window?.stopFindInPage?.(action),
829
+ startDrag: (filePath: string | string[]) => window.intools?.window?.startDrag?.(filePath),
830
+ },
831
+
832
+ // SubInput API
833
+ subInput: {
834
+ set: (placeholder?: string, isFocus?: boolean) => window.intools?.subInput?.set?.(placeholder, isFocus),
835
+ remove: () => window.intools?.subInput?.remove?.(),
836
+ setValue: (text: string) => window.intools?.subInput?.setValue?.(text),
837
+ focus: () => window.intools?.subInput?.focus?.(),
838
+ blur: () => window.intools?.subInput?.blur?.(),
839
+ select: () => window.intools?.subInput?.select?.(),
840
+ onChange: (callback: (data: { text: string }) => void) => window.intools?.subInput?.onChange?.(callback),
841
+ },
842
+
843
+ // Plugin API
844
+ plugin: {
845
+ getAll: () => window.intools?.plugin?.getAll?.(),
846
+ search: (query: string) => window.intools?.plugin?.search?.(query),
847
+ run: (name: string, featureCode: string, input?: string) => window.intools?.plugin?.run?.(name, featureCode, input),
848
+ install: (filePath: string) => window.intools?.plugin?.install?.(filePath),
849
+ uninstall: (name: string) => window.intools?.plugin?.uninstall?.(name),
850
+ getReadme: (name: string) => window.intools?.plugin?.getReadme?.(name),
851
+ redirect: (label: string | [string, string], payload?: unknown) =>
852
+ window.intools?.plugin?.redirect?.(label, payload),
853
+ outPlugin: (isKill?: boolean) => window.intools?.plugin?.outPlugin?.(isKill),
854
+ enable: (name: string) => window.intools?.plugin?.enable?.(name),
855
+ disable: (name: string) => window.intools?.plugin?.disable?.(name),
856
+ listBackground: () => window.intools?.plugin?.listBackground?.(),
857
+ startBackground: (pluginId: string) => window.intools?.plugin?.startBackground?.(pluginId),
858
+ stopBackground: (pluginId: string) => window.intools?.plugin?.stopBackground?.(pluginId),
859
+ getBackgroundInfo: (pluginId: string) => window.intools?.plugin?.getBackgroundInfo?.(pluginId),
860
+ stopPlugin: (pluginId: string) => window.intools?.plugin?.stopPlugin?.(pluginId),
861
+ },
862
+
863
+ // HTTP API
864
+ http: {
865
+ request: (options: {
866
+ url: string
867
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD'
868
+ headers?: Record<string, string>
869
+ body?: unknown
870
+ timeout?: number
871
+ }) => window.intools?.http?.request(options),
872
+ get: (url: string, headers?: Record<string, string>) => window.intools?.http?.get(url, headers),
873
+ post: (url: string, body?: unknown, headers?: Record<string, string>) =>
874
+ window.intools?.http?.post(url, body, headers),
875
+ put: (url: string, body?: unknown, headers?: Record<string, string>) =>
876
+ window.intools?.http?.put(url, body, headers),
877
+ delete: (url: string, headers?: Record<string, string>) => window.intools?.http?.delete(url, headers),
878
+ },
879
+
880
+ // Filesystem API
881
+ filesystem: {
882
+ readFile: (path: string, encoding?: 'utf-8' | 'base64') => window.intools?.filesystem?.readFile(path, encoding),
883
+ writeFile: (path: string, data: string | ArrayBuffer, encoding?: 'utf-8' | 'base64') =>
884
+ window.intools?.filesystem?.writeFile(path, data, encoding),
885
+ exists: (path: string) => window.intools?.filesystem?.exists(path),
886
+ readdir: (path: string) => window.intools?.filesystem?.readdir(path),
887
+ mkdir: (path: string) => window.intools?.filesystem?.mkdir(path),
888
+ stat: (path: string) => window.intools?.filesystem?.stat(path),
889
+ copy: (src: string, dest: string) => window.intools?.filesystem?.copy(src, dest),
890
+ move: (src: string, dest: string) => window.intools?.filesystem?.move(src, dest),
891
+ unlink: (path: string) => window.intools?.filesystem?.unlink(path),
892
+ },
893
+
894
+ // Screen API
895
+ screen: {
896
+ getAllDisplays: () => window.intools?.screen?.getAllDisplays(),
897
+ getPrimaryDisplay: () => window.intools?.screen?.getPrimaryDisplay(),
898
+ getCursorScreenPoint: () => window.intools?.screen?.getCursorScreenPoint(),
899
+ getDisplayNearestPoint: (point: { x: number; y: number }) =>
900
+ window.intools?.screen?.getDisplayNearestPoint?.(point),
901
+ getDisplayMatching: (rect: { x: number; y: number; width: number; height: number }) =>
902
+ window.intools?.screen?.getDisplayMatching?.(rect),
903
+ getSources: (options?: { types?: ('screen' | 'window')[]; thumbnailSize?: { width: number; height: number } }) =>
904
+ window.intools?.screen?.getSources(options),
905
+ capture: (options?: { sourceId?: string; format?: 'png' | 'jpeg'; quality?: number }) =>
906
+ window.intools?.screen?.capture(options),
907
+ captureRegion: (region: { x: number; y: number; width: number; height: number }, options?: { format?: 'png' | 'jpeg'; quality?: number }) =>
908
+ window.intools?.screen?.captureRegion(region, options),
909
+ screenCapture: () => window.intools?.screen?.screenCapture(),
910
+ colorPick: () => window.intools?.screen?.colorPick?.(),
911
+ },
912
+
913
+ // Shell API
914
+ shell: {
915
+ openPath: (path: string) => window.intools?.shell?.openPath(path),
916
+ openExternal: (url: string) => window.intools?.shell?.openExternal(url),
917
+ showItemInFolder: (path: string) => window.intools?.shell?.showItemInFolder(path),
918
+ openFolder: (path: string) => window.intools?.shell?.openFolder(path),
919
+ trashItem: (path: string) => window.intools?.shell?.trashItem(path),
920
+ beep: () => window.intools?.shell?.beep(),
921
+ },
922
+
923
+ // Dialog API
924
+ dialog: {
925
+ showOpenDialog: (options?: {
926
+ title?: string
927
+ defaultPath?: string
928
+ filters?: { name: string; extensions: string[] }[]
929
+ properties?: ('openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles')[]
930
+ }) => window.intools?.dialog?.showOpenDialog(options),
931
+ showSaveDialog: (options?: {
932
+ title?: string
933
+ defaultPath?: string
934
+ filters?: { name: string; extensions: string[] }[]
935
+ }) => window.intools?.dialog?.showSaveDialog(options),
936
+ showMessageBox: (options: {
937
+ type?: 'none' | 'info' | 'error' | 'question' | 'warning'
938
+ title?: string
939
+ message: string
940
+ detail?: string
941
+ buttons?: string[]
942
+ }) => window.intools?.dialog?.showMessageBox(options),
943
+ },
944
+
945
+ // System API
946
+ system: {
947
+ getSystemInfo: () => window.intools?.system?.getSystemInfo(),
948
+ getAppInfo: () => window.intools?.system?.getAppInfo(),
949
+ getPath: (name: string) => window.intools?.system?.getPath(name as any),
950
+ getEnv: (name: string) => window.intools?.system?.getEnv(name),
951
+ getIdleTime: () => window.intools?.system?.getIdleTime(),
952
+ getFileIcon: (filePath: string) => window.intools?.system?.getFileIcon?.(filePath),
953
+ getNativeId: () => window.intools?.system?.getNativeId?.(),
954
+ isDev: () => window.intools?.system?.isDev?.(),
955
+ isMacOS: () => window.intools?.system?.isMacOS?.(),
956
+ isWindows: () => window.intools?.system?.isWindows?.(),
957
+ isLinux: () => window.intools?.system?.isLinux?.(),
958
+ },
959
+
960
+ // Permission API
961
+ permission: {
962
+ getStatus: (type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar') =>
963
+ window.intools?.permission?.getStatus(type),
964
+ request: (type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar') =>
965
+ window.intools?.permission?.request(type),
966
+ canRequest: (type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar') =>
967
+ window.intools?.permission?.canRequest(type),
968
+ openSystemSettings: (type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar') =>
969
+ window.intools?.permission?.openSystemSettings(type),
970
+ isAccessibilityTrusted: () => window.intools?.permission?.isAccessibilityTrusted()
971
+ },
972
+
973
+ // Power API
974
+ power: {
975
+ getSystemIdleTime: () => window.intools?.power?.getSystemIdleTime(),
976
+ getSystemIdleState: (threshold: number) => window.intools?.power?.getSystemIdleState(threshold),
977
+ isOnBatteryPower: () => window.intools?.power?.isOnBatteryPower(),
978
+ getCurrentThermalState: () => window.intools?.power?.getCurrentThermalState(),
979
+ },
980
+
981
+ // Network API
982
+ network: {
983
+ isOnline: () => window.intools?.network?.isOnline(),
984
+ },
985
+
986
+ // Geolocation API
987
+ geolocation: {
988
+ getAccessStatus: () => window.intools?.geolocation?.getAccessStatus(),
989
+ requestAccess: () => window.intools?.geolocation?.requestAccess(),
990
+ canGetPosition: () => window.intools?.geolocation?.canGetPosition(),
991
+ openSettings: () => window.intools?.geolocation?.openSettings(),
992
+ getCurrentPosition: () => window.intools?.geolocation?.getCurrentPosition(),
993
+ },
994
+
995
+ // TTS API
996
+ tts: {
997
+ speak: (text: string, options?: { lang?: string; rate?: number; pitch?: number; volume?: number }) =>
998
+ window.intools?.tts?.speak(text, options),
999
+ stop: () => window.intools?.tts?.stop(),
1000
+ pause: () => window.intools?.tts?.pause(),
1001
+ resume: () => window.intools?.tts?.resume(),
1002
+ getVoices: () => window.intools?.tts?.getVoices(),
1003
+ isSpeaking: () => window.intools?.tts?.isSpeaking(),
1004
+ },
1005
+
1006
+ // Media API
1007
+ media: {
1008
+ getAccessStatus: (type: 'camera' | 'microphone') => window.intools?.media?.getAccessStatus(type),
1009
+ askForAccess: (type: 'camera' | 'microphone') => window.intools?.media?.askForAccess(type),
1010
+ hasCameraAccess: () => window.intools?.media?.hasCameraAccess(),
1011
+ hasMicrophoneAccess: () => window.intools?.media?.hasMicrophoneAccess(),
1012
+ },
1013
+
1014
+ // Shortcut API
1015
+ shortcut: {
1016
+ register: (accelerator: string) => window.intools?.shortcut?.register(accelerator),
1017
+ unregister: (accelerator: string) => window.intools?.shortcut?.unregister(accelerator),
1018
+ unregisterAll: () => window.intools?.shortcut?.unregisterAll(),
1019
+ isRegistered: (accelerator: string) => window.intools?.shortcut?.isRegistered(accelerator),
1020
+ },
1021
+
1022
+ // Security API
1023
+ security: {
1024
+ isEncryptionAvailable: () => window.intools?.security?.isEncryptionAvailable(),
1025
+ encryptString: (text: string) => window.intools?.security?.encryptString(text),
1026
+ decryptString: (data: ArrayBuffer) => window.intools?.security?.decryptString(data),
1027
+ },
1028
+
1029
+ // Tray API
1030
+ tray: {
1031
+ create: (options: { icon: string; tooltip?: string; title?: string }) =>
1032
+ window.intools?.tray?.create(options),
1033
+ destroy: () => window.intools?.tray?.destroy(),
1034
+ setIcon: (icon: string) => window.intools?.tray?.setIcon(icon),
1035
+ setTooltip: (tooltip: string) => window.intools?.tray?.setTooltip(tooltip),
1036
+ setTitle: (title: string) => window.intools?.tray?.setTitle(title),
1037
+ exists: () => window.intools?.tray?.exists(),
1038
+ },
1039
+
1040
+ // Menu API
1041
+ menu: {
1042
+ showContextMenu: (items: {
1043
+ label?: string
1044
+ type?: 'normal' | 'separator' | 'checkbox' | 'radio'
1045
+ checked?: boolean
1046
+ enabled?: boolean
1047
+ id?: string
1048
+ submenu?: unknown[]
1049
+ }[]) => window.intools?.menu?.showContextMenu(items as Parameters<typeof window.intools.menu.showContextMenu>[0]),
1050
+ },
1051
+
1052
+ // Theme API
1053
+ theme: {
1054
+ get: () => window.intools?.theme?.get(),
1055
+ set: (mode: 'light' | 'dark' | 'system') => window.intools?.theme?.set(mode),
1056
+ getActual: () => window.intools?.theme?.getActual(),
1057
+ },
1058
+
1059
+ // Host API
1060
+ host: {
1061
+ invoke: (method: string, ...args: unknown[]) =>
1062
+ window.intools?.host?.invoke(pluginId || '', method, ...args),
1063
+ call: (method: string, ...args: unknown[]) =>
1064
+ window.intools?.host?.call?.(pluginId || '', method, ...args),
1065
+ status: () => window.intools?.host?.status(pluginId || ''),
1066
+ restart: () => window.intools?.host?.restart(pluginId || ''),
1067
+ },
1068
+
1069
+ // InBrowser API
1070
+ inbrowser: window.intools?.inbrowser,
1071
+
1072
+ // Sharp API
1073
+ sharp: window.intools?.sharp,
1074
+ getSharpVersion: () => window.intools?.getSharpVersion?.(),
1075
+
1076
+ // FFmpeg API
1077
+ ffmpeg: window.intools?.ffmpeg,
1078
+ }), [pluginId])
1079
+ }
1080
+ `;
1081
+ }
1082
+ function buildGitignore() {
1083
+ return `node_modules
1084
+ dist
1085
+ /ui/
1086
+ .DS_Store
1087
+ *.log
1088
+ `;
1089
+ }
1090
+ function buildReactReadme(name) {
1091
+ return `# ${name}
1092
+
1093
+ 插件描述
1094
+
1095
+ ## 功能特性
1096
+
1097
+ - 功能 1
1098
+ - 功能 2
1099
+ - 功能 3
1100
+
1101
+ ## 触发方式
1102
+
1103
+ - \`${name}\` - 主功能
1104
+
1105
+ ## 开发
1106
+
1107
+ ### 安装依赖
1108
+
1109
+ \`\`\`bash
1110
+ npm install
1111
+ \`\`\`
1112
+
1113
+ ### 开发模式
1114
+
1115
+ \`\`\`bash
1116
+ npm run dev
1117
+ \`\`\`
1118
+
1119
+ ### 构建
1120
+
1121
+ \`\`\`bash
1122
+ npm run build
1123
+ \`\`\`
1124
+
1125
+ ### 打包
1126
+
1127
+ \`\`\`bash
1128
+ npm run pack
1129
+ \`\`\`
1130
+
1131
+ ## 项目结构
1132
+
1133
+ \`\`\`
1134
+ ${name}/
1135
+ ├── manifest.json # 插件配置
1136
+ ├── package.json
1137
+ ├── src/
1138
+ │ ├── main.ts # 后端入口
1139
+ │ ├── ui/
1140
+ │ │ ├── App.tsx # 主应用
1141
+ │ │ ├── main.tsx # UI 入口
1142
+ │ │ ├── index.html # HTML 模板
1143
+ │ │ ├── styles.css # 全局样式
1144
+ │ │ ├── hooks/
1145
+ │ │ │ └── useIntools.ts # InTools API Hook
1146
+ │ │ └── types/
1147
+ │ │ └── intools.d.ts # 类型定义
1148
+ ├── dist/ # 后端构建输出
1149
+ ├── ui/ # UI 构建输出
1150
+ └── icon.png # 插件图标
1151
+ \`\`\`
1152
+
1153
+ ## 许可证
1154
+
1155
+ MIT License
1156
+ `;
1157
+ }
1158
+ function buildIntoolsTypes() {
1159
+ return `// InTools API 类型定义
1160
+
1161
+ interface ClipboardFileInfo {
1162
+ path: string
1163
+ name: string
1164
+ size: number
1165
+ isDirectory: boolean
1166
+ }
1167
+
1168
+ interface IntoolsClipboard {
1169
+ readText(): Promise<string>
1170
+ writeText(text: string): Promise<void>
1171
+ readImage(): Promise<ArrayBuffer | null>
1172
+ writeImage(image: string | ArrayBuffer): Promise<void>
1173
+ readFiles(): Promise<ClipboardFileInfo[]>
1174
+ writeFiles(files: string | string[]): Promise<boolean>
1175
+ getFormat(): Promise<'text' | 'image' | 'files' | 'empty'>
1176
+ }
1177
+
1178
+ interface ClipboardHistoryItem {
1179
+ id: string
1180
+ type: 'text' | 'image' | 'files'
1181
+ content: string
1182
+ plainText?: string
1183
+ files?: string[]
1184
+ timestamp: number
1185
+ size: number
1186
+ favorite: boolean
1187
+ tags?: string[]
1188
+ }
1189
+
1190
+ interface ClipboardHistoryStats {
1191
+ total: number
1192
+ text: number
1193
+ image: number
1194
+ files: number
1195
+ favorite: number
1196
+ }
1197
+
1198
+ interface IntoolsClipboardHistory {
1199
+ query(options?: {
1200
+ type?: 'text' | 'image' | 'files'
1201
+ search?: string
1202
+ favorite?: boolean
1203
+ limit?: number
1204
+ offset?: number
1205
+ }): Promise<ClipboardHistoryItem[]>
1206
+ get(id: string): Promise<ClipboardHistoryItem | null>
1207
+ copy(id: string): Promise<{ success: boolean; error?: string }>
1208
+ toggleFavorite(id: string): Promise<{ success: boolean }>
1209
+ delete(id: string): Promise<{ success: boolean }>
1210
+ clear(): Promise<{ success: boolean }>
1211
+ stats(): Promise<ClipboardHistoryStats>
1212
+ }
1213
+
1214
+ interface IntoolsInput {
1215
+ hideMainWindowPasteText(text: string): Promise<boolean>
1216
+ hideMainWindowPasteImage(image: string | ArrayBuffer): Promise<boolean>
1217
+ hideMainWindowPasteFile(filePaths: string | string[]): Promise<boolean>
1218
+ hideMainWindowTypeString(text: string): Promise<boolean>
1219
+ restoreWindows(): Promise<boolean>
1220
+ simulateKeyboardTap(key: string, ...modifiers: string[]): Promise<boolean>
1221
+ simulateMouseMove(x: number, y: number): Promise<boolean>
1222
+ simulateMouseClick(x: number, y: number): Promise<boolean>
1223
+ simulateMouseDoubleClick(x: number, y: number): Promise<boolean>
1224
+ simulateMouseRightClick(x: number, y: number): Promise<boolean>
1225
+ }
1226
+
1227
+ interface IntoolsNotification {
1228
+ show(message: string, type?: 'info' | 'success' | 'warning' | 'error'): void
1229
+ }
1230
+
1231
+ interface BrowserWindowProxy {
1232
+ id: number
1233
+ show(): Promise<void>
1234
+ hide(): Promise<void>
1235
+ close(): Promise<void>
1236
+ focus(): Promise<void>
1237
+ setTitle(title: string): Promise<void>
1238
+ setSize(width: number, height: number): Promise<void>
1239
+ setPosition(x: number, y: number): Promise<void>
1240
+ postMessage(channel: string, ...args: unknown[]): Promise<void>
1241
+ }
1242
+
1243
+ interface IntoolsWindow {
1244
+ hide(isRestorePreWindow?: boolean): void
1245
+ show(): void
1246
+ setSize(width: number, height: number): void
1247
+ setExpendHeight(height: number): void
1248
+ center(): void
1249
+ create(url: string, options?: { width?: number; height?: number; title?: string }): Promise<BrowserWindowProxy | null>
1250
+ close(): void
1251
+ detach(): void
1252
+ setAlwaysOnTop(flag: boolean): void
1253
+ getMode(): Promise<'attached' | 'detached'>
1254
+ getWindowType(): Promise<'main' | 'detach'>
1255
+ minimize(): void
1256
+ maximize(): void
1257
+ getState(): Promise<{ isMaximized: boolean; isAlwaysOnTop: boolean }>
1258
+ reload(): void
1259
+ sendToParent(channel: string, ...args: unknown[]): void
1260
+ onChildMessage(callback: (channel: string, ...args: unknown[]) => void): void
1261
+ findInPage(text: string, options?: { forward?: boolean; findNext?: boolean; matchCase?: boolean }): Promise<number>
1262
+ stopFindInPage(action?: 'clearSelection' | 'keepSelection' | 'activateSelection'): void
1263
+ startDrag(filePath: string | string[]): void
1264
+ }
1265
+
1266
+ interface IntoolsSubInput {
1267
+ set(placeholder?: string, isFocus?: boolean): Promise<boolean>
1268
+ remove(): Promise<boolean>
1269
+ setValue(text: string): void
1270
+ focus(): void
1271
+ blur(): void
1272
+ select(): void
1273
+ onChange(callback: (data: { text: string }) => void): void
1274
+ }
1275
+
1276
+ interface IntoolsTheme {
1277
+ get(): Promise<{ mode: 'light' | 'dark' | 'system'; actual: 'light' | 'dark' }>
1278
+ set(mode: 'light' | 'dark' | 'system'): Promise<{ mode: 'light' | 'dark' | 'system'; actual: 'light' | 'dark' }>
1279
+ getActual(): Promise<'light' | 'dark'>
1280
+ }
1281
+
1282
+ interface PluginInfo {
1283
+ id: string
1284
+ name: string
1285
+ displayName: string
1286
+ description?: string
1287
+ features: Array<{ code: string; explain?: string }>
1288
+ enabled: boolean
1289
+ }
1290
+
1291
+ interface PluginSearchResult {
1292
+ pluginId: string
1293
+ pluginName: string
1294
+ displayName: string
1295
+ featureCode: string
1296
+ featureExplain?: string
1297
+ matchType: 'keyword' | 'regex' | 'prefix' | 'exact' | string
1298
+ icon?: string
1299
+ }
1300
+
1301
+ interface BackgroundPluginInfo {
1302
+ pluginId: string
1303
+ pluginName: string
1304
+ displayName: string
1305
+ runMode: 'background' | 'active'
1306
+ startedAt?: number
1307
+ uptime?: number
1308
+ memoryUsage?: number
1309
+ cpuUsage?: number
1310
+ requestCount?: number
1311
+ errorCount?: number
1312
+ healthy?: boolean
1313
+ lastHeartbeat?: number
1314
+ missedHeartbeats?: number
1315
+ }
1316
+
1317
+ interface IntoolsPlugin {
1318
+ getAll(): Promise<PluginInfo[]>
1319
+ search(query: string): Promise<PluginSearchResult[]>
1320
+ run(name: string, featureCode: string, input?: string): Promise<{ success: boolean; hasUI?: boolean; error?: string }>
1321
+ install(filePath: string): Promise<{ success: boolean; pluginName?: string; error?: string }>
1322
+ enable(name: string): Promise<{ success: boolean; error?: string }>
1323
+ disable(name: string): Promise<{ success: boolean; error?: string }>
1324
+ uninstall(name: string): Promise<{ success: boolean; error?: string }>
1325
+ getReadme(name: string): Promise<string | null>
1326
+ redirect(label: string | [string, string], payload?: unknown): Promise<boolean | { candidates: { name: string; displayName: string }[] }>
1327
+ outPlugin(isKill?: boolean): Promise<boolean>
1328
+ listBackground(): Promise<BackgroundPluginInfo[]>
1329
+ startBackground(pluginId: string): Promise<{ success: boolean; error?: string }>
1330
+ stopBackground(pluginId: string): Promise<{ success: boolean }>
1331
+ getBackgroundInfo(pluginId: string): Promise<BackgroundPluginInfo>
1332
+ stopPlugin(pluginId: string): Promise<void>
1333
+ }
1334
+
1335
+ interface DisplayInfo {
1336
+ id: number
1337
+ label: string
1338
+ bounds: { x: number; y: number; width: number; height: number }
1339
+ workArea: { x: number; y: number; width: number; height: number }
1340
+ scaleFactor: number
1341
+ rotation: number
1342
+ isPrimary: boolean
1343
+ }
1344
+
1345
+ interface CaptureSource {
1346
+ id: string
1347
+ name: string
1348
+ thumbnailDataUrl: string
1349
+ displayId?: string
1350
+ appIconDataUrl?: string
1351
+ }
1352
+
1353
+ interface ColorPickResult {
1354
+ hex: string
1355
+ rgb: string
1356
+ r: number
1357
+ g: number
1358
+ b: number
1359
+ }
1360
+
1361
+ interface IntoolsScreen {
1362
+ getAllDisplays(): Promise<DisplayInfo[]>
1363
+ getPrimaryDisplay(): Promise<DisplayInfo>
1364
+ getDisplayNearestPoint(point: { x: number; y: number }): Promise<DisplayInfo>
1365
+ getDisplayMatching(rect: { x: number; y: number; width: number; height: number }): Promise<DisplayInfo>
1366
+ getCursorScreenPoint(): Promise<{ x: number; y: number }>
1367
+ getSources(options?: { types?: ('screen' | 'window')[]; thumbnailSize?: { width: number; height: number } }): Promise<CaptureSource[]>
1368
+ capture(options?: { sourceId?: string; format?: 'png' | 'jpeg'; quality?: number }): Promise<ArrayBuffer>
1369
+ captureRegion(region: { x: number; y: number; width: number; height: number }, options?: { format?: 'png' | 'jpeg'; quality?: number }): Promise<ArrayBuffer>
1370
+ getMediaStreamConstraints(options: { sourceId: string; audio?: boolean; frameRate?: number }): Promise<object>
1371
+ screenCapture(): Promise<string | null>
1372
+ colorPick(): Promise<ColorPickResult | null>
1373
+ }
1374
+
1375
+ interface IntoolsShell {
1376
+ openPath(path: string): Promise<string>
1377
+ openExternal(url: string): Promise<void>
1378
+ showItemInFolder(path: string): Promise<void>
1379
+ openFolder(path: string): Promise<string>
1380
+ trashItem(path: string): Promise<void>
1381
+ beep(): Promise<void>
1382
+ }
1383
+
1384
+ interface IntoolsDialog {
1385
+ showOpenDialog(options?: {
1386
+ title?: string
1387
+ defaultPath?: string
1388
+ buttonLabel?: string
1389
+ filters?: { name: string; extensions: string[] }[]
1390
+ properties?: ('openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles')[]
1391
+ }): Promise<string[]>
1392
+ showSaveDialog(options?: {
1393
+ title?: string
1394
+ defaultPath?: string
1395
+ buttonLabel?: string
1396
+ filters?: { name: string; extensions: string[] }[]
1397
+ }): Promise<string | null>
1398
+ showMessageBox(options: {
1399
+ type?: 'none' | 'info' | 'error' | 'question' | 'warning'
1400
+ title?: string
1401
+ message: string
1402
+ detail?: string
1403
+ buttons?: string[]
1404
+ defaultId?: number
1405
+ cancelId?: number
1406
+ }): Promise<{ response: number; checkboxChecked: boolean }>
1407
+ showErrorBox(title: string, content: string): Promise<void>
1408
+ }
1409
+
1410
+ interface SystemInfo {
1411
+ platform: string
1412
+ arch: string
1413
+ hostname: string
1414
+ username: string
1415
+ homedir: string
1416
+ tmpdir: string
1417
+ cpus: number
1418
+ totalmem: number
1419
+ freemem: number
1420
+ uptime: number
1421
+ osVersion: string
1422
+ osRelease: string
1423
+ }
1424
+
1425
+ interface AppInfo {
1426
+ name: string
1427
+ version: string
1428
+ locale: string
1429
+ isPackaged: boolean
1430
+ userDataPath: string
1431
+ }
1432
+
1433
+ interface IntoolsSystem {
1434
+ getSystemInfo(): Promise<SystemInfo>
1435
+ getAppInfo(): Promise<AppInfo>
1436
+ getPath(name: 'home' | 'appData' | 'userData' | 'temp' | 'exe' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'logs'): Promise<string>
1437
+ getEnv(name: string): Promise<string | undefined>
1438
+ getIdleTime(): Promise<number>
1439
+ getFileIcon(filePath: string): Promise<string>
1440
+ getNativeId(): Promise<string>
1441
+ isDev(): Promise<boolean>
1442
+ isMacOS(): Promise<boolean>
1443
+ isWindows(): Promise<boolean>
1444
+ isLinux(): Promise<boolean>
1445
+ }
1446
+
1447
+ interface IntoolsPermission {
1448
+ getStatus(type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar'): Promise<'authorized' | 'granted' | 'denied' | 'not-determined' | 'restricted' | 'limited' | 'unknown'>
1449
+ request(type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar'): Promise<'authorized' | 'granted' | 'denied' | 'not-determined' | 'restricted' | 'limited' | 'unknown'>
1450
+ canRequest(type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar'): Promise<boolean>
1451
+ openSystemSettings(type: 'geolocation' | 'camera' | 'microphone' | 'notifications' | 'screen' | 'accessibility' | 'contacts' | 'calendar'): Promise<boolean>
1452
+ isAccessibilityTrusted(): Promise<boolean>
1453
+ }
1454
+
1455
+ interface IntoolsShortcut {
1456
+ register(accelerator: string): Promise<boolean>
1457
+ unregister(accelerator: string): Promise<void>
1458
+ unregisterAll(): Promise<void>
1459
+ isRegistered(accelerator: string): Promise<boolean>
1460
+ onTriggered(callback: (accelerator: string) => void): void
1461
+ }
1462
+
1463
+ interface IntoolsSecurity {
1464
+ isEncryptionAvailable(): Promise<boolean>
1465
+ encryptString(plainText: string): Promise<ArrayBuffer>
1466
+ decryptString(encrypted: ArrayBuffer): Promise<string>
1467
+ }
1468
+
1469
+ interface IntoolsMedia {
1470
+ getAccessStatus(mediaType: 'microphone' | 'camera'): Promise<'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'>
1471
+ askForAccess(mediaType: 'microphone' | 'camera'): Promise<boolean>
1472
+ hasCameraAccess(): Promise<boolean>
1473
+ hasMicrophoneAccess(): Promise<boolean>
1474
+ }
1475
+
1476
+ interface IntoolsPower {
1477
+ getSystemIdleTime(): Promise<number>
1478
+ getSystemIdleState(idleThreshold: number): Promise<'active' | 'idle' | 'locked' | 'unknown'>
1479
+ isOnBatteryPower(): Promise<boolean>
1480
+ getCurrentThermalState(): Promise<'unknown' | 'nominal' | 'fair' | 'serious' | 'critical'>
1481
+ onSuspend(callback: () => void): void
1482
+ onResume(callback: () => void): void
1483
+ onAC(callback: () => void): void
1484
+ onBattery(callback: () => void): void
1485
+ onLockScreen(callback: () => void): void
1486
+ onUnlockScreen(callback: () => void): void
1487
+ }
1488
+
1489
+ interface IntoolsTray {
1490
+ create(options: { icon: string; tooltip?: string; title?: string }): Promise<boolean>
1491
+ destroy(): Promise<void>
1492
+ setIcon(icon: string): Promise<void>
1493
+ setTooltip(tooltip: string): Promise<void>
1494
+ setTitle(title: string): Promise<void>
1495
+ exists(): Promise<boolean>
1496
+ }
1497
+
1498
+ interface IntoolsNetwork {
1499
+ isOnline(): Promise<boolean>
1500
+ }
1501
+
1502
+ interface IntoolsMenu {
1503
+ showContextMenu(items: {
1504
+ label: string
1505
+ type?: 'normal' | 'separator' | 'checkbox' | 'radio'
1506
+ checked?: boolean
1507
+ enabled?: boolean
1508
+ id?: string
1509
+ submenu?: any[]
1510
+ }[]): Promise<string | null>
1511
+ }
1512
+
1513
+ interface IntoolsGeolocation {
1514
+ getAccessStatus(): Promise<'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'>
1515
+ requestAccess(): Promise<'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'>
1516
+ canGetPosition(): Promise<boolean>
1517
+ openSettings(): Promise<void>
1518
+ getCurrentPosition(): Promise<{
1519
+ latitude: number
1520
+ longitude: number
1521
+ accuracy: number
1522
+ altitude?: number | null
1523
+ altitudeAccuracy?: number | null
1524
+ heading?: number | null
1525
+ speed?: number | null
1526
+ timestamp: number
1527
+ }>
1528
+ }
1529
+
1530
+ interface IntoolsTTS {
1531
+ speak(text: string, options?: { lang?: string; rate?: number; pitch?: number; volume?: number }): Promise<void>
1532
+ stop(): void
1533
+ pause(): void
1534
+ resume(): void
1535
+ getVoices(): { name: string; lang: string; default: boolean; localService: boolean }[]
1536
+ isSpeaking(): boolean
1537
+ }
1538
+
1539
+ interface IntoolsStorage {
1540
+ get(key: string, namespace?: string): Promise<unknown>
1541
+ set(key: string, value: unknown, namespace?: string): Promise<void>
1542
+ remove(key: string, namespace?: string): Promise<void>
1543
+ }
1544
+
1545
+ interface IntoolsMessaging {
1546
+ send(targetPluginId: string, type: string, payload: unknown): Promise<void>
1547
+ broadcast(type: string, payload: unknown): Promise<void>
1548
+ on(handler: (message: {
1549
+ id: string
1550
+ from: string
1551
+ to?: string
1552
+ type: string
1553
+ payload: unknown
1554
+ timestamp: number
1555
+ }) => void | Promise<void>): void
1556
+ off(handler?: (message: any) => void): void
1557
+ }
1558
+
1559
+ interface IntoolsScheduler {
1560
+ schedule(task: {
1561
+ name: string
1562
+ type: 'once' | 'repeat' | 'delay'
1563
+ callback: string
1564
+ time?: number
1565
+ cron?: string
1566
+ delay?: number
1567
+ payload?: any
1568
+ maxRetries?: number
1569
+ retryDelay?: number
1570
+ timeout?: number
1571
+ description?: string
1572
+ endTime?: number
1573
+ maxExecutions?: number
1574
+ }): Promise<any>
1575
+ cancelTask(taskId: string): Promise<void>
1576
+ pauseTask(taskId: string): Promise<void>
1577
+ resumeTask(taskId: string): Promise<void>
1578
+ listTasks(filter?: { status?: string; type?: string; limit?: number; offset?: number }): Promise<any[]>
1579
+ getTaskCount(filter?: { status?: string; type?: string }): Promise<number>
1580
+ getTask(taskId: string): Promise<any>
1581
+ deleteTasks(taskIds: string[]): Promise<{ success: boolean; deletedCount: number }>
1582
+ cleanupTasks(olderThan?: number): Promise<{ success: boolean; deletedCount: number }>
1583
+ getExecutions(taskId: string, limit?: number): Promise<any[]>
1584
+ validateCron(expression: string): boolean
1585
+ getNextCronTime(expression: string, after?: Date): Date
1586
+ describeCron(expression: string): string
1587
+ }
1588
+
1589
+ interface HttpResponse {
1590
+ status: number
1591
+ statusText: string
1592
+ headers: Record<string, string>
1593
+ data: string
1594
+ }
1595
+
1596
+ interface IntoolsHttp {
1597
+ request(options: {
1598
+ url: string
1599
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD'
1600
+ headers?: Record<string, string>
1601
+ body?: unknown
1602
+ timeout?: number
1603
+ }): Promise<HttpResponse>
1604
+ get(url: string, headers?: Record<string, string>): Promise<HttpResponse>
1605
+ post(url: string, body?: unknown, headers?: Record<string, string>): Promise<HttpResponse>
1606
+ put(url: string, body?: unknown, headers?: Record<string, string>): Promise<HttpResponse>
1607
+ delete(url: string, headers?: Record<string, string>): Promise<HttpResponse>
1608
+ }
1609
+
1610
+ type AiMessage = { role: 'system' | 'user' | 'assistant'; content?: string | AiMessageContent[]; reasoning_content?: string }
1611
+ type AiMessageContent =
1612
+ | { type: 'text'; text: string }
1613
+ | { type: 'image'; attachmentId: string; mimeType?: string }
1614
+ | { type: 'file'; attachmentId: string; mimeType?: string; filename?: string }
1615
+ type AiTool = { type: 'function'; function: { name: string; description?: string; parameters?: object } }
1616
+ type AiModelParameters = {
1617
+ contextWindow?: number
1618
+ temperatureEnabled?: boolean
1619
+ topPEnabled?: boolean
1620
+ maxOutputTokensEnabled?: boolean
1621
+ temperature?: number
1622
+ topP?: number
1623
+ topK?: number
1624
+ maxOutputTokens?: number
1625
+ presencePenalty?: number
1626
+ frequencyPenalty?: number
1627
+ stopSequences?: string[]
1628
+ seed?: number
1629
+ }
1630
+ type AiOption = { model?: string; messages: AiMessage[]; tools?: AiTool[]; params?: AiModelParameters }
1631
+ type AiModel = { id: string; label: string; description: string; icon?: string; providerLabel?: string; params?: AiModelParameters }
1632
+ type AiProviderConfig = {
1633
+ id: string
1634
+ label?: string
1635
+ enabled: boolean
1636
+ apiKey?: string
1637
+ baseURL?: string
1638
+ headers?: Record<string, string>
1639
+ defaultModel?: string
1640
+ defaultParams?: AiModelParameters
1641
+ }
1642
+ type AiSettings = { providers: AiProviderConfig[]; models?: AiModel[]; defaultParams?: AiModelParameters }
1643
+ type AiAttachmentRef = { attachmentId: string; mimeType: string; size: number; filename?: string; expiresAt?: string; purpose?: string }
1644
+ type AiTokenBreakdown = { inputTokens?: number; outputTokens?: number; totalTokens?: number }
1645
+
1646
+ interface IntoolsAi {
1647
+ call(option: AiOption, onChunk?: (chunk: AiMessage) => void): Promise<AiMessage>
1648
+ allModels(): Promise<AiModel[]>
1649
+ tokens: {
1650
+ estimate(input: { model?: string; messages: AiMessage[] }): Promise<{ inputTokens: number; outputTokens: number }>
1651
+ }
1652
+ attachments: {
1653
+ upload(input: { filePath?: string; buffer?: ArrayBuffer; mimeType: string; purpose?: string }): Promise<AiAttachmentRef>
1654
+ get(attachmentId: string): Promise<AiAttachmentRef | null>
1655
+ delete(attachmentId: string): Promise<void>
1656
+ }
1657
+ images: {
1658
+ generate(input: { model: string; prompt: string; size?: string; count?: number }): Promise<{ images: string[]; tokens: AiTokenBreakdown }>
1659
+ edit(input: { model: string; imageAttachmentId: string; prompt: string }): Promise<{ images: string[]; tokens: AiTokenBreakdown }>
1660
+ }
1661
+ videos: {
1662
+ generate(input: { model: string; prompt: string; duration?: number; size?: string }): Promise<void>
1663
+ }
1664
+ models: {
1665
+ fetch(input: { providerId: string; baseURL?: string; apiKey?: string }): Promise<{ models: AiModel[]; message?: string }>
1666
+ }
1667
+ testConnection(input?: { providerId?: string; model?: string; baseURL?: string; apiKey?: string }): Promise<{ success: boolean; message?: string }>
1668
+ testConnectionStream(
1669
+ input: { providerId?: string; model?: string; baseURL?: string; apiKey?: string },
1670
+ onChunk: (chunk: { type: 'reasoning' | 'content'; text: string }) => void
1671
+ ): Promise<{ success: boolean; message?: string; reasoning?: string }>
1672
+ settings: {
1673
+ get(): Promise<AiSettings>
1674
+ update(next: Partial<AiSettings>): Promise<AiSettings>
1675
+ }
1676
+ }
1677
+
1678
+ interface FileStat {
1679
+ name: string
1680
+ path: string
1681
+ size: number
1682
+ isFile: boolean
1683
+ isDirectory: boolean
1684
+ createdAt: number
1685
+ modifiedAt: number
1686
+ }
1687
+
1688
+ interface IntoolsFilesystem {
1689
+ readFile(path: string, encoding?: 'utf-8' | 'base64'): Promise<string | ArrayBuffer>
1690
+ writeFile(path: string, data: string | ArrayBuffer, encoding?: 'utf-8' | 'base64'): Promise<void>
1691
+ exists(path: string): Promise<boolean>
1692
+ unlink(path: string): Promise<void>
1693
+ readdir(path: string): Promise<string[]>
1694
+ mkdir(path: string): Promise<void>
1695
+ stat(path: string): Promise<FileStat | null>
1696
+ copy(src: string, dest: string): Promise<void>
1697
+ move(src: string, dest: string): Promise<void>
1698
+ }
1699
+
1700
+ interface IntoolsHost {
1701
+ invoke(pluginName: string, method: string, ...args: unknown[]): Promise<unknown>
1702
+ call(pluginName: string, method: string, ...args: unknown[]): Promise<{ data: any }>
1703
+ status(pluginName: string): Promise<{ ready: boolean; active: boolean }>
1704
+ restart(pluginName: string): Promise<boolean>
1705
+ }
1706
+
1707
+ interface FFmpegRunProgress {
1708
+ bitrate: string
1709
+ fps: number
1710
+ frame: number
1711
+ percent?: number
1712
+ q: number | string
1713
+ size: string
1714
+ speed: string
1715
+ time: string
1716
+ }
1717
+
1718
+ interface FFmpegDownloadProgress {
1719
+ phase: 'downloading' | 'extracting' | 'done'
1720
+ percent: number
1721
+ downloaded?: number
1722
+ total?: number
1723
+ }
1724
+
1725
+ interface FFmpegTask {
1726
+ promise: Promise<void>
1727
+ kill(): void
1728
+ quit(): void
1729
+ }
1730
+
1731
+ interface IntoolsFFmpeg {
1732
+ isAvailable(): Promise<boolean>
1733
+ getVersion(): Promise<string | null>
1734
+ getPath(): Promise<string | null>
1735
+ download(onProgress?: (progress: FFmpegDownloadProgress) => void): Promise<{ success: boolean; error?: string }>
1736
+ run(args: string[], onProgress?: (progress: FFmpegRunProgress) => void): FFmpegTask
1737
+ }
1738
+
1739
+ interface Attachment {
1740
+ id: string
1741
+ name: string
1742
+ size: number
1743
+ kind: 'file' | 'image'
1744
+ mime?: string
1745
+ ext?: string
1746
+ path?: string
1747
+ dataUrl?: string
1748
+ }
1749
+
1750
+ interface PluginInitData {
1751
+ pluginName: string
1752
+ featureCode: string
1753
+ feature?: string
1754
+ input: string
1755
+ mode?: string
1756
+ route?: string
1757
+ attachments?: Attachment[]
1758
+ }
1759
+
1760
+ interface IntoolsAPI {
1761
+ clipboard: IntoolsClipboard
1762
+ clipboardHistory: IntoolsClipboardHistory
1763
+ input: IntoolsInput
1764
+ notification: IntoolsNotification
1765
+ window: IntoolsWindow
1766
+ subInput: IntoolsSubInput
1767
+ plugin: IntoolsPlugin
1768
+ theme?: IntoolsTheme
1769
+ ai: IntoolsAi
1770
+ screen: IntoolsScreen
1771
+ shell: IntoolsShell
1772
+ dialog: IntoolsDialog
1773
+ system: IntoolsSystem
1774
+ permission: IntoolsPermission
1775
+ shortcut: IntoolsShortcut
1776
+ security: IntoolsSecurity
1777
+ media: IntoolsMedia
1778
+ power: IntoolsPower
1779
+ tray: IntoolsTray
1780
+ network: IntoolsNetwork
1781
+ menu: IntoolsMenu
1782
+ geolocation: IntoolsGeolocation
1783
+ tts: IntoolsTTS
1784
+ storage: IntoolsStorage
1785
+ http: IntoolsHttp
1786
+ filesystem: IntoolsFilesystem
1787
+ messaging: IntoolsMessaging
1788
+ scheduler: IntoolsScheduler
1789
+ host?: IntoolsHost
1790
+ onPluginInit(callback: (data: PluginInitData) => void): void
1791
+ onPluginAttach?(callback: (data: { pluginName: string; displayName: string; featureCode: string; input: string; uiPath: string; preloadPath: string }) => void): void
1792
+ onPluginDetached?(callback: () => void): void
1793
+ onThemeChange?(callback: (theme: 'light' | 'dark') => void): void
1794
+ onWindowStateChange?(callback: (state: { isMaximized: boolean }) => void): void
1795
+ inbrowser: {
1796
+ goto: (url: string, headers?: Record<string, string>, timeout?: number) => any
1797
+ useragent: (ua: string) => any
1798
+ device: (name: string) => any
1799
+ viewport: (width: number, height: number) => any
1800
+ show: () => any
1801
+ hide: () => any
1802
+ evaluate: (func: string | Function, ...params: any[]) => any
1803
+ wait: (msOrSelector: number | string) => any
1804
+ click: (selector: string) => any
1805
+ mousedown: (selector: string) => any
1806
+ mouseup: (selector: string) => any
1807
+ scroll: (selector: string | number, y?: number) => any
1808
+ devTools: (mode?: 'right' | 'bottom' | 'undocked' | 'detach') => any
1809
+ paste: (text: string) => any
1810
+ file: (selector: string, payload: string | string[]) => any
1811
+ end: () => any
1812
+ type: (selector: string, text: string) => any
1813
+ press: (key: string, modifiers?: string[]) => any
1814
+ check: (selector: string, checked: boolean) => any
1815
+ value: (selector: string, val: string) => any
1816
+ focus: (selector: string) => any
1817
+ when: (selector: string | Function, ...params: any[]) => any
1818
+ css: (css: string) => any
1819
+ pdf: (options?: any, savePath?: string) => any
1820
+ cookies: (nameOrFilter?: string | any) => any
1821
+ clearCookies: (url?: string) => any
1822
+ input: (selectorOrText: string, text?: string) => any
1823
+ dblclick: (selector: string) => any
1824
+ hover: (selector: string) => any
1825
+ screenshot: (target?: any, savePath?: string) => any
1826
+ drop: (selector: string, payload: any) => any
1827
+ download: (urlOrFunc: string | Function, savePath?: string, ...params: any[]) => any
1828
+ removeCookies: (name: string) => any
1829
+ setCookies: (nameOrCookies: any, value?: string) => any
1830
+ markdown: (selector?: string) => any
1831
+ getIdleInBrowsers: () => Promise<any[]>
1832
+ setInBrowserProxy: (config: any) => Promise<boolean>
1833
+ clearInBrowserCache: () => Promise<boolean>
1834
+ run: (idOrOptions?: number | any, options?: any) => Promise<any[]>
1835
+ }
1836
+ sharp: IntoolsSharpFunction
1837
+ getSharpVersion: () => Promise<{ sharp: Record<string, string>; format: Record<string, any> }>
1838
+ ffmpeg: IntoolsFFmpeg
1839
+ }
1840
+
1841
+ interface IntoolsSharpProxy {
1842
+ resize(width?: number, height?: number, options?: object): IntoolsSharpProxy
1843
+ extend(options: object): IntoolsSharpProxy
1844
+ extract(options: { left: number; top: number; width: number; height: number }): IntoolsSharpProxy
1845
+ trim(options?: object): IntoolsSharpProxy
1846
+ rotate(angle?: number, options?: object): IntoolsSharpProxy
1847
+ flip(): IntoolsSharpProxy
1848
+ flop(): IntoolsSharpProxy
1849
+ blur(sigma?: number): IntoolsSharpProxy
1850
+ sharpen(options?: object): IntoolsSharpProxy
1851
+ flatten(options?: object): IntoolsSharpProxy
1852
+ gamma(gamma?: number): IntoolsSharpProxy
1853
+ negate(options?: object): IntoolsSharpProxy
1854
+ normalize(options?: object): IntoolsSharpProxy
1855
+ threshold(threshold?: number, options?: object): IntoolsSharpProxy
1856
+ modulate(options?: object): IntoolsSharpProxy
1857
+ tint(color: string | object): IntoolsSharpProxy
1858
+ greyscale(greyscale?: boolean): IntoolsSharpProxy
1859
+ grayscale(grayscale?: boolean): IntoolsSharpProxy
1860
+ composite(images: object[]): IntoolsSharpProxy
1861
+ png(options?: object): IntoolsSharpProxy
1862
+ jpeg(options?: object): IntoolsSharpProxy
1863
+ webp(options?: object): IntoolsSharpProxy
1864
+ gif(options?: object): IntoolsSharpProxy
1865
+ tiff(options?: object): IntoolsSharpProxy
1866
+ avif(options?: object): IntoolsSharpProxy
1867
+ withMetadata(options?: object): IntoolsSharpProxy
1868
+ clone(): IntoolsSharpProxy
1869
+ toBuffer(options?: object): Promise<ArrayBuffer>
1870
+ toFile(fileOut: string): Promise<{ format: string; width: number; height: number; channels: number; size: number }>
1871
+ metadata(): Promise<{ format?: string; width?: number; height?: number; channels?: number; space?: string; depth?: string; density?: number; hasAlpha?: boolean; orientation?: number }>
1872
+ stats(): Promise<object>
1873
+ }
1874
+
1875
+ type IntoolsSharpFunction = (
1876
+ input?: string | ArrayBuffer | Uint8Array | object | any[],
1877
+ options?: object
1878
+ ) => IntoolsSharpProxy
1879
+
1880
+ declare global {
1881
+ interface Window {
1882
+ intools: IntoolsAPI
1883
+ }
1884
+ }
1885
+
1886
+ export {}
1887
+ `;
1888
+ }