@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
@@ -1,12 +1,94 @@
1
- import type { ITouchClientChannel } from '@talex-touch/utils/channel'
1
+ import type { ITouchClientChannel, StandardChannelData } from '@talex-touch/utils/channel'
2
+ import type { ITuffTransport } from '@talex-touch/utils/transport'
2
3
  import type { IPluginRendererChannel, PluginChannelHandler } from './types'
4
+ import { ChannelType, DataCode } from '@talex-touch/utils/channel'
5
+ import { hasWindow } from '@talex-touch/utils/env'
6
+ import { defineRawEvent } from '@talex-touch/utils/transport/event/builder'
3
7
  import { genChannel } from '../channel'
4
8
 
5
9
  const ensureClientChannel = (): ITouchClientChannel => genChannel()
10
+ function resolvePluginName(): string | undefined {
11
+ if (!hasWindow()) {
12
+ return undefined
13
+ }
14
+
15
+ return (window as { $plugin?: { name?: string } } | undefined)?.$plugin?.name
16
+ }
17
+ function buildStandardChannelEvent(eventName: string, payload: unknown, pluginName: string | undefined, reply: (code: DataCode, data: unknown) => void): StandardChannelData {
18
+ return {
19
+ name: eventName,
20
+ header: {
21
+ status: 'request',
22
+ type: ChannelType.PLUGIN,
23
+ plugin: pluginName,
24
+ },
25
+ code: DataCode.SUCCESS,
26
+ data: payload,
27
+ plugin: pluginName,
28
+ reply,
29
+ }
30
+ }
31
+ function createTransportClientChannel(transport: ITuffTransport, fallback: ITouchClientChannel | null): ITouchClientChannel {
32
+ const handlerMap = new Map<string, Map<(data: StandardChannelData) => any, () => void>>()
33
+
34
+ return {
35
+ regChannel: (eventName, callback) => {
36
+ const disposer = transport.on(defineRawEvent(eventName), async (payload) => {
37
+ let replied = false
38
+ let replyData: unknown
39
+ const event = buildStandardChannelEvent(
40
+ eventName,
41
+ payload,
42
+ resolvePluginName(),
43
+ (_code, data) => {
44
+ replied = true
45
+ replyData = data
46
+ },
47
+ )
48
+
49
+ const result = await callback(event)
50
+ return replied ? replyData : result
51
+ })
52
+
53
+ let handlers = handlerMap.get(eventName)
54
+ if (!handlers) {
55
+ handlers = new Map()
56
+ handlerMap.set(eventName, handlers)
57
+ }
58
+ handlers.set(callback, disposer)
59
+
60
+ return () => {
61
+ disposer()
62
+ handlers?.delete(callback)
63
+ }
64
+ },
65
+ unRegChannel: (eventName, callback) => {
66
+ const disposer = handlerMap.get(eventName)?.get(callback)
67
+ if (!disposer) {
68
+ return false
69
+ }
70
+ disposer()
71
+ handlerMap.get(eventName)?.delete(callback)
72
+ return true
73
+ },
74
+ send: (eventName, arg) => transport.send(defineRawEvent(eventName), arg),
75
+ sendSync: (eventName, arg) => {
76
+ if (fallback?.sendSync) {
77
+ return fallback.sendSync(eventName, arg)
78
+ }
79
+ throw new Error(`[Plugin SDK] sendSync is not supported without legacy channel: ${eventName}`)
80
+ },
81
+ }
82
+ }
83
+ function resolveRendererTransport(): ITuffTransport | null {
84
+ const globalWindow = hasWindow() ? window : undefined
85
+ return globalWindow?.$transport ?? null
86
+ }
6
87
 
7
88
  const DEFAULT_CHANNEL_ERROR = '[Plugin SDK] Channel not available. Make sure this code runs inside a plugin renderer context.'
8
89
 
9
90
  let cachedWindowChannel: ITouchClientChannel | null = null
91
+ let cachedTransportChannel: ITouchClientChannel | null = null
10
92
 
11
93
  /**
12
94
  * Ensures that the renderer-side plugin channel (window.$channel) exists and returns it.
@@ -14,7 +96,16 @@ let cachedWindowChannel: ITouchClientChannel | null = null
14
96
  * @param errorMessage - Optional custom error message when the channel is unavailable
15
97
  */
16
98
  export function ensureRendererChannel(errorMessage = DEFAULT_CHANNEL_ERROR): ITouchClientChannel {
17
- const globalWindow = typeof window === 'undefined' ? undefined : window
99
+ const globalWindow = hasWindow() ? window : undefined
100
+ const transport = globalWindow?.$transport ?? null
101
+ if (transport) {
102
+ if (!cachedTransportChannel) {
103
+ const fallback = globalWindow?.$channel ?? cachedWindowChannel ?? null
104
+ cachedTransportChannel = createTransportClientChannel(transport, fallback)
105
+ }
106
+ return cachedTransportChannel
107
+ }
108
+
18
109
  const channel = globalWindow?.$channel ?? cachedWindowChannel
19
110
 
20
111
  if (!channel) {
@@ -34,9 +125,13 @@ export function useChannel(errorMessage?: string): ITouchClientChannel {
34
125
 
35
126
  export function createPluginRendererChannel(): IPluginRendererChannel {
36
127
  const client = ensureClientChannel()
128
+ const transport = resolveRendererTransport()
37
129
 
38
130
  return {
39
131
  send(eventName, payload) {
132
+ if (transport) {
133
+ return transport.send(defineRawEvent(eventName), payload)
134
+ }
40
135
  return client.send(eventName, payload)
41
136
  },
42
137
 
@@ -45,7 +140,26 @@ export function createPluginRendererChannel(): IPluginRendererChannel {
45
140
  },
46
141
 
47
142
  on(eventName, handler) {
48
- return client.regChannel(eventName, handler)
143
+ if (!transport) {
144
+ return client.regChannel(eventName, handler)
145
+ }
146
+
147
+ return transport.on(defineRawEvent(eventName), async (payload) => {
148
+ let replied = false
149
+ let replyData: unknown
150
+ const event = buildStandardChannelEvent(
151
+ eventName,
152
+ payload,
153
+ resolvePluginName(),
154
+ (_code, data) => {
155
+ replied = true
156
+ replyData = data
157
+ },
158
+ )
159
+
160
+ const result = await handler(event)
161
+ return replied ? replyData : result
162
+ })
49
163
  },
50
164
 
51
165
  once(eventName, handler) {
@@ -55,7 +169,7 @@ export function createPluginRendererChannel(): IPluginRendererChannel {
55
169
  handler(event)
56
170
  }
57
171
 
58
- dispose = client.regChannel(eventName, wrapped)
172
+ dispose = this.on(eventName, wrapped)
59
173
  return dispose
60
174
  },
61
175
 
@@ -78,5 +192,6 @@ export function usePluginRendererChannel(): IPluginRendererChannel {
78
192
  declare global {
79
193
  interface Window {
80
194
  $channel: ITouchClientChannel
195
+ $transport?: ITuffTransport
81
196
  }
82
197
  }
@@ -1,12 +1,5 @@
1
1
  import type { PluginClipboardHistoryResponse, PluginClipboardItem, PluginClipboardSearchOptions, PluginClipboardSearchResponse } from './types'
2
-
3
- function ensurePluginChannel() {
4
- const channel = (window as any)?.$channel
5
- if (!channel) {
6
- throw new Error('[Plugin SDK] Clipboard channel requires plugin renderer context with $channel available.')
7
- }
8
- return channel
9
- }
2
+ import { useChannel } from './channel'
10
3
 
11
4
  function normalizeItem(item: PluginClipboardItem | null): PluginClipboardItem | null {
12
5
  if (!item)
@@ -63,6 +56,11 @@ export interface ClipboardImageResult {
63
56
  dataUrl: string
64
57
  width: number
65
58
  height: number
59
+ /**
60
+ * Original image as a local streamable URL (Electron only).
61
+ * Returned when calling `readImage({ preview: false })`.
62
+ */
63
+ tfileUrl?: string
66
64
  }
67
65
 
68
66
  export interface ClipboardCopyAndPasteOptions {
@@ -117,7 +115,7 @@ export function useClipboardHistory() {
117
115
  * ```
118
116
  */
119
117
  export function useClipboard() {
120
- const channel = ensurePluginChannel()
118
+ const channel = useChannel('[Plugin SDK] Clipboard channel requires plugin renderer context with $channel available.')
121
119
 
122
120
  const history = {
123
121
  /**
@@ -214,7 +212,8 @@ export function useClipboard() {
214
212
  * ```
215
213
  */
216
214
  onDidChange(callback: (item: PluginClipboardItem) => void): () => void {
217
- return channel.regChannel('core-box:clipboard-change', ({ data }: { data: unknown }) => {
215
+ return channel.regChannel('core-box:clipboard-change', (event) => {
216
+ const data = event?.data
218
217
  const item = (data && typeof data === 'object' && 'item' in data ? (data as { item: PluginClipboardItem }).item : data) as PluginClipboardItem
219
218
  callback(normalizeItem(item) ?? item)
220
219
  })
@@ -264,8 +263,23 @@ export function useClipboard() {
264
263
  /**
265
264
  * Reads image from clipboard as data URL
266
265
  */
267
- async readImage(): Promise<ClipboardImageResult | null> {
268
- return await channel.send('clipboard:read-image')
266
+ async readImage(options?: { preview?: boolean }): Promise<ClipboardImageResult | null> {
267
+ return await channel.send('clipboard:read-image', { preview: options?.preview ?? true })
268
+ },
269
+
270
+ /**
271
+ * Resolves the original image URL for a clipboard history item (streamable via tfile://).
272
+ *
273
+ * @remarks
274
+ * - This avoids transferring large base64 payloads over IPC.
275
+ * - Returns null if the item is not an image or original asset is not available.
276
+ */
277
+ async getHistoryImageUrl(id: number): Promise<string | null> {
278
+ const res = await channel.send('clipboard:get-image-url', { id })
279
+ if (res && typeof res === 'object' && 'url' in res && typeof (res as any).url === 'string') {
280
+ return (res as any).url as string
281
+ }
282
+ return null
269
283
  },
270
284
 
271
285
  /**
@@ -0,0 +1,113 @@
1
+ import type { CloudSyncSDKOptions as ClientOptions } from '../../cloud-sync/cloud-sync-sdk'
2
+ import { CloudSyncSDK as CloudSyncClientSDK, CloudSyncError } from '../../cloud-sync/cloud-sync-sdk'
3
+ import type { HandshakeResponse, PullResponse, PushResponse, SyncItemInput } from '../../types/cloud-sync'
4
+ import { accountSDK } from '../../account'
5
+ import { ensureRendererChannel } from './channel'
6
+
7
+ export interface CloudSyncSDKOptions {
8
+ baseUrl?: string
9
+ serviceBaseUrl?: string
10
+ fetch?: typeof fetch
11
+ now?: () => number
12
+ syncTokenCache?: { token?: string, expiresAt?: string }
13
+ onSyncTokenUpdate?: (token: string, expiresAt: string) => void
14
+ onHandshake?: (response: HandshakeResponse) => void
15
+ onStepUpRequired?: () => string | null | Promise<string | null>
16
+ formDataFactory?: () => FormData
17
+ channelSend?: (event: string, data?: any) => Promise<any>
18
+ ignoreSyncPreferenceCheck?: boolean
19
+ }
20
+
21
+ let accountChannelBound = false
22
+
23
+ function bindAccountChannel(options?: CloudSyncSDKOptions) {
24
+ if (accountChannelBound)
25
+ return
26
+
27
+ if (options?.channelSend) {
28
+ accountSDK.setChannelSend(options.channelSend)
29
+ accountChannelBound = true
30
+ return
31
+ }
32
+
33
+ const channel = ensureRendererChannel('[CloudSyncSDK] Channel not available. Make sure this runs in plugin renderer context.')
34
+ accountSDK.setChannelSend(channel.send)
35
+ accountChannelBound = true
36
+ }
37
+
38
+ async function resolveAuthToken(options?: CloudSyncSDKOptions): Promise<string> {
39
+ bindAccountChannel(options)
40
+ if (!options?.ignoreSyncPreferenceCheck) {
41
+ const syncEnabled = await accountSDK.getSyncEnabled()
42
+ if (!syncEnabled) {
43
+ throw new CloudSyncError(
44
+ '[CloudSyncSDK] Sync is disabled by user preference.',
45
+ 403,
46
+ 'SYNC_DISABLED'
47
+ )
48
+ }
49
+ }
50
+ const token = await accountSDK.getAuthToken()
51
+ if (!token) {
52
+ throw new Error('[CloudSyncSDK] Auth token is not available. Ensure user is signed in and AccountSDK channel is configured.')
53
+ }
54
+ return token
55
+ }
56
+
57
+ async function resolveDeviceId(options?: CloudSyncSDKOptions): Promise<string> {
58
+ bindAccountChannel(options)
59
+ const deviceId = await accountSDK.getDeviceId()
60
+ if (!deviceId) {
61
+ throw new Error('[CloudSyncSDK] Device ID is not available. Ensure AccountSDK channel is configured.')
62
+ }
63
+ return deviceId
64
+ }
65
+
66
+ export class CloudSyncSDK extends CloudSyncClientSDK {
67
+ constructor(options: CloudSyncSDKOptions = {}) {
68
+ bindAccountChannel(options)
69
+ const clientOptions: ClientOptions = {
70
+ baseUrl: options.baseUrl ?? options.serviceBaseUrl,
71
+ fetch: options.fetch,
72
+ now: options.now,
73
+ syncTokenCache: options.syncTokenCache,
74
+ onSyncTokenUpdate: options.onSyncTokenUpdate,
75
+ onHandshake: options.onHandshake,
76
+ onStepUpRequired: options.onStepUpRequired,
77
+ formDataFactory: options.formDataFactory,
78
+ getAuthToken: () => resolveAuthToken(options),
79
+ getDeviceId: () => resolveDeviceId(options),
80
+ }
81
+ super(clientOptions)
82
+ }
83
+
84
+ async push(items: SyncItemInput[]): Promise<PushResponse> {
85
+ const response = await super.push(items)
86
+ await accountSDK.recordSyncActivity('push')
87
+ return response
88
+ }
89
+
90
+ async pull(params?: { cursor?: number, limit?: number }): Promise<PullResponse> {
91
+ const response = await super.pull(params)
92
+ await accountSDK.recordSyncActivity('pull')
93
+ return response
94
+ }
95
+ }
96
+
97
+ let cachedSDK: CloudSyncSDK | null = null
98
+
99
+ export function createCloudSyncSDK(options?: CloudSyncSDKOptions): CloudSyncSDK {
100
+ return new CloudSyncSDK(options)
101
+ }
102
+
103
+ export function useCloudSyncSDK(options?: CloudSyncSDKOptions): CloudSyncSDK {
104
+ if (!cachedSDK)
105
+ cachedSDK = new CloudSyncSDK(options)
106
+ return cachedSDK
107
+ }
108
+
109
+ export function resetCloudSyncSDK(): void {
110
+ cachedSDK = null
111
+ }
112
+
113
+ export { CloudSyncError }
@@ -5,7 +5,10 @@
5
5
  * 提供插件SDK的通用功能,包括通信、快捷键等
6
6
  */
7
7
 
8
- import { genChannel } from '../channel'
8
+ import { getLogger } from '../../common/logger'
9
+ import { useChannel } from './channel'
10
+
11
+ const sdkLog = getLogger('plugin-sdk')
9
12
 
10
13
  /**
11
14
  * Register a shortcut
@@ -13,16 +16,21 @@ import { genChannel } from '../channel'
13
16
  * @param func - The trigger function
14
17
  * @returns Whether the shortcut is registered successfully
15
18
  */
16
- export function regShortcut(key: string, func: Function): boolean {
17
- const channel = genChannel()
19
+ export async function regShortcut(key: string, func: () => void): Promise<boolean> {
20
+ const channel = useChannel('[Plugin SDK] Shortcut registration requires renderer channel.')
18
21
 
19
- const res = channel.sendSync('shortcon:reg', { key })
20
- if (res instanceof String)
22
+ const res = await channel.send('shortcon:reg', { key })
23
+ if (typeof res === 'string' || Object.prototype.toString.call(res) === '[object String]')
21
24
  throw new Error(String(res))
22
25
  if (res === false)
23
26
  return false
24
27
 
25
- channel.regChannel('shortcon:trigger', ({ data }) => key === data.key && func())
28
+ channel.regChannel('shortcon:trigger', ({ data }) => {
29
+ const payload = data as { key?: string, id?: string } | undefined
30
+ const triggerKey = payload?.key ?? payload?.id
31
+ if (triggerKey === key)
32
+ func()
33
+ })
26
34
 
27
35
  return true
28
36
  }
@@ -37,7 +45,7 @@ export async function communicateWithPlugin(
37
45
  key: string,
38
46
  info: any = {},
39
47
  ): Promise<any> {
40
- const channel = genChannel()
48
+ const channel = useChannel('[Plugin SDK] Communication requires renderer channel.')
41
49
 
42
50
  try {
43
51
  return await channel.send('index:communicate', {
@@ -46,7 +54,7 @@ export async function communicateWithPlugin(
46
54
  })
47
55
  }
48
56
  catch (error) {
49
- console.error(`[Plugin SDK] Failed to communicate`, error)
57
+ sdkLog.error('Failed to communicate', { error })
50
58
  throw error
51
59
  }
52
60
  }
@@ -58,13 +66,13 @@ export async function communicateWithPlugin(
58
66
  * @returns Promise<any> The message result
59
67
  */
60
68
  export async function sendMessage(message: string, data: any = {}): Promise<any> {
61
- const channel = genChannel()
69
+ const channel = useChannel('[Plugin SDK] Messaging requires renderer channel.')
62
70
 
63
71
  try {
64
72
  return await channel.send(`plugin:${message}`, data)
65
73
  }
66
74
  catch (error) {
67
- console.error(`[Plugin SDK] Failed to send message: ${message}`, error)
75
+ sdkLog.error(`Failed to send message: ${message}`, { error })
68
76
  throw error
69
77
  }
70
78
  }
@@ -75,7 +83,7 @@ export async function sendMessage(message: string, data: any = {}): Promise<any>
75
83
  * @returns The channel object for the plugin
76
84
  */
77
85
  export function getChannel() {
78
- return genChannel()
86
+ return useChannel('[Plugin SDK] Channel requires renderer context.')
79
87
  }
80
88
 
81
89
  export * from './window'
@@ -1,20 +1,11 @@
1
- import type { IPluginRendererChannel } from './types'
1
+ import { useChannel } from './channel'
2
+ import { usePluginName } from './plugin-info'
2
3
 
3
- function ensurePluginContext(): { channel: IPluginRendererChannel, pluginName: string } {
4
- const plugin = (window as any)?.$plugin
5
- if (!plugin?.name) {
6
- throw new Error('[TouchSDK] Unable to resolve plugin name inside renderer context.')
7
- }
4
+ function ensurePluginContext(): { channel: ReturnType<typeof useChannel>, pluginName: string } {
5
+ const pluginName = usePluginName('[TouchSDK] Unable to resolve plugin name inside renderer context.')
6
+ const channel = useChannel('[TouchSDK] Channel bridge is not available for the current plugin renderer.')
8
7
 
9
- const channel = (window as any)?.$channel as IPluginRendererChannel | undefined
10
- if (!channel) {
11
- throw new Error('[TouchSDK] Channel bridge is not available for the current plugin renderer.')
12
- }
13
-
14
- return {
15
- channel,
16
- pluginName: plugin.name as string,
17
- }
8
+ return { channel, pluginName }
18
9
  }
19
10
 
20
11
  /**