@talex-touch/utils 1.0.38 → 1.0.40
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/core-box/tuff/tuff-dsl.ts +69 -55
- package/package.json +1 -1
- package/plugin/index.ts +5 -0
- package/plugin/sdk/README.md +54 -6
- package/plugin/sdk/clipboard.ts +198 -27
- package/plugin/sdk/enum/bridge-event.ts +1 -0
- package/plugin/sdk/feature-sdk.ts +85 -6
- package/plugin/sdk/flow.ts +246 -0
- package/plugin/sdk/hooks/bridge.ts +18 -1
- package/plugin/sdk/index.ts +2 -0
- package/plugin/sdk/performance.ts +201 -0
- package/plugin/widget.ts +5 -0
- package/renderer/storage/base-storage.ts +98 -15
- package/renderer/storage/storage-subscription.ts +17 -9
- package/search/fuzzy-match.ts +254 -0
- package/types/flow.ts +283 -0
- package/types/index.ts +1 -0
|
@@ -14,6 +14,24 @@ import { ensureRendererChannel } from './channel'
|
|
|
14
14
|
*/
|
|
15
15
|
export type InputChangeHandler = (input: string) => void
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Keyboard event data forwarded from CoreBox
|
|
19
|
+
*/
|
|
20
|
+
export interface ForwardedKeyEvent {
|
|
21
|
+
key: string
|
|
22
|
+
code: string
|
|
23
|
+
metaKey: boolean
|
|
24
|
+
ctrlKey: boolean
|
|
25
|
+
altKey: boolean
|
|
26
|
+
shiftKey: boolean
|
|
27
|
+
repeat: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Key event handler
|
|
32
|
+
*/
|
|
33
|
+
export type KeyEventHandler = (event: ForwardedKeyEvent) => void
|
|
34
|
+
|
|
17
35
|
/**
|
|
18
36
|
* Feature SDK interface for plugins
|
|
19
37
|
*
|
|
@@ -125,6 +143,36 @@ export interface FeatureSDK {
|
|
|
125
143
|
* ```
|
|
126
144
|
*/
|
|
127
145
|
onInputChange(handler: InputChangeHandler): () => void
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Registers a listener for keyboard events forwarded from CoreBox
|
|
149
|
+
*
|
|
150
|
+
* When a plugin's UI view is attached to CoreBox, certain key events
|
|
151
|
+
* (Enter, Arrow keys, Meta+key combinations) are forwarded to the plugin.
|
|
152
|
+
*
|
|
153
|
+
* @param handler - Callback function invoked when a key event is forwarded
|
|
154
|
+
* @returns Unsubscribe function
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* const unsubscribe = plugin.feature.onKeyEvent((event) => {
|
|
159
|
+
* if (event.key === 'Enter') {
|
|
160
|
+
* // Handle enter key
|
|
161
|
+
* submitSelection()
|
|
162
|
+
* } else if (event.key === 'ArrowDown') {
|
|
163
|
+
* // Navigate down in list
|
|
164
|
+
* selectNext()
|
|
165
|
+
* } else if (event.metaKey && event.key === 'k') {
|
|
166
|
+
* // Handle Cmd+K
|
|
167
|
+
* openSearch()
|
|
168
|
+
* }
|
|
169
|
+
* })
|
|
170
|
+
*
|
|
171
|
+
* // Later, unsubscribe
|
|
172
|
+
* unsubscribe()
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
onKeyEvent(handler: KeyEventHandler): () => void
|
|
128
176
|
}
|
|
129
177
|
|
|
130
178
|
/**
|
|
@@ -138,25 +186,48 @@ export interface FeatureSDK {
|
|
|
138
186
|
*/
|
|
139
187
|
export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
|
|
140
188
|
const inputChangeHandlers: Set<InputChangeHandler> = new Set()
|
|
189
|
+
const keyEventHandlers: Set<KeyEventHandler> = new Set()
|
|
141
190
|
|
|
142
191
|
// Register listener for input change events from main process
|
|
143
|
-
const
|
|
192
|
+
const registerInputListener = () => {
|
|
144
193
|
if (channel.onMain) {
|
|
145
194
|
// Main process plugin context
|
|
146
|
-
channel.onMain('core-box:input-
|
|
147
|
-
const input = event.data?.input || event.input || ''
|
|
195
|
+
channel.onMain('core-box:input-change', (event: any) => {
|
|
196
|
+
const input = event.data?.input || event.data?.query?.text || event.input || ''
|
|
148
197
|
inputChangeHandlers.forEach(handler => handler(input))
|
|
149
198
|
})
|
|
150
199
|
} else if (channel.on) {
|
|
151
200
|
// Renderer process context
|
|
152
|
-
channel.on('core-box:input-
|
|
153
|
-
const input = data?.input || data || ''
|
|
201
|
+
channel.on('core-box:input-change', (data: any) => {
|
|
202
|
+
const input = data?.input || data?.query?.text || data || ''
|
|
154
203
|
inputChangeHandlers.forEach(handler => handler(input))
|
|
155
204
|
})
|
|
156
205
|
}
|
|
157
206
|
}
|
|
158
207
|
|
|
159
|
-
|
|
208
|
+
// Register listener for key events from main process
|
|
209
|
+
const registerKeyListener = () => {
|
|
210
|
+
if (channel.onMain) {
|
|
211
|
+
// Main process plugin context
|
|
212
|
+
channel.onMain('core-box:key-event', (event: any) => {
|
|
213
|
+
const keyEvent = event.data as ForwardedKeyEvent
|
|
214
|
+
if (keyEvent) {
|
|
215
|
+
keyEventHandlers.forEach(handler => handler(keyEvent))
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
} else if (channel.on) {
|
|
219
|
+
// Renderer process context
|
|
220
|
+
channel.on('core-box:key-event', (data: any) => {
|
|
221
|
+
const keyEvent = data as ForwardedKeyEvent
|
|
222
|
+
if (keyEvent) {
|
|
223
|
+
keyEventHandlers.forEach(handler => handler(keyEvent))
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
registerInputListener()
|
|
230
|
+
registerKeyListener()
|
|
160
231
|
|
|
161
232
|
return {
|
|
162
233
|
pushItems(items: TuffItem[]): void {
|
|
@@ -200,6 +271,14 @@ export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
|
|
|
200
271
|
return () => {
|
|
201
272
|
inputChangeHandlers.delete(handler)
|
|
202
273
|
}
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
onKeyEvent(handler: KeyEventHandler): () => void {
|
|
277
|
+
keyEventHandlers.add(handler)
|
|
278
|
+
|
|
279
|
+
return () => {
|
|
280
|
+
keyEventHandlers.delete(handler)
|
|
281
|
+
}
|
|
203
282
|
}
|
|
204
283
|
}
|
|
205
284
|
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow SDK
|
|
3
|
+
*
|
|
4
|
+
* Plugin-side API for Flow Transfer operations.
|
|
5
|
+
* Allows plugins to dispatch flows and receive flow data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
FlowPayload,
|
|
10
|
+
FlowDispatchOptions,
|
|
11
|
+
FlowDispatchResult,
|
|
12
|
+
FlowTargetInfo,
|
|
13
|
+
FlowSessionUpdate,
|
|
14
|
+
FlowPayloadType
|
|
15
|
+
} from '../../types/flow'
|
|
16
|
+
import { FlowIPCChannel } from '../../types/flow'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Flow SDK interface
|
|
20
|
+
*/
|
|
21
|
+
export interface IFlowSDK {
|
|
22
|
+
/**
|
|
23
|
+
* Dispatches a flow payload to another plugin
|
|
24
|
+
*
|
|
25
|
+
* @param payload - Data to transfer
|
|
26
|
+
* @param options - Dispatch options
|
|
27
|
+
* @returns Dispatch result
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const result = await flow.dispatch(
|
|
32
|
+
* {
|
|
33
|
+
* type: 'text',
|
|
34
|
+
* data: 'Hello from plugin A',
|
|
35
|
+
* context: { sourcePluginId: 'plugin-a' }
|
|
36
|
+
* },
|
|
37
|
+
* {
|
|
38
|
+
* title: 'Share Text',
|
|
39
|
+
* preferredTarget: 'plugin-b.quick-note'
|
|
40
|
+
* }
|
|
41
|
+
* )
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
dispatch(payload: FlowPayload, options?: FlowDispatchOptions): Promise<FlowDispatchResult>
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets available flow targets
|
|
48
|
+
*
|
|
49
|
+
* @param payloadType - Filter by payload type (optional)
|
|
50
|
+
* @returns List of available targets
|
|
51
|
+
*/
|
|
52
|
+
getAvailableTargets(payloadType?: FlowPayloadType): Promise<FlowTargetInfo[]>
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Listens for session updates
|
|
56
|
+
*
|
|
57
|
+
* @param sessionId - Session to listen to
|
|
58
|
+
* @param handler - Update handler
|
|
59
|
+
* @returns Unsubscribe function
|
|
60
|
+
*/
|
|
61
|
+
onSessionUpdate(
|
|
62
|
+
sessionId: string,
|
|
63
|
+
handler: (update: FlowSessionUpdate) => void
|
|
64
|
+
): () => void
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Cancels a flow session
|
|
68
|
+
*
|
|
69
|
+
* @param sessionId - Session to cancel
|
|
70
|
+
*/
|
|
71
|
+
cancel(sessionId: string): Promise<void>
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Acknowledges a received flow (for target plugins)
|
|
75
|
+
*
|
|
76
|
+
* @param sessionId - Session to acknowledge
|
|
77
|
+
* @param ackPayload - Optional acknowledgment data
|
|
78
|
+
*/
|
|
79
|
+
acknowledge(sessionId: string, ackPayload?: any): Promise<void>
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Reports an error for a received flow (for target plugins)
|
|
83
|
+
*
|
|
84
|
+
* @param sessionId - Session to report error for
|
|
85
|
+
* @param message - Error message
|
|
86
|
+
*/
|
|
87
|
+
reportError(sessionId: string, message: string): Promise<void>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates a Flow SDK instance
|
|
92
|
+
*
|
|
93
|
+
* @param channel - Channel for IPC communication
|
|
94
|
+
* @param pluginId - Current plugin ID
|
|
95
|
+
* @returns Flow SDK instance
|
|
96
|
+
*/
|
|
97
|
+
export function createFlowSDK(
|
|
98
|
+
channel: { send: (event: string, data?: any) => Promise<any> },
|
|
99
|
+
pluginId: string
|
|
100
|
+
): IFlowSDK {
|
|
101
|
+
const sessionListeners = new Map<string, Set<(update: FlowSessionUpdate) => void>>()
|
|
102
|
+
|
|
103
|
+
// Listen for session updates
|
|
104
|
+
if (typeof window !== 'undefined') {
|
|
105
|
+
window.addEventListener('message', (event) => {
|
|
106
|
+
if (event.data?.type === FlowIPCChannel.SESSION_UPDATE) {
|
|
107
|
+
const update = event.data.payload as FlowSessionUpdate
|
|
108
|
+
const listeners = sessionListeners.get(update.sessionId)
|
|
109
|
+
if (listeners) {
|
|
110
|
+
for (const listener of listeners) {
|
|
111
|
+
try {
|
|
112
|
+
listener(update)
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('[FlowSDK] Error in session listener:', error)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
async dispatch(payload: FlowPayload, options?: FlowDispatchOptions): Promise<FlowDispatchResult> {
|
|
124
|
+
// Ensure context has sourcePluginId
|
|
125
|
+
const enrichedPayload: FlowPayload = {
|
|
126
|
+
...payload,
|
|
127
|
+
context: {
|
|
128
|
+
...payload.context,
|
|
129
|
+
sourcePluginId: payload.context?.sourcePluginId || pluginId
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const response = await channel.send(FlowIPCChannel.DISPATCH, {
|
|
134
|
+
senderId: pluginId,
|
|
135
|
+
payload: enrichedPayload,
|
|
136
|
+
options
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
if (!response?.success) {
|
|
140
|
+
throw new Error(response?.error?.message || 'Flow dispatch failed')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return response.data
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
async getAvailableTargets(payloadType?: FlowPayloadType): Promise<FlowTargetInfo[]> {
|
|
147
|
+
const response = await channel.send(FlowIPCChannel.GET_TARGETS, {
|
|
148
|
+
payloadType
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
if (!response?.success) {
|
|
152
|
+
throw new Error(response?.error?.message || 'Failed to get targets')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return response.data || []
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
onSessionUpdate(
|
|
159
|
+
sessionId: string,
|
|
160
|
+
handler: (update: FlowSessionUpdate) => void
|
|
161
|
+
): () => void {
|
|
162
|
+
if (!sessionListeners.has(sessionId)) {
|
|
163
|
+
sessionListeners.set(sessionId, new Set())
|
|
164
|
+
}
|
|
165
|
+
sessionListeners.get(sessionId)!.add(handler)
|
|
166
|
+
|
|
167
|
+
return () => {
|
|
168
|
+
const listeners = sessionListeners.get(sessionId)
|
|
169
|
+
if (listeners) {
|
|
170
|
+
listeners.delete(handler)
|
|
171
|
+
if (listeners.size === 0) {
|
|
172
|
+
sessionListeners.delete(sessionId)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
async cancel(sessionId: string): Promise<void> {
|
|
179
|
+
const response = await channel.send(FlowIPCChannel.CANCEL, {
|
|
180
|
+
sessionId
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
if (!response?.success) {
|
|
184
|
+
throw new Error(response?.error?.message || 'Failed to cancel session')
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
async acknowledge(sessionId: string, ackPayload?: any): Promise<void> {
|
|
189
|
+
const response = await channel.send(FlowIPCChannel.ACKNOWLEDGE, {
|
|
190
|
+
sessionId,
|
|
191
|
+
ackPayload
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
if (!response?.success) {
|
|
195
|
+
throw new Error(response?.error?.message || 'Failed to acknowledge session')
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
async reportError(sessionId: string, message: string): Promise<void> {
|
|
200
|
+
const response = await channel.send(FlowIPCChannel.REPORT_ERROR, {
|
|
201
|
+
sessionId,
|
|
202
|
+
message
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
if (!response?.success) {
|
|
206
|
+
throw new Error(response?.error?.message || 'Failed to report error')
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Helper to extract flow data from TuffQuery
|
|
214
|
+
*
|
|
215
|
+
* When a feature is triggered via Flow, the query will contain flow information.
|
|
216
|
+
*
|
|
217
|
+
* @param query - TuffQuery from feature trigger
|
|
218
|
+
* @returns Flow data if present, null otherwise
|
|
219
|
+
*/
|
|
220
|
+
export function extractFlowData(query: any): {
|
|
221
|
+
sessionId: string
|
|
222
|
+
payload: FlowPayload
|
|
223
|
+
senderId: string
|
|
224
|
+
senderName?: string
|
|
225
|
+
} | null {
|
|
226
|
+
if (!query?.flow) {
|
|
227
|
+
return null
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
sessionId: query.flow.sessionId,
|
|
232
|
+
payload: query.flow.payload,
|
|
233
|
+
senderId: query.flow.senderId,
|
|
234
|
+
senderName: query.flow.senderName
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Helper to check if a feature was triggered via Flow
|
|
240
|
+
*
|
|
241
|
+
* @param query - TuffQuery from feature trigger
|
|
242
|
+
* @returns True if triggered via Flow
|
|
243
|
+
*/
|
|
244
|
+
export function isFlowTriggered(query: any): boolean {
|
|
245
|
+
return !!query?.flow
|
|
246
|
+
}
|
|
@@ -12,6 +12,7 @@ export type BridgeHook<T = any> = (data: T) => void
|
|
|
12
12
|
const __hooks: Record<BridgeEvent, Array<BridgeHook>> = {
|
|
13
13
|
[BridgeEventForCoreBox.CORE_BOX_INPUT_CHANGE]: [],
|
|
14
14
|
[BridgeEventForCoreBox.CORE_BOX_CLIPBOARD_CHANGE]: [],
|
|
15
|
+
[BridgeEventForCoreBox.CORE_BOX_KEY_EVENT]: [],
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -65,6 +66,22 @@ export const createBridgeHook = <T>(type: BridgeEvent) => (hook: BridgeHook<T>)
|
|
|
65
66
|
* The hook receives the new input value as a string.
|
|
66
67
|
* @param data The input change data (string).
|
|
67
68
|
*/
|
|
68
|
-
export const onCoreBoxInputChange = createBridgeHook<{ query: string }>(BridgeEventForCoreBox.CORE_BOX_INPUT_CHANGE)
|
|
69
|
+
export const onCoreBoxInputChange = createBridgeHook<{ query: { inputs: Array<any>, text: string } }>(BridgeEventForCoreBox.CORE_BOX_INPUT_CHANGE)
|
|
69
70
|
|
|
70
71
|
export const onCoreBoxClipboardChange = createBridgeHook<{ item: any }>(BridgeEventForCoreBox.CORE_BOX_CLIPBOARD_CHANGE)
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Hook for when a keyboard event is forwarded from CoreBox.
|
|
75
|
+
* This is triggered when the plugin's UI view is attached and the user
|
|
76
|
+
* presses certain keys (Enter, Arrow keys, Meta+key combinations).
|
|
77
|
+
* @param data The forwarded keyboard event data.
|
|
78
|
+
*/
|
|
79
|
+
export const onCoreBoxKeyEvent = createBridgeHook<{
|
|
80
|
+
key: string
|
|
81
|
+
code: string
|
|
82
|
+
metaKey: boolean
|
|
83
|
+
ctrlKey: boolean
|
|
84
|
+
altKey: boolean
|
|
85
|
+
shiftKey: boolean
|
|
86
|
+
repeat: boolean
|
|
87
|
+
}>(BridgeEventForCoreBox.CORE_BOX_KEY_EVENT)
|
package/plugin/sdk/index.ts
CHANGED
|
@@ -13,9 +13,11 @@ export * from './clipboard'
|
|
|
13
13
|
export * from './core-box'
|
|
14
14
|
export * from './division-box'
|
|
15
15
|
export * from './feature-sdk'
|
|
16
|
+
export * from './flow'
|
|
16
17
|
export { createFeaturesManager, useFeatures } from './features'
|
|
17
18
|
|
|
18
19
|
export * from './hooks/index'
|
|
20
|
+
export * from './performance'
|
|
19
21
|
export * from './service/index'
|
|
20
22
|
export * from './storage'
|
|
21
23
|
export * from './system'
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Performance SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides APIs for plugins to access their own performance metrics
|
|
5
|
+
* and storage statistics.
|
|
6
|
+
*/
|
|
7
|
+
import type { ITouchClientChannel } from '@talex-touch/utils/channel'
|
|
8
|
+
import { ensureRendererChannel } from './channel'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Storage statistics interface
|
|
12
|
+
*/
|
|
13
|
+
export interface StorageStats {
|
|
14
|
+
/** Total size in bytes */
|
|
15
|
+
totalSize: number
|
|
16
|
+
/** Number of files */
|
|
17
|
+
fileCount: number
|
|
18
|
+
/** Number of directories */
|
|
19
|
+
dirCount: number
|
|
20
|
+
/** Maximum allowed size in bytes */
|
|
21
|
+
maxSize: number
|
|
22
|
+
/** Usage percentage (0-100) */
|
|
23
|
+
usagePercent: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Performance metrics interface
|
|
28
|
+
*/
|
|
29
|
+
export interface PerformanceMetrics {
|
|
30
|
+
/** Plugin load time in milliseconds */
|
|
31
|
+
loadTime: number
|
|
32
|
+
/** Estimated memory usage in bytes */
|
|
33
|
+
memoryUsage: number
|
|
34
|
+
/** CPU usage percentage (0-100) */
|
|
35
|
+
cpuUsage: number
|
|
36
|
+
/** Last active timestamp */
|
|
37
|
+
lastActiveTime: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Plugin paths interface
|
|
42
|
+
*/
|
|
43
|
+
export interface PluginPaths {
|
|
44
|
+
/** Plugin installation directory */
|
|
45
|
+
pluginPath: string
|
|
46
|
+
/** Plugin data directory */
|
|
47
|
+
dataPath: string
|
|
48
|
+
/** Plugin config directory */
|
|
49
|
+
configPath: string
|
|
50
|
+
/** Plugin logs directory */
|
|
51
|
+
logsPath: string
|
|
52
|
+
/** Plugin temp directory */
|
|
53
|
+
tempPath: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Performance SDK interface
|
|
58
|
+
*/
|
|
59
|
+
export interface PerformanceSDK {
|
|
60
|
+
/**
|
|
61
|
+
* Get storage statistics for the current plugin
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const stats = await plugin.performance.getStorageStats()
|
|
66
|
+
* console.log(`Using ${stats.usagePercent}% of storage`)
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
getStorageStats: () => Promise<StorageStats>
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get performance metrics for the current plugin
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const metrics = await plugin.performance.getMetrics()
|
|
77
|
+
* console.log(`Load time: ${metrics.loadTime}ms`)
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
getMetrics: () => Promise<PerformanceMetrics>
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get all paths for the current plugin
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const paths = await plugin.performance.getPaths()
|
|
88
|
+
* console.log(`Plugin installed at: ${paths.pluginPath}`)
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
getPaths: () => Promise<PluginPaths>
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get combined performance data (storage + metrics + paths)
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const data = await plugin.performance.getAll()
|
|
99
|
+
* console.log(data.storage, data.metrics, data.paths)
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
getAll: () => Promise<{
|
|
103
|
+
storage: StorageStats
|
|
104
|
+
metrics: PerformanceMetrics
|
|
105
|
+
paths: PluginPaths
|
|
106
|
+
}>
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Creates a Performance SDK instance for plugin use
|
|
111
|
+
*
|
|
112
|
+
* @param channel - The plugin channel bridge for IPC communication
|
|
113
|
+
* @returns Configured Performance SDK instance
|
|
114
|
+
*
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export function createPerformanceSDK(channel: ITouchClientChannel): PerformanceSDK {
|
|
118
|
+
return {
|
|
119
|
+
async getStorageStats(): Promise<StorageStats> {
|
|
120
|
+
try {
|
|
121
|
+
const result = await channel.send('plugin:storage:get-stats')
|
|
122
|
+
return result?.data || {
|
|
123
|
+
totalSize: 0,
|
|
124
|
+
fileCount: 0,
|
|
125
|
+
dirCount: 0,
|
|
126
|
+
maxSize: 10 * 1024 * 1024,
|
|
127
|
+
usagePercent: 0,
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('[Performance SDK] Failed to get storage stats:', error)
|
|
132
|
+
throw error
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
async getMetrics(): Promise<PerformanceMetrics> {
|
|
137
|
+
try {
|
|
138
|
+
const result = await channel.send('plugin:performance:get-metrics')
|
|
139
|
+
return result?.data || {
|
|
140
|
+
loadTime: 0,
|
|
141
|
+
memoryUsage: 0,
|
|
142
|
+
cpuUsage: 0,
|
|
143
|
+
lastActiveTime: 0,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error('[Performance SDK] Failed to get performance metrics:', error)
|
|
148
|
+
throw error
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
async getPaths(): Promise<PluginPaths> {
|
|
153
|
+
try {
|
|
154
|
+
const result = await channel.send('plugin:performance:get-paths')
|
|
155
|
+
return result?.data || {
|
|
156
|
+
pluginPath: '',
|
|
157
|
+
dataPath: '',
|
|
158
|
+
configPath: '',
|
|
159
|
+
logsPath: '',
|
|
160
|
+
tempPath: '',
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error('[Performance SDK] Failed to get plugin paths:', error)
|
|
165
|
+
throw error
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
async getAll(): Promise<{
|
|
170
|
+
storage: StorageStats
|
|
171
|
+
metrics: PerformanceMetrics
|
|
172
|
+
paths: PluginPaths
|
|
173
|
+
}> {
|
|
174
|
+
const [storage, metrics, paths] = await Promise.all([
|
|
175
|
+
this.getStorageStats(),
|
|
176
|
+
this.getMetrics(),
|
|
177
|
+
this.getPaths(),
|
|
178
|
+
])
|
|
179
|
+
|
|
180
|
+
return { storage, metrics, paths }
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Hook for using Performance SDK in plugin context
|
|
187
|
+
*
|
|
188
|
+
* @returns Performance SDK instance
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* const performance = usePerformance()
|
|
193
|
+
*
|
|
194
|
+
* const stats = await performance.getStorageStats()
|
|
195
|
+
* const metrics = await performance.getMetrics()
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export function usePerformance(): PerformanceSDK {
|
|
199
|
+
const channel = ensureRendererChannel('[Performance SDK] Channel not available. Make sure this is called in a plugin context.')
|
|
200
|
+
return createPerformanceSDK(channel)
|
|
201
|
+
}
|
package/plugin/widget.ts
CHANGED
|
@@ -18,6 +18,11 @@ export interface WidgetRegistrationPayload {
|
|
|
18
18
|
code: string
|
|
19
19
|
styles: string
|
|
20
20
|
hash: string
|
|
21
|
+
/**
|
|
22
|
+
* List of allowed module dependencies for widget sandbox
|
|
23
|
+
* Widget 沙箱允许的模块依赖列表
|
|
24
|
+
*/
|
|
25
|
+
dependencies?: string[]
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export function makeWidgetId(pluginName: string, featureId: string): string {
|