@talex-touch/utils 1.0.40 → 1.0.44
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/.eslintcache +1 -0
- package/__tests__/cloud-sync-sdk.test.ts +442 -0
- package/__tests__/icons/icons.test.ts +84 -0
- package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
- package/__tests__/power-sdk.test.ts +143 -0
- package/__tests__/preset-export-types.test.ts +108 -0
- package/__tests__/search/fuzzy-match.test.ts +137 -0
- package/__tests__/transport/port-policy.test.ts +44 -0
- package/__tests__/transport-domain-sdks.test.ts +152 -0
- package/__tests__/types/update.test.ts +67 -0
- package/account/account-sdk.ts +915 -0
- package/account/index.ts +2 -0
- package/account/types.ts +321 -0
- package/analytics/client.ts +136 -0
- package/analytics/index.ts +2 -0
- package/analytics/types.ts +156 -0
- package/animation/auto-resize.ts +322 -0
- package/animation/window-node.ts +26 -19
- package/auth/clerk-types.ts +12 -30
- package/auth/index.ts +0 -2
- package/auth/useAuthState.ts +6 -14
- package/base/index.ts +2 -0
- package/base/log-level.ts +105 -0
- package/channel/index.ts +170 -69
- package/cloud-sync/cloud-sync-sdk.ts +450 -0
- package/cloud-sync/index.ts +1 -0
- package/common/file-scan-utils.ts +17 -9
- package/common/index.ts +4 -0
- package/common/logger/index.ts +46 -0
- package/common/logger/logger-manager.ts +303 -0
- package/common/logger/module-logger.ts +270 -0
- package/common/logger/transport-logger.ts +234 -0
- package/common/logger/types.ts +93 -0
- package/common/search/gather.ts +48 -6
- package/common/search/index.ts +8 -0
- package/common/storage/constants.ts +13 -0
- package/common/storage/entity/app-settings.ts +245 -0
- package/common/storage/entity/index.ts +3 -0
- package/common/storage/entity/layout-atom-types.ts +147 -0
- package/common/storage/entity/openers.ts +1 -0
- package/common/storage/entity/preset-cloud-api.ts +132 -0
- package/common/storage/entity/preset-export-types.ts +256 -0
- package/common/storage/entity/shortcut-settings.ts +1 -0
- package/common/storage/shortcut-storage.ts +11 -0
- package/common/utils/clone-diagnostics.ts +105 -0
- package/common/utils/file.ts +16 -8
- package/common/utils/index.ts +6 -2
- package/common/utils/payload-preview.ts +173 -0
- package/common/utils/polling.ts +167 -13
- package/common/utils/safe-path.ts +103 -0
- package/common/utils/safe-shell.ts +115 -0
- package/common/utils/task-queue.ts +4 -1
- package/core-box/builder/tuff-builder.ts +0 -1
- package/core-box/index.ts +1 -1
- package/core-box/recommendation.ts +38 -1
- package/core-box/tuff/tuff-dsl.ts +97 -0
- package/electron/download-manager.ts +10 -7
- package/electron/env-tool.ts +42 -40
- package/electron/index.ts +0 -1
- package/env/index.ts +156 -0
- package/eslint.config.js +55 -0
- package/i18n/index.ts +62 -0
- package/i18n/locales/en.json +226 -0
- package/i18n/locales/zh.json +226 -0
- package/i18n/message-keys.ts +236 -0
- package/i18n/resolver.ts +181 -0
- package/icons/index.ts +257 -0
- package/icons/svg.ts +69 -0
- package/index.ts +9 -1
- package/intelligence/client.ts +72 -42
- package/market/constants.ts +21 -3
- package/market/index.ts +1 -1
- package/market/types.ts +20 -5
- package/package.json +15 -5
- package/permission/index.ts +143 -46
- package/permission/legacy.ts +26 -0
- package/permission/registry.ts +304 -0
- package/permission/types.ts +164 -0
- package/plugin/channel.ts +68 -39
- package/plugin/index.ts +82 -8
- package/plugin/install.ts +3 -0
- package/plugin/log/types.ts +22 -5
- package/plugin/node/logger-manager.ts +11 -3
- package/plugin/node/logger.ts +24 -17
- package/plugin/preload.ts +25 -2
- package/plugin/providers/index.ts +4 -0
- package/plugin/providers/market-client.ts +218 -0
- package/plugin/providers/npm-provider.ts +228 -0
- package/plugin/providers/tpex-provider.ts +297 -0
- package/plugin/providers/tpex-types.ts +34 -0
- package/plugin/sdk/box-items.ts +14 -0
- package/plugin/sdk/box-sdk.ts +64 -0
- package/plugin/sdk/channel.ts +119 -4
- package/plugin/sdk/clipboard.ts +26 -12
- package/plugin/sdk/cloud-sync.ts +113 -0
- package/plugin/sdk/common.ts +19 -11
- package/plugin/sdk/core-box.ts +6 -15
- package/plugin/sdk/division-box.ts +160 -65
- package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
- package/plugin/sdk/feature-sdk.ts +111 -76
- package/plugin/sdk/flow.ts +146 -45
- package/plugin/sdk/hooks/bridge.ts +113 -49
- package/plugin/sdk/hooks/life-cycle.ts +35 -16
- package/plugin/sdk/index.ts +14 -3
- package/plugin/sdk/intelligence.ts +87 -0
- package/plugin/sdk/meta/README.md +179 -0
- package/plugin/sdk/meta-sdk.ts +244 -0
- package/plugin/sdk/notification.ts +9 -0
- package/plugin/sdk/performance.ts +1 -16
- package/plugin/sdk/plugin-info.ts +64 -0
- package/plugin/sdk/power.ts +155 -0
- package/plugin/sdk/recommend.ts +21 -0
- package/plugin/sdk/service/index.ts +12 -8
- package/plugin/sdk/sqlite.ts +141 -0
- package/plugin/sdk/storage.ts +2 -6
- package/plugin/sdk/system.ts +2 -9
- package/plugin/sdk/temp-files.ts +41 -0
- package/plugin/sdk/touch-sdk.ts +18 -0
- package/plugin/sdk/types.ts +44 -4
- package/plugin/sdk/window/index.ts +12 -9
- package/plugin/sdk-version.ts +231 -0
- package/preload/renderer.ts +3 -2
- package/renderer/hooks/arg-mapper.ts +34 -6
- package/renderer/hooks/index.ts +13 -0
- package/renderer/hooks/initialize.ts +2 -1
- package/renderer/hooks/use-agent-market-sdk.ts +7 -0
- package/renderer/hooks/use-agent-market.ts +106 -0
- package/renderer/hooks/use-agents-sdk.ts +7 -0
- package/renderer/hooks/use-app-sdk.ts +7 -0
- package/renderer/hooks/use-channel.ts +33 -4
- package/renderer/hooks/use-download-sdk.ts +21 -0
- package/renderer/hooks/use-intelligence-sdk.ts +7 -0
- package/renderer/hooks/use-intelligence-stats.ts +290 -0
- package/renderer/hooks/use-intelligence.ts +202 -104
- package/renderer/hooks/use-market-sdk.ts +16 -0
- package/renderer/hooks/use-notification-sdk.ts +7 -0
- package/renderer/hooks/use-permission-sdk.ts +7 -0
- package/renderer/hooks/use-permission.ts +325 -0
- package/renderer/hooks/use-platform-sdk.ts +7 -0
- package/renderer/hooks/use-plugin-sdk.ts +16 -0
- package/renderer/hooks/use-settings-sdk.ts +7 -0
- package/renderer/hooks/use-update-sdk.ts +21 -0
- package/renderer/index.ts +1 -0
- package/renderer/ref.ts +19 -10
- package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
- package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
- package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
- package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
- package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
- package/renderer/shared/components/index.ts +5 -0
- package/renderer/shared/components/shims-vue.d.ts +5 -0
- package/renderer/shared/index.ts +2 -0
- package/renderer/shared/plugin-detail.ts +62 -0
- package/renderer/storage/app-settings.ts +3 -1
- package/renderer/storage/base-storage.ts +508 -82
- package/renderer/storage/intelligence-storage.ts +37 -46
- package/renderer/storage/openers.ts +3 -1
- package/renderer/storage/storage-subscription.ts +126 -42
- package/renderer/touch-sdk/env.ts +10 -10
- package/renderer/touch-sdk/index.ts +114 -18
- package/renderer/touch-sdk/terminal.ts +24 -13
- package/search/feature-matcher.ts +279 -0
- package/search/fuzzy-match.ts +64 -34
- package/search/index.ts +10 -0
- package/search/levenshtein-utils.ts +17 -11
- package/transport/errors.ts +310 -0
- package/transport/event/builder.ts +378 -0
- package/transport/event/index.ts +7 -0
- package/transport/event/types.ts +292 -0
- package/transport/events/index.ts +2670 -0
- package/transport/events/meta-overlay.ts +79 -0
- package/transport/events/types/agents.ts +177 -0
- package/transport/events/types/app-index.ts +9 -0
- package/transport/events/types/app.ts +475 -0
- package/transport/events/types/box-item.ts +222 -0
- package/transport/events/types/clipboard.ts +80 -0
- package/transport/events/types/core-box.ts +534 -0
- package/transport/events/types/device-idle.ts +7 -0
- package/transport/events/types/division-box.ts +99 -0
- package/transport/events/types/download.ts +115 -0
- package/transport/events/types/file-index.ts +73 -0
- package/transport/events/types/flow.ts +149 -0
- package/transport/events/types/index.ts +70 -0
- package/transport/events/types/market.ts +39 -0
- package/transport/events/types/meta-overlay.ts +184 -0
- package/transport/events/types/notification.ts +140 -0
- package/transport/events/types/permission.ts +90 -0
- package/transport/events/types/platform.ts +8 -0
- package/transport/events/types/plugin.ts +620 -0
- package/transport/events/types/sentry.ts +20 -0
- package/transport/events/types/storage.ts +208 -0
- package/transport/events/types/transport.ts +60 -0
- package/transport/events/types/tray.ts +16 -0
- package/transport/events/types/update.ts +78 -0
- package/transport/index.ts +139 -0
- package/transport/main.ts +2 -0
- package/transport/sdk/constants.ts +29 -0
- package/transport/sdk/domains/agents-market.ts +47 -0
- package/transport/sdk/domains/agents.ts +62 -0
- package/transport/sdk/domains/app.ts +48 -0
- package/transport/sdk/domains/disposable.ts +35 -0
- package/transport/sdk/domains/download.ts +139 -0
- package/transport/sdk/domains/index.ts +13 -0
- package/transport/sdk/domains/intelligence.ts +616 -0
- package/transport/sdk/domains/market.ts +35 -0
- package/transport/sdk/domains/notification.ts +62 -0
- package/transport/sdk/domains/permission.ts +85 -0
- package/transport/sdk/domains/platform.ts +19 -0
- package/transport/sdk/domains/plugin.ts +144 -0
- package/transport/sdk/domains/settings.ts +92 -0
- package/transport/sdk/domains/update.ts +64 -0
- package/transport/sdk/index.ts +60 -0
- package/transport/sdk/main-transport.ts +710 -0
- package/transport/sdk/main.ts +9 -0
- package/transport/sdk/plugin-transport.ts +654 -0
- package/transport/sdk/port-policy.ts +38 -0
- package/transport/sdk/renderer-transport.ts +1165 -0
- package/transport/types.ts +605 -0
- package/types/agent.ts +399 -0
- package/types/cloud-sync.ts +157 -0
- package/types/division-box.ts +47 -27
- package/types/download.ts +1 -0
- package/types/flow.ts +63 -12
- package/types/icon.ts +2 -1
- package/types/index.ts +5 -0
- package/types/intelligence.ts +1492 -81
- package/types/modules/base.ts +2 -0
- package/types/path-browserify.d.ts +5 -0
- package/types/platform.ts +12 -0
- package/types/startup-info.ts +32 -0
- package/types/touch-app-core.ts +8 -8
- package/types/update.ts +94 -1
- package/vitest.config.ts +25 -0
- package/auth/useClerkConfig.ts +0 -40
- package/auth/useClerkProvider.ts +0 -52
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetaOverlay SDK for Plugin Development
|
|
3
|
+
*
|
|
4
|
+
* Provides API for plugins to register global actions in MetaOverlay.
|
|
5
|
+
* MetaOverlay is a floating action panel that appears above plugin UI.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TuffItem } from '../../core-box/tuff/tuff-dsl'
|
|
9
|
+
import type { MetaAction } from '../../transport/events/types/meta-overlay'
|
|
10
|
+
import { createPluginTuffTransport } from '../../transport'
|
|
11
|
+
import { defineRawEvent } from '../../transport/event/builder'
|
|
12
|
+
import { MetaOverlayEvents } from '../../transport/events/meta-overlay'
|
|
13
|
+
import { createDisposableBag } from '../../transport/sdk'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Action execution handler
|
|
17
|
+
*/
|
|
18
|
+
export type ActionExecuteHandler = (data: {
|
|
19
|
+
actionId: string
|
|
20
|
+
item: TuffItem
|
|
21
|
+
}) => void
|
|
22
|
+
|
|
23
|
+
const metaOverlayActionExecutedEvent = defineRawEvent<{ pluginId: string, actionId: string, item: TuffItem }, void>('meta-overlay:action-executed')
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* MetaOverlay SDK interface for plugins
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // Register a global action
|
|
31
|
+
* const unregister = plugin.meta.registerAction({
|
|
32
|
+
* id: 'my-plugin-action',
|
|
33
|
+
* render: {
|
|
34
|
+
* basic: {
|
|
35
|
+
* title: 'My Action',
|
|
36
|
+
* subtitle: 'Execute my plugin action',
|
|
37
|
+
* icon: { type: 'class', value: 'i-ri-star-line' }
|
|
38
|
+
* },
|
|
39
|
+
* shortcut: '⌘M',
|
|
40
|
+
* group: '插件操作'
|
|
41
|
+
* },
|
|
42
|
+
* priority: 100
|
|
43
|
+
* })
|
|
44
|
+
*
|
|
45
|
+
* // Listen for action execution
|
|
46
|
+
* plugin.meta.onActionExecute((data) => {
|
|
47
|
+
* if (data.actionId === 'my-plugin-action') {
|
|
48
|
+
* // Handle action
|
|
49
|
+
* }
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* // Unregister all actions
|
|
53
|
+
* plugin.meta.unregisterAll()
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export interface MetaSDK {
|
|
57
|
+
/**
|
|
58
|
+
* Registers a global action that will appear in MetaOverlay
|
|
59
|
+
*
|
|
60
|
+
* @param action - Action definition
|
|
61
|
+
* @returns Cleanup function to unregister this action
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const unregister = plugin.meta.registerAction({
|
|
66
|
+
* id: 'custom-action',
|
|
67
|
+
* render: {
|
|
68
|
+
* basic: {
|
|
69
|
+
* title: 'Custom Action',
|
|
70
|
+
* subtitle: 'Description',
|
|
71
|
+
* icon: { type: 'emoji', value: '🚀' }
|
|
72
|
+
* },
|
|
73
|
+
* shortcut: '⌘K',
|
|
74
|
+
* group: 'Custom'
|
|
75
|
+
* },
|
|
76
|
+
* priority: 100
|
|
77
|
+
* })
|
|
78
|
+
*
|
|
79
|
+
* // Later: unregister
|
|
80
|
+
* unregister()
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
registerAction: (action: MetaAction) => () => void
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Unregisters all actions registered by this plugin
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Cleanup on plugin unload
|
|
91
|
+
* plugin.meta.unregisterAll()
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
unregisterAll: () => void
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Registers a listener for when actions registered by this plugin are executed
|
|
98
|
+
*
|
|
99
|
+
* @param handler - Handler function
|
|
100
|
+
* @returns Cleanup function to remove listener
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* const unsubscribe = plugin.meta.onActionExecute((data) => {
|
|
105
|
+
* console.log(`Action ${data.actionId} executed for item ${data.item.id}`)
|
|
106
|
+
* // Handle the action
|
|
107
|
+
* })
|
|
108
|
+
*
|
|
109
|
+
* // Later: unsubscribe
|
|
110
|
+
* unsubscribe()
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
onActionExecute: (handler: ActionExecuteHandler) => () => void
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 释放 SDK 内部监听器
|
|
117
|
+
*/
|
|
118
|
+
dispose: () => void
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates a MetaSDK instance for plugin use
|
|
123
|
+
*
|
|
124
|
+
* @param channel - The plugin channel bridge for IPC communication
|
|
125
|
+
* @param pluginId - Plugin identifier
|
|
126
|
+
* @returns Configured MetaSDK instance
|
|
127
|
+
*
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
export function createMetaSDK(channel: any, pluginId: string): MetaSDK {
|
|
131
|
+
const actionExecuteHandlers: Set<ActionExecuteHandler> = new Set()
|
|
132
|
+
const registeredActionIds: Set<string> = new Set()
|
|
133
|
+
const transport = createPluginTuffTransport(channel)
|
|
134
|
+
const disposables = createDisposableBag()
|
|
135
|
+
let disposed = false
|
|
136
|
+
|
|
137
|
+
const emitActionExecute = (data: { pluginId: string, actionId: string, item: TuffItem }) => {
|
|
138
|
+
if (data.pluginId !== pluginId || !registeredActionIds.has(data.actionId)) {
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const handler of actionExecuteHandlers) {
|
|
143
|
+
try {
|
|
144
|
+
handler({
|
|
145
|
+
actionId: data.actionId,
|
|
146
|
+
item: data.item,
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error('[MetaSDK] onActionExecute handler error', error)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const dispose = () => {
|
|
156
|
+
if (disposed) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
disposed = true
|
|
161
|
+
actionExecuteHandlers.clear()
|
|
162
|
+
|
|
163
|
+
if (registeredActionIds.size > 0) {
|
|
164
|
+
void transport.send(MetaOverlayEvents.action.unregister, { pluginId }).catch((error) => {
|
|
165
|
+
console.error('[MetaSDK] Failed to unregister all actions during dispose', error)
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
registeredActionIds.clear()
|
|
170
|
+
disposables.dispose()
|
|
171
|
+
transport.destroy()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const ensureActive = (method: string) => {
|
|
175
|
+
if (disposed) {
|
|
176
|
+
throw new Error(`[MetaSDK] Cannot call ${method} after dispose`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
disposables.add(
|
|
181
|
+
transport.on(metaOverlayActionExecutedEvent, (payload) => {
|
|
182
|
+
emitActionExecute(payload)
|
|
183
|
+
}),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
|
|
187
|
+
const onBeforeUnload = () => dispose()
|
|
188
|
+
window.addEventListener('beforeunload', onBeforeUnload, { once: true })
|
|
189
|
+
disposables.add(() => window.removeEventListener('beforeunload', onBeforeUnload))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
registerAction(action: MetaAction): () => void {
|
|
194
|
+
ensureActive('registerAction')
|
|
195
|
+
registeredActionIds.add(action.id)
|
|
196
|
+
|
|
197
|
+
void transport.send(MetaOverlayEvents.action.register, {
|
|
198
|
+
pluginId,
|
|
199
|
+
action: {
|
|
200
|
+
...action,
|
|
201
|
+
handler: pluginId,
|
|
202
|
+
priority: action.priority ?? 100,
|
|
203
|
+
},
|
|
204
|
+
}).catch((error) => {
|
|
205
|
+
console.error('[MetaSDK] Failed to register action', error)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
return () => {
|
|
209
|
+
if (!registeredActionIds.has(action.id)) {
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
registeredActionIds.delete(action.id)
|
|
214
|
+
void transport.send(MetaOverlayEvents.action.unregister, {
|
|
215
|
+
pluginId,
|
|
216
|
+
actionId: action.id,
|
|
217
|
+
} as any).catch((error) => {
|
|
218
|
+
console.error('[MetaSDK] Failed to unregister action', error)
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
unregisterAll(): void {
|
|
224
|
+
ensureActive('unregisterAll')
|
|
225
|
+
registeredActionIds.clear()
|
|
226
|
+
|
|
227
|
+
void transport.send(MetaOverlayEvents.action.unregister, {
|
|
228
|
+
pluginId,
|
|
229
|
+
}).catch((error) => {
|
|
230
|
+
console.error('[MetaSDK] Failed to unregister all actions', error)
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
onActionExecute(handler: ActionExecuteHandler): () => void {
|
|
235
|
+
ensureActive('onActionExecute')
|
|
236
|
+
actionExecuteHandlers.add(handler)
|
|
237
|
+
return () => {
|
|
238
|
+
actionExecuteHandlers.delete(handler)
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
dispose,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createNotificationSdk } from '../../transport/sdk/domains/notification'
|
|
2
|
+
import { createPluginTuffTransport } from '../../transport/sdk/plugin-transport'
|
|
3
|
+
import { useChannel } from './channel'
|
|
4
|
+
|
|
5
|
+
export function useNotificationSdk() {
|
|
6
|
+
const channel = useChannel()
|
|
7
|
+
const transport = createPluginTuffTransport(channel)
|
|
8
|
+
return createNotificationSdk(transport)
|
|
9
|
+
}
|
|
@@ -5,24 +5,9 @@
|
|
|
5
5
|
* and storage statistics.
|
|
6
6
|
*/
|
|
7
7
|
import type { ITouchClientChannel } from '@talex-touch/utils/channel'
|
|
8
|
+
import type { StorageStats } from '../../types/storage'
|
|
8
9
|
import { ensureRendererChannel } from './channel'
|
|
9
10
|
|
|
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
11
|
/**
|
|
27
12
|
* Performance metrics interface
|
|
28
13
|
*/
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getCurrentInstance } from 'vue'
|
|
2
|
+
import { hasWindow } from '../../env'
|
|
3
|
+
|
|
4
|
+
export interface PluginRuntimeInfo {
|
|
5
|
+
name?: string
|
|
6
|
+
sdkapi?: number
|
|
7
|
+
[key: string]: unknown
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DEFAULT_PLUGIN_INFO_ERROR
|
|
11
|
+
= '[Plugin SDK] Plugin info not available. Make sure this is called in a plugin context.'
|
|
12
|
+
|
|
13
|
+
const DEFAULT_PLUGIN_NAME_ERROR
|
|
14
|
+
= '[Plugin SDK] Cannot determine plugin name. Make sure this is called in a plugin context.'
|
|
15
|
+
|
|
16
|
+
function tryResolvePluginInfoFromInstance(): PluginRuntimeInfo | null {
|
|
17
|
+
const instance = getCurrentInstance()
|
|
18
|
+
if (!instance) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const proxy = instance.proxy as { $plugin?: PluginRuntimeInfo } | null
|
|
23
|
+
if (proxy?.$plugin) {
|
|
24
|
+
return proxy.$plugin
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const appPlugin = instance.appContext?.config.globalProperties?.$plugin as PluginRuntimeInfo | undefined
|
|
28
|
+
return appPlugin ?? null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function tryUsePluginInfo(): PluginRuntimeInfo | null {
|
|
32
|
+
const injected = tryResolvePluginInfoFromInstance()
|
|
33
|
+
if (injected) {
|
|
34
|
+
return injected
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!hasWindow()) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
return (window as any)?.$plugin ?? null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function usePluginInfo(errorMessage = DEFAULT_PLUGIN_INFO_ERROR): PluginRuntimeInfo {
|
|
44
|
+
const plugin = tryUsePluginInfo()
|
|
45
|
+
if (!plugin) {
|
|
46
|
+
throw new Error(errorMessage)
|
|
47
|
+
}
|
|
48
|
+
return plugin
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function usePluginName(errorMessage = DEFAULT_PLUGIN_NAME_ERROR): string {
|
|
52
|
+
const plugin = usePluginInfo(errorMessage)
|
|
53
|
+
const name = typeof plugin.name === 'string' ? plugin.name : undefined
|
|
54
|
+
if (!name) {
|
|
55
|
+
throw new Error(errorMessage)
|
|
56
|
+
}
|
|
57
|
+
return name
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function tryGetPluginSdkApi(): number | undefined {
|
|
61
|
+
const plugin = tryUsePluginInfo()
|
|
62
|
+
const sdkapi = plugin?.sdkapi
|
|
63
|
+
return typeof sdkapi === 'number' ? sdkapi : undefined
|
|
64
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { ITouchClientChannel } from '@talex-touch/utils/channel'
|
|
2
|
+
import type { BatteryStatusPayload, FileIndexBatteryStatus } from '../../transport/events'
|
|
3
|
+
import { AppEvents } from '../../transport/events'
|
|
4
|
+
import { createPluginTuffTransport } from '../../transport/sdk/plugin-transport'
|
|
5
|
+
import { ensureRendererChannel } from './channel'
|
|
6
|
+
|
|
7
|
+
const DEFAULT_LOW_POWER_THRESHOLD = 20
|
|
8
|
+
|
|
9
|
+
function normalizeThreshold(threshold?: number): number {
|
|
10
|
+
if (typeof threshold !== 'number' || !Number.isFinite(threshold)) {
|
|
11
|
+
return DEFAULT_LOW_POWER_THRESHOLD
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const normalized = Math.floor(threshold)
|
|
15
|
+
if (normalized <= 0) return 1
|
|
16
|
+
if (normalized > 100) return 100
|
|
17
|
+
return normalized
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizePercent(percent: unknown): number | null {
|
|
21
|
+
if (typeof percent !== 'number' || !Number.isFinite(percent)) {
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (percent < 0) return 0
|
|
26
|
+
if (percent > 100) return 100
|
|
27
|
+
return percent
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function toStatusFromBattery(
|
|
31
|
+
battery: FileIndexBatteryStatus | null | undefined,
|
|
32
|
+
threshold: number
|
|
33
|
+
): LowPowerStatus {
|
|
34
|
+
const percent = normalizePercent(battery?.level)
|
|
35
|
+
const onBattery = percent !== null ? battery?.charging === false : false
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
lowPower: onBattery && percent !== null && percent <= threshold,
|
|
39
|
+
onBattery,
|
|
40
|
+
percent,
|
|
41
|
+
threshold
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toStatusFromBroadcast(
|
|
46
|
+
payload: BatteryStatusPayload | undefined,
|
|
47
|
+
threshold: number
|
|
48
|
+
): LowPowerStatus | null {
|
|
49
|
+
if (typeof payload?.onBattery !== 'boolean') {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const percent = normalizePercent(payload.percent)
|
|
54
|
+
const onBattery = payload.onBattery
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
lowPower: onBattery && percent !== null && percent <= threshold,
|
|
58
|
+
onBattery,
|
|
59
|
+
percent,
|
|
60
|
+
threshold
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toSignature(status: LowPowerStatus): string {
|
|
65
|
+
return [status.lowPower, status.onBattery, status.percent ?? 'null', status.threshold].join(':')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface LowPowerStatus {
|
|
69
|
+
lowPower: boolean
|
|
70
|
+
onBattery: boolean
|
|
71
|
+
percent: number | null
|
|
72
|
+
threshold: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface PowerSDK {
|
|
76
|
+
getLowPowerStatus: (options?: { threshold?: number }) => Promise<LowPowerStatus>
|
|
77
|
+
isLowPower: (options?: { threshold?: number }) => Promise<boolean>
|
|
78
|
+
onLowPowerChanged: (
|
|
79
|
+
callback: (status: LowPowerStatus) => void,
|
|
80
|
+
options?: { threshold?: number, emitImmediately?: boolean }
|
|
81
|
+
) => () => void
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function createPowerSDK(channel: ITouchClientChannel): PowerSDK {
|
|
85
|
+
const transport = createPluginTuffTransport(channel)
|
|
86
|
+
|
|
87
|
+
const getStatus = async (options: { threshold?: number } = {}): Promise<LowPowerStatus> => {
|
|
88
|
+
const threshold = normalizeThreshold(options.threshold)
|
|
89
|
+
try {
|
|
90
|
+
const battery = await transport.send(AppEvents.fileIndex.batteryLevel)
|
|
91
|
+
return toStatusFromBattery(battery, threshold)
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return {
|
|
95
|
+
lowPower: false,
|
|
96
|
+
onBattery: false,
|
|
97
|
+
percent: null,
|
|
98
|
+
threshold
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
getLowPowerStatus: getStatus,
|
|
105
|
+
|
|
106
|
+
async isLowPower(options: { threshold?: number } = {}): Promise<boolean> {
|
|
107
|
+
const status = await getStatus(options)
|
|
108
|
+
return status.lowPower
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
onLowPowerChanged(
|
|
112
|
+
callback: (status: LowPowerStatus) => void,
|
|
113
|
+
options: { threshold?: number, emitImmediately?: boolean } = {}
|
|
114
|
+
): () => void {
|
|
115
|
+
const threshold = normalizeThreshold(options.threshold)
|
|
116
|
+
let disposed = false
|
|
117
|
+
let lastSignature = ''
|
|
118
|
+
|
|
119
|
+
const emit = async (status: LowPowerStatus, force = false) => {
|
|
120
|
+
if (disposed) return
|
|
121
|
+
const signature = toSignature(status)
|
|
122
|
+
if (!force && signature === lastSignature) return
|
|
123
|
+
lastSignature = signature
|
|
124
|
+
callback(status)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dispose = transport.on(AppEvents.power.batteryStatus, async (payload) => {
|
|
128
|
+
const fromBroadcast = toStatusFromBroadcast(payload, threshold)
|
|
129
|
+
if (fromBroadcast) {
|
|
130
|
+
await emit(fromBroadcast)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const fromQuery = await getStatus({ threshold })
|
|
135
|
+
await emit(fromQuery)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
if (options.emitImmediately !== false) {
|
|
139
|
+
void getStatus({ threshold }).then((status) => emit(status, true))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
disposed = true
|
|
144
|
+
dispose()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function usePowerSDK(): PowerSDK {
|
|
151
|
+
const channel = ensureRendererChannel(
|
|
152
|
+
'[PowerSDK] Channel not available. Make sure this is called in a plugin renderer context.'
|
|
153
|
+
)
|
|
154
|
+
return createPowerSDK(channel)
|
|
155
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RecommendProvider } from '../../core-box/recommendation'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SDK for plugins to register custom recommendation providers.
|
|
5
|
+
*
|
|
6
|
+
* Providers registered through this SDK will be called by the RecommendationEngine
|
|
7
|
+
* when generating recommendations for the CoreBox empty-query state.
|
|
8
|
+
*/
|
|
9
|
+
export interface RecommendSDK {
|
|
10
|
+
/**
|
|
11
|
+
* Register a recommendation provider.
|
|
12
|
+
* @returns A dispose function to unregister the provider.
|
|
13
|
+
*/
|
|
14
|
+
registerProvider(provider: RecommendProvider): () => void
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Unregister a recommendation provider by its ID.
|
|
18
|
+
* @returns true if the provider was found and removed.
|
|
19
|
+
*/
|
|
20
|
+
unregisterProvider(providerId: string): boolean
|
|
21
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { IService } from '../../../service'
|
|
2
|
-
import {
|
|
2
|
+
import { useChannel } from '../channel'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
type ServiceHandler = (data: any) => unknown
|
|
5
|
+
|
|
6
|
+
export async function regService(service: IService, handler: ServiceHandler): Promise<boolean> {
|
|
7
|
+
const channel = useChannel('[Plugin SDK] Service registration requires renderer channel.')
|
|
8
|
+
const res = !!(await channel.send('service:reg', { service: service.name }))
|
|
6
9
|
|
|
7
10
|
if (res)
|
|
8
11
|
onHandleService(service, handler)
|
|
@@ -10,13 +13,14 @@ export function regService(service: IService, handler: Function): boolean {
|
|
|
10
13
|
return res
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
export function unRegService(service: IService): boolean {
|
|
14
|
-
|
|
16
|
+
export async function unRegService(service: IService): Promise<boolean> {
|
|
17
|
+
const channel = useChannel('[Plugin SDK] Service unregistration requires renderer channel.')
|
|
18
|
+
return !!(await channel.send('service:unreg', { service: service.name }))
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
export function onHandleService(service: IService, handler:
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
export function onHandleService(service: IService, handler: ServiceHandler) {
|
|
22
|
+
const channel = useChannel('[Plugin SDK] Service handling requires renderer channel.')
|
|
23
|
+
channel.regChannel('service:handle', ({ data: _data }) => {
|
|
20
24
|
const { data } = _data
|
|
21
25
|
|
|
22
26
|
// console.log('service:handle', data, service)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ensureRendererChannel } from './channel'
|
|
2
|
+
import { usePluginName } from './plugin-info'
|
|
3
|
+
|
|
4
|
+
export interface PluginSqliteStatement {
|
|
5
|
+
sql: string
|
|
6
|
+
params?: unknown[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PluginSqliteExecuteResult {
|
|
10
|
+
rowsAffected: number
|
|
11
|
+
lastInsertRowId: number | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PluginSqliteQueryResult<T extends Record<string, unknown>> {
|
|
15
|
+
rows: T[]
|
|
16
|
+
columns: string[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PluginSqliteTransactionResult {
|
|
20
|
+
results: PluginSqliteExecuteResult[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeSql(sql: string): string {
|
|
24
|
+
return typeof sql === 'string' ? sql.trim() : ''
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeParams(params?: unknown[]): unknown[] {
|
|
28
|
+
return Array.isArray(params) ? params : []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function usePluginSqlite() {
|
|
32
|
+
const pluginName = usePluginName(
|
|
33
|
+
'[Plugin SQLite SDK] Cannot determine plugin name. Make sure this is called in a plugin context.'
|
|
34
|
+
)
|
|
35
|
+
const channel = ensureRendererChannel(
|
|
36
|
+
'[Plugin SQLite SDK] Channel not available. Make sure this is called in a plugin context.'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
execute: async (
|
|
41
|
+
sql: string,
|
|
42
|
+
params?: unknown[]
|
|
43
|
+
): Promise<PluginSqliteExecuteResult> => {
|
|
44
|
+
const normalizedSql = normalizeSql(sql)
|
|
45
|
+
if (!normalizedSql) {
|
|
46
|
+
throw new Error('[Plugin SQLite SDK] SQL is required.')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const response = await channel.send('plugin:sqlite:execute', {
|
|
50
|
+
pluginName,
|
|
51
|
+
sql: normalizedSql,
|
|
52
|
+
params: normalizeParams(params)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (!response || typeof response !== 'object' || (response as { success?: unknown }).success !== true) {
|
|
56
|
+
const error =
|
|
57
|
+
response && typeof response === 'object' && 'error' in response
|
|
58
|
+
? String((response as { error?: unknown }).error ?? 'Unknown error')
|
|
59
|
+
: 'Unknown error'
|
|
60
|
+
throw new Error(`[Plugin SQLite SDK] Execute failed: ${error}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
rowsAffected: Number((response as { rowsAffected?: unknown }).rowsAffected ?? 0),
|
|
65
|
+
lastInsertRowId:
|
|
66
|
+
typeof (response as { lastInsertRowId?: unknown }).lastInsertRowId === 'number'
|
|
67
|
+
? Math.trunc((response as { lastInsertRowId?: number }).lastInsertRowId ?? 0)
|
|
68
|
+
: null
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
query: async <T extends Record<string, unknown> = Record<string, unknown>>(
|
|
73
|
+
sql: string,
|
|
74
|
+
params?: unknown[]
|
|
75
|
+
): Promise<PluginSqliteQueryResult<T>> => {
|
|
76
|
+
const normalizedSql = normalizeSql(sql)
|
|
77
|
+
if (!normalizedSql) {
|
|
78
|
+
throw new Error('[Plugin SQLite SDK] SQL is required.')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const response = await channel.send('plugin:sqlite:query', {
|
|
82
|
+
pluginName,
|
|
83
|
+
sql: normalizedSql,
|
|
84
|
+
params: normalizeParams(params)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if (!response || typeof response !== 'object' || (response as { success?: unknown }).success !== true) {
|
|
88
|
+
const error =
|
|
89
|
+
response && typeof response === 'object' && 'error' in response
|
|
90
|
+
? String((response as { error?: unknown }).error ?? 'Unknown error')
|
|
91
|
+
: 'Unknown error'
|
|
92
|
+
throw new Error(`[Plugin SQLite SDK] Query failed: ${error}`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
rows: Array.isArray((response as { rows?: unknown }).rows)
|
|
97
|
+
? ((response as { rows: T[] }).rows)
|
|
98
|
+
: [],
|
|
99
|
+
columns: Array.isArray((response as { columns?: unknown }).columns)
|
|
100
|
+
? ((response as { columns: string[] }).columns)
|
|
101
|
+
: []
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
transaction: async (
|
|
106
|
+
statements: PluginSqliteStatement[]
|
|
107
|
+
): Promise<PluginSqliteTransactionResult> => {
|
|
108
|
+
if (!Array.isArray(statements) || statements.length === 0) {
|
|
109
|
+
throw new Error('[Plugin SQLite SDK] Transaction statements are required.')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const payload = statements.map((statement) => ({
|
|
113
|
+
sql: normalizeSql(statement.sql),
|
|
114
|
+
params: normalizeParams(statement.params)
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
if (payload.some((statement) => !statement.sql)) {
|
|
118
|
+
throw new Error('[Plugin SQLite SDK] Each transaction statement must include SQL.')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const response = await channel.send('plugin:sqlite:transaction', {
|
|
122
|
+
pluginName,
|
|
123
|
+
statements: payload
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (!response || typeof response !== 'object' || (response as { success?: unknown }).success !== true) {
|
|
127
|
+
const error =
|
|
128
|
+
response && typeof response === 'object' && 'error' in response
|
|
129
|
+
? String((response as { error?: unknown }).error ?? 'Unknown error')
|
|
130
|
+
: 'Unknown error'
|
|
131
|
+
throw new Error(`[Plugin SQLite SDK] Transaction failed: ${error}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const results = Array.isArray((response as { results?: unknown }).results)
|
|
135
|
+
? (response as { results: PluginSqliteExecuteResult[] }).results
|
|
136
|
+
: []
|
|
137
|
+
|
|
138
|
+
return { results }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|