@talex-touch/utils 1.0.18 → 1.0.20

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 (80) hide show
  1. package/channel/index.ts +49 -1
  2. package/common/index.ts +2 -0
  3. package/common/search/gather.ts +45 -0
  4. package/common/search/index.ts +67 -0
  5. package/common/storage/constants.ts +16 -2
  6. package/common/storage/entity/index.ts +2 -1
  7. package/common/storage/entity/openers.ts +32 -0
  8. package/common/storage/entity/shortcut-settings.ts +22 -0
  9. package/common/storage/shortcut-storage.ts +58 -0
  10. package/common/utils/file.ts +62 -0
  11. package/common/{utils.ts → utils/index.ts} +14 -2
  12. package/common/utils/polling.ts +184 -0
  13. package/common/utils/task-queue.ts +108 -0
  14. package/common/utils/time.ts +374 -0
  15. package/core-box/README.md +8 -8
  16. package/core-box/builder/index.ts +6 -0
  17. package/core-box/builder/tuff-builder.example.ts.bak +258 -0
  18. package/core-box/builder/tuff-builder.ts +1162 -0
  19. package/core-box/index.ts +5 -2
  20. package/core-box/run-tests.sh +7 -0
  21. package/core-box/search.ts +1 -536
  22. package/core-box/tuff/index.ts +6 -0
  23. package/core-box/tuff/tuff-dsl.ts +1412 -0
  24. package/electron/clipboard-helper.ts +199 -0
  25. package/electron/env-tool.ts +36 -2
  26. package/electron/file-parsers/index.ts +8 -0
  27. package/electron/file-parsers/parsers/text-parser.ts +109 -0
  28. package/electron/file-parsers/registry.ts +92 -0
  29. package/electron/file-parsers/types.ts +58 -0
  30. package/electron/index.ts +3 -0
  31. package/eventbus/index.ts +0 -7
  32. package/index.ts +3 -1
  33. package/package.json +4 -29
  34. package/plugin/channel.ts +48 -16
  35. package/plugin/index.ts +194 -30
  36. package/plugin/log/types.ts +11 -0
  37. package/plugin/node/index.ts +4 -0
  38. package/plugin/node/logger-manager.ts +113 -0
  39. package/plugin/{log → node}/logger.ts +41 -7
  40. package/plugin/plugin-source.ts +74 -0
  41. package/plugin/preload.ts +5 -15
  42. package/plugin/providers/index.ts +2 -0
  43. package/plugin/providers/registry.ts +47 -0
  44. package/plugin/providers/types.ts +54 -0
  45. package/plugin/risk/index.ts +1 -0
  46. package/plugin/risk/types.ts +20 -0
  47. package/plugin/sdk/enum/bridge-event.ts +4 -0
  48. package/plugin/sdk/enum/index.ts +1 -0
  49. package/plugin/sdk/hooks/bridge.ts +68 -0
  50. package/plugin/sdk/hooks/index.ts +2 -1
  51. package/plugin/sdk/hooks/life-cycle.ts +2 -4
  52. package/plugin/sdk/index.ts +2 -0
  53. package/plugin/sdk/storage.ts +84 -0
  54. package/plugin/sdk/types.ts +2 -2
  55. package/plugin/sdk/window/index.ts +5 -3
  56. package/preload/index.ts +2 -0
  57. package/preload/loading.ts +15 -0
  58. package/preload/renderer.ts +41 -0
  59. package/renderer/hooks/arg-mapper.ts +79 -0
  60. package/renderer/hooks/index.ts +2 -0
  61. package/renderer/hooks/initialize.ts +198 -0
  62. package/renderer/index.ts +3 -0
  63. package/renderer/storage/app-settings.ts +2 -0
  64. package/renderer/storage/base-storage.ts +1 -0
  65. package/renderer/storage/openers.ts +11 -0
  66. package/renderer/touch-sdk/env.ts +106 -0
  67. package/renderer/touch-sdk/index.ts +108 -0
  68. package/renderer/touch-sdk/terminal.ts +85 -0
  69. package/renderer/touch-sdk/utils.ts +61 -0
  70. package/search/levenshtein-utils.ts +39 -0
  71. package/search/types.ts +16 -16
  72. package/types/index.ts +2 -1
  73. package/types/modules/base.ts +146 -0
  74. package/types/modules/index.ts +4 -0
  75. package/types/modules/module-lifecycle.ts +148 -0
  76. package/types/modules/module-manager.ts +99 -0
  77. package/types/modules/module.ts +112 -0
  78. package/types/touch-app-core.ts +16 -93
  79. package/core-box/types.ts +0 -384
  80. package/plugin/log/logger-manager.ts +0 -60
@@ -0,0 +1,1162 @@
1
+ /**
2
+ * TUFF Builder: Typed Unified Flex Format 构建工具
3
+ * 提供便捷的 TuffItem 创建和管理工具
4
+ *
5
+ * @description
6
+ * 这个模块提供了一套高效的工具,用于创建和管理 TuffItem 对象。
7
+ * 它结合了 Builder 模式和工厂方法,既保证了 API 的流畅性,又确保了性能。
8
+ *
9
+ * @design 设计理念:
10
+ * - 流畅的 API:支持链式调用,简化创建过程
11
+ * - 性能优化:最小化中间对象创建,适合大量对象场景
12
+ * - 类型安全:完整的 TypeScript 类型支持
13
+ * - 便捷工厂:针对常见场景提供快捷创建方法
14
+ *
15
+ * @version 2.0.0
16
+ * @module core-box/tuff-builder
17
+ */
18
+
19
+ import type {
20
+ TuffItem,
21
+ TuffSource,
22
+ TuffSourceType,
23
+ TuffItemKind,
24
+ TuffRender,
25
+ TuffRenderMode,
26
+ TuffBasicRender,
27
+ TuffCustomRender,
28
+ TuffIcon,
29
+ TuffTag,
30
+ TuffLayout,
31
+ TuffPreview,
32
+ TuffAction,
33
+ TuffActionType,
34
+ TuffScoring,
35
+ TuffContext,
36
+ TuffMeta,
37
+ TuffPermissionLevel,
38
+ TuffSearchResult,
39
+ TuffQuery,
40
+ IProviderActivate
41
+ } from '../tuff/tuff-dsl'
42
+
43
+ // ==================== Builder 类 ====================
44
+
45
+ /**
46
+ * TuffItemBuilder - TuffItem 构建器
47
+ *
48
+ * @description
49
+ * 使用 Builder 模式实现的 TuffItem 构建器,支持链式调用。
50
+ * 提供流畅的 API 用于创建和配置 TuffItem 对象。
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const item = new TuffItemBuilder()
55
+ * .setSource('plugin', 'my-plugin')
56
+ * .setTitle('我的项目')
57
+ * .setIcon('🚀')
58
+ * .createAndAddAction('open', 'open', '打开')
59
+ * .build();
60
+ * ```
61
+ */
62
+ class TuffItemBuilder {
63
+ private item: Partial<TuffItem> = {};
64
+ private basicRender: Partial<TuffBasicRender> = {};
65
+ private customRender: Partial<TuffCustomRender> | null = null;
66
+ private renderMode: TuffRenderMode = 'default';
67
+ private layout: Partial<TuffLayout> | null = null;
68
+ private preview: Partial<TuffPreview> | null = null;
69
+ private renderStyle: Record<string, string> | null = null;
70
+ private renderClassName: string | null = null;
71
+ private scoring: Partial<TuffScoring> = {};
72
+
73
+ /**
74
+ * 创建一个新的 TuffItemBuilder 实例
75
+ *
76
+ * @param id - 项目 ID
77
+ * @param sourceType - 可选的来源类型
78
+ * @param sourceId - 可选的来源 ID
79
+ */
80
+ constructor(id: string, sourceType?: TuffSourceType, sourceId?: string) {
81
+ this.item.id = id
82
+ if (sourceType && sourceId) {
83
+ this.item.source = {
84
+ type: sourceType,
85
+ id: sourceId
86
+ };
87
+ }
88
+ }
89
+
90
+ /**
91
+ * 设置项目 ID
92
+ *
93
+ * @param id - 项目唯一标识符
94
+ * @returns 当前构建器实例,用于链式调用
95
+ */
96
+ setId(id: string): TuffItemBuilder {
97
+ this.item.id = id;
98
+ return this;
99
+ }
100
+
101
+ /**
102
+ * 设置数据来源
103
+ *
104
+ * @param type - 来源类型
105
+ * @param id - 来源标识符
106
+ * @param name - 可选的来源名称
107
+ * @param version - 可选的来源版本
108
+ * @param permission - 可选的权限级别
109
+ * @returns 当前构建器实例,用于链式调用
110
+ */
111
+ setSource(
112
+ type: TuffSourceType,
113
+ id: string,
114
+ name?: string,
115
+ version?: string,
116
+ permission?: TuffPermissionLevel
117
+ ): TuffItemBuilder {
118
+ this.item.source = { type, id };
119
+
120
+ if (name) this.item.source.name = name;
121
+ if (version) this.item.source.version = version;
122
+ if (permission) this.item.source.permission = permission;
123
+
124
+ return this;
125
+ }
126
+
127
+ /**
128
+ * 设置项目类型
129
+ *
130
+ * @param kind - 项目类型
131
+ * @returns 当前构建器实例,用于链式调用
132
+ */
133
+ setKind(kind: TuffItemKind): TuffItemBuilder {
134
+ this.item.kind = kind;
135
+ return this;
136
+ }
137
+
138
+ /**
139
+ * 设置项目标题
140
+ *
141
+ * @param title - 项目标题
142
+ * @returns 当前构建器实例,用于链式调用
143
+ */
144
+ setTitle(title: string): TuffItemBuilder {
145
+ this.basicRender.title = title;
146
+ return this;
147
+ }
148
+
149
+ /**
150
+ * 设置项目副标题
151
+ *
152
+ * @param subtitle - 项目副标题
153
+ * @returns 当前构建器实例,用于链式调用
154
+ */
155
+ setSubtitle(subtitle: string): this {
156
+ this.basicRender.subtitle = subtitle;
157
+ return this;
158
+ }
159
+
160
+ /**
161
+ * 设置项目描述
162
+ *
163
+ * @param description - 项目详细描述
164
+ * @returns 当前构建器实例,用于链式调用
165
+ */
166
+ setDescription(description: string): this {
167
+ this.basicRender.description = description;
168
+ return this;
169
+ }
170
+
171
+ /**
172
+ * 设置项目图标
173
+ *
174
+ * @param icon - 项目图标
175
+ * @returns 当前构建器实例,用于链式调用
176
+ */
177
+ setIcon(icon: TuffIcon): this {
178
+ this.basicRender.icon = icon;
179
+ return this;
180
+ }
181
+
182
+ /**
183
+ * 添加标签
184
+ *
185
+ * @param tag - 要添加的标签
186
+ * @returns 当前构建器实例,用于链式调用
187
+ */
188
+ addTag(tag: TuffTag): this {
189
+ if (!this.basicRender.tags) {
190
+ this.basicRender.tags = [];
191
+ }
192
+ this.basicRender.tags.push(tag);
193
+ return this;
194
+ }
195
+
196
+ /**
197
+ * 设置标签列表
198
+ *
199
+ * @param tags - 标签列表
200
+ * @returns 当前构建器实例,用于链式调用
201
+ */
202
+ setTags(tags: TuffTag[]): this {
203
+ this.basicRender.tags = tags;
204
+ return this;
205
+ }
206
+
207
+ /**
208
+ * 设置右侧附加信息
209
+ *
210
+ * @param accessory - 右侧附加信息
211
+ * @returns 当前构建器实例,用于链式调用
212
+ */
213
+ setAccessory(accessory: string): this {
214
+ this.basicRender.accessory = accessory;
215
+ return this;
216
+ }
217
+
218
+ /**
219
+ * 设置渲染模式
220
+ *
221
+ * @param mode - 渲染模式
222
+ * @returns 当前构建器实例,用于链式调用
223
+ */
224
+ setRenderMode(mode: TuffRenderMode): this {
225
+ this.renderMode = mode;
226
+ return this;
227
+ }
228
+
229
+ /**
230
+ * 设置自定义渲染内容
231
+ *
232
+ * @param type - 渲染类型
233
+ * @param content - 渲染内容
234
+ * @param data - 可选的渲染数据
235
+ * @param styles - 可选的样式资源
236
+ * @param scripts - 可选的脚本资源
237
+ * @returns 当前构建器实例,用于链式调用
238
+ */
239
+ setCustomRender(
240
+ type: 'html' | 'vue' | 'react' | 'markdown',
241
+ content: string,
242
+ data?: Record<string, any>,
243
+ styles?: string[],
244
+ scripts?: string[]
245
+ ): this {
246
+ this.renderMode = 'custom';
247
+ this.customRender = { type, content };
248
+
249
+ if (data) this.customRender.data = data;
250
+ if (styles) this.customRender.styles = styles;
251
+ if (scripts) this.customRender.scripts = scripts;
252
+
253
+ return this;
254
+ }
255
+
256
+ /**
257
+ * 设置布局配置
258
+ *
259
+ * @param display - 展示方式
260
+ * @param size - 可选的尺寸配置
261
+ * @param align - 可选的对齐方式
262
+ * @returns 当前构建器实例,用于链式调用
263
+ */
264
+ setLayout(
265
+ display: 'list' | 'card' | 'grid' | 'compact' | 'detailed',
266
+ size?: 'small' | 'medium' | 'large',
267
+ align?: 'left' | 'center' | 'right'
268
+ ): this {
269
+ this.layout = { display };
270
+
271
+ if (size) this.layout.size = size;
272
+ if (align) this.layout.align = align;
273
+
274
+ return this;
275
+ }
276
+
277
+ /**
278
+ * 设置网格布局配置
279
+ *
280
+ * @param columns - 列数
281
+ * @param gap - 间距
282
+ * @returns 当前构建器实例,用于链式调用
283
+ */
284
+ setGridLayout(columns: number, gap?: number): this {
285
+ if (!this.layout) {
286
+ this.layout = { display: 'grid' };
287
+ } else {
288
+ this.layout.display = 'grid';
289
+ }
290
+
291
+ if (!this.layout.grid) {
292
+ this.layout.grid = {};
293
+ }
294
+
295
+ this.layout.grid.columns = columns;
296
+ if (gap !== undefined) this.layout.grid.gap = gap;
297
+
298
+ return this;
299
+ }
300
+
301
+ /**
302
+ * 设置预览配置
303
+ *
304
+ * @param type - 预览类型
305
+ * @param title - 可选的预览标题
306
+ * @param content - 可选的预览内容
307
+ * @param image - 可选的预览图片
308
+ * @param lazy - 可选的懒加载配置
309
+ * @returns 当前构建器实例,用于链式调用
310
+ */
311
+ setPreview(
312
+ type: 'tooltip' | 'panel' | 'modal',
313
+ title?: string,
314
+ content?: string,
315
+ image?: string,
316
+ lazy?: boolean
317
+ ): this {
318
+ this.preview = { type };
319
+
320
+ if (title) this.preview.title = title;
321
+ if (content) this.preview.content = content;
322
+ if (image) this.preview.image = image;
323
+ if (lazy !== undefined) this.preview.lazy = lazy;
324
+
325
+ return this;
326
+ }
327
+
328
+ /**
329
+ * 设置自定义预览组件
330
+ *
331
+ * @param customRender - 自定义预览渲染配置
332
+ * @returns 当前构建器实例,用于链式调用
333
+ */
334
+ setPreviewComponent(customRender: TuffCustomRender): this {
335
+ if (!this.preview) {
336
+ this.preview = { type: 'panel' };
337
+ }
338
+
339
+ this.preview.component = customRender;
340
+ return this;
341
+ }
342
+
343
+ /**
344
+ * 设置渲染样式类名
345
+ *
346
+ * @param className - CSS 类名
347
+ * @returns 当前构建器实例,用于链式调用
348
+ */
349
+ setClassName(className: string): this {
350
+ this.renderClassName = className;
351
+ return this;
352
+ }
353
+
354
+ /**
355
+ * 设置渲染内联样式
356
+ *
357
+ * @param style - 内联样式对象
358
+ * @returns 当前构建器实例,用于链式调用
359
+ */
360
+ setStyle(style: Record<string, string>): this {
361
+ this.renderStyle = style;
362
+ return this;
363
+ }
364
+
365
+ /**
366
+ * 添加行为
367
+ *
368
+ * @param action - 要添加的行为
369
+ * @returns 当前构建器实例,用于链式调用
370
+ */
371
+ addAction(action: TuffAction): this {
372
+ if (!this.item.actions) {
373
+ this.item.actions = [];
374
+ }
375
+ this.item.actions.push(action);
376
+ return this;
377
+ }
378
+
379
+ /**
380
+ * 设置行为列表
381
+ *
382
+ * @param actions - 行为列表
383
+ * @returns 当前构建器实例,用于链式调用
384
+ */
385
+ setActions(actions: TuffAction[]): this {
386
+ this.item.actions = actions;
387
+ return this;
388
+ }
389
+
390
+ /**
391
+ * 创建并添加一个行为
392
+ *
393
+ * @description 便捷方法,用于快速创建并添加一个行为。
394
+ * 如果这是第一个被添加的行为,它将被自动设为主要行为 (primary: true)。
395
+ *
396
+ * @param id - 行为 ID
397
+ * @param type - 行为类型
398
+ * @param label - 行为标签
399
+ * @param payload - 可选的行为参数
400
+ * @returns 当前构建器实例,用于链式调用
401
+ */
402
+ createAndAddAction(
403
+ id: string,
404
+ type: TuffActionType,
405
+ label: string,
406
+ payload?: any
407
+ ): this {
408
+ const isFirstAction = !this.item.actions || this.item.actions.length === 0;
409
+
410
+ const action = TuffUtils.createAction(
411
+ id,
412
+ type,
413
+ label,
414
+ isFirstAction, // Set primary to true if it's the first action
415
+ payload
416
+ );
417
+
418
+ return this.addAction(action);
419
+ }
420
+
421
+ /**
422
+ * 设置评分信息
423
+ *
424
+ * @param scoring - 评分信息
425
+ * @returns 当前构建器实例,用于链式调用
426
+ */
427
+ setScoring(scoring: TuffScoring): this {
428
+ this.item.scoring = scoring;
429
+ return this;
430
+ }
431
+
432
+ /**
433
+ * 设置项目的最终分数
434
+ *
435
+ * @param score - 最终分数值 (0-1之间)
436
+ * @returns 当前构建器实例,用于链式调用
437
+ */
438
+ setFinalScore(score: number): this {
439
+ if (score < 0 || score > 1) {
440
+ throw new Error('Score must be between 0 and 1');
441
+ }
442
+ this.scoring.final = score;
443
+ return this;
444
+ }
445
+
446
+ /**
447
+ * 设置上下文信息
448
+ *
449
+ * @param context - 上下文信息
450
+ * @returns 当前构建器实例,用于链式调用
451
+ */
452
+ setContext(context: TuffContext): this {
453
+ this.item.context = context;
454
+ return this;
455
+ }
456
+
457
+ /**
458
+ * 设置元数据
459
+ *
460
+ * @param meta - 元数据
461
+ * @returns 当前构建器实例,用于链式调用
462
+ */
463
+ setMeta(meta: TuffMeta): this {
464
+ this.item.meta = { ...this.item.meta, ...meta };
465
+ return this;
466
+ }
467
+
468
+ /**
469
+ * 构建 TuffItem 对象
470
+ *
471
+ * @returns 构建好的 TuffItem 对象
472
+ * @throws 如果缺少必要的属性(id, source 或 render.basic.title)
473
+ */
474
+ build(): TuffItem {
475
+ // 检查并自动生成 ID
476
+ if (!this.item.id) {
477
+ // this.item.id = TuffUtils.generateId();
478
+ throw new Error('TuffItem 必须设置 id 属性');
479
+ }
480
+
481
+ // 检查必要属性
482
+ if (!this.item.source) {
483
+ throw new Error('TuffItem 必须设置 source 属性');
484
+ }
485
+
486
+ // 构建渲染配置
487
+ const render: TuffRender = {
488
+ mode: this.renderMode
489
+ };
490
+
491
+ // 根据渲染模式设置相应的渲染配置
492
+ if (this.renderMode === 'default' || this.renderMode === 'rich' || this.renderMode === 'card') {
493
+ if (!this.basicRender.title) {
494
+ throw new Error('默认渲染模式下 TuffItem 必须设置 title 属性');
495
+ }
496
+ render.basic = this.basicRender as TuffBasicRender;
497
+ } else if (this.renderMode === 'custom') {
498
+ if (!this.customRender) {
499
+ throw new Error('自定义渲染模式下必须设置 customRender 属性');
500
+ }
501
+ render.custom = this.customRender as TuffCustomRender;
502
+ }
503
+
504
+ // 设置其他渲染属性
505
+ if (this.layout) render.layout = this.layout as TuffLayout;
506
+ if (this.preview) render.preview = this.preview as TuffPreview;
507
+ if (this.renderClassName) render.className = this.renderClassName;
508
+ if (this.renderStyle) render.style = this.renderStyle;
509
+
510
+ // 设置渲染配置
511
+ this.item.render = render;
512
+
513
+ // 设置评分信息
514
+ if (Object.keys(this.scoring).length > 0) {
515
+ this.item.scoring = this.scoring as TuffScoring;
516
+ }
517
+
518
+ // 返回完整的 TuffItem
519
+ return this.item as TuffItem;
520
+ }
521
+ }
522
+
523
+ // ==================== Fluent Builder ====================
524
+
525
+ /**
526
+ * A fluent builder for creating TuffSearchResult objects.
527
+ */
528
+ class TuffSearchResultBuilder {
529
+ private readonly result: TuffSearchResult;
530
+
531
+ constructor(query: TuffQuery) {
532
+ this.result = {
533
+ query,
534
+ items: [],
535
+ duration: 0,
536
+ sources: [],
537
+ activate: []
538
+ };
539
+ }
540
+
541
+ public setItems(items: TuffItem[]): this {
542
+ this.result.items = items;
543
+ return this;
544
+ }
545
+
546
+ public setDuration(duration: number): this {
547
+ this.result.duration = duration;
548
+ return this;
549
+ }
550
+
551
+ public setActivate(activate: IProviderActivate[]): this {
552
+ this.result.activate = activate;
553
+ return this;
554
+ }
555
+
556
+ public setSources(sources: TuffSearchResult['sources']): this {
557
+ this.result.sources = sources;
558
+ return this;
559
+ }
560
+
561
+ public setSortStats(stats: any[]): this {
562
+ // @ts-ignore
563
+ this.result.sort_stats = stats;
564
+ return this;
565
+ }
566
+
567
+ public build(): TuffSearchResult {
568
+ return this.result;
569
+ }
570
+ }
571
+
572
+
573
+ // ==================== 工厂方法 ====================
574
+
575
+ /**
576
+ * Factory for creating TUFF objects using a fluent builder pattern.
577
+ */
578
+ class TuffFactory {
579
+ /**
580
+ * 创建基本项目
581
+ *
582
+ * @param title - 项目标题
583
+ * @param sourceType - 来源类型
584
+ * @param sourceId - 来源标识符
585
+ * @param kind - 可选的项目类型
586
+ * @returns 创建的 TuffItem 对象
587
+ */
588
+ static createBasicItem(
589
+ title: string,
590
+ sourceType: TuffSourceType,
591
+ sourceId: string,
592
+ kind?: TuffItemKind
593
+ ): TuffItem {
594
+ const builder = new TuffItemBuilder(TuffUtils.generateId())
595
+ .setSource(sourceType, sourceId)
596
+ .setTitle(title)
597
+
598
+ if (kind) builder.setKind(kind);
599
+
600
+ return builder.build();
601
+ }
602
+
603
+ /**
604
+ * 创建系统项目
605
+ *
606
+ * @param title - 项目标题
607
+ * @param id - 系统项目标识符
608
+ * @param kind - 可选的项目类型
609
+ * @returns 创建的 TuffItem 对象
610
+ */
611
+ static createSystemItem(
612
+ title: string,
613
+ id: string,
614
+ kind?: TuffItemKind
615
+ ): TuffItem {
616
+ return TuffFactory.createBasicItem(title, 'system', id, kind);
617
+ }
618
+
619
+ /**
620
+ * 创建插件项目
621
+ *
622
+ * @param title - 项目标题
623
+ * @param pluginId - 插件标识符
624
+ * @param kind - 可选的项目类型
625
+ * @returns 创建的 TuffItem 对象
626
+ */
627
+ static createPluginItem(
628
+ title: string,
629
+ pluginId: string,
630
+ kind?: TuffItemKind
631
+ ): TuffItem {
632
+ return TuffFactory.createBasicItem(title, 'plugin', pluginId, kind);
633
+ }
634
+
635
+ /**
636
+ * 创建 AI 推荐项目
637
+ *
638
+ * @param title - 项目标题
639
+ * @param aiSourceId - AI 来源标识符
640
+ * @param kind - 可选的项目类型
641
+ * @returns 创建的 TuffItem 对象
642
+ */
643
+ static createAIItem(
644
+ title: string,
645
+ aiSourceId: string,
646
+ kind?: TuffItemKind
647
+ ): TuffItem {
648
+ return TuffFactory.createBasicItem(title, 'ai', aiSourceId, kind);
649
+ }
650
+
651
+ /**
652
+ * 创建文件项目
653
+ *
654
+ * @param title - 文件名称
655
+ * @param path - 文件路径
656
+ * @param sourceType - 来源类型
657
+ * @param sourceId - 来源标识符
658
+ * @returns 创建的 TuffItem 对象
659
+ */
660
+ static createFileItem(
661
+ title: string,
662
+ path: string,
663
+ sourceType: TuffSourceType,
664
+ sourceId: string
665
+ ): TuffItem {
666
+ return new TuffItemBuilder(TuffUtils.generateId())
667
+ .setSource(sourceType, sourceId)
668
+ .setTitle(title)
669
+ .setKind('file')
670
+ .setMeta({
671
+ file: {
672
+ path: path
673
+ }
674
+ })
675
+ .createAndAddAction('open', 'open', '打开')
676
+ .build();
677
+ }
678
+
679
+ /**
680
+ * 创建文件夹项目
681
+ *
682
+ * @param title - 文件夹名称
683
+ * @param path - 文件夹路径
684
+ * @param sourceType - 来源类型
685
+ * @param sourceId - 来源标识符
686
+ * @returns 创建的 TuffItem 对象
687
+ */
688
+ static createFolderItem(
689
+ title: string,
690
+ path: string,
691
+ sourceType: TuffSourceType,
692
+ sourceId: string
693
+ ): TuffItem {
694
+ return new TuffItemBuilder(TuffUtils.generateId())
695
+ .setSource(sourceType, sourceId)
696
+ .setTitle(title)
697
+ .setKind('folder')
698
+ .setMeta({
699
+ file: {
700
+ path: path
701
+ }
702
+ })
703
+ .createAndAddAction('open', 'open', '打开')
704
+ .build();
705
+ }
706
+
707
+ /**
708
+ * 创建链接项目
709
+ *
710
+ * @param title - 链接标题
711
+ * @param url - 链接地址
712
+ * @param sourceType - 来源类型
713
+ * @param sourceId - 来源标识符
714
+ * @returns 创建的 TuffItem 对象
715
+ */
716
+ static createUrlItem(
717
+ title: string,
718
+ url: string,
719
+ sourceType: TuffSourceType,
720
+ sourceId: string
721
+ ): TuffItem {
722
+ return new TuffItemBuilder(TuffUtils.generateId())
723
+ .setSource(sourceType, sourceId)
724
+ .setTitle(title)
725
+ .setKind('url')
726
+ .setMeta({
727
+ web: {
728
+ url: url
729
+ }
730
+ })
731
+ .createAndAddAction('open', 'open', '打开')
732
+ .build();
733
+ }
734
+
735
+ /**
736
+ * 创建应用项目
737
+ *
738
+ * @param title - 应用名称
739
+ * @param path - 应用路径
740
+ * @param bundleId - 应用包标识符
741
+ * @param sourceType - 来源类型
742
+ * @param sourceId - 来源标识符
743
+ * @returns 创建的 TuffItem 对象
744
+ */
745
+ static createAppItem(
746
+ title: string,
747
+ path: string,
748
+ bundleId: string,
749
+ sourceType: TuffSourceType,
750
+ sourceId: string
751
+ ): TuffItem {
752
+ return new TuffItemBuilder(TuffUtils.generateId())
753
+ .setSource(sourceType, sourceId)
754
+ .setTitle(title)
755
+ .setKind('app')
756
+ .setMeta({
757
+ app: {
758
+ path: path,
759
+ bundle_id: bundleId
760
+ }
761
+ })
762
+ .createAndAddAction('execute', 'execute', '启动')
763
+ .build();
764
+ }
765
+
766
+ /**
767
+ * 创建命令项目
768
+ *
769
+ * @param title - 命令标题
770
+ * @param command - 命令内容
771
+ * @param sourceType - 来源类型
772
+ * @param sourceId - 来源标识符
773
+ * @returns 创建的 TuffItem 对象
774
+ */
775
+ static createCommandItem(
776
+ title: string,
777
+ command: string,
778
+ sourceType: TuffSourceType,
779
+ sourceId: string
780
+ ): TuffItem {
781
+ return new TuffItemBuilder(TuffUtils.generateId())
782
+ .setSource(sourceType, sourceId)
783
+ .setTitle(title)
784
+ .setKind('command')
785
+ .createAndAddAction('execute', 'execute', '执行', { command })
786
+ .build();
787
+ }
788
+
789
+ /**
790
+ * 创建操作项目
791
+ *
792
+ * @param title - 操作标题
793
+ * @param action - 操作对象
794
+ * @param sourceType - 来源类型
795
+ * @param sourceId - 来源标识符
796
+ * @returns 创建的 TuffItem 对象
797
+ */
798
+ static createActionItem(
799
+ title: string,
800
+ action: TuffAction,
801
+ sourceType: TuffSourceType,
802
+ sourceId: string
803
+ ): TuffItem {
804
+ return new TuffItemBuilder(TuffUtils.generateId())
805
+ .setSource(sourceType, sourceId)
806
+ .setTitle(title)
807
+ .setKind('action')
808
+ .addAction(action)
809
+ .build();
810
+ }
811
+
812
+ public static createSearchResult(query: TuffQuery): TuffSearchResultBuilder {
813
+ return new TuffSearchResultBuilder(query);
814
+ }
815
+ }
816
+
817
+ // ==================== 批量创建工具 ====================
818
+
819
+ /**
820
+ * TuffListBuilder - TuffItem 列表构建器
821
+ *
822
+ * @description
823
+ * 用于高效地批量创建 TuffItem 对象,适用于需要创建大量相似项目的场景。
824
+ * 通过共享配置和独立的自定义函数,简化了批量创建的过程。
825
+ *
826
+ * @example
827
+ * ```typescript
828
+ * const items = new TuffListBuilder('plugin', 'my-plugin')
829
+ * .setSharedKind('file')
830
+ * .addSharedAction({ id: 'open', type: 'open', label: '打开' })
831
+ * .addItem(builder => {
832
+ * builder.setTitle('文件 A').setMeta({ file: { path: '/path/a' } });
833
+ * })
834
+ * .addItemsFromData([{ name: '文件 B', path: '/path/b' }], (builder, data) => {
835
+ * builder.setTitle(data.name).setMeta({ file: { path: data.path } });
836
+ * })
837
+ * .build();
838
+ * ```
839
+ */
840
+ class TuffListBuilder {
841
+ private items: TuffItem[] = [];
842
+ private sharedSource: TuffSource;
843
+ private sharedKind?: TuffItemKind;
844
+ private sharedActions: TuffAction[] = [];
845
+
846
+ /**
847
+ * 创建一个新的列表构建器
848
+ *
849
+ * @param sourceType - 共享的来源类型
850
+ * @param sourceId - 共享的来源标识符
851
+ */
852
+ constructor(sourceType: TuffSourceType, sourceId: string) {
853
+ this.sharedSource = { type: sourceType, id: sourceId };
854
+ }
855
+
856
+ /**
857
+ * 设置共享的项目类型
858
+ *
859
+ * @param kind - 项目类型
860
+ * @returns 当前列表构建器实例,用于链式调用
861
+ */
862
+ setSharedKind(kind: TuffItemKind): TuffListBuilder {
863
+ this.sharedKind = kind;
864
+ return this;
865
+ }
866
+
867
+ /**
868
+ * 添加共享的行为
869
+ *
870
+ * @param action - 要添加的共享行为
871
+ * @returns 当前列表构建器实例,用于链式调用
872
+ */
873
+ addSharedAction(action: TuffAction): TuffListBuilder {
874
+ this.sharedActions.push(action);
875
+ return this;
876
+ }
877
+
878
+ /**
879
+ * 添加一个项目
880
+ *
881
+ * @param customize - 一个函数,接收 TuffItemBuilder 实例用于配置单个项目
882
+ * @returns 当前列表构建器实例,用于链式调用
883
+ */
884
+ addItem(customize: (builder: TuffItemBuilder) => void): TuffListBuilder {
885
+ const builder = new TuffItemBuilder(TuffUtils.generateId())
886
+ .setSource(this.sharedSource.type, this.sharedSource.id)
887
+
888
+ // 应用共享配置
889
+ if (this.sharedKind) {
890
+ builder.setKind(this.sharedKind);
891
+ }
892
+ if (this.sharedActions.length > 0) {
893
+ // 克隆共享操作以避免交叉污染
894
+ builder.setActions(JSON.parse(JSON.stringify(this.sharedActions)));
895
+ }
896
+
897
+ // 应用自定义配置
898
+ customize(builder);
899
+
900
+ try {
901
+ this.items.push(builder.build());
902
+ } catch (error) {
903
+ console.error('构建 TuffItem 失败:', error);
904
+ }
905
+
906
+ return this;
907
+ }
908
+
909
+ /**
910
+ * 从数据对象数组批量创建项目
911
+ *
912
+ * @param dataItems - 数据对象数组
913
+ * @param customize - 一个函数,接收 TuffItemBuilder 实例和当前数据对象用于配置项目
914
+ * @returns 当前列表构建器实例,用于链式调用
915
+ */
916
+ addItemsFromData<T>(
917
+ dataItems: T[],
918
+ customize: (builder: TuffItemBuilder, dataItem: T) => void
919
+ ): TuffListBuilder {
920
+ for (const dataItem of dataItems) {
921
+ this.addItem(builder => customize(builder, dataItem));
922
+ }
923
+ return this;
924
+ }
925
+
926
+ /**
927
+ * 构建并返回所有创建的项目
928
+ *
929
+ * @returns 创建的 TuffItem 对象数组
930
+ */
931
+ build(): TuffItem[] {
932
+ return this.items;
933
+ }
934
+
935
+ /**
936
+ * 清空已创建的项目列表
937
+ *
938
+ * @returns 当前列表构建器实例,用于链式调用
939
+ */
940
+ clear(): TuffListBuilder {
941
+ this.items = [];
942
+ return this;
943
+ }
944
+ }
945
+
946
+ // ==================== 工具函数 ====================
947
+
948
+ /**
949
+ * TuffUtils - 实用工具函数集合
950
+ *
951
+ * @description
952
+ * 提供一系列实用函数,用于处理和转换 TuffItem 对象。
953
+ */
954
+ class TuffUtils {
955
+ /**
956
+ * 生成一个全局唯一的 ID
957
+ *
958
+ * @description
959
+ * 结合了时间戳和随机数,确保在高并发场景下也能保持唯一性。
960
+ * 格式: tuff_[timestamp]_[random1]_[random2]
961
+ *
962
+ * @returns {string} 生成的唯一 ID
963
+ */
964
+ static generateId(): string {
965
+ return `tuff_${Date.now()}_${Math.random().toString(36).substring(2, 9)}_${Math.random().toString(36).substring(2, 5)}`;
966
+ }
967
+
968
+ /**
969
+ * 创建图标对象
970
+ *
971
+ * @param value - 图标值 (emoji, URL, base64, etc.)
972
+ * @param type - 图标类型
973
+ * @returns {TuffIcon} 创建的图标对象
974
+ */
975
+ static createIcon(value: string, type: 'emoji' | 'url' | 'base64' | 'component' = 'emoji'): TuffIcon {
976
+ return {
977
+ type,
978
+ value
979
+ };
980
+ }
981
+
982
+ /**
983
+ * 创建标签对象
984
+ *
985
+ * @param text - 标签文本
986
+ * @param color - 可选的标签颜色
987
+ * @param variant - 可选的标签样式
988
+ * @returns 创建的标签对象
989
+ */
990
+ static createTag(text: string, color?: string, variant?: 'filled' | 'outlined' | 'ghost'): TuffTag {
991
+ const tag: TuffTag = { text };
992
+ if (color) tag.color = color;
993
+ if (variant) tag.variant = variant;
994
+ return tag;
995
+ }
996
+
997
+ /**
998
+ * 创建简单的行为对象
999
+ *
1000
+ * @param id - 行为 ID
1001
+ * @param type - 行为类型
1002
+ * @param label - 行为标签
1003
+ * @param primary - 是否为主要行为
1004
+ * @param payload - 可选的行为参数
1005
+ * @returns 创建的行为对象
1006
+ */
1007
+ static createAction(
1008
+ id: string,
1009
+ type: TuffActionType,
1010
+ label: string,
1011
+ primary: boolean = false,
1012
+ payload?: any
1013
+ ): TuffAction {
1014
+ const action: TuffAction = { id, type, label, primary };
1015
+ if (payload) action.payload = payload;
1016
+ return action;
1017
+ }
1018
+
1019
+ /**
1020
+ * 过滤项目列表
1021
+ *
1022
+ * @param items - 项目列表
1023
+ * @param predicate - 过滤函数
1024
+ * @returns 过滤后的项目列表
1025
+ */
1026
+ static filterItems(items: TuffItem[], predicate: (item: TuffItem) => boolean): TuffItem[] {
1027
+ return items.filter(predicate);
1028
+ }
1029
+
1030
+ /**
1031
+ * 按类型过滤项目
1032
+ *
1033
+ * @param items - 项目列表
1034
+ * @param kind - 项目类型
1035
+ * @returns 过滤后的项目列表
1036
+ */
1037
+ static filterByKind(items: TuffItem[], kind: TuffItemKind): TuffItem[] {
1038
+ return TuffUtils.filterItems(items, item => item.kind === kind);
1039
+ }
1040
+
1041
+ /**
1042
+ * 按来源过滤项目
1043
+ *
1044
+ * @param items - 项目列表
1045
+ * @param sourceType - 来源类型
1046
+ * @returns 过滤后的项目列表
1047
+ */
1048
+ static filterBySourceType(items: TuffItem[], sourceType: TuffSourceType): TuffItem[] {
1049
+ return TuffUtils.filterItems(items, item => item.source.type === sourceType);
1050
+ }
1051
+
1052
+ /**
1053
+ * 按标题搜索项目
1054
+ *
1055
+ * @param items - 项目列表
1056
+ * @param query - 搜索关键词
1057
+ * @param caseSensitive - 是否区分大小写
1058
+ * @returns 匹配的项目列表
1059
+ */
1060
+ static searchByTitle(items: TuffItem[], query: string, caseSensitive: boolean = false): TuffItem[] {
1061
+ const normalizedQuery = caseSensitive ? query : query.toLowerCase();
1062
+ return TuffUtils.filterItems(items, item => {
1063
+ const title = item.render.basic?.title;
1064
+ if (!title) return false;
1065
+ const normalizedTitle = caseSensitive ? title : title.toLowerCase();
1066
+ return normalizedTitle.includes(normalizedQuery);
1067
+ });
1068
+ }
1069
+
1070
+ /**
1071
+ * 按评分排序项目
1072
+ *
1073
+ * @param items - 项目列表
1074
+ * @param ascending - 是否升序排列
1075
+ * @returns 排序后的项目列表
1076
+ */
1077
+ static sortByScore(items: TuffItem[], ascending: boolean = false): TuffItem[] {
1078
+ return [...items].sort((a, b) => {
1079
+ const scoreA = a.scoring?.final ?? a.scoring?.base ?? 0;
1080
+ const scoreB = b.scoring?.final ?? b.scoring?.base ?? 0;
1081
+ return ascending ? scoreA - scoreB : scoreB - scoreA;
1082
+ });
1083
+ }
1084
+
1085
+ /**
1086
+ * 按标题排序项目
1087
+ *
1088
+ * @param items - 项目列表
1089
+ * @param ascending - 是否升序排列
1090
+ * @returns 排序后的项目列表
1091
+ */
1092
+ static sortByTitle(items: TuffItem[], ascending: boolean = true): TuffItem[] {
1093
+ return [...items].sort((a, b) => {
1094
+ const titleA = a.render.basic?.title ?? '';
1095
+ const titleB = b.render.basic?.title ?? '';
1096
+ return ascending
1097
+ ? titleA.localeCompare(titleB)
1098
+ : titleB.localeCompare(titleA);
1099
+ });
1100
+ }
1101
+
1102
+ /**
1103
+ * 将普通对象转换为 TuffItem
1104
+ *
1105
+ * @param obj - 普通对象
1106
+ * @param sourceType - 来源类型
1107
+ * @param sourceId - 来源标识符
1108
+ * @returns 转换后的 TuffItem 对象
1109
+ */
1110
+ static fromObject(obj: any, sourceType: TuffSourceType, sourceId: string): TuffItem {
1111
+ const builder = new TuffItemBuilder(TuffUtils.generateId())
1112
+ .setSource(sourceType, sourceId)
1113
+
1114
+ // 尝试提取标题
1115
+ if (obj.title || obj.name || obj.label) {
1116
+ builder.setTitle(obj.title || obj.name || obj.label);
1117
+ } else {
1118
+ builder.setTitle(String(obj));
1119
+ }
1120
+
1121
+ // 尝试提取描述
1122
+ if (obj.description || obj.desc) {
1123
+ builder.setDescription(obj.description || obj.desc);
1124
+ }
1125
+
1126
+ // 尝试提取图标
1127
+ if (obj.icon) {
1128
+ builder.setIcon(obj.icon);
1129
+ }
1130
+
1131
+ // 尝试提取类型
1132
+ if (obj.kind || obj.type) {
1133
+ const kind = obj.kind || obj.type;
1134
+ builder.setKind(kind as TuffItemKind);
1135
+ }
1136
+
1137
+ // 尝试提取分数
1138
+ if (obj.score !== undefined) {
1139
+ builder.setFinalScore(obj.score);
1140
+ }
1141
+
1142
+ // 保存原始数据
1143
+ builder.setMeta({ raw: obj });
1144
+
1145
+ return builder.build();
1146
+ }
1147
+
1148
+ /**
1149
+ * 从对象数组批量创建 TuffItem
1150
+ *
1151
+ * @param objects - 对象数组
1152
+ * @param sourceType - 来源类型
1153
+ * @param sourceId - 来源标识符
1154
+ * @returns 创建的 TuffItem 对象数组
1155
+ */
1156
+ static fromObjects(objects: any[], sourceType: TuffSourceType, sourceId: string): TuffItem[] {
1157
+ return objects.map(obj => TuffUtils.fromObject(obj, sourceType, sourceId));
1158
+ }
1159
+ }
1160
+
1161
+ // 导出所有工具
1162
+ export { TuffItemBuilder, TuffSearchResultBuilder, TuffFactory, TuffListBuilder, TuffUtils };