@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.
Files changed (235) hide show
  1. package/.eslintcache +1 -0
  2. package/__tests__/cloud-sync-sdk.test.ts +442 -0
  3. package/__tests__/icons/icons.test.ts +84 -0
  4. package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
  5. package/__tests__/power-sdk.test.ts +143 -0
  6. package/__tests__/preset-export-types.test.ts +108 -0
  7. package/__tests__/search/fuzzy-match.test.ts +137 -0
  8. package/__tests__/transport/port-policy.test.ts +44 -0
  9. package/__tests__/transport-domain-sdks.test.ts +152 -0
  10. package/__tests__/types/update.test.ts +67 -0
  11. package/account/account-sdk.ts +915 -0
  12. package/account/index.ts +2 -0
  13. package/account/types.ts +321 -0
  14. package/analytics/client.ts +136 -0
  15. package/analytics/index.ts +2 -0
  16. package/analytics/types.ts +156 -0
  17. package/animation/auto-resize.ts +322 -0
  18. package/animation/window-node.ts +26 -19
  19. package/auth/clerk-types.ts +12 -30
  20. package/auth/index.ts +0 -2
  21. package/auth/useAuthState.ts +6 -14
  22. package/base/index.ts +2 -0
  23. package/base/log-level.ts +105 -0
  24. package/channel/index.ts +170 -69
  25. package/cloud-sync/cloud-sync-sdk.ts +450 -0
  26. package/cloud-sync/index.ts +1 -0
  27. package/common/file-scan-utils.ts +17 -9
  28. package/common/index.ts +4 -0
  29. package/common/logger/index.ts +46 -0
  30. package/common/logger/logger-manager.ts +303 -0
  31. package/common/logger/module-logger.ts +270 -0
  32. package/common/logger/transport-logger.ts +234 -0
  33. package/common/logger/types.ts +93 -0
  34. package/common/search/gather.ts +48 -6
  35. package/common/search/index.ts +8 -0
  36. package/common/storage/constants.ts +13 -0
  37. package/common/storage/entity/app-settings.ts +245 -0
  38. package/common/storage/entity/index.ts +3 -0
  39. package/common/storage/entity/layout-atom-types.ts +147 -0
  40. package/common/storage/entity/openers.ts +1 -0
  41. package/common/storage/entity/preset-cloud-api.ts +132 -0
  42. package/common/storage/entity/preset-export-types.ts +256 -0
  43. package/common/storage/entity/shortcut-settings.ts +1 -0
  44. package/common/storage/shortcut-storage.ts +11 -0
  45. package/common/utils/clone-diagnostics.ts +105 -0
  46. package/common/utils/file.ts +16 -8
  47. package/common/utils/index.ts +6 -2
  48. package/common/utils/payload-preview.ts +173 -0
  49. package/common/utils/polling.ts +167 -13
  50. package/common/utils/safe-path.ts +103 -0
  51. package/common/utils/safe-shell.ts +115 -0
  52. package/common/utils/task-queue.ts +4 -1
  53. package/core-box/builder/tuff-builder.ts +0 -1
  54. package/core-box/index.ts +1 -1
  55. package/core-box/recommendation.ts +38 -1
  56. package/core-box/tuff/tuff-dsl.ts +97 -0
  57. package/electron/download-manager.ts +10 -7
  58. package/electron/env-tool.ts +42 -40
  59. package/electron/index.ts +0 -1
  60. package/env/index.ts +156 -0
  61. package/eslint.config.js +55 -0
  62. package/i18n/index.ts +62 -0
  63. package/i18n/locales/en.json +226 -0
  64. package/i18n/locales/zh.json +226 -0
  65. package/i18n/message-keys.ts +236 -0
  66. package/i18n/resolver.ts +181 -0
  67. package/icons/index.ts +257 -0
  68. package/icons/svg.ts +69 -0
  69. package/index.ts +9 -1
  70. package/intelligence/client.ts +72 -42
  71. package/market/constants.ts +21 -3
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +20 -5
  74. package/package.json +15 -5
  75. package/permission/index.ts +143 -46
  76. package/permission/legacy.ts +26 -0
  77. package/permission/registry.ts +304 -0
  78. package/permission/types.ts +164 -0
  79. package/plugin/channel.ts +68 -39
  80. package/plugin/index.ts +82 -8
  81. package/plugin/install.ts +3 -0
  82. package/plugin/log/types.ts +22 -5
  83. package/plugin/node/logger-manager.ts +11 -3
  84. package/plugin/node/logger.ts +24 -17
  85. package/plugin/preload.ts +25 -2
  86. package/plugin/providers/index.ts +4 -0
  87. package/plugin/providers/market-client.ts +218 -0
  88. package/plugin/providers/npm-provider.ts +228 -0
  89. package/plugin/providers/tpex-provider.ts +297 -0
  90. package/plugin/providers/tpex-types.ts +34 -0
  91. package/plugin/sdk/box-items.ts +14 -0
  92. package/plugin/sdk/box-sdk.ts +64 -0
  93. package/plugin/sdk/channel.ts +119 -4
  94. package/plugin/sdk/clipboard.ts +26 -12
  95. package/plugin/sdk/cloud-sync.ts +113 -0
  96. package/plugin/sdk/common.ts +19 -11
  97. package/plugin/sdk/core-box.ts +6 -15
  98. package/plugin/sdk/division-box.ts +160 -65
  99. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  100. package/plugin/sdk/feature-sdk.ts +111 -76
  101. package/plugin/sdk/flow.ts +146 -45
  102. package/plugin/sdk/hooks/bridge.ts +113 -49
  103. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  104. package/plugin/sdk/index.ts +14 -3
  105. package/plugin/sdk/intelligence.ts +87 -0
  106. package/plugin/sdk/meta/README.md +179 -0
  107. package/plugin/sdk/meta-sdk.ts +244 -0
  108. package/plugin/sdk/notification.ts +9 -0
  109. package/plugin/sdk/performance.ts +1 -16
  110. package/plugin/sdk/plugin-info.ts +64 -0
  111. package/plugin/sdk/power.ts +155 -0
  112. package/plugin/sdk/recommend.ts +21 -0
  113. package/plugin/sdk/service/index.ts +12 -8
  114. package/plugin/sdk/sqlite.ts +141 -0
  115. package/plugin/sdk/storage.ts +2 -6
  116. package/plugin/sdk/system.ts +2 -9
  117. package/plugin/sdk/temp-files.ts +41 -0
  118. package/plugin/sdk/touch-sdk.ts +18 -0
  119. package/plugin/sdk/types.ts +44 -4
  120. package/plugin/sdk/window/index.ts +12 -9
  121. package/plugin/sdk-version.ts +231 -0
  122. package/preload/renderer.ts +3 -2
  123. package/renderer/hooks/arg-mapper.ts +34 -6
  124. package/renderer/hooks/index.ts +13 -0
  125. package/renderer/hooks/initialize.ts +2 -1
  126. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  127. package/renderer/hooks/use-agent-market.ts +106 -0
  128. package/renderer/hooks/use-agents-sdk.ts +7 -0
  129. package/renderer/hooks/use-app-sdk.ts +7 -0
  130. package/renderer/hooks/use-channel.ts +33 -4
  131. package/renderer/hooks/use-download-sdk.ts +21 -0
  132. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  133. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  134. package/renderer/hooks/use-intelligence.ts +202 -104
  135. package/renderer/hooks/use-market-sdk.ts +16 -0
  136. package/renderer/hooks/use-notification-sdk.ts +7 -0
  137. package/renderer/hooks/use-permission-sdk.ts +7 -0
  138. package/renderer/hooks/use-permission.ts +325 -0
  139. package/renderer/hooks/use-platform-sdk.ts +7 -0
  140. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  141. package/renderer/hooks/use-settings-sdk.ts +7 -0
  142. package/renderer/hooks/use-update-sdk.ts +21 -0
  143. package/renderer/index.ts +1 -0
  144. package/renderer/ref.ts +19 -10
  145. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  146. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  147. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  148. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  149. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  150. package/renderer/shared/components/index.ts +5 -0
  151. package/renderer/shared/components/shims-vue.d.ts +5 -0
  152. package/renderer/shared/index.ts +2 -0
  153. package/renderer/shared/plugin-detail.ts +62 -0
  154. package/renderer/storage/app-settings.ts +3 -1
  155. package/renderer/storage/base-storage.ts +508 -82
  156. package/renderer/storage/intelligence-storage.ts +37 -46
  157. package/renderer/storage/openers.ts +3 -1
  158. package/renderer/storage/storage-subscription.ts +126 -42
  159. package/renderer/touch-sdk/env.ts +10 -10
  160. package/renderer/touch-sdk/index.ts +114 -18
  161. package/renderer/touch-sdk/terminal.ts +24 -13
  162. package/search/feature-matcher.ts +279 -0
  163. package/search/fuzzy-match.ts +64 -34
  164. package/search/index.ts +10 -0
  165. package/search/levenshtein-utils.ts +17 -11
  166. package/transport/errors.ts +310 -0
  167. package/transport/event/builder.ts +378 -0
  168. package/transport/event/index.ts +7 -0
  169. package/transport/event/types.ts +292 -0
  170. package/transport/events/index.ts +2670 -0
  171. package/transport/events/meta-overlay.ts +79 -0
  172. package/transport/events/types/agents.ts +177 -0
  173. package/transport/events/types/app-index.ts +9 -0
  174. package/transport/events/types/app.ts +475 -0
  175. package/transport/events/types/box-item.ts +222 -0
  176. package/transport/events/types/clipboard.ts +80 -0
  177. package/transport/events/types/core-box.ts +534 -0
  178. package/transport/events/types/device-idle.ts +7 -0
  179. package/transport/events/types/division-box.ts +99 -0
  180. package/transport/events/types/download.ts +115 -0
  181. package/transport/events/types/file-index.ts +73 -0
  182. package/transport/events/types/flow.ts +149 -0
  183. package/transport/events/types/index.ts +70 -0
  184. package/transport/events/types/market.ts +39 -0
  185. package/transport/events/types/meta-overlay.ts +184 -0
  186. package/transport/events/types/notification.ts +140 -0
  187. package/transport/events/types/permission.ts +90 -0
  188. package/transport/events/types/platform.ts +8 -0
  189. package/transport/events/types/plugin.ts +620 -0
  190. package/transport/events/types/sentry.ts +20 -0
  191. package/transport/events/types/storage.ts +208 -0
  192. package/transport/events/types/transport.ts +60 -0
  193. package/transport/events/types/tray.ts +16 -0
  194. package/transport/events/types/update.ts +78 -0
  195. package/transport/index.ts +139 -0
  196. package/transport/main.ts +2 -0
  197. package/transport/sdk/constants.ts +29 -0
  198. package/transport/sdk/domains/agents-market.ts +47 -0
  199. package/transport/sdk/domains/agents.ts +62 -0
  200. package/transport/sdk/domains/app.ts +48 -0
  201. package/transport/sdk/domains/disposable.ts +35 -0
  202. package/transport/sdk/domains/download.ts +139 -0
  203. package/transport/sdk/domains/index.ts +13 -0
  204. package/transport/sdk/domains/intelligence.ts +616 -0
  205. package/transport/sdk/domains/market.ts +35 -0
  206. package/transport/sdk/domains/notification.ts +62 -0
  207. package/transport/sdk/domains/permission.ts +85 -0
  208. package/transport/sdk/domains/platform.ts +19 -0
  209. package/transport/sdk/domains/plugin.ts +144 -0
  210. package/transport/sdk/domains/settings.ts +92 -0
  211. package/transport/sdk/domains/update.ts +64 -0
  212. package/transport/sdk/index.ts +60 -0
  213. package/transport/sdk/main-transport.ts +710 -0
  214. package/transport/sdk/main.ts +9 -0
  215. package/transport/sdk/plugin-transport.ts +654 -0
  216. package/transport/sdk/port-policy.ts +38 -0
  217. package/transport/sdk/renderer-transport.ts +1165 -0
  218. package/transport/types.ts +605 -0
  219. package/types/agent.ts +399 -0
  220. package/types/cloud-sync.ts +157 -0
  221. package/types/division-box.ts +47 -27
  222. package/types/download.ts +1 -0
  223. package/types/flow.ts +63 -12
  224. package/types/icon.ts +2 -1
  225. package/types/index.ts +5 -0
  226. package/types/intelligence.ts +1492 -81
  227. package/types/modules/base.ts +2 -0
  228. package/types/path-browserify.d.ts +5 -0
  229. package/types/platform.ts +12 -0
  230. package/types/startup-info.ts +32 -0
  231. package/types/touch-app-core.ts +8 -8
  232. package/types/update.ts +94 -1
  233. package/vitest.config.ts +25 -0
  234. package/auth/useClerkConfig.ts +0 -40
  235. 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 { genChannel } from '../../channel'
2
+ import { useChannel } from '../channel'
3
3
 
4
- export function regService(service: IService, handler: Function): boolean {
5
- const res = !!genChannel().sendSync('service:reg', { service: service.name })
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
- return !!genChannel().sendSync('service:unreg', { service: service.name })
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: Function) {
18
- // @ts-ignore
19
- genChannel().regChannel('service:handle', ({ data: _data }) => {
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
+ }