@talex-touch/utils 1.0.39 → 1.0.42
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 +133 -54
- package/intelligence/client.ts +8 -8
- package/market/constants.ts +14 -0
- package/market/types.ts +1 -1
- package/package.json +1 -1
- package/plugin/index.ts +7 -1
- package/plugin/providers/index.ts +4 -0
- package/plugin/providers/market-client.ts +215 -0
- package/plugin/providers/npm-provider.ts +213 -0
- package/plugin/providers/tpex-provider.ts +283 -0
- package/plugin/providers/tpex-types.ts +34 -0
- package/plugin/sdk/README.md +54 -6
- package/plugin/sdk/clipboard.ts +196 -23
- 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 +113 -39
- package/plugin/sdk/index.ts +2 -0
- package/plugin/sdk/performance.ts +186 -0
- package/plugin/widget.ts +5 -0
- package/renderer/hooks/arg-mapper.ts +20 -6
- package/renderer/hooks/use-intelligence.ts +291 -34
- package/renderer/storage/base-storage.ts +98 -15
- package/renderer/storage/intelligence-storage.ts +9 -9
- package/renderer/storage/storage-subscription.ts +17 -9
- package/search/fuzzy-match.ts +254 -0
- package/types/division-box.ts +20 -0
- package/types/flow.ts +283 -0
- package/types/index.ts +1 -0
- package/types/intelligence.ts +1496 -78
|
@@ -3,53 +3,109 @@ import { ensureRendererChannel } from '../channel'
|
|
|
3
3
|
|
|
4
4
|
export type BridgeEvent = BridgeEventForCoreBox
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
export interface BridgeEventMeta {
|
|
7
|
+
timestamp: number
|
|
8
|
+
fromCache: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BridgeEventPayload<T = any> {
|
|
12
|
+
data: T
|
|
13
|
+
meta: BridgeEventMeta
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @template T The type of data the hook will receive. */
|
|
17
|
+
export type BridgeHook<T = any> = (payload: BridgeEventPayload<T>) => void
|
|
18
|
+
|
|
19
|
+
interface CachedEvent<T = any> {
|
|
20
|
+
data: T
|
|
21
|
+
timestamp: number
|
|
22
|
+
}
|
|
11
23
|
|
|
12
24
|
const __hooks: Record<BridgeEvent, Array<BridgeHook>> = {
|
|
13
25
|
[BridgeEventForCoreBox.CORE_BOX_INPUT_CHANGE]: [],
|
|
14
26
|
[BridgeEventForCoreBox.CORE_BOX_CLIPBOARD_CHANGE]: [],
|
|
27
|
+
[BridgeEventForCoreBox.CORE_BOX_KEY_EVENT]: [],
|
|
15
28
|
}
|
|
16
29
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
const __eventCache: Map<BridgeEvent, CachedEvent[]> = new Map()
|
|
31
|
+
const __channelRegistered = new Set<BridgeEvent>()
|
|
32
|
+
|
|
33
|
+
const CACHE_MAX_SIZE: Record<BridgeEvent, number> = {
|
|
34
|
+
[BridgeEventForCoreBox.CORE_BOX_INPUT_CHANGE]: 1,
|
|
35
|
+
[BridgeEventForCoreBox.CORE_BOX_CLIPBOARD_CHANGE]: 1,
|
|
36
|
+
[BridgeEventForCoreBox.CORE_BOX_KEY_EVENT]: 10,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function invokeHook<T>(hook: BridgeHook<T>, data: T, fromCache: boolean, timestamp: number): void {
|
|
40
|
+
try {
|
|
41
|
+
hook({ data, meta: { timestamp, fromCache } })
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error('[TouchSDK] Bridge hook error:', e)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function registerEarlyListener(type: BridgeEvent): void {
|
|
49
|
+
if (__channelRegistered.has(type)) return
|
|
27
50
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const channel = ensureRendererChannel('[TouchSDK] Bridge channel not available. Make sure hooks run in plugin renderer context.')
|
|
51
|
+
try {
|
|
52
|
+
const channel = ensureRendererChannel()
|
|
31
53
|
channel.regChannel(type, ({ data }) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
54
|
+
const timestamp = Date.now()
|
|
55
|
+
const hooks = __hooks[type]
|
|
56
|
+
|
|
57
|
+
if (hooks && hooks.length > 0) {
|
|
58
|
+
hooks.forEach(h => invokeHook(h, data, false, timestamp))
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (!__eventCache.has(type)) __eventCache.set(type, [])
|
|
62
|
+
const cache = __eventCache.get(type)!
|
|
63
|
+
const maxSize = CACHE_MAX_SIZE[type] ?? 1
|
|
64
|
+
cache.push({ data, timestamp })
|
|
65
|
+
while (cache.length > maxSize) cache.shift()
|
|
66
|
+
console.debug(`[TouchSDK] ${type} cached, size: ${cache.length}`)
|
|
37
67
|
}
|
|
38
68
|
})
|
|
69
|
+
__channelRegistered.add(type)
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Channel not ready yet
|
|
39
73
|
}
|
|
74
|
+
}
|
|
40
75
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
/** Clears the event cache for a specific event type or all types. */
|
|
77
|
+
export function clearBridgeEventCache(type?: BridgeEvent): void {
|
|
78
|
+
if (type) {
|
|
79
|
+
__eventCache.delete(type)
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
__eventCache.clear()
|
|
48
83
|
}
|
|
84
|
+
}
|
|
49
85
|
|
|
50
|
-
|
|
86
|
+
// Auto-init on module load
|
|
87
|
+
;(function initBridgeEventCache() {
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
Object.values(BridgeEventForCoreBox).forEach(e => registerEarlyListener(e as BridgeEvent))
|
|
90
|
+
}, 0)
|
|
91
|
+
})()
|
|
51
92
|
|
|
52
|
-
|
|
93
|
+
/** @internal Injects a hook for a given bridge event with cache replay. */
|
|
94
|
+
export function injectBridgeEvent<T>(type: BridgeEvent, hook: BridgeHook<T>) {
|
|
95
|
+
const hooks: Array<BridgeHook<T>> = __hooks[type] || (__hooks[type] = [])
|
|
96
|
+
|
|
97
|
+
// Ensure channel listener is registered
|
|
98
|
+
registerEarlyListener(type)
|
|
99
|
+
|
|
100
|
+
// Replay cached events to this new hook
|
|
101
|
+
const cached = __eventCache.get(type)
|
|
102
|
+
if (cached && cached.length > 0) {
|
|
103
|
+
cached.forEach(({ data, timestamp }) => invokeHook(hook, data as T, true, timestamp))
|
|
104
|
+
__eventCache.delete(type)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
hooks.push(hook)
|
|
108
|
+
return hook
|
|
53
109
|
}
|
|
54
110
|
|
|
55
111
|
/**
|
|
@@ -60,11 +116,29 @@ export function injectBridgeEvent<T>(type: BridgeEvent, hook: BridgeHook<T>) {
|
|
|
60
116
|
*/
|
|
61
117
|
export const createBridgeHook = <T>(type: BridgeEvent) => (hook: BridgeHook<T>) => injectBridgeEvent<T>(type, hook)
|
|
62
118
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
119
|
+
export interface CoreBoxInputData {
|
|
120
|
+
query: { inputs: Array<any>, text: string }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface CoreBoxKeyEventData {
|
|
124
|
+
key: string
|
|
125
|
+
code: string
|
|
126
|
+
metaKey: boolean
|
|
127
|
+
ctrlKey: boolean
|
|
128
|
+
altKey: boolean
|
|
129
|
+
shiftKey: boolean
|
|
130
|
+
repeat: boolean
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface CoreBoxClipboardData {
|
|
134
|
+
item: any
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Hook for CoreBox input changes. Payload includes `data` and `meta` (timestamp, fromCache). */
|
|
138
|
+
export const onCoreBoxInputChange = createBridgeHook<CoreBoxInputData>(BridgeEventForCoreBox.CORE_BOX_INPUT_CHANGE)
|
|
139
|
+
|
|
140
|
+
/** Hook for CoreBox clipboard changes. Payload includes `data` and `meta` (timestamp, fromCache). */
|
|
141
|
+
export const onCoreBoxClipboardChange = createBridgeHook<CoreBoxClipboardData>(BridgeEventForCoreBox.CORE_BOX_CLIPBOARD_CHANGE)
|
|
69
142
|
|
|
70
|
-
|
|
143
|
+
/** Hook for keyboard events forwarded from CoreBox. Payload includes `data` and `meta` (timestamp, fromCache). */
|
|
144
|
+
export const onCoreBoxKeyEvent = createBridgeHook<CoreBoxKeyEventData>(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,186 @@
|
|
|
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 type { StorageStats } from '../../types/storage'
|
|
9
|
+
import { ensureRendererChannel } from './channel'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Performance metrics interface
|
|
13
|
+
*/
|
|
14
|
+
export interface PerformanceMetrics {
|
|
15
|
+
/** Plugin load time in milliseconds */
|
|
16
|
+
loadTime: number
|
|
17
|
+
/** Estimated memory usage in bytes */
|
|
18
|
+
memoryUsage: number
|
|
19
|
+
/** CPU usage percentage (0-100) */
|
|
20
|
+
cpuUsage: number
|
|
21
|
+
/** Last active timestamp */
|
|
22
|
+
lastActiveTime: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Plugin paths interface
|
|
27
|
+
*/
|
|
28
|
+
export interface PluginPaths {
|
|
29
|
+
/** Plugin installation directory */
|
|
30
|
+
pluginPath: string
|
|
31
|
+
/** Plugin data directory */
|
|
32
|
+
dataPath: string
|
|
33
|
+
/** Plugin config directory */
|
|
34
|
+
configPath: string
|
|
35
|
+
/** Plugin logs directory */
|
|
36
|
+
logsPath: string
|
|
37
|
+
/** Plugin temp directory */
|
|
38
|
+
tempPath: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Performance SDK interface
|
|
43
|
+
*/
|
|
44
|
+
export interface PerformanceSDK {
|
|
45
|
+
/**
|
|
46
|
+
* Get storage statistics for the current plugin
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const stats = await plugin.performance.getStorageStats()
|
|
51
|
+
* console.log(`Using ${stats.usagePercent}% of storage`)
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
getStorageStats: () => Promise<StorageStats>
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get performance metrics for the current plugin
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const metrics = await plugin.performance.getMetrics()
|
|
62
|
+
* console.log(`Load time: ${metrics.loadTime}ms`)
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
getMetrics: () => Promise<PerformanceMetrics>
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get all paths for the current plugin
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const paths = await plugin.performance.getPaths()
|
|
73
|
+
* console.log(`Plugin installed at: ${paths.pluginPath}`)
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
getPaths: () => Promise<PluginPaths>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get combined performance data (storage + metrics + paths)
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const data = await plugin.performance.getAll()
|
|
84
|
+
* console.log(data.storage, data.metrics, data.paths)
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
getAll: () => Promise<{
|
|
88
|
+
storage: StorageStats
|
|
89
|
+
metrics: PerformanceMetrics
|
|
90
|
+
paths: PluginPaths
|
|
91
|
+
}>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates a Performance SDK instance for plugin use
|
|
96
|
+
*
|
|
97
|
+
* @param channel - The plugin channel bridge for IPC communication
|
|
98
|
+
* @returns Configured Performance SDK instance
|
|
99
|
+
*
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
export function createPerformanceSDK(channel: ITouchClientChannel): PerformanceSDK {
|
|
103
|
+
return {
|
|
104
|
+
async getStorageStats(): Promise<StorageStats> {
|
|
105
|
+
try {
|
|
106
|
+
const result = await channel.send('plugin:storage:get-stats')
|
|
107
|
+
return result?.data || {
|
|
108
|
+
totalSize: 0,
|
|
109
|
+
fileCount: 0,
|
|
110
|
+
dirCount: 0,
|
|
111
|
+
maxSize: 10 * 1024 * 1024,
|
|
112
|
+
usagePercent: 0,
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error('[Performance SDK] Failed to get storage stats:', error)
|
|
117
|
+
throw error
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
async getMetrics(): Promise<PerformanceMetrics> {
|
|
122
|
+
try {
|
|
123
|
+
const result = await channel.send('plugin:performance:get-metrics')
|
|
124
|
+
return result?.data || {
|
|
125
|
+
loadTime: 0,
|
|
126
|
+
memoryUsage: 0,
|
|
127
|
+
cpuUsage: 0,
|
|
128
|
+
lastActiveTime: 0,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.error('[Performance SDK] Failed to get performance metrics:', error)
|
|
133
|
+
throw error
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
async getPaths(): Promise<PluginPaths> {
|
|
138
|
+
try {
|
|
139
|
+
const result = await channel.send('plugin:performance:get-paths')
|
|
140
|
+
return result?.data || {
|
|
141
|
+
pluginPath: '',
|
|
142
|
+
dataPath: '',
|
|
143
|
+
configPath: '',
|
|
144
|
+
logsPath: '',
|
|
145
|
+
tempPath: '',
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('[Performance SDK] Failed to get plugin paths:', error)
|
|
150
|
+
throw error
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
async getAll(): Promise<{
|
|
155
|
+
storage: StorageStats
|
|
156
|
+
metrics: PerformanceMetrics
|
|
157
|
+
paths: PluginPaths
|
|
158
|
+
}> {
|
|
159
|
+
const [storage, metrics, paths] = await Promise.all([
|
|
160
|
+
this.getStorageStats(),
|
|
161
|
+
this.getMetrics(),
|
|
162
|
+
this.getPaths(),
|
|
163
|
+
])
|
|
164
|
+
|
|
165
|
+
return { storage, metrics, paths }
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Hook for using Performance SDK in plugin context
|
|
172
|
+
*
|
|
173
|
+
* @returns Performance SDK instance
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const performance = usePerformance()
|
|
178
|
+
*
|
|
179
|
+
* const stats = await performance.getStorageStats()
|
|
180
|
+
* const metrics = await performance.getMetrics()
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export function usePerformance(): PerformanceSDK {
|
|
184
|
+
const channel = ensureRendererChannel('[Performance SDK] Channel not available. Make sure this is called in a plugin context.')
|
|
185
|
+
return createPerformanceSDK(channel)
|
|
186
|
+
}
|
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 {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
export interface IArgMapperOptions {
|
|
6
6
|
/** The type of touch window - either main window or core-box popup */
|
|
7
7
|
touchType?: 'main' | 'core-box'
|
|
8
|
+
/** The sub-type for core-box windows (e.g., division-box) */
|
|
9
|
+
coreType?: 'division-box'
|
|
8
10
|
/** User data directory path */
|
|
9
11
|
userDataDir?: string
|
|
10
12
|
/** Application path */
|
|
@@ -32,22 +34,17 @@ declare global {
|
|
|
32
34
|
* @returns Mapped command line arguments as key-value pairs
|
|
33
35
|
*/
|
|
34
36
|
export function useArgMapper(args: string[] = process.argv): IArgMapperOptions {
|
|
35
|
-
if (window.$argMapper)
|
|
36
|
-
return window.$argMapper
|
|
37
|
-
}
|
|
37
|
+
if (window.$argMapper) return window.$argMapper
|
|
38
38
|
|
|
39
39
|
const mapper: IArgMapperOptions = {}
|
|
40
|
-
|
|
41
40
|
for (const arg of args) {
|
|
42
41
|
if (arg.startsWith('--') && arg.includes('=')) {
|
|
43
42
|
const [key, ...valueParts] = arg.slice(2).split('=')
|
|
44
43
|
const value = valueParts.join('=')
|
|
45
|
-
|
|
46
44
|
const camelCaseKey = key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
47
45
|
mapper[camelCaseKey] = value
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
|
-
|
|
51
48
|
return window.$argMapper = mapper
|
|
52
49
|
}
|
|
53
50
|
|
|
@@ -76,3 +73,20 @@ export function isMainWindow() {
|
|
|
76
73
|
export function isCoreBox() {
|
|
77
74
|
return useTouchType() === 'core-box'
|
|
78
75
|
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gets the core-box sub-type from command line arguments
|
|
79
|
+
* @returns The core type ('division-box') or undefined
|
|
80
|
+
*/
|
|
81
|
+
export function useCoreType() {
|
|
82
|
+
const argMapper = useArgMapper()
|
|
83
|
+
return argMapper.coreType
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Checks if the current window is a division-box window
|
|
88
|
+
* @returns True if the current window is a division-box
|
|
89
|
+
*/
|
|
90
|
+
export function isDivisionBox() {
|
|
91
|
+
return isCoreBox() && useCoreType() === 'division-box'
|
|
92
|
+
}
|