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.
@@ -0,0 +1,1038 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.create = create;
40
+ const fs = __importStar(require("fs-extra"));
41
+ const path = __importStar(require("path"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ // 获取 CLI 包的 assets 目录路径
44
+ function getAssetsDir() {
45
+ return path.resolve(__dirname, '../../assets');
46
+ }
47
+ async function create(name, options) {
48
+ const targetDir = path.resolve(process.cwd(), name);
49
+ if (fs.existsSync(targetDir)) {
50
+ console.log(chalk_1.default.red(`错误: 目录 ${name} 已存在`));
51
+ process.exit(1);
52
+ }
53
+ const template = options.template || 'react';
54
+ console.log(chalk_1.default.blue(`创建插件项目: ${name}`));
55
+ console.log(chalk_1.default.gray(`模板: ${template}`));
56
+ console.log();
57
+ if (template === 'react') {
58
+ await createReactProject(targetDir, name);
59
+ }
60
+ else {
61
+ await createBasicProject(targetDir, name);
62
+ }
63
+ console.log();
64
+ console.log(chalk_1.default.green('插件创建成功!'));
65
+ console.log();
66
+ console.log('下一步:');
67
+ console.log(chalk_1.default.cyan(` cd ${name}`));
68
+ console.log(chalk_1.default.cyan(' npm install'));
69
+ console.log(chalk_1.default.cyan(' npm run dev'));
70
+ }
71
+ // ============================================
72
+ // 基础插件模板(无 UI)
73
+ // ============================================
74
+ async function createBasicProject(targetDir, name) {
75
+ fs.mkdirSync(targetDir, { recursive: true });
76
+ fs.mkdirSync(path.join(targetDir, 'src'));
77
+ // 复制默认图标
78
+ copyDefaultIcon(targetDir);
79
+ // manifest.json
80
+ const manifest = {
81
+ name,
82
+ version: '1.0.0',
83
+ displayName: name,
84
+ description: '插件描述',
85
+ main: 'dist/main.js',
86
+ icon: 'icon.png',
87
+ features: [
88
+ {
89
+ code: 'main',
90
+ explain: '主功能',
91
+ cmds: [{ type: 'keyword', value: name }]
92
+ }
93
+ ]
94
+ };
95
+ fs.writeJsonSync(path.join(targetDir, 'manifest.json'), manifest, { spaces: 2 });
96
+ console.log(chalk_1.default.green(' ✓ manifest.json'));
97
+ // package.json
98
+ const pkg = {
99
+ name,
100
+ version: '1.0.0',
101
+ scripts: {
102
+ build: 'esbuild src/main.ts --bundle --platform=node --outfile=dist/main.js',
103
+ dev: 'intools dev',
104
+ pack: 'intools pack'
105
+ },
106
+ devDependencies: {
107
+ esbuild: '^0.20.0',
108
+ typescript: '^5.0.0'
109
+ }
110
+ };
111
+ fs.writeJsonSync(path.join(targetDir, 'package.json'), pkg, { spaces: 2 });
112
+ console.log(chalk_1.default.green(' ✓ package.json'));
113
+ // src/main.ts
114
+ const mainTs = `module.exports = {
115
+ async run(context: any) {
116
+ const { clipboard, notification } = context.api
117
+ const { featureCode, input } = context
118
+ const text = input || await clipboard.readText()
119
+
120
+ // 在这里实现你的逻辑
121
+ const result = text.toUpperCase()
122
+
123
+ await clipboard.writeText(result)
124
+ notification.show('处理完成')
125
+ }
126
+ }
127
+ `;
128
+ fs.writeFileSync(path.join(targetDir, 'src/main.ts'), mainTs);
129
+ console.log(chalk_1.default.green(' ✓ src/main.ts'));
130
+ }
131
+ // ============================================
132
+ // React 插件模板(默认)
133
+ // ============================================
134
+ async function createReactProject(targetDir, name) {
135
+ // 创建目录结构
136
+ fs.mkdirSync(targetDir, { recursive: true });
137
+ fs.mkdirSync(path.join(targetDir, 'src'));
138
+ fs.mkdirSync(path.join(targetDir, 'src/ui'));
139
+ // 0. 复制默认图标
140
+ copyDefaultIcon(targetDir);
141
+ // 1. manifest.json
142
+ createReactManifest(targetDir, name);
143
+ // 2. package.json
144
+ createReactPackageJson(targetDir, name);
145
+ // 3. tsconfig.json
146
+ createTsConfig(targetDir);
147
+ // 4. vite.config.ts
148
+ createViteConfig(targetDir);
149
+ // 5. src/main.ts (后端逻辑)
150
+ createBackendMain(targetDir);
151
+ // 6. src/ui/* (React UI)
152
+ createReactUI(targetDir, name);
153
+ // 7. src/types/intools.d.ts (类型定义)
154
+ createIntoolsTypes(targetDir);
155
+ }
156
+ function createReactManifest(targetDir, name) {
157
+ const manifest = {
158
+ name,
159
+ version: '1.0.0',
160
+ displayName: name,
161
+ description: '插件描述',
162
+ main: 'dist/main.js',
163
+ ui: 'ui/index.html',
164
+ icon: 'icon.png',
165
+ features: [
166
+ {
167
+ code: 'main',
168
+ explain: '主功能',
169
+ cmds: [{ type: 'keyword', value: name }]
170
+ }
171
+ ]
172
+ };
173
+ fs.writeJsonSync(path.join(targetDir, 'manifest.json'), manifest, { spaces: 2 });
174
+ console.log(chalk_1.default.green(' ✓ manifest.json'));
175
+ }
176
+ function createReactPackageJson(targetDir, name) {
177
+ const pkg = {
178
+ name,
179
+ version: '1.0.0',
180
+ type: 'module',
181
+ scripts: {
182
+ dev: 'intools dev',
183
+ build: 'npm run build:backend && npm run build:ui',
184
+ 'build:backend': 'esbuild src/main.ts --bundle --platform=node --outfile=dist/main.js',
185
+ 'build:ui': 'vite build',
186
+ pack: 'intools pack'
187
+ },
188
+ dependencies: {
189
+ react: '^18.2.0',
190
+ 'react-dom': '^18.2.0'
191
+ },
192
+ devDependencies: {
193
+ '@types/react': '^18.2.0',
194
+ '@types/react-dom': '^18.2.0',
195
+ '@vitejs/plugin-react': '^4.2.0',
196
+ esbuild: '^0.20.0',
197
+ typescript: '^5.3.0',
198
+ vite: '^5.0.0'
199
+ }
200
+ };
201
+ fs.writeJsonSync(path.join(targetDir, 'package.json'), pkg, { spaces: 2 });
202
+ console.log(chalk_1.default.green(' ✓ package.json'));
203
+ }
204
+ function createTsConfig(targetDir) {
205
+ const tsconfig = {
206
+ compilerOptions: {
207
+ target: 'ES2020',
208
+ useDefineForClassFields: true,
209
+ lib: ['ES2020', 'DOM', 'DOM.Iterable'],
210
+ module: 'ESNext',
211
+ skipLibCheck: true,
212
+ moduleResolution: 'bundler',
213
+ allowImportingTsExtensions: true,
214
+ resolveJsonModule: true,
215
+ isolatedModules: true,
216
+ noEmit: true,
217
+ jsx: 'react-jsx',
218
+ strict: true,
219
+ noUnusedLocals: true,
220
+ noUnusedParameters: true,
221
+ noFallthroughCasesInSwitch: true
222
+ },
223
+ include: ['src']
224
+ };
225
+ fs.writeJsonSync(path.join(targetDir, 'tsconfig.json'), tsconfig, { spaces: 2 });
226
+ console.log(chalk_1.default.green(' ✓ tsconfig.json'));
227
+ }
228
+ function createViteConfig(targetDir) {
229
+ const viteConfig = `import { defineConfig } from 'vite'
230
+ import react from '@vitejs/plugin-react'
231
+ import path from 'path'
232
+
233
+ export default defineConfig({
234
+ plugins: [react()],
235
+ root: 'src/ui',
236
+ base: './',
237
+ build: {
238
+ outDir: '../../ui',
239
+ emptyOutDir: true
240
+ },
241
+ resolve: {
242
+ alias: {
243
+ '@': path.resolve(__dirname, 'src')
244
+ }
245
+ }
246
+ })
247
+ `;
248
+ fs.writeFileSync(path.join(targetDir, 'vite.config.ts'), viteConfig);
249
+ console.log(chalk_1.default.green(' ✓ vite.config.ts'));
250
+ }
251
+ function createBackendMain(targetDir) {
252
+ const mainTs = `// 后端逻辑 - 在沙箱中运行
253
+ // 如果插件有 UI,此文件可以为空或用于后台任务
254
+
255
+ module.exports = {
256
+ // 插件加载时调用
257
+ onLoad() {
258
+ console.log('插件已加载')
259
+ },
260
+
261
+ // 插件卸载时调用
262
+ onUnload() {
263
+ console.log('插件已卸载')
264
+ },
265
+
266
+ // 主执行函数(无 UI 时使用)
267
+ async run(context: any) {
268
+ const { notification } = context.api
269
+ notification.show('插件已启动')
270
+ }
271
+ }
272
+ `;
273
+ fs.writeFileSync(path.join(targetDir, 'src/main.ts'), mainTs);
274
+ console.log(chalk_1.default.green(' ✓ src/main.ts'));
275
+ }
276
+ function createReactUI(targetDir, name) {
277
+ createReactHooks(targetDir);
278
+ // index.html
279
+ const indexHtml = `<!DOCTYPE html>
280
+ <html lang="zh-CN">
281
+ <head>
282
+ <meta charset="UTF-8">
283
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
284
+ <title>${name}</title>
285
+ </head>
286
+ <body>
287
+ <div id="root"></div>
288
+ <script type="module" src="./main.tsx"></script>
289
+ </body>
290
+ </html>
291
+ `;
292
+ fs.writeFileSync(path.join(targetDir, 'src/ui/index.html'), indexHtml);
293
+ console.log(chalk_1.default.green(' ✓ src/ui/index.html'));
294
+ // main.tsx
295
+ const mainTsx = `import React from 'react'
296
+ import ReactDOM from 'react-dom/client'
297
+ import App from './App'
298
+ import './styles.css'
299
+
300
+ ReactDOM.createRoot(document.getElementById('root')!).render(
301
+ <React.StrictMode>
302
+ <App />
303
+ </React.StrictMode>
304
+ )
305
+ `;
306
+ fs.writeFileSync(path.join(targetDir, 'src/ui/main.tsx'), mainTsx);
307
+ console.log(chalk_1.default.green(' ✓ src/ui/main.tsx'));
308
+ // App.tsx
309
+ const appTsx = `import { useEffect, useState } from 'react'
310
+ import { useIntools } from './hooks/useIntools'
311
+
312
+ interface PluginInitData {
313
+ pluginName: string
314
+ featureCode: string
315
+ input: string
316
+ }
317
+
318
+ export default function App() {
319
+ const [input, setInput] = useState('')
320
+ const [output, setOutput] = useState('')
321
+ const { clipboard, notification } = useIntools('${name}')
322
+
323
+ useEffect(() => {
324
+ // 接收插件初始化数据
325
+ window.intools?.onPluginInit?.((data: PluginInitData) => {
326
+ if (data.input) {
327
+ setInput(data.input)
328
+ }
329
+ })
330
+ }, [])
331
+
332
+ const handleProcess = async () => {
333
+ // 示例:将输入转为大写
334
+ const result = input.toUpperCase()
335
+ setOutput(result)
336
+
337
+ // 复制到剪贴板并通知
338
+ await clipboard.writeText(result)
339
+ notification.show('已复制到剪贴板')
340
+ }
341
+
342
+ return (
343
+ <div className="app">
344
+ <div className="titlebar">${name}</div>
345
+ <div className="container">
346
+ <div className="field">
347
+ <label>输入</label>
348
+ <textarea
349
+ value={input}
350
+ onChange={(e) => setInput(e.target.value)}
351
+ placeholder="请输入内容..."
352
+ />
353
+ </div>
354
+ <div className="actions">
355
+ <button className="btn-primary" onClick={handleProcess}>
356
+ 处理
357
+ </button>
358
+ </div>
359
+ <div className="field">
360
+ <label>输出</label>
361
+ <textarea
362
+ value={output}
363
+ readOnly
364
+ placeholder="结果将显示在这里..."
365
+ />
366
+ </div>
367
+ </div>
368
+ </div>
369
+ )
370
+ }
371
+ `;
372
+ fs.writeFileSync(path.join(targetDir, 'src/ui/App.tsx'), appTsx);
373
+ console.log(chalk_1.default.green(' ✓ src/ui/App.tsx'));
374
+ // styles.css
375
+ const stylesCss = `* {
376
+ margin: 0;
377
+ padding: 0;
378
+ box-sizing: border-box;
379
+ }
380
+
381
+ body {
382
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
383
+ background: #1e1e1e;
384
+ color: #e0e0e0;
385
+ min-height: 100vh;
386
+ }
387
+
388
+ .app {
389
+ display: flex;
390
+ flex-direction: column;
391
+ height: 100vh;
392
+ }
393
+
394
+ .titlebar {
395
+ height: 32px;
396
+ background: #2d2d2d;
397
+ display: flex;
398
+ align-items: center;
399
+ justify-content: center;
400
+ font-size: 13px;
401
+ color: #999;
402
+ -webkit-app-region: drag;
403
+ flex-shrink: 0;
404
+ }
405
+
406
+ .container {
407
+ flex: 1;
408
+ padding: 16px;
409
+ display: flex;
410
+ flex-direction: column;
411
+ gap: 12px;
412
+ overflow: auto;
413
+ }
414
+
415
+ .field {
416
+ display: flex;
417
+ flex-direction: column;
418
+ gap: 6px;
419
+ flex: 1;
420
+ min-height: 0;
421
+ }
422
+
423
+ .field label {
424
+ font-size: 12px;
425
+ color: #999;
426
+ }
427
+
428
+ .field textarea {
429
+ flex: 1;
430
+ background: #2d2d2d;
431
+ border: 1px solid #3d3d3d;
432
+ border-radius: 6px;
433
+ padding: 12px;
434
+ color: #fff;
435
+ font-family: 'Monaco', 'Consolas', monospace;
436
+ font-size: 13px;
437
+ resize: none;
438
+ outline: none;
439
+ min-height: 80px;
440
+ }
441
+
442
+ .field textarea:focus {
443
+ border-color: #0078d4;
444
+ }
445
+
446
+ .field textarea::placeholder {
447
+ color: #666;
448
+ }
449
+
450
+ .actions {
451
+ display: flex;
452
+ gap: 12px;
453
+ justify-content: center;
454
+ }
455
+
456
+ button {
457
+ padding: 8px 24px;
458
+ border: none;
459
+ border-radius: 4px;
460
+ font-size: 14px;
461
+ cursor: pointer;
462
+ transition: background 0.2s;
463
+ }
464
+
465
+ .btn-primary {
466
+ background: #0078d4;
467
+ color: #fff;
468
+ }
469
+
470
+ .btn-primary:hover {
471
+ background: #1084d8;
472
+ }
473
+
474
+ .btn-secondary {
475
+ background: #3d3d3d;
476
+ color: #fff;
477
+ }
478
+
479
+ .btn-secondary:hover {
480
+ background: #4d4d4d;
481
+ }
482
+ `;
483
+ fs.writeFileSync(path.join(targetDir, 'src/ui/styles.css'), stylesCss);
484
+ console.log(chalk_1.default.green(' ✓ src/ui/styles.css'));
485
+ }
486
+ function createReactHooks(targetDir) {
487
+ fs.mkdirSync(path.join(targetDir, 'src/ui/hooks'));
488
+ const useIntools = `import { useMemo } from 'react'
489
+
490
+ export function useIntools(pluginId?: string) {
491
+ return useMemo(() => ({
492
+ clipboard: {
493
+ readText: () => window.intools?.clipboard?.readText(),
494
+ writeText: (text: string) => window.intools?.clipboard?.writeText(text),
495
+ readImage: () => window.intools?.clipboard?.readImage(),
496
+ writeImage: (buffer: ArrayBuffer) => window.intools?.clipboard?.writeImage(buffer),
497
+ readFiles: () => window.intools?.clipboard?.readFiles(),
498
+ getFormat: () => window.intools?.clipboard?.getFormat(),
499
+ },
500
+ storage: {
501
+ get: (key: string) => window.intools?.storage?.get(key, pluginId),
502
+ set: (key: string, value: unknown) => window.intools?.storage?.set(key, value, pluginId),
503
+ remove: (key: string) => window.intools?.storage?.remove(key, pluginId),
504
+ },
505
+ notification: {
506
+ show: (message: string, type?: 'info' | 'success' | 'warning' | 'error') =>
507
+ window.intools?.notification?.show(message, type),
508
+ },
509
+ window: {
510
+ hide: () => window.intools?.window?.hide(),
511
+ setSize: (width: number, height: number) => window.intools?.window?.setSize(width, height),
512
+ center: () => window.intools?.window?.center(),
513
+ },
514
+
515
+ // Theme API
516
+ theme: {
517
+ get: () => window.intools?.theme?.get(),
518
+ set: (mode: 'light' | 'dark' | 'system') => window.intools?.theme?.set(mode),
519
+ getActual: () => window.intools?.theme?.getActual(),
520
+ },
521
+
522
+ // Screen API
523
+ screen: {
524
+ getAllDisplays: () => window.intools?.screen?.getAllDisplays(),
525
+ getPrimaryDisplay: () => window.intools?.screen?.getPrimaryDisplay(),
526
+ getDisplayNearestPoint: (point: { x: number; y: number }) => window.intools?.screen?.getDisplayNearestPoint(point),
527
+ getCursorScreenPoint: () => window.intools?.screen?.getCursorScreenPoint(),
528
+ getSources: (options?: any) => window.intools?.screen?.getSources(options),
529
+ capture: (options?: any) => window.intools?.screen?.capture(options),
530
+ captureRegion: (region: any, options?: any) => window.intools?.screen?.captureRegion(region, options),
531
+ getMediaStreamConstraints: (options: any) => window.intools?.screen?.getMediaStreamConstraints(options),
532
+ },
533
+
534
+ // Shell API
535
+ shell: {
536
+ openPath: (path: string) => window.intools?.shell?.openPath(path),
537
+ openExternal: (url: string) => window.intools?.shell?.openExternal(url),
538
+ showItemInFolder: (path: string) => window.intools?.shell?.showItemInFolder(path),
539
+ openFolder: (path: string) => window.intools?.shell?.openFolder(path),
540
+ trashItem: (path: string) => window.intools?.shell?.trashItem(path),
541
+ beep: () => window.intools?.shell?.beep(),
542
+ },
543
+
544
+ // Filesystem API
545
+ filesystem: {
546
+ readFile: (path: string, encoding?: 'utf-8' | 'base64') => window.intools?.filesystem?.readFile(path, encoding),
547
+ writeFile: (path: string, data: string | ArrayBuffer, encoding?: 'utf-8' | 'base64') => window.intools?.filesystem?.writeFile(path, data, encoding),
548
+ exists: (path: string) => window.intools?.filesystem?.exists(path),
549
+ readdir: (path: string) => window.intools?.filesystem?.readdir(path),
550
+ mkdir: (path: string) => window.intools?.filesystem?.mkdir(path),
551
+ stat: (path: string) => window.intools?.filesystem?.stat(path),
552
+ copy: (src: string, dest: string) => window.intools?.filesystem?.copy(src, dest),
553
+ move: (src: string, dest: string) => window.intools?.filesystem?.move(src, dest),
554
+ unlink: (path: string) => window.intools?.filesystem?.unlink(path),
555
+ },
556
+
557
+ // Dialog API
558
+ dialog: {
559
+ showOpenDialog: (options?: any) => window.intools?.dialog?.showOpenDialog(options),
560
+ showSaveDialog: (options?: any) => window.intools?.dialog?.showSaveDialog(options),
561
+ showMessageBox: (options: any) => window.intools?.dialog?.showMessageBox(options),
562
+ showErrorBox: (title: string, content: string) => window.intools?.dialog?.showErrorBox(title, content),
563
+ },
564
+
565
+ // System API
566
+ system: {
567
+ getSystemInfo: () => window.intools?.system?.getSystemInfo(),
568
+ getAppInfo: () => window.intools?.system?.getAppInfo(),
569
+ getPath: (name: string) => window.intools?.system?.getPath(name as any),
570
+ getEnv: (name: string) => window.intools?.system?.getEnv(name),
571
+ getIdleTime: () => window.intools?.system?.getIdleTime(),
572
+ },
573
+
574
+ // Shortcut API
575
+ shortcut: {
576
+ register: (accelerator: string) => window.intools?.shortcut?.register(accelerator),
577
+ unregister: (accelerator: string) => window.intools?.shortcut?.unregister(accelerator),
578
+ unregisterAll: () => window.intools?.shortcut?.unregisterAll(),
579
+ isRegistered: (accelerator: string) => window.intools?.shortcut?.isRegistered(accelerator),
580
+ onTriggered: (callback: (accelerator: string) => void) => window.intools?.shortcut?.onTriggered(callback),
581
+ },
582
+
583
+ // Security API
584
+ security: {
585
+ isEncryptionAvailable: () => window.intools?.security?.isEncryptionAvailable(),
586
+ encryptString: (plainText: string) => window.intools?.security?.encryptString(plainText),
587
+ decryptString: (encrypted: ArrayBuffer) => window.intools?.security?.decryptString(encrypted),
588
+ },
589
+
590
+ // Media API
591
+ media: {
592
+ getAccessStatus: (mediaType: 'microphone' | 'camera') => window.intools?.media?.getAccessStatus(mediaType),
593
+ askForAccess: (mediaType: 'microphone' | 'camera') => window.intools?.media?.askForAccess(mediaType),
594
+ hasCameraAccess: () => window.intools?.media?.hasCameraAccess(),
595
+ hasMicrophoneAccess: () => window.intools?.media?.hasMicrophoneAccess(),
596
+ },
597
+
598
+ // Power API
599
+ power: {
600
+ getSystemIdleTime: () => window.intools?.power?.getSystemIdleTime(),
601
+ getSystemIdleState: (idleThreshold: number) => window.intools?.power?.getSystemIdleState(idleThreshold),
602
+ isOnBatteryPower: () => window.intools?.power?.isOnBatteryPower(),
603
+ getCurrentThermalState: () => window.intools?.power?.getCurrentThermalState(),
604
+ onSuspend: (callback: () => void) => window.intools?.power?.onSuspend(callback),
605
+ onResume: (callback: () => void) => window.intools?.power?.onResume(callback),
606
+ onAC: (callback: () => void) => window.intools?.power?.onAC(callback),
607
+ onBattery: (callback: () => void) => window.intools?.power?.onBattery(callback),
608
+ onLockScreen: (callback: () => void) => window.intools?.power?.onLockScreen(callback),
609
+ onUnlockScreen: (callback: () => void) => window.intools?.power?.onUnlockScreen(callback),
610
+ },
611
+
612
+ // Tray API
613
+ tray: {
614
+ create: (options: any) => window.intools?.tray?.create(options),
615
+ destroy: () => window.intools?.tray?.destroy(),
616
+ setIcon: (icon: string) => window.intools?.tray?.setIcon(icon),
617
+ setTooltip: (tooltip: string) => window.intools?.tray?.setTooltip(tooltip),
618
+ setTitle: (title: string) => window.intools?.tray?.setTitle(title),
619
+ exists: () => window.intools?.tray?.exists(),
620
+ },
621
+
622
+ // HTTP API
623
+ http: {
624
+ request: (options: any) => window.intools?.http?.request(options),
625
+ get: (url: string, headers?: Record<string, string>) => window.intools?.http?.get(url, headers),
626
+ post: (url: string, body?: any, headers?: Record<string, string>) => window.intools?.http?.post(url, body, headers),
627
+ put: (url: string, body?: any, headers?: Record<string, string>) => window.intools?.http?.put(url, body, headers),
628
+ delete: (url: string, headers?: Record<string, string>) => window.intools?.http?.delete(url, headers),
629
+ },
630
+
631
+ // Network API
632
+ network: {
633
+ isOnline: () => window.intools?.network?.isOnline(),
634
+ onOnline: (callback: () => void) => window.intools?.network?.onOnline(callback),
635
+ onOffline: (callback: () => void) => window.intools?.network?.onOffline(callback),
636
+ },
637
+
638
+ // Menu API
639
+ menu: {
640
+ showContextMenu: (items: any[]) => window.intools?.menu?.showContextMenu(items),
641
+ },
642
+
643
+ // Geolocation API
644
+ geolocation: {
645
+ getAccessStatus: () => window.intools?.geolocation?.getAccessStatus(),
646
+ requestAccess: () => window.intools?.geolocation?.requestAccess(),
647
+ canGetPosition: () => window.intools?.geolocation?.canGetPosition(),
648
+ openSettings: () => window.intools?.geolocation?.openSettings(),
649
+ getCurrentPosition: () => window.intools?.geolocation?.getCurrentPosition(),
650
+ },
651
+
652
+ // TTS API
653
+ tts: {
654
+ speak: (text: string, options?: any) => window.intools?.tts?.speak(text, options),
655
+ stop: () => window.intools?.tts?.stop(),
656
+ pause: () => window.intools?.tts?.pause(),
657
+ resume: () => window.intools?.tts?.resume(),
658
+ getVoices: () => window.intools?.tts?.getVoices(),
659
+ isSpeaking: () => window.intools?.tts?.isSpeaking(),
660
+ },
661
+
662
+ // Host API
663
+ host: {
664
+ invoke: (pluginName: string, method: string, ...args: any[]) => window.intools?.host?.invoke(pluginName, method, ...args),
665
+ status: (pluginName: string) => window.intools?.host?.status(pluginName),
666
+ restart: (pluginName: string) => window.intools?.host?.restart(pluginName),
667
+ },
668
+ }), [pluginId])
669
+ }
670
+ `;
671
+ fs.writeFileSync(path.join(targetDir, 'src/ui/hooks/useIntools.ts'), useIntools);
672
+ console.log(chalk_1.default.green(' ✓ src/ui/hooks/useIntools.ts'));
673
+ }
674
+ function createIntoolsTypes(targetDir) {
675
+ fs.mkdirSync(path.join(targetDir, 'src/types'));
676
+ const typesDts = `// InTools API 类型定义
677
+
678
+ interface ClipboardFileInfo {
679
+ path: string
680
+ name: string
681
+ size: number
682
+ isDirectory: boolean
683
+ }
684
+
685
+ interface IntoolsClipboard {
686
+ readText(): Promise<string>
687
+ writeText(text: string): Promise<void>
688
+ readImage(): Promise<ArrayBuffer | null>
689
+ writeImage(buffer: ArrayBuffer): Promise<void>
690
+ readFiles(): Promise<ClipboardFileInfo[]>
691
+ getFormat(): Promise<'text' | 'image' | 'files' | 'empty'>
692
+ }
693
+
694
+ interface IntoolsNotification {
695
+ show(message: string, type?: 'info' | 'success' | 'warning' | 'error'): void
696
+ }
697
+
698
+ interface IntoolsWindow {
699
+ hide(): void
700
+ setSize(width: number, height: number): void
701
+ center(): void
702
+ detach(): void
703
+ close(): void
704
+ setAlwaysOnTop(flag: boolean): void
705
+ getMode(): Promise<'attached' | 'detached'>
706
+ minimize(): void
707
+ maximize(): void
708
+ getState(): Promise<{ isMaximized: boolean }>
709
+ reload(): void
710
+ }
711
+
712
+ interface IntoolsTheme {
713
+ get(): Promise<{ mode: 'light' | 'dark' | 'system'; actual: 'light' | 'dark' }>
714
+ getActual(): Promise<'light' | 'dark'>
715
+ }
716
+
717
+ interface IntoolsPlugin {
718
+ getAll(): Promise<any[]>
719
+ search(query: string): Promise<any[]>
720
+ run(name: string, featureCode: string, input?: string): Promise<any>
721
+ install(filePath: string): Promise<any>
722
+ enable(name: string): Promise<any>
723
+ disable(name: string): Promise<any>
724
+ uninstall(name: string): Promise<any>
725
+ getReadme(name: string): Promise<string | null>
726
+ }
727
+
728
+ // Screen API 类型
729
+ interface DisplayInfo {
730
+ id: number
731
+ label: string
732
+ bounds: { x: number; y: number; width: number; height: number }
733
+ workArea: { x: number; y: number; width: number; height: number }
734
+ scaleFactor: number
735
+ rotation: number
736
+ isPrimary: boolean
737
+ }
738
+
739
+ interface CaptureSource {
740
+ id: string
741
+ name: string
742
+ thumbnailDataUrl: string
743
+ displayId?: string
744
+ appIconDataUrl?: string
745
+ }
746
+
747
+ interface IntoolsScreen {
748
+ getAllDisplays(): Promise<DisplayInfo[]>
749
+ getPrimaryDisplay(): Promise<DisplayInfo>
750
+ getDisplayNearestPoint(point: { x: number; y: number }): Promise<DisplayInfo>
751
+ getCursorScreenPoint(): Promise<{ x: number; y: number }>
752
+ getSources(options?: { types?: ('screen' | 'window')[]; thumbnailSize?: { width: number; height: number } }): Promise<CaptureSource[]>
753
+ capture(options?: { sourceId?: string; format?: 'png' | 'jpeg'; quality?: number }): Promise<ArrayBuffer>
754
+ captureRegion(region: { x: number; y: number; width: number; height: number }, options?: { format?: 'png' | 'jpeg'; quality?: number }): Promise<ArrayBuffer>
755
+ getMediaStreamConstraints(options: { sourceId: string; audio?: boolean; frameRate?: number }): Promise<object>
756
+ }
757
+
758
+ // Shell API 类型
759
+ interface IntoolsShell {
760
+ openPath(path: string): Promise<string>
761
+ openExternal(url: string): Promise<void>
762
+ showItemInFolder(path: string): Promise<void>
763
+ openFolder(path: string): Promise<string>
764
+ trashItem(path: string): Promise<void>
765
+ beep(): Promise<void>
766
+ }
767
+
768
+ // Dialog API 类型
769
+ interface IntoolsDialog {
770
+ showOpenDialog(options?: {
771
+ title?: string
772
+ defaultPath?: string
773
+ buttonLabel?: string
774
+ filters?: { name: string; extensions: string[] }[]
775
+ properties?: ('openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles')[]
776
+ }): Promise<string[]>
777
+ showSaveDialog(options?: {
778
+ title?: string
779
+ defaultPath?: string
780
+ buttonLabel?: string
781
+ filters?: { name: string; extensions: string[] }[]
782
+ }): Promise<string | null>
783
+ showMessageBox(options: {
784
+ type?: 'none' | 'info' | 'error' | 'question' | 'warning'
785
+ title?: string
786
+ message: string
787
+ detail?: string
788
+ buttons?: string[]
789
+ defaultId?: number
790
+ cancelId?: number
791
+ }): Promise<{ response: number; checkboxChecked: boolean }>
792
+ showErrorBox(title: string, content: string): Promise<void>
793
+ }
794
+
795
+ // System API 类型
796
+ interface SystemInfo {
797
+ platform: string
798
+ arch: string
799
+ hostname: string
800
+ username: string
801
+ homedir: string
802
+ tmpdir: string
803
+ cpus: number
804
+ totalmem: number
805
+ freemem: number
806
+ uptime: number
807
+ osVersion: string
808
+ osRelease: string
809
+ }
810
+
811
+ interface AppInfo {
812
+ name: string
813
+ version: string
814
+ locale: string
815
+ isPackaged: boolean
816
+ userDataPath: string
817
+ }
818
+
819
+ interface IntoolsSystem {
820
+ getSystemInfo(): Promise<SystemInfo>
821
+ getAppInfo(): Promise<AppInfo>
822
+ getPath(name: 'home' | 'appData' | 'userData' | 'temp' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos'): Promise<string>
823
+ getEnv(name: string): Promise<string | undefined>
824
+ getIdleTime(): Promise<number>
825
+ }
826
+
827
+ // GlobalShortcut API 类型
828
+ interface IntoolsShortcut {
829
+ register(accelerator: string): Promise<boolean>
830
+ unregister(accelerator: string): Promise<void>
831
+ unregisterAll(): Promise<void>
832
+ isRegistered(accelerator: string): Promise<boolean>
833
+ onTriggered(callback: (accelerator: string) => void): void
834
+ }
835
+
836
+ // Security API 类型
837
+ interface IntoolsSecurity {
838
+ isEncryptionAvailable(): Promise<boolean>
839
+ encryptString(plainText: string): Promise<ArrayBuffer>
840
+ decryptString(encrypted: ArrayBuffer): Promise<string>
841
+ }
842
+
843
+ // Media API 类型
844
+ interface IntoolsMedia {
845
+ getAccessStatus(mediaType: 'microphone' | 'camera'): Promise<'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'>
846
+ askForAccess(mediaType: 'microphone' | 'camera'): Promise<boolean>
847
+ hasCameraAccess(): Promise<boolean>
848
+ hasMicrophoneAccess(): Promise<boolean>
849
+ }
850
+
851
+ // Power API 类型
852
+ interface IntoolsPower {
853
+ getSystemIdleTime(): Promise<number>
854
+ getSystemIdleState(idleThreshold: number): Promise<'active' | 'idle' | 'locked' | 'unknown'>
855
+ isOnBatteryPower(): Promise<boolean>
856
+ getCurrentThermalState(): Promise<'unknown' | 'nominal' | 'fair' | 'serious' | 'critical'>
857
+ onSuspend(callback: () => void): void
858
+ onResume(callback: () => void): void
859
+ onAC(callback: () => void): void
860
+ onBattery(callback: () => void): void
861
+ onLockScreen(callback: () => void): void
862
+ onUnlockScreen(callback: () => void): void
863
+ }
864
+
865
+ // Tray API 类型
866
+ interface IntoolsTray {
867
+ create(options: { icon: string; tooltip?: string; title?: string }): Promise<boolean>
868
+ destroy(): Promise<void>
869
+ setIcon(icon: string): Promise<void>
870
+ setTooltip(tooltip: string): Promise<void>
871
+ setTitle(title: string): Promise<void>
872
+ exists(): Promise<boolean>
873
+ }
874
+
875
+ // Network API 类型
876
+ interface IntoolsNetwork {
877
+ isOnline(): Promise<boolean>
878
+ onOnline(callback: () => void): void
879
+ onOffline(callback: () => void): void
880
+ }
881
+
882
+ // Menu API 类型
883
+ interface IntoolsMenu {
884
+ showContextMenu(items: {
885
+ label: string
886
+ type?: 'normal' | 'separator' | 'checkbox' | 'radio'
887
+ checked?: boolean
888
+ enabled?: boolean
889
+ id?: string
890
+ submenu?: any[]
891
+ }[]): Promise<string | null>
892
+ }
893
+
894
+ // Geolocation API 类型
895
+ interface IntoolsGeolocation {
896
+ getAccessStatus(): Promise<'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'>
897
+ getCurrentPosition(): Promise<{
898
+ latitude: number
899
+ longitude: number
900
+ accuracy: number
901
+ altitude?: number | null
902
+ altitudeAccuracy?: number | null
903
+ heading?: number | null
904
+ speed?: number | null
905
+ timestamp: number
906
+ }>
907
+ }
908
+
909
+ // TTS API 类型
910
+ interface IntoolsTTS {
911
+ speak(text: string, options?: { lang?: string; rate?: number; pitch?: number; volume?: number }): Promise<void>
912
+ stop(): void
913
+ pause(): void
914
+ resume(): void
915
+ getVoices(): { name: string; lang: string; default: boolean; localService: boolean }[]
916
+ isSpeaking(): boolean
917
+ }
918
+
919
+ // Plugin Host API 类型
920
+ interface IntoolsHost {
921
+ invoke(pluginName: string, method: string, ...args: unknown[]): Promise<any>
922
+ status(pluginName: string): Promise<any>
923
+ restart(pluginName: string): Promise<void>
924
+ }
925
+
926
+ // Storage API 类型
927
+ interface IntoolsStorage {
928
+ get(key: string, namespace?: string): Promise<unknown>
929
+ set(key: string, value: unknown, namespace?: string): Promise<void>
930
+ remove(key: string, namespace?: string): Promise<void>
931
+ getAll(namespace?: string): Promise<Record<string, unknown>>
932
+ clear(namespace?: string): Promise<boolean>
933
+ }
934
+
935
+ // HTTP API 类型
936
+ interface HttpResponse {
937
+ status: number
938
+ statusText: string
939
+ headers: Record<string, string>
940
+ data: string
941
+ }
942
+
943
+ interface IntoolsHttp {
944
+ request(options: {
945
+ url: string
946
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD'
947
+ headers?: Record<string, string>
948
+ body?: unknown
949
+ timeout?: number
950
+ }): Promise<HttpResponse>
951
+ get(url: string, headers?: Record<string, string>): Promise<HttpResponse>
952
+ post(url: string, body?: unknown, headers?: Record<string, string>): Promise<HttpResponse>
953
+ put(url: string, body?: unknown, headers?: Record<string, string>): Promise<HttpResponse>
954
+ delete(url: string, headers?: Record<string, string>): Promise<HttpResponse>
955
+ }
956
+
957
+ // Filesystem API 类型
958
+ interface FileStat {
959
+ name: string
960
+ path: string
961
+ size: number
962
+ isFile: boolean
963
+ isDirectory: boolean
964
+ createdAt: number
965
+ modifiedAt: number
966
+ }
967
+
968
+ interface IntoolsFilesystem {
969
+ readFile(path: string, encoding?: 'utf-8' | 'base64'): Promise<string | ArrayBuffer>
970
+ writeFile(path: string, data: string | ArrayBuffer, encoding?: 'utf-8' | 'base64'): Promise<void>
971
+ exists(path: string): Promise<boolean>
972
+ unlink(path: string): Promise<void>
973
+ readdir(path: string): Promise<string[]>
974
+ mkdir(path: string): Promise<void>
975
+ stat(path: string): Promise<FileStat | null>
976
+ copy(src: string, dest: string): Promise<void>
977
+ move(src: string, dest: string): Promise<void>
978
+ extname(path: string): string
979
+ dirname(path: string): string
980
+ basename(path: string, ext?: string): string
981
+ join(...paths: string[]): string
982
+ }
983
+
984
+ interface PluginInitData {
985
+ pluginName: string
986
+ featureCode: string
987
+ input: string
988
+ }
989
+
990
+ interface IntoolsAPI {
991
+ clipboard: IntoolsClipboard
992
+ notification: IntoolsNotification
993
+ window: IntoolsWindow
994
+ plugin: IntoolsPlugin
995
+ theme?: IntoolsTheme
996
+ screen: IntoolsScreen
997
+ shell: IntoolsShell
998
+ dialog: IntoolsDialog
999
+ system: IntoolsSystem
1000
+ shortcut: IntoolsShortcut
1001
+ security: IntoolsSecurity
1002
+ media: IntoolsMedia
1003
+ power: IntoolsPower
1004
+ tray: IntoolsTray
1005
+ network: IntoolsNetwork
1006
+ menu: IntoolsMenu
1007
+ geolocation: IntoolsGeolocation
1008
+ tts: IntoolsTTS
1009
+ host: IntoolsHost
1010
+ storage: IntoolsStorage
1011
+ http: IntoolsHttp
1012
+ filesystem: IntoolsFilesystem
1013
+ onPluginInit(callback: (data: PluginInitData) => void): void
1014
+ onThemeChange?(callback: (theme: 'light' | 'dark') => void): void
1015
+ }
1016
+
1017
+ declare global {
1018
+ interface Window {
1019
+ intools: IntoolsAPI
1020
+ }
1021
+ }
1022
+
1023
+ export {}
1024
+ `;
1025
+ fs.writeFileSync(path.join(targetDir, 'src/types/intools.d.ts'), typesDts);
1026
+ console.log(chalk_1.default.green(' ✓ src/types/intools.d.ts'));
1027
+ }
1028
+ // ============================================
1029
+ // 复制默认图标
1030
+ // ============================================
1031
+ function copyDefaultIcon(targetDir) {
1032
+ const defaultIconPath = path.join(getAssetsDir(), 'default-icon.png');
1033
+ const targetIconPath = path.join(targetDir, 'icon.png');
1034
+ if (fs.existsSync(defaultIconPath)) {
1035
+ fs.copyFileSync(defaultIconPath, targetIconPath);
1036
+ console.log(chalk_1.default.green(' ✓ icon.png'));
1037
+ }
1038
+ }