@talex-touch/utils 1.0.33 → 1.0.34
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/common/file-scan-utils.ts +0 -1
- package/common/storage/constants.ts +1 -0
- package/index.ts +1 -0
- package/market/constants.ts +95 -0
- package/market/index.ts +2 -0
- package/market/types.ts +118 -0
- package/package.json +1 -1
- package/plugin/index.ts +1 -0
- package/plugin/widget.ts +25 -0
- package/renderer/storage/storage-subscription.ts +196 -0
- package/types/icon.ts +7 -0
package/index.ts
CHANGED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { StorageList } from '../common/storage/constants'
|
|
2
|
+
import type {
|
|
3
|
+
MarketProviderDefinition,
|
|
4
|
+
MarketSourcesPayload,
|
|
5
|
+
MarketSourcesStorageInfo,
|
|
6
|
+
MarketProviderTrustLevel,
|
|
7
|
+
} from './types'
|
|
8
|
+
|
|
9
|
+
export const MARKET_SOURCES_STORAGE_KEY = StorageList.MARKET_SOURCES
|
|
10
|
+
export const MARKET_SOURCES_STORAGE_VERSION = 1
|
|
11
|
+
|
|
12
|
+
function defineProvider(
|
|
13
|
+
provider: Omit<MarketProviderDefinition, 'trustLevel'> & {
|
|
14
|
+
trustLevel?: MarketProviderTrustLevel
|
|
15
|
+
},
|
|
16
|
+
): MarketProviderDefinition {
|
|
17
|
+
return {
|
|
18
|
+
trustLevel: provider.trustLevel ?? 'unverified',
|
|
19
|
+
...provider,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_MARKET_PROVIDERS: MarketProviderDefinition[] = [
|
|
24
|
+
defineProvider({
|
|
25
|
+
id: 'talex-official',
|
|
26
|
+
name: 'Talex Official',
|
|
27
|
+
type: 'nexusStore',
|
|
28
|
+
url: 'https://raw.githubusercontent.com/talex-touch/tuff-official-plugins/main/plugins.json',
|
|
29
|
+
description: '官方插件市场,提供经过审核的核心插件。',
|
|
30
|
+
enabled: true,
|
|
31
|
+
priority: 100,
|
|
32
|
+
trustLevel: 'official',
|
|
33
|
+
readOnly: true,
|
|
34
|
+
config: {
|
|
35
|
+
manifestUrl:
|
|
36
|
+
'https://raw.githubusercontent.com/talex-touch/tuff-official-plugins/main/plugins.json',
|
|
37
|
+
baseUrl: 'https://raw.githubusercontent.com/talex-touch/tuff-official-plugins/main/',
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
defineProvider({
|
|
41
|
+
id: 'github-releases',
|
|
42
|
+
name: 'GitHub Releases',
|
|
43
|
+
type: 'repository',
|
|
44
|
+
description: '从 GitHub 仓库 releases / manifest 中读取插件。',
|
|
45
|
+
enabled: false,
|
|
46
|
+
priority: 80,
|
|
47
|
+
trustLevel: 'unverified',
|
|
48
|
+
config: {
|
|
49
|
+
platform: 'github',
|
|
50
|
+
apiBase: 'https://api.github.com',
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
defineProvider({
|
|
54
|
+
id: 'gitee-repos',
|
|
55
|
+
name: 'Gitee 仓库',
|
|
56
|
+
type: 'repository',
|
|
57
|
+
description: 'Gitee 平台插件仓库,适合国内网络。',
|
|
58
|
+
enabled: false,
|
|
59
|
+
priority: 70,
|
|
60
|
+
trustLevel: 'unverified',
|
|
61
|
+
config: {
|
|
62
|
+
platform: 'gitee',
|
|
63
|
+
apiBase: 'https://gitee.com/api/v5',
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
defineProvider({
|
|
67
|
+
id: 'npm-scope',
|
|
68
|
+
name: 'NPM 包',
|
|
69
|
+
type: 'npmPackage',
|
|
70
|
+
description: '基于 NPM 关键字或 scope 的插件发布渠道。',
|
|
71
|
+
enabled: false,
|
|
72
|
+
priority: 60,
|
|
73
|
+
trustLevel: 'unverified',
|
|
74
|
+
config: {
|
|
75
|
+
registryUrl: 'https://registry.npmjs.org',
|
|
76
|
+
keyword: 'talex-touch-plugin',
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
export const MARKET_SOURCES_STORAGE_INFO: MarketSourcesStorageInfo = {
|
|
82
|
+
storageKey: MARKET_SOURCES_STORAGE_KEY,
|
|
83
|
+
version: MARKET_SOURCES_STORAGE_VERSION,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function createDefaultMarketSourcesPayload(): MarketSourcesPayload {
|
|
87
|
+
const clone = typeof structuredClone === 'function'
|
|
88
|
+
? structuredClone(DEFAULT_MARKET_PROVIDERS)
|
|
89
|
+
: JSON.parse(JSON.stringify(DEFAULT_MARKET_PROVIDERS))
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
version: MARKET_SOURCES_STORAGE_VERSION,
|
|
93
|
+
sources: clone,
|
|
94
|
+
}
|
|
95
|
+
}
|
package/market/index.ts
ADDED
package/market/types.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { StorageList } from '../common/storage/constants'
|
|
2
|
+
|
|
3
|
+
export type MarketProviderType = 'repository' | 'nexusStore' | 'npmPackage'
|
|
4
|
+
|
|
5
|
+
export type MarketProviderTrustLevel = 'official' | 'verified' | 'unverified'
|
|
6
|
+
|
|
7
|
+
export interface MarketProviderDefinition {
|
|
8
|
+
id: string
|
|
9
|
+
name: string
|
|
10
|
+
type: MarketProviderType
|
|
11
|
+
/**
|
|
12
|
+
* Base URL or identifier for the provider.
|
|
13
|
+
* Individual provider implementations can interpret this differently.
|
|
14
|
+
*/
|
|
15
|
+
url?: string
|
|
16
|
+
/**
|
|
17
|
+
* Additional configuration object for provider specific options.
|
|
18
|
+
*/
|
|
19
|
+
config?: Record<string, any>
|
|
20
|
+
description?: string
|
|
21
|
+
enabled: boolean
|
|
22
|
+
priority: number
|
|
23
|
+
trustLevel?: MarketProviderTrustLevel
|
|
24
|
+
tags?: string[]
|
|
25
|
+
/**
|
|
26
|
+
* Whether this provider should be treated as read-only (no install)
|
|
27
|
+
*/
|
|
28
|
+
readOnly?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MarketSourcesPayload {
|
|
32
|
+
/**
|
|
33
|
+
* Schema version, used for migrations.
|
|
34
|
+
*/
|
|
35
|
+
version: number
|
|
36
|
+
sources: MarketProviderDefinition[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type MarketInstallInstruction =
|
|
40
|
+
| {
|
|
41
|
+
type: 'url'
|
|
42
|
+
url: string
|
|
43
|
+
format?: 'zip' | 'tar' | 'tgz' | 'tpex'
|
|
44
|
+
integrity?: string
|
|
45
|
+
}
|
|
46
|
+
| {
|
|
47
|
+
type: 'npm'
|
|
48
|
+
packageName: string
|
|
49
|
+
version?: string
|
|
50
|
+
registry?: string
|
|
51
|
+
}
|
|
52
|
+
| {
|
|
53
|
+
type: 'git'
|
|
54
|
+
repo: string
|
|
55
|
+
ref?: string
|
|
56
|
+
sparse?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface MarketPlugin {
|
|
60
|
+
id: string
|
|
61
|
+
name: string
|
|
62
|
+
version?: string
|
|
63
|
+
description?: string
|
|
64
|
+
category?: string
|
|
65
|
+
tags?: string[]
|
|
66
|
+
author?: string
|
|
67
|
+
icon?: string
|
|
68
|
+
metadata?: Record<string, unknown>
|
|
69
|
+
readmeUrl?: string
|
|
70
|
+
homepage?: string
|
|
71
|
+
downloadUrl?: string
|
|
72
|
+
install?: MarketInstallInstruction
|
|
73
|
+
providerId: string
|
|
74
|
+
providerName: string
|
|
75
|
+
providerType: MarketProviderType
|
|
76
|
+
providerTrustLevel: MarketProviderTrustLevel
|
|
77
|
+
trusted: boolean
|
|
78
|
+
official?: boolean
|
|
79
|
+
timestamp?: number | string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface MarketProviderResultMeta {
|
|
83
|
+
providerId: string
|
|
84
|
+
providerName: string
|
|
85
|
+
providerType: MarketProviderType
|
|
86
|
+
success: boolean
|
|
87
|
+
error?: string
|
|
88
|
+
fetchedAt: number
|
|
89
|
+
itemCount: number
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface MarketProviderListOptions {
|
|
93
|
+
keyword?: string
|
|
94
|
+
force?: boolean
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface MarketHttpRequestOptions {
|
|
98
|
+
url: string
|
|
99
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
100
|
+
headers?: Record<string, string>
|
|
101
|
+
params?: Record<string, any>
|
|
102
|
+
data?: any
|
|
103
|
+
timeout?: number
|
|
104
|
+
responseType?: 'json' | 'text' | 'arraybuffer'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface MarketHttpResponse<T = unknown> {
|
|
108
|
+
status: number
|
|
109
|
+
statusText: string
|
|
110
|
+
headers: Record<string, string>
|
|
111
|
+
data: T
|
|
112
|
+
url: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface MarketSourcesStorageInfo {
|
|
116
|
+
storageKey: StorageList
|
|
117
|
+
version: number
|
|
118
|
+
}
|
package/package.json
CHANGED
package/plugin/index.ts
CHANGED
package/plugin/widget.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const DEFAULT_WIDGET_RENDERERS = {
|
|
2
|
+
CORE_PREVIEW_CARD: 'core-preview-card',
|
|
3
|
+
CORE_INTELLIGENCE_ANSWER: 'core-intelligence-answer',
|
|
4
|
+
} as const
|
|
5
|
+
|
|
6
|
+
const values = Object.values(DEFAULT_WIDGET_RENDERERS)
|
|
7
|
+
export const DEFAULT_WIDGET_RENDERER_IDS = new Set<string>(values)
|
|
8
|
+
|
|
9
|
+
export function isDefaultWidgetRenderer(id: string | undefined): boolean {
|
|
10
|
+
return Boolean(id) && DEFAULT_WIDGET_RENDERER_IDS.has(id!)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface WidgetRegistrationPayload {
|
|
14
|
+
widgetId: string
|
|
15
|
+
pluginName: string
|
|
16
|
+
featureId: string
|
|
17
|
+
filePath: string
|
|
18
|
+
code: string
|
|
19
|
+
styles: string
|
|
20
|
+
hash: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function makeWidgetId(pluginName: string, featureId: string): string {
|
|
24
|
+
return `${pluginName}::${featureId}`
|
|
25
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { IStorageChannel } from './base-storage'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Storage subscription callback type
|
|
5
|
+
*/
|
|
6
|
+
export type StorageSubscriptionCallback = (data: any) => void
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Storage subscription manager for renderer process
|
|
10
|
+
* Provides easy subscription to storage updates via channel events
|
|
11
|
+
*/
|
|
12
|
+
class StorageSubscriptionManager {
|
|
13
|
+
private channel: IStorageChannel | null = null
|
|
14
|
+
private subscribers = new Map<string, Set<StorageSubscriptionCallback>>()
|
|
15
|
+
private channelListenerRegistered = false
|
|
16
|
+
private pendingUpdates = new Map<string, NodeJS.Timeout>()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the subscription manager with a channel
|
|
20
|
+
*/
|
|
21
|
+
init(channel: IStorageChannel): void {
|
|
22
|
+
this.channel = channel
|
|
23
|
+
|
|
24
|
+
if (!this.channelListenerRegistered) {
|
|
25
|
+
// Listen to storage:update events from main process
|
|
26
|
+
this.channel.regChannel('storage:update', ({ data }) => {
|
|
27
|
+
const { name } = data as { name: string }
|
|
28
|
+
this.handleStorageUpdate(name)
|
|
29
|
+
})
|
|
30
|
+
this.channelListenerRegistered = true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Subscribe to storage changes for a specific config
|
|
36
|
+
* @param configName - The configuration file name (e.g., 'app-setting.ini')
|
|
37
|
+
* @param callback - Callback function to receive updates
|
|
38
|
+
* @returns Unsubscribe function
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const unsubscribe = subscribeStorage('app-setting.ini', (data) => {
|
|
43
|
+
* console.log('Config updated:', data)
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* // Later:
|
|
47
|
+
* unsubscribe()
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
subscribe(configName: string, callback: StorageSubscriptionCallback): () => void {
|
|
51
|
+
if (!this.subscribers.has(configName)) {
|
|
52
|
+
this.subscribers.set(configName, new Set())
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.subscribers.get(configName)!.add(callback)
|
|
56
|
+
|
|
57
|
+
// Immediately load and call with current data
|
|
58
|
+
if (this.channel) {
|
|
59
|
+
const currentData = this.channel.sendSync('storage:get', configName)
|
|
60
|
+
if (currentData) {
|
|
61
|
+
try {
|
|
62
|
+
callback(currentData)
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error(`[StorageSubscription] Callback error for "${configName}":`, error)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Return unsubscribe function
|
|
71
|
+
return () => {
|
|
72
|
+
this.unsubscribe(configName, callback)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Unsubscribe from storage changes
|
|
78
|
+
* @param configName - The configuration file name
|
|
79
|
+
* @param callback - The same callback function used in subscribe
|
|
80
|
+
*/
|
|
81
|
+
unsubscribe(configName: string, callback: StorageSubscriptionCallback): void {
|
|
82
|
+
const callbacks = this.subscribers.get(configName)
|
|
83
|
+
if (callbacks) {
|
|
84
|
+
callbacks.delete(callback)
|
|
85
|
+
if (callbacks.size === 0) {
|
|
86
|
+
this.subscribers.delete(configName)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Handle storage update events from main process
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
private async handleStorageUpdate(configName: string): Promise<void> {
|
|
96
|
+
const callbacks = this.subscribers.get(configName)
|
|
97
|
+
if (!callbacks || callbacks.size === 0) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!this.channel) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Debounce updates to avoid excessive IPC and callback invocations
|
|
106
|
+
const existing = this.pendingUpdates.get(configName)
|
|
107
|
+
if (existing) {
|
|
108
|
+
clearTimeout(existing)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const timer = setTimeout(async () => {
|
|
112
|
+
// Fetch latest data
|
|
113
|
+
const data = await this.channel!.send('storage:get', configName)
|
|
114
|
+
if (!data) {
|
|
115
|
+
this.pendingUpdates.delete(configName)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Notify all subscribers
|
|
120
|
+
callbacks.forEach((callback) => {
|
|
121
|
+
try {
|
|
122
|
+
callback(data)
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
console.error(`[StorageSubscription] Callback error for "${configName}":`, error)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
this.pendingUpdates.delete(configName)
|
|
130
|
+
}, 50) // 50ms debounce window
|
|
131
|
+
|
|
132
|
+
this.pendingUpdates.set(configName, timer)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get the number of active subscriptions for a config
|
|
137
|
+
* @param configName - The configuration file name
|
|
138
|
+
* @returns Number of active callbacks subscribed to this config
|
|
139
|
+
*/
|
|
140
|
+
getSubscriberCount(configName: string): number {
|
|
141
|
+
return this.subscribers.get(configName)?.size ?? 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Clear all subscriptions
|
|
146
|
+
*/
|
|
147
|
+
clear(): void {
|
|
148
|
+
this.subscribers.clear()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Global singleton instance
|
|
153
|
+
const subscriptionManager = new StorageSubscriptionManager()
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Initialize storage subscription system with channel
|
|
157
|
+
* Must be called before using subscribeStorage
|
|
158
|
+
*
|
|
159
|
+
* @param channel - The storage channel
|
|
160
|
+
*/
|
|
161
|
+
export function initStorageSubscription(channel: IStorageChannel): void {
|
|
162
|
+
subscriptionManager.init(channel)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Subscribe to storage configuration changes
|
|
167
|
+
*
|
|
168
|
+
* @param configName - Configuration file name (e.g., 'app-setting.ini')
|
|
169
|
+
* @param callback - Callback function that receives updated data
|
|
170
|
+
* @returns Unsubscribe function
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* import { subscribeStorage } from '@talex-touch/utils/renderer/storage/storage-subscription'
|
|
175
|
+
*
|
|
176
|
+
* const unsubscribe = subscribeStorage('app-setting.ini', (data) => {
|
|
177
|
+
* console.log('Settings updated:', data)
|
|
178
|
+
* })
|
|
179
|
+
*
|
|
180
|
+
* // Clean up when no longer needed
|
|
181
|
+
* unsubscribe()
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
export function subscribeStorage(
|
|
185
|
+
configName: string,
|
|
186
|
+
callback: StorageSubscriptionCallback,
|
|
187
|
+
): () => void {
|
|
188
|
+
return subscriptionManager.subscribe(configName, callback)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get subscription manager instance (for debugging)
|
|
193
|
+
*/
|
|
194
|
+
export function getSubscriptionManager(): StorageSubscriptionManager {
|
|
195
|
+
return subscriptionManager
|
|
196
|
+
}
|
package/types/icon.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface ITuffIcon {
|
|
|
37
37
|
/** Icon value */
|
|
38
38
|
value: string
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Icon Colorful (Only for URL type)
|
|
42
|
+
* @desc This prop defines whether a URL icon should be rendered in colorful mode.
|
|
43
|
+
* It is only applicable when the icon type is 'url'.
|
|
44
|
+
*/
|
|
45
|
+
colorful?: boolean
|
|
46
|
+
|
|
40
47
|
/** Icon status (optional) */
|
|
41
48
|
status?: TuffIconStatus
|
|
42
49
|
|