@talex-touch/utils 1.0.29 → 1.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/animation/window-node.ts +202 -0
- package/auth/clerk-types.ts +56 -0
- package/auth/index.ts +4 -0
- package/auth/useAuth.ts +0 -0
- package/auth/useAuthState.ts +44 -0
- package/auth/useClerkConfig.ts +40 -0
- package/auth/useClerkProvider.ts +51 -0
- package/channel/index.ts +6 -0
- package/common/file-scan-utils.ts +4 -1
- package/common/storage/entity/app-settings.ts +15 -1
- package/common/utils/file.ts +4 -1
- package/common/utils/polling.ts +51 -3
- package/core-box/README.md +1 -1
- package/core-box/builder/tuff-builder.ts +1 -1
- package/core-box/tuff/tuff-dsl.ts +112 -48
- package/index.ts +4 -0
- package/package.json +1 -1
- package/plugin/index.ts +35 -12
- package/plugin/preload.ts +5 -2
- package/plugin/sdk/features.ts +1 -1
- package/plugin/sdk/index.ts +2 -7
- package/plugin/sdk/storage.ts +64 -0
- package/plugin/sdk/types.ts +96 -1
- package/preload/renderer.ts +1 -1
- package/renderer/hooks/index.ts +1 -0
- package/renderer/hooks/initialize.ts +5 -0
- package/renderer/hooks/performance.ts +87 -0
- package/renderer/index.ts +1 -0
- package/renderer/storage/app-settings.ts +0 -2
- package/renderer/storage/base-storage.ts +52 -13
- package/search/types.ts +7 -7
- package/types/download.ts +162 -0
- package/types/icon.ts +45 -0
- package/types/index.ts +1 -0
- package/types/modules/module-lifecycle.ts +1 -1
- package/types/storage.ts +56 -0
- package/types/update.ts +99 -0
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* @module core-box/tuff-dsl
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { TalexTouch } from "packages/utils/types";
|
|
19
|
+
// import { TalexTouch } from "packages/utils/types";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* 定义高亮范围
|
|
@@ -375,55 +375,15 @@ export interface TuffCustomRender {
|
|
|
375
375
|
scripts?: string[];
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
import type { ITuffIcon } from '../../types/icon'
|
|
379
|
+
|
|
378
380
|
/**
|
|
379
|
-
*
|
|
381
|
+
* Icon definition
|
|
380
382
|
*
|
|
381
383
|
* @description
|
|
382
|
-
*
|
|
383
|
-
* 可以是简单字符串或包含详细配置的对象。
|
|
384
|
+
* Unified icon type supporting only ITuffIcon object format
|
|
384
385
|
*/
|
|
385
|
-
export type TuffIcon =
|
|
386
|
-
| string // 简单字符串:emoji、URL、组件名
|
|
387
|
-
| {
|
|
388
|
-
/**
|
|
389
|
-
* 图标类型
|
|
390
|
-
* @description 指定图标的数据格式和来源
|
|
391
|
-
* @required
|
|
392
|
-
*/
|
|
393
|
-
type: 'emoji' | 'url' | 'base64' | 'fluent' | 'component';
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* 图标值
|
|
397
|
-
* @description 根据type不同,可能是emoji字符、URL地址、Base64编码或组件名
|
|
398
|
-
* @required
|
|
399
|
-
*/
|
|
400
|
-
value: string;
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* 备用图标
|
|
404
|
-
* @description 当主图标无法加载时显示的替代图标
|
|
405
|
-
*/
|
|
406
|
-
fallback?: string;
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* 动态加载函数
|
|
410
|
-
* @description 用于异步加载图标资源的函数
|
|
411
|
-
*/
|
|
412
|
-
loader?: () => Promise<string>;
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* 样式配置
|
|
416
|
-
* @description 控制图标的视觉效果
|
|
417
|
-
*/
|
|
418
|
-
style?: {
|
|
419
|
-
/** 图标尺寸 */
|
|
420
|
-
size?: number;
|
|
421
|
-
/** 图标颜色 */
|
|
422
|
-
color?: string;
|
|
423
|
-
/** 动画效果 */
|
|
424
|
-
animation?: 'spin' | 'pulse' | 'bounce';
|
|
425
|
-
};
|
|
426
|
-
};
|
|
386
|
+
export type TuffIcon = ITuffIcon
|
|
427
387
|
|
|
428
388
|
/**
|
|
429
389
|
* 标签定义
|
|
@@ -1081,18 +1041,88 @@ export interface TuffDisplayAction extends Omit<TuffAction, 'payload'> {
|
|
|
1081
1041
|
|
|
1082
1042
|
// ==================== 工具类型 ====================
|
|
1083
1043
|
|
|
1044
|
+
/**
|
|
1045
|
+
* 查询输入类型枚举
|
|
1046
|
+
*
|
|
1047
|
+
* @description
|
|
1048
|
+
* 定义系统支持的所有输入类型。
|
|
1049
|
+
* 用于插件、providers 和 features 声明其支持的输入类型。
|
|
1050
|
+
*/
|
|
1051
|
+
export enum TuffInputType {
|
|
1052
|
+
/** 纯文本输入 */
|
|
1053
|
+
Text = 'text',
|
|
1054
|
+
/** 图像输入(data URL 格式) */
|
|
1055
|
+
Image = 'image',
|
|
1056
|
+
/** 文件输入(文件路径数组) */
|
|
1057
|
+
Files = 'files',
|
|
1058
|
+
/** 富文本输入(HTML 格式) */
|
|
1059
|
+
Html = 'html'
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* 查询输入项
|
|
1064
|
+
*
|
|
1065
|
+
* @description
|
|
1066
|
+
* 定义查询中除文本外的多种类型输入(如剪贴板数据)。
|
|
1067
|
+
* 用于支持图像、文件、富文本等多媒体输入。
|
|
1068
|
+
*/
|
|
1069
|
+
export interface TuffQueryInput {
|
|
1070
|
+
/**
|
|
1071
|
+
* 输入类型
|
|
1072
|
+
* @description 定义输入数据的类型
|
|
1073
|
+
* @required
|
|
1074
|
+
*/
|
|
1075
|
+
type: TuffInputType;
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* 输入内容
|
|
1079
|
+
* @description 根据类型不同包含不同格式的数据:
|
|
1080
|
+
* - text: 纯文本字符串
|
|
1081
|
+
* - image: data URL 格式的图像数据
|
|
1082
|
+
* - files: JSON 序列化的文件路径数组
|
|
1083
|
+
* - html: HTML 格式的富文本
|
|
1084
|
+
* @required
|
|
1085
|
+
*/
|
|
1086
|
+
content: string;
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* 原始内容
|
|
1090
|
+
* @description 可选的原始格式内容,如富文本的 HTML 源码
|
|
1091
|
+
*/
|
|
1092
|
+
rawContent?: string;
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* 缩略图
|
|
1096
|
+
* @description 图像的缩略图 data URL(用于预览)
|
|
1097
|
+
*/
|
|
1098
|
+
thumbnail?: string;
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* 元数据
|
|
1102
|
+
* @description 附加的元数据信息
|
|
1103
|
+
*/
|
|
1104
|
+
metadata?: Record<string, any>;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1084
1107
|
/**
|
|
1085
1108
|
* 搜索查询结构
|
|
1086
1109
|
*
|
|
1087
1110
|
* @description
|
|
1088
1111
|
* 定义搜索请求的参数和过滤条件。
|
|
1089
1112
|
* 系统根据这些参数执行搜索并返回匹配结果。
|
|
1113
|
+
*
|
|
1114
|
+
* **重要区分**:
|
|
1115
|
+
* - `text`: 用户在输入框中主动输入的查询文本
|
|
1116
|
+
* - `inputs`: 来自剪贴板或其他来源的附加输入数据(图像、文件、富文本等)
|
|
1090
1117
|
*/
|
|
1091
1118
|
export interface TuffQuery {
|
|
1092
1119
|
/**
|
|
1093
|
-
*
|
|
1094
|
-
* @description
|
|
1120
|
+
* 用户输入的查询文本
|
|
1121
|
+
* @description 这是用户在搜索框中主动输入的文本,不包括剪贴板内容
|
|
1095
1122
|
* @required
|
|
1123
|
+
*
|
|
1124
|
+
* @example
|
|
1125
|
+
* 用户输入 "translate" → text = "translate"
|
|
1096
1126
|
*/
|
|
1097
1127
|
text: string;
|
|
1098
1128
|
|
|
@@ -1102,6 +1132,31 @@ export interface TuffQuery {
|
|
|
1102
1132
|
*/
|
|
1103
1133
|
type?: 'text' | 'voice' | 'image';
|
|
1104
1134
|
|
|
1135
|
+
/**
|
|
1136
|
+
* 多类型输入(附加数据)
|
|
1137
|
+
* @description 除了用户输入的文本外的其他输入数据(如剪贴板中的图像、文件、富文本等)
|
|
1138
|
+
*
|
|
1139
|
+
* **与 text 的区别**:
|
|
1140
|
+
* - `text`: 用户主动输入,总是存在
|
|
1141
|
+
* - `inputs`: 系统自动检测的附加数据,可能为空
|
|
1142
|
+
*
|
|
1143
|
+
* 当用户触发 feature 时,系统会自动检测剪贴板并填充此字段。
|
|
1144
|
+
*
|
|
1145
|
+
* @example
|
|
1146
|
+
* 场景 1: 用户输入 "translate" + 剪贴板有图片
|
|
1147
|
+
* text: "translate"
|
|
1148
|
+
* inputs: [{ type: 'image', content: 'data:image/png;base64,...' }]
|
|
1149
|
+
*
|
|
1150
|
+
* 场景 2: 用户输入 "compress" + 剪贴板有文件
|
|
1151
|
+
* text: "compress"
|
|
1152
|
+
* inputs: [{ type: 'files', content: '["/path/to/file"]' }]
|
|
1153
|
+
*
|
|
1154
|
+
* 场景 3: 用户输入 "format" + 剪贴板有富文本
|
|
1155
|
+
* text: "format"
|
|
1156
|
+
* inputs: [{ type: 'html', content: 'plain text', rawContent: '<p>html</p>' }]
|
|
1157
|
+
*/
|
|
1158
|
+
inputs?: TuffQueryInput[];
|
|
1159
|
+
|
|
1105
1160
|
/**
|
|
1106
1161
|
* 过滤条件
|
|
1107
1162
|
* @description 限制搜索范围的过滤器
|
|
@@ -1277,6 +1332,15 @@ export interface ISearchProvider<C> {
|
|
|
1277
1332
|
*/
|
|
1278
1333
|
readonly icon?: any
|
|
1279
1334
|
|
|
1335
|
+
/**
|
|
1336
|
+
* Supported input types
|
|
1337
|
+
* @description Declares which types of inputs this provider can handle.
|
|
1338
|
+
* If not specified, defaults to [TuffInputType.Text] only.
|
|
1339
|
+
* When query contains non-text inputs, only providers supporting those types will be searched.
|
|
1340
|
+
* @example [TuffInputType.Text, TuffInputType.Image, TuffInputType.Files]
|
|
1341
|
+
*/
|
|
1342
|
+
readonly supportedInputTypes?: TuffInputType[]
|
|
1343
|
+
|
|
1280
1344
|
/**
|
|
1281
1345
|
* Core search method (PULL mode).
|
|
1282
1346
|
* The engine calls this method to get results from the provider.
|
package/index.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
// export * from './auth' // Renderer-only, use @talex-touch/utils/renderer instead
|
|
1
2
|
export * from './base'
|
|
2
3
|
export * from './common'
|
|
3
4
|
export * from './plugin'
|
|
4
5
|
export * from './core-box'
|
|
5
6
|
export * from './channel'
|
|
6
7
|
export * from './types'
|
|
8
|
+
export * from './types/icon'
|
|
7
9
|
export * from './eventbus'
|
|
8
10
|
export * from './preload'
|
|
11
|
+
export * from './types/download'
|
|
12
|
+
export * from './types/update'
|
package/package.json
CHANGED
package/plugin/index.ts
CHANGED
|
@@ -14,6 +14,9 @@ export enum PluginStatus {
|
|
|
14
14
|
LOADING,
|
|
15
15
|
LOADED,
|
|
16
16
|
LOAD_FAILED,
|
|
17
|
+
|
|
18
|
+
DEV_DISCONNECTED, // Dev Server 断连
|
|
19
|
+
DEV_RECONNECTING, // 正在重连
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export interface PluginIssue {
|
|
@@ -26,14 +29,16 @@ export interface PluginIssue {
|
|
|
26
29
|
timestamp?: number
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
export interface
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
init(): Promise<void>
|
|
32
|
+
export interface DevServerHealthCheckResult {
|
|
33
|
+
healthy: boolean
|
|
34
|
+
version?: string
|
|
35
|
+
timestamp: number
|
|
36
|
+
error?: string
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
import type { ITuffIcon } from '../types/icon'
|
|
40
|
+
|
|
41
|
+
|
|
37
42
|
export interface IPlatformInfo {
|
|
38
43
|
enable: boolean
|
|
39
44
|
arch: Arch[]
|
|
@@ -51,7 +56,7 @@ export interface IPluginBaseInfo {
|
|
|
51
56
|
readme: string
|
|
52
57
|
version: string
|
|
53
58
|
desc: string
|
|
54
|
-
icon:
|
|
59
|
+
icon: ITuffIcon
|
|
55
60
|
platforms: IPlatform
|
|
56
61
|
_uniqueChannelKey: string
|
|
57
62
|
}
|
|
@@ -134,7 +139,7 @@ export interface IPluginFeature {
|
|
|
134
139
|
id: string
|
|
135
140
|
name: string
|
|
136
141
|
desc: string
|
|
137
|
-
icon:
|
|
142
|
+
icon: ITuffIcon
|
|
138
143
|
push: boolean
|
|
139
144
|
platform: IPlatform
|
|
140
145
|
commands: IFeatureCommand[]
|
|
@@ -145,6 +150,15 @@ export interface IPluginFeature {
|
|
|
145
150
|
* Default is 0
|
|
146
151
|
*/
|
|
147
152
|
priority?: number
|
|
153
|
+
/**
|
|
154
|
+
* Accepted input types for this feature
|
|
155
|
+
* @description Declares which types of inputs this feature can accept and process.
|
|
156
|
+
* If not specified, defaults to ['text'] only (backward compatible).
|
|
157
|
+
* When query contains inputs, only features accepting those input types will be shown.
|
|
158
|
+
* @example ['text', 'image'] - Feature accepts both text and images
|
|
159
|
+
* @example ['image', 'files'] - Feature only accepts images and files (no text-only queries)
|
|
160
|
+
*/
|
|
161
|
+
acceptedInputTypes?: Array<'text' | 'image' | 'files' | 'html'>
|
|
148
162
|
}
|
|
149
163
|
|
|
150
164
|
export type IFeatureInteraction = {
|
|
@@ -182,11 +196,14 @@ export interface IFeatureLifeCycle {
|
|
|
182
196
|
/**
|
|
183
197
|
* Called when a feature is triggered via a matching command.
|
|
184
198
|
* @param id - Feature ID
|
|
185
|
-
* @param data - The triggering payload
|
|
199
|
+
* @param data - The triggering payload. Can be:
|
|
200
|
+
* - string: Plain text query (backward compatible)
|
|
201
|
+
* - TuffQuery object: Complete query with text and optional inputs array containing clipboard data (images, files, HTML)
|
|
186
202
|
* @param feature - The full feature definition
|
|
187
203
|
* @param signal - An AbortSignal to cancel the operation
|
|
204
|
+
* @returns If returns false, the feature will not enter activation state (e.g., just opens browser and exits)
|
|
188
205
|
*/
|
|
189
|
-
onFeatureTriggered(id: string, data: any, feature: IPluginFeature, signal?: AbortSignal): void
|
|
206
|
+
onFeatureTriggered(id: string, data: any, feature: IPluginFeature, signal?: AbortSignal): boolean | void
|
|
190
207
|
|
|
191
208
|
/**
|
|
192
209
|
* Called when user input changes within this feature’s input box.
|
|
@@ -248,10 +265,13 @@ export interface ITargetFeatureLifeCycle {
|
|
|
248
265
|
|
|
249
266
|
/**
|
|
250
267
|
* Called when the feature is triggered via a matching command.
|
|
251
|
-
* @param data - The triggering payload
|
|
268
|
+
* @param data - The triggering payload. Can be:
|
|
269
|
+
* - string: Plain text query (backward compatible)
|
|
270
|
+
* - TuffQuery object: Complete query with text and optional inputs array containing clipboard data (images, files, HTML)
|
|
252
271
|
* @param feature - The full feature definition
|
|
272
|
+
* @returns If returns false, the feature will not enter activation state (e.g., just opens browser and exits)
|
|
253
273
|
*/
|
|
254
|
-
onFeatureTriggered(data: any, feature: IPluginFeature): void
|
|
274
|
+
onFeatureTriggered(data: any, feature: IPluginFeature): boolean | void
|
|
255
275
|
|
|
256
276
|
/**
|
|
257
277
|
* Called when user input changes within this feature’s input box.
|
|
@@ -323,11 +343,14 @@ export interface IPluginManager {
|
|
|
323
343
|
pluginPath: string
|
|
324
344
|
watcher: FSWatcher | null
|
|
325
345
|
devWatcher: any // Temporarily any, as DevPluginWatcher is internal to core-app
|
|
346
|
+
healthMonitor: any | null // DevServerHealthMonitor instance, set by PluginModule
|
|
326
347
|
|
|
327
348
|
getPluginList(): Array<object>
|
|
328
349
|
setActivePlugin(pluginName: string): boolean
|
|
329
350
|
hasPlugin(name: string): boolean
|
|
330
351
|
getPluginByName(name: string): ITouchPlugin | undefined
|
|
352
|
+
enablePlugin(pluginName: string): Promise<boolean>
|
|
353
|
+
disablePlugin(pluginName: string): Promise<boolean>
|
|
331
354
|
reloadPlugin(pluginName: string): Promise<void>
|
|
332
355
|
persistEnabledPlugins(): Promise<void>
|
|
333
356
|
listPlugins(): Promise<Array<string>>
|
package/plugin/preload.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ITouchClientChannel } from '../channel'
|
|
2
|
+
import type { ITouchSDK } from './sdk/index'
|
|
3
|
+
// Import SDK for side effects (initializes hooks)
|
|
2
4
|
import './sdk/index'
|
|
3
5
|
|
|
4
|
-
// window type
|
|
6
|
+
// window type - includes both plugin preload types and SDK types
|
|
5
7
|
declare global {
|
|
6
|
-
|
|
8
|
+
interface Window {
|
|
7
9
|
$plugin: {
|
|
8
10
|
name: string
|
|
9
11
|
path: Object
|
|
@@ -13,6 +15,7 @@ declare global {
|
|
|
13
15
|
$config: {
|
|
14
16
|
themeStyle: any
|
|
15
17
|
}
|
|
18
|
+
$touchSDK: ITouchSDK
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
|
package/plugin/sdk/features.ts
CHANGED
package/plugin/sdk/index.ts
CHANGED
|
@@ -5,12 +5,7 @@ export interface ITouchSDK {
|
|
|
5
5
|
__hooks: {}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
declare global {
|
|
10
|
-
export interface Window {
|
|
11
|
-
$touchSDK: ITouchSDK
|
|
12
|
-
}
|
|
13
|
-
}
|
|
8
|
+
// Note: Window.$touchSDK is declared in ../preload.ts to avoid duplicate declarations
|
|
14
9
|
|
|
15
10
|
export * from './types'
|
|
16
11
|
export * from './window/index'
|
|
@@ -22,4 +17,4 @@ export * from './clipboard'
|
|
|
22
17
|
export * from './core-box'
|
|
23
18
|
export * from './storage'
|
|
24
19
|
export * from './system'
|
|
25
|
-
export
|
|
20
|
+
export { createFeaturesManager, useFeatures } from './features'
|
package/plugin/sdk/storage.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { StorageStats, StorageTreeNode, FileDetails } from '../../types/storage'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Get the storage for the current plugin.
|
|
3
5
|
* It provides simple file-based storage that is persisted across application launches.
|
|
@@ -50,6 +52,68 @@ export function usePluginStorage() {
|
|
|
50
52
|
*/
|
|
51
53
|
listFiles: async (): Promise<string[]> => {
|
|
52
54
|
return channel.send('plugin:storage:list-files', { pluginName })
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets storage statistics for the current plugin.
|
|
59
|
+
* @returns A promise that resolves with storage statistics.
|
|
60
|
+
*/
|
|
61
|
+
getStats: async (): Promise<StorageStats> => {
|
|
62
|
+
return channel.send('plugin:storage:get-stats', { pluginName })
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets the directory tree structure of plugin storage.
|
|
67
|
+
* @returns A promise that resolves with the tree structure.
|
|
68
|
+
*/
|
|
69
|
+
getTree: async (): Promise<StorageTreeNode[]> => {
|
|
70
|
+
return channel.send('plugin:storage:get-tree', { pluginName })
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets detailed information about a specific file.
|
|
75
|
+
* @param fileName The name of the file to get details for.
|
|
76
|
+
* @returns A promise that resolves with file details.
|
|
77
|
+
*/
|
|
78
|
+
getFileDetails: async (fileName: string): Promise<FileDetails | null> => {
|
|
79
|
+
return channel.send('plugin:storage:get-file-details', { pluginName, fileName })
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Clears all storage for the current plugin.
|
|
84
|
+
* @returns A promise that resolves with the operation result.
|
|
85
|
+
*/
|
|
86
|
+
clearAll: async (): Promise<{ success: boolean, error?: string }> => {
|
|
87
|
+
return channel.send('plugin:storage:clear', { pluginName })
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Opens the plugin storage folder in the system file manager.
|
|
92
|
+
* @returns A promise that resolves when the folder is opened.
|
|
93
|
+
*/
|
|
94
|
+
openFolder: async (): Promise<void> => {
|
|
95
|
+
await channel.send('plugin:storage:open-folder', { pluginName })
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Listens for changes to the storage.
|
|
100
|
+
* @param fileName The file name to listen for changes
|
|
101
|
+
* @param callback The function to call when the storage changes for the current plugin.
|
|
102
|
+
* @returns A function to unsubscribe from the listener.
|
|
103
|
+
*/
|
|
104
|
+
onDidChange: (fileName: string, callback: (newConfig: any) => void) => {
|
|
105
|
+
const listener = (data: { name: string, fileName?: string }) => {
|
|
106
|
+
if (data.name === pluginName &&
|
|
107
|
+
(data.fileName === fileName || data.fileName === undefined)) {
|
|
108
|
+
callback(data)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
channel.regChannel('plugin:storage:update', listener)
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
channel.unRegChannel('plugin:storage:update', listener)
|
|
116
|
+
}
|
|
53
117
|
}
|
|
54
118
|
}
|
|
55
119
|
}
|
package/plugin/sdk/types.ts
CHANGED
|
@@ -518,9 +518,29 @@ export interface IPluginLifecycle {
|
|
|
518
518
|
/**
|
|
519
519
|
* Called when a plugin feature is triggered
|
|
520
520
|
* @param featureId - The ID of the triggered feature
|
|
521
|
-
* @param query - The search query or input data
|
|
521
|
+
* @param query - The search query or input data. Can be:
|
|
522
|
+
* - string: Plain text query (backward compatible)
|
|
523
|
+
* - TuffQuery object: Complete query with text and optional inputs array
|
|
524
|
+
* - query.text: The text query string
|
|
525
|
+
* - query.inputs: Array of TuffQueryInput objects (images, files, HTML)
|
|
522
526
|
* @param feature - The feature configuration object
|
|
523
527
|
* @returns Promise or void
|
|
528
|
+
* @example
|
|
529
|
+
* ```typescript
|
|
530
|
+
* onFeatureTriggered(featureId, query, feature) {
|
|
531
|
+
* if (typeof query === 'string') {
|
|
532
|
+
* // Backward compatible: plain text query
|
|
533
|
+
* console.log('Text query:', query)
|
|
534
|
+
* } else {
|
|
535
|
+
* // New: complete query object
|
|
536
|
+
* console.log('Text:', query.text)
|
|
537
|
+
* const imageInput = query.inputs?.find(i => i.type === 'image')
|
|
538
|
+
* if (imageInput) {
|
|
539
|
+
* console.log('Image data:', imageInput.content)
|
|
540
|
+
* }
|
|
541
|
+
* }
|
|
542
|
+
* }
|
|
543
|
+
* ```
|
|
524
544
|
*/
|
|
525
545
|
onFeatureTriggered(featureId: string, query: any, feature: any): Promise<void> | void;
|
|
526
546
|
|
|
@@ -842,3 +862,78 @@ export interface IPluginInfoManager {
|
|
|
842
862
|
*/
|
|
843
863
|
getPlatforms(): any
|
|
844
864
|
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Plugin state change event types
|
|
868
|
+
*
|
|
869
|
+
* @description
|
|
870
|
+
* Represents different types of plugin state changes for incremental updates
|
|
871
|
+
*/
|
|
872
|
+
export type PluginStateEvent =
|
|
873
|
+
| { type: 'added'; plugin: any }
|
|
874
|
+
| { type: 'removed'; name: string }
|
|
875
|
+
| { type: 'updated'; name: string; changes: Partial<any> }
|
|
876
|
+
| { type: 'status-changed'; name: string; status: number }
|
|
877
|
+
| { type: 'readme-updated'; name: string; readme: string }
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Plugin filter options for list queries
|
|
881
|
+
*/
|
|
882
|
+
export interface PluginFilters {
|
|
883
|
+
/** Filter by plugin status */
|
|
884
|
+
status?: number
|
|
885
|
+
/** Filter by enabled state */
|
|
886
|
+
enabled?: boolean
|
|
887
|
+
/** Filter by development mode */
|
|
888
|
+
dev?: boolean
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Trigger feature request payload
|
|
893
|
+
*/
|
|
894
|
+
export interface TriggerFeatureRequest {
|
|
895
|
+
/** Plugin name */
|
|
896
|
+
plugin: string
|
|
897
|
+
/** Feature ID */
|
|
898
|
+
feature: string
|
|
899
|
+
/** Search query or trigger input */
|
|
900
|
+
query?: string
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Input changed event payload
|
|
905
|
+
*/
|
|
906
|
+
export interface InputChangedRequest {
|
|
907
|
+
/** Plugin name */
|
|
908
|
+
plugin: string
|
|
909
|
+
/** Feature ID */
|
|
910
|
+
feature: string
|
|
911
|
+
/** Current input value */
|
|
912
|
+
query: string
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Plugin install request payload
|
|
917
|
+
*/
|
|
918
|
+
export interface InstallRequest {
|
|
919
|
+
/** Install source (URL, file path, etc) */
|
|
920
|
+
source: string
|
|
921
|
+
/** Hint type for installer */
|
|
922
|
+
hintType?: string
|
|
923
|
+
/** Additional metadata */
|
|
924
|
+
metadata?: Record<string, any>
|
|
925
|
+
/** Client metadata */
|
|
926
|
+
clientMetadata?: Record<string, any>
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Plugin install response
|
|
931
|
+
*/
|
|
932
|
+
export interface InstallResponse {
|
|
933
|
+
/** Whether installation was successful */
|
|
934
|
+
success: boolean
|
|
935
|
+
/** Error message if failed */
|
|
936
|
+
error?: string
|
|
937
|
+
/** Installed plugin name */
|
|
938
|
+
pluginName?: string
|
|
939
|
+
}
|
package/preload/renderer.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { LoadingEvent, LoadingMode, LoadingState, PreloadAPI } from './load
|
|
|
2
2
|
|
|
3
3
|
function getPreloadApi(): PreloadAPI | null {
|
|
4
4
|
if (typeof window === 'undefined') return null
|
|
5
|
-
return window.api ?? null
|
|
5
|
+
return (window as any).api ?? null
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function sendPreloadEvent(event: LoadingEvent): void {
|
package/renderer/hooks/index.ts
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderer Process Performance Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Collects detailed performance metrics in the renderer process
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Renderer performance metrics
|
|
9
|
+
*/
|
|
10
|
+
export interface RendererPerformanceMetrics {
|
|
11
|
+
/** Performance timing origin */
|
|
12
|
+
timeOrigin: number
|
|
13
|
+
/** Navigation start time */
|
|
14
|
+
navigationStart: number
|
|
15
|
+
/** DOM content loaded event end */
|
|
16
|
+
domContentLoadedEventEnd?: number
|
|
17
|
+
/** Load event end */
|
|
18
|
+
loadEventEnd?: number
|
|
19
|
+
/** DOM interactive time */
|
|
20
|
+
domInteractive?: number
|
|
21
|
+
/** First paint time */
|
|
22
|
+
firstPaint?: number
|
|
23
|
+
/** First contentful paint time */
|
|
24
|
+
firstContentfulPaint?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get current renderer performance metrics
|
|
29
|
+
*/
|
|
30
|
+
export function getRendererPerformanceMetrics(): RendererPerformanceMetrics {
|
|
31
|
+
const navEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
|
|
32
|
+
const paintEntries = performance.getEntriesByType('paint')
|
|
33
|
+
|
|
34
|
+
const firstPaint = paintEntries.find(e => e.name === 'first-paint')
|
|
35
|
+
const firstContentfulPaint = paintEntries.find(e => e.name === 'first-contentful-paint')
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
timeOrigin: performance.timeOrigin,
|
|
39
|
+
navigationStart: performance.timeOrigin,
|
|
40
|
+
domContentLoadedEventEnd: navEntry?.domContentLoadedEventEnd,
|
|
41
|
+
loadEventEnd: navEntry?.loadEventEnd,
|
|
42
|
+
domInteractive: navEntry?.domInteractive,
|
|
43
|
+
firstPaint: firstPaint?.startTime,
|
|
44
|
+
firstContentfulPaint: firstContentfulPaint?.startTime
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Wait for page load and collect metrics
|
|
50
|
+
*/
|
|
51
|
+
export function collectPerformanceMetricsOnLoad(): Promise<RendererPerformanceMetrics> {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
if (document.readyState === 'complete') {
|
|
54
|
+
// Page already loaded
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
resolve(getRendererPerformanceMetrics())
|
|
57
|
+
}, 0)
|
|
58
|
+
} else {
|
|
59
|
+
// Wait for load event
|
|
60
|
+
window.addEventListener('load', () => {
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
resolve(getRendererPerformanceMetrics())
|
|
63
|
+
}, 0)
|
|
64
|
+
}, { once: true })
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get performance summary for display
|
|
71
|
+
*/
|
|
72
|
+
export function getPerformanceSummary(): {
|
|
73
|
+
domReady: number
|
|
74
|
+
pageLoad: number
|
|
75
|
+
firstPaint: number | undefined
|
|
76
|
+
firstContentfulPaint: number | undefined
|
|
77
|
+
} {
|
|
78
|
+
const metrics = getRendererPerformanceMetrics()
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
domReady: metrics.domContentLoadedEventEnd || 0,
|
|
82
|
+
pageLoad: metrics.loadEventEnd || 0,
|
|
83
|
+
firstPaint: metrics.firstPaint,
|
|
84
|
+
firstContentfulPaint: metrics.firstContentfulPaint
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|