@talex-touch/utils 1.0.42 → 1.0.45

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 (234) 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 +32 -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 +9 -5
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +19 -4
  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 +80 -7
  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 -4
  87. package/plugin/providers/market-client.ts +6 -3
  88. package/plugin/providers/npm-provider.ts +22 -7
  89. package/plugin/providers/tpex-provider.ts +22 -8
  90. package/plugin/sdk/box-items.ts +14 -0
  91. package/plugin/sdk/box-sdk.ts +64 -0
  92. package/plugin/sdk/channel.ts +119 -4
  93. package/plugin/sdk/clipboard.ts +26 -12
  94. package/plugin/sdk/cloud-sync.ts +113 -0
  95. package/plugin/sdk/common.ts +19 -11
  96. package/plugin/sdk/core-box.ts +6 -15
  97. package/plugin/sdk/division-box.ts +160 -65
  98. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  99. package/plugin/sdk/feature-sdk.ts +111 -76
  100. package/plugin/sdk/flow.ts +146 -45
  101. package/plugin/sdk/hooks/bridge.ts +13 -6
  102. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  103. package/plugin/sdk/index.ts +14 -3
  104. package/plugin/sdk/intelligence.ts +87 -0
  105. package/plugin/sdk/meta/README.md +179 -0
  106. package/plugin/sdk/meta-sdk.ts +244 -0
  107. package/plugin/sdk/notification.ts +9 -0
  108. package/plugin/sdk/plugin-info.ts +64 -0
  109. package/plugin/sdk/power.ts +155 -0
  110. package/plugin/sdk/recommend.ts +21 -0
  111. package/plugin/sdk/service/index.ts +12 -8
  112. package/plugin/sdk/sqlite.ts +141 -0
  113. package/plugin/sdk/storage.ts +2 -6
  114. package/plugin/sdk/system.ts +2 -9
  115. package/plugin/sdk/temp-files.ts +41 -0
  116. package/plugin/sdk/touch-sdk.ts +18 -0
  117. package/plugin/sdk/types.ts +44 -4
  118. package/plugin/sdk/window/index.ts +12 -9
  119. package/plugin/sdk-version.ts +231 -0
  120. package/preload/renderer.ts +3 -2
  121. package/renderer/hooks/arg-mapper.ts +16 -2
  122. package/renderer/hooks/index.ts +13 -0
  123. package/renderer/hooks/initialize.ts +2 -1
  124. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  125. package/renderer/hooks/use-agent-market.ts +106 -0
  126. package/renderer/hooks/use-agents-sdk.ts +7 -0
  127. package/renderer/hooks/use-app-sdk.ts +7 -0
  128. package/renderer/hooks/use-channel.ts +33 -4
  129. package/renderer/hooks/use-download-sdk.ts +21 -0
  130. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  131. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  132. package/renderer/hooks/use-intelligence.ts +55 -214
  133. package/renderer/hooks/use-market-sdk.ts +16 -0
  134. package/renderer/hooks/use-notification-sdk.ts +7 -0
  135. package/renderer/hooks/use-permission-sdk.ts +7 -0
  136. package/renderer/hooks/use-permission.ts +325 -0
  137. package/renderer/hooks/use-platform-sdk.ts +7 -0
  138. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  139. package/renderer/hooks/use-settings-sdk.ts +7 -0
  140. package/renderer/hooks/use-update-sdk.ts +21 -0
  141. package/renderer/index.ts +1 -0
  142. package/renderer/ref.ts +19 -10
  143. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  144. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  145. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  146. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  147. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  148. package/renderer/shared/components/index.ts +5 -0
  149. package/renderer/shared/components/shims-vue.d.ts +5 -0
  150. package/renderer/shared/index.ts +2 -0
  151. package/renderer/shared/plugin-detail.ts +62 -0
  152. package/renderer/storage/app-settings.ts +3 -1
  153. package/renderer/storage/base-storage.ts +508 -82
  154. package/renderer/storage/intelligence-storage.ts +31 -40
  155. package/renderer/storage/openers.ts +3 -1
  156. package/renderer/storage/storage-subscription.ts +126 -42
  157. package/renderer/touch-sdk/env.ts +10 -10
  158. package/renderer/touch-sdk/index.ts +114 -18
  159. package/renderer/touch-sdk/terminal.ts +24 -13
  160. package/search/feature-matcher.ts +279 -0
  161. package/search/fuzzy-match.ts +64 -34
  162. package/search/index.ts +10 -0
  163. package/search/levenshtein-utils.ts +17 -11
  164. package/transport/errors.ts +310 -0
  165. package/transport/event/builder.ts +378 -0
  166. package/transport/event/index.ts +7 -0
  167. package/transport/event/types.ts +292 -0
  168. package/transport/events/index.ts +2690 -0
  169. package/transport/events/meta-overlay.ts +79 -0
  170. package/transport/events/types/agents.ts +177 -0
  171. package/transport/events/types/app-index.ts +20 -0
  172. package/transport/events/types/app.ts +475 -0
  173. package/transport/events/types/box-item.ts +222 -0
  174. package/transport/events/types/clipboard.ts +80 -0
  175. package/transport/events/types/core-box.ts +534 -0
  176. package/transport/events/types/device-idle.ts +7 -0
  177. package/transport/events/types/division-box.ts +99 -0
  178. package/transport/events/types/download.ts +115 -0
  179. package/transport/events/types/file-index.ts +84 -0
  180. package/transport/events/types/flow.ts +149 -0
  181. package/transport/events/types/index.ts +70 -0
  182. package/transport/events/types/market.ts +39 -0
  183. package/transport/events/types/meta-overlay.ts +184 -0
  184. package/transport/events/types/notification.ts +140 -0
  185. package/transport/events/types/permission.ts +90 -0
  186. package/transport/events/types/platform.ts +8 -0
  187. package/transport/events/types/plugin.ts +631 -0
  188. package/transport/events/types/sentry.ts +20 -0
  189. package/transport/events/types/storage.ts +208 -0
  190. package/transport/events/types/transport.ts +60 -0
  191. package/transport/events/types/tray.ts +16 -0
  192. package/transport/events/types/update.ts +78 -0
  193. package/transport/index.ts +141 -0
  194. package/transport/main.ts +2 -0
  195. package/transport/prelude.ts +208 -0
  196. package/transport/sdk/constants.ts +29 -0
  197. package/transport/sdk/domains/agents-market.ts +47 -0
  198. package/transport/sdk/domains/agents.ts +62 -0
  199. package/transport/sdk/domains/app.ts +48 -0
  200. package/transport/sdk/domains/disposable.ts +35 -0
  201. package/transport/sdk/domains/download.ts +139 -0
  202. package/transport/sdk/domains/index.ts +13 -0
  203. package/transport/sdk/domains/intelligence.ts +616 -0
  204. package/transport/sdk/domains/market.ts +35 -0
  205. package/transport/sdk/domains/notification.ts +62 -0
  206. package/transport/sdk/domains/permission.ts +85 -0
  207. package/transport/sdk/domains/platform.ts +19 -0
  208. package/transport/sdk/domains/plugin.ts +144 -0
  209. package/transport/sdk/domains/settings.ts +102 -0
  210. package/transport/sdk/domains/update.ts +64 -0
  211. package/transport/sdk/index.ts +60 -0
  212. package/transport/sdk/main-transport.ts +710 -0
  213. package/transport/sdk/main.ts +9 -0
  214. package/transport/sdk/plugin-transport.ts +654 -0
  215. package/transport/sdk/port-policy.ts +38 -0
  216. package/transport/sdk/renderer-transport.ts +1165 -0
  217. package/transport/types.ts +605 -0
  218. package/types/agent.ts +399 -0
  219. package/types/cloud-sync.ts +157 -0
  220. package/types/division-box.ts +31 -31
  221. package/types/download.ts +1 -0
  222. package/types/flow.ts +63 -12
  223. package/types/icon.ts +2 -1
  224. package/types/index.ts +5 -0
  225. package/types/intelligence.ts +166 -173
  226. package/types/modules/base.ts +2 -0
  227. package/types/path-browserify.d.ts +5 -0
  228. package/types/platform.ts +12 -0
  229. package/types/startup-info.ts +32 -0
  230. package/types/touch-app-core.ts +8 -8
  231. package/types/update.ts +94 -1
  232. package/vitest.config.ts +25 -0
  233. package/auth/useClerkConfig.ts +0 -40
  234. package/auth/useClerkProvider.ts +0 -52
@@ -1,12 +1,16 @@
1
1
  /**
2
2
  * Feature SDK for Plugin Development
3
- *
3
+ *
4
4
  * Provides a unified API for plugins to manage search result items (TuffItems)
5
5
  * in the CoreBox interface. This SDK handles item lifecycle, updates, and
6
6
  * input change notifications.
7
7
  */
8
8
 
9
9
  import type { TuffItem } from '../../core-box/tuff'
10
+ import { createPluginTuffTransport } from '../../transport'
11
+ import { defineRawEvent } from '../../transport/event/builder'
12
+ import { createDisposableBag } from '../../transport/sdk'
13
+ import { useBoxItems } from './box-items'
10
14
  import { ensureRendererChannel } from './channel'
11
15
 
12
16
  /**
@@ -32,9 +36,12 @@ export interface ForwardedKeyEvent {
32
36
  */
33
37
  export type KeyEventHandler = (event: ForwardedKeyEvent) => void
34
38
 
39
+ const featureInputChangeEvent = defineRawEvent<unknown, unknown>('core-box:input-change')
40
+ const featureKeyEvent = defineRawEvent<unknown, unknown>('core-box:key-event')
41
+
35
42
  /**
36
43
  * Feature SDK interface for plugins
37
- *
44
+ *
38
45
  * @example
39
46
  * ```typescript
40
47
  * // Push items to CoreBox
@@ -42,10 +49,10 @@ export type KeyEventHandler = (event: ForwardedKeyEvent) => void
42
49
  * { id: 'item-1', title: 'Result 1', ... },
43
50
  * { id: 'item-2', title: 'Result 2', ... }
44
51
  * ])
45
- *
52
+ *
46
53
  * // Update a specific item
47
54
  * plugin.feature.updateItem('item-1', { title: 'Updated Title' })
48
- *
55
+ *
49
56
  * // Listen for input changes
50
57
  * plugin.feature.onInputChange((input) => {
51
58
  * console.log('User typed:', input)
@@ -55,9 +62,9 @@ export type KeyEventHandler = (event: ForwardedKeyEvent) => void
55
62
  export interface FeatureSDK {
56
63
  /**
57
64
  * Pushes multiple items to the CoreBox search results
58
- *
65
+ *
59
66
  * @param items - Array of TuffItem objects to display
60
- *
67
+ *
61
68
  * @example
62
69
  * ```typescript
63
70
  * plugin.feature.pushItems([
@@ -70,14 +77,14 @@ export interface FeatureSDK {
70
77
  * ])
71
78
  * ```
72
79
  */
73
- pushItems(items: TuffItem[]): void
80
+ pushItems: (items: TuffItem[]) => void
74
81
 
75
82
  /**
76
83
  * Updates a specific item by ID
77
- *
84
+ *
78
85
  * @param id - The unique ID of the item to update
79
86
  * @param updates - Partial TuffItem with fields to update
80
- *
87
+ *
81
88
  * @example
82
89
  * ```typescript
83
90
  * // Update title and subtitle
@@ -87,49 +94,49 @@ export interface FeatureSDK {
87
94
  * })
88
95
  * ```
89
96
  */
90
- updateItem(id: string, updates: Partial<TuffItem>): void
97
+ updateItem: (id: string, updates: Partial<TuffItem>) => void
91
98
 
92
99
  /**
93
100
  * Removes a specific item by ID
94
- *
101
+ *
95
102
  * @param id - The unique ID of the item to remove
96
- *
103
+ *
97
104
  * @example
98
105
  * ```typescript
99
106
  * plugin.feature.removeItem('item-1')
100
107
  * ```
101
108
  */
102
- removeItem(id: string): void
109
+ removeItem: (id: string) => void
103
110
 
104
111
  /**
105
112
  * Clears all items pushed by this plugin
106
- *
113
+ *
107
114
  * @example
108
115
  * ```typescript
109
116
  * plugin.feature.clearItems()
110
117
  * ```
111
118
  */
112
- clearItems(): void
119
+ clearItems: () => void
113
120
 
114
121
  /**
115
122
  * Gets all items currently pushed by this plugin
116
- *
123
+ *
117
124
  * @returns Array of TuffItem objects
118
- *
125
+ *
119
126
  * @example
120
127
  * ```typescript
121
128
  * const items = plugin.feature.getItems()
122
129
  * console.log(`Currently showing ${items.length} items`)
123
130
  * ```
124
131
  */
125
- getItems(): TuffItem[]
132
+ getItems: () => TuffItem[]
126
133
 
127
134
  /**
128
135
  * Registers a listener for input changes in the CoreBox search field
129
- *
136
+ *
130
137
  * @param handler - Callback function invoked when input changes
131
138
  * @returns Unsubscribe function
132
- *
139
+ *
133
140
  * @example
134
141
  * ```typescript
135
142
  * const unsubscribe = plugin.feature.onInputChange((input) => {
@@ -137,22 +144,22 @@ export interface FeatureSDK {
137
144
  * // Perform real-time search
138
145
  * performSearch(input)
139
146
  * })
140
- *
147
+ *
141
148
  * // Later, unsubscribe
142
149
  * unsubscribe()
143
150
  * ```
144
151
  */
145
- onInputChange(handler: InputChangeHandler): () => void
152
+ onInputChange: (handler: InputChangeHandler) => () => void
146
153
 
147
154
  /**
148
155
  * Registers a listener for keyboard events forwarded from CoreBox
149
- *
156
+ *
150
157
  * When a plugin's UI view is attached to CoreBox, certain key events
151
158
  * (Enter, Arrow keys, Meta+key combinations) are forwarded to the plugin.
152
- *
159
+ *
153
160
  * @param handler - Callback function invoked when a key event is forwarded
154
161
  * @returns Unsubscribe function
155
- *
162
+ *
156
163
  * @example
157
164
  * ```typescript
158
165
  * const unsubscribe = plugin.feature.onKeyEvent((event) => {
@@ -167,70 +174,95 @@ export interface FeatureSDK {
167
174
  * openSearch()
168
175
  * }
169
176
  * })
170
- *
177
+ *
171
178
  * // Later, unsubscribe
172
179
  * unsubscribe()
173
180
  * ```
174
181
  */
175
- onKeyEvent(handler: KeyEventHandler): () => void
182
+ onKeyEvent: (handler: KeyEventHandler) => () => void
183
+
184
+ /**
185
+ * 释放内部监听器,避免重复注册导致的内存泄漏
186
+ */
187
+ dispose: () => void
176
188
  }
177
189
 
178
190
  /**
179
191
  * Creates a Feature SDK instance for plugin use
180
- *
192
+ *
181
193
  * @param boxItemsAPI - The boxItems API object from plugin context
182
194
  * @param channel - The plugin channel bridge for IPC communication
183
195
  * @returns Configured Feature SDK instance
184
- *
196
+ *
185
197
  * @internal
186
198
  */
187
199
  export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
188
200
  const inputChangeHandlers: Set<InputChangeHandler> = new Set()
189
201
  const keyEventHandlers: Set<KeyEventHandler> = new Set()
202
+ const disposables = createDisposableBag()
203
+ const transport = createPluginTuffTransport(channel)
204
+ let disposed = false
205
+
206
+ const emitInput = (payload: any) => {
207
+ const input = payload?.input || payload?.query?.text || payload || ''
208
+ for (const handler of inputChangeHandlers) {
209
+ try {
210
+ handler(input)
211
+ }
212
+ catch (error) {
213
+ console.error('[Feature SDK] onInputChange handler error:', error)
214
+ }
215
+ }
216
+ }
217
+
218
+ const emitKeyEvent = (payload: any) => {
219
+ const keyEvent = payload as ForwardedKeyEvent
220
+ if (!keyEvent) {
221
+ return
222
+ }
223
+
224
+ for (const handler of keyEventHandlers) {
225
+ try {
226
+ handler(keyEvent)
227
+ }
228
+ catch (error) {
229
+ console.error('[Feature SDK] onKeyEvent handler error:', error)
230
+ }
231
+ }
232
+ }
190
233
 
191
- // Register listener for input change events from main process
192
- const registerInputListener = () => {
193
- if (channel.onMain) {
194
- // Main process plugin context
195
- channel.onMain('core-box:input-change', (event: any) => {
196
- const input = event.data?.input || event.data?.query?.text || event.input || ''
197
- inputChangeHandlers.forEach(handler => handler(input))
198
- })
199
- } else if (channel.on) {
200
- // Renderer process context
201
- channel.on('core-box:input-change', (data: any) => {
202
- const input = data?.input || data?.query?.text || data || ''
203
- inputChangeHandlers.forEach(handler => handler(input))
204
- })
234
+ const ensureActive = (method: string) => {
235
+ if (disposed) {
236
+ throw new Error(`[Feature SDK] Cannot call ${method} after dispose`)
205
237
  }
206
238
  }
207
239
 
208
- // Register listener for key events from main process
209
- const registerKeyListener = () => {
210
- if (channel.onMain) {
211
- // Main process plugin context
212
- channel.onMain('core-box:key-event', (event: any) => {
213
- const keyEvent = event.data as ForwardedKeyEvent
214
- if (keyEvent) {
215
- keyEventHandlers.forEach(handler => handler(keyEvent))
216
- }
217
- })
218
- } else if (channel.on) {
219
- // Renderer process context
220
- channel.on('core-box:key-event', (data: any) => {
221
- const keyEvent = data as ForwardedKeyEvent
222
- if (keyEvent) {
223
- keyEventHandlers.forEach(handler => handler(keyEvent))
224
- }
225
- })
240
+ const dispose = () => {
241
+ if (disposed) {
242
+ return
226
243
  }
244
+
245
+ disposed = true
246
+ inputChangeHandlers.clear()
247
+ keyEventHandlers.clear()
248
+ disposables.dispose()
249
+ transport.destroy()
227
250
  }
228
251
 
229
- registerInputListener()
230
- registerKeyListener()
252
+ disposables.add(
253
+ transport.on(featureInputChangeEvent, emitInput),
254
+ transport.on(featureKeyEvent, emitKeyEvent),
255
+ )
256
+
257
+ if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
258
+ const onBeforeUnload = () => dispose()
259
+ window.addEventListener('beforeunload', onBeforeUnload, { once: true })
260
+ disposables.add(() => window.removeEventListener('beforeunload', onBeforeUnload))
261
+ }
231
262
 
232
263
  return {
233
264
  pushItems(items: TuffItem[]): void {
265
+ ensureActive('pushItems')
234
266
  if (!boxItemsAPI || !boxItemsAPI.pushItems) {
235
267
  throw new Error('[Feature SDK] boxItems API not available')
236
268
  }
@@ -238,6 +270,7 @@ export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
238
270
  },
239
271
 
240
272
  updateItem(id: string, updates: Partial<TuffItem>): void {
273
+ ensureActive('updateItem')
241
274
  if (!boxItemsAPI || !boxItemsAPI.update) {
242
275
  throw new Error('[Feature SDK] boxItems API not available')
243
276
  }
@@ -245,6 +278,7 @@ export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
245
278
  },
246
279
 
247
280
  removeItem(id: string): void {
281
+ ensureActive('removeItem')
248
282
  if (!boxItemsAPI || !boxItemsAPI.remove) {
249
283
  throw new Error('[Feature SDK] boxItems API not available')
250
284
  }
@@ -252,6 +286,7 @@ export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
252
286
  },
253
287
 
254
288
  clearItems(): void {
289
+ ensureActive('clearItems')
255
290
  if (!boxItemsAPI || !boxItemsAPI.clear) {
256
291
  throw new Error('[Feature SDK] boxItems API not available')
257
292
  }
@@ -259,6 +294,7 @@ export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
259
294
  },
260
295
 
261
296
  getItems(): TuffItem[] {
297
+ ensureActive('getItems')
262
298
  if (!boxItemsAPI || !boxItemsAPI.getItems) {
263
299
  throw new Error('[Feature SDK] boxItems API not available')
264
300
  }
@@ -266,45 +302,44 @@ export function createFeatureSDK(boxItemsAPI: any, channel: any): FeatureSDK {
266
302
  },
267
303
 
268
304
  onInputChange(handler: InputChangeHandler): () => void {
305
+ ensureActive('onInputChange')
269
306
  inputChangeHandlers.add(handler)
270
-
307
+
271
308
  return () => {
272
309
  inputChangeHandlers.delete(handler)
273
310
  }
274
311
  },
275
312
 
276
313
  onKeyEvent(handler: KeyEventHandler): () => void {
314
+ ensureActive('onKeyEvent')
277
315
  keyEventHandlers.add(handler)
278
-
316
+
279
317
  return () => {
280
318
  keyEventHandlers.delete(handler)
281
319
  }
282
- }
320
+ },
321
+
322
+ dispose,
283
323
  }
284
324
  }
285
325
 
286
326
  /**
287
327
  * Hook for using Feature SDK in plugin context
288
- *
328
+ *
289
329
  * @returns Feature SDK instance
290
- *
330
+ *
291
331
  * @example
292
332
  * ```typescript
293
333
  * const feature = useFeature()
294
- *
334
+ *
295
335
  * feature.pushItems([
296
336
  * { id: '1', title: { text: 'Item 1' }, ... }
297
337
  * ])
298
338
  * ```
299
339
  */
300
340
  export function useFeature(): FeatureSDK {
301
- // @ts-ignore - window.$boxItems is injected by the plugin system
302
- const boxItemsAPI = window.$boxItems
341
+ const boxItemsAPI = useBoxItems()
303
342
  const channel = ensureRendererChannel('[Feature SDK] Channel not available. Make sure this is called in a plugin context.')
304
-
305
- if (!boxItemsAPI) {
306
- throw new Error('[Feature SDK] boxItems API not available. Make sure this is called in a plugin context.')
307
- }
308
-
343
+
309
344
  return createFeatureSDK(boxItemsAPI, channel)
310
345
  }
@@ -6,14 +6,29 @@
6
6
  */
7
7
 
8
8
  import type {
9
- FlowPayload,
10
9
  FlowDispatchOptions,
11
10
  FlowDispatchResult,
12
- FlowTargetInfo,
11
+ FlowPayload,
12
+ FlowPayloadType,
13
13
  FlowSessionUpdate,
14
- FlowPayloadType
14
+ FlowTargetInfo,
15
15
  } from '../../types/flow'
16
- import { FlowIPCChannel } from '../../types/flow'
16
+ import { createPluginTuffTransport } from '../../transport'
17
+ import { FlowEvents } from '../../transport/events'
18
+ import { tryGetPluginSdkApi } from './plugin-info'
19
+
20
+ function resolveSdkApi(): number | undefined {
21
+ return tryGetPluginSdkApi()
22
+ }
23
+
24
+ /**
25
+ * Flow transfer handler function type
26
+ */
27
+ export type FlowTransferHandler = (
28
+ payload: FlowPayload,
29
+ sessionId: string,
30
+ senderInfo: { senderId: string, senderName?: string },
31
+ ) => Promise<void> | void
17
32
 
18
33
  /**
19
34
  * Flow SDK interface
@@ -41,7 +56,7 @@ export interface IFlowSDK {
41
56
  * )
42
57
  * ```
43
58
  */
44
- dispatch(payload: FlowPayload, options?: FlowDispatchOptions): Promise<FlowDispatchResult>
59
+ dispatch: (payload: FlowPayload, options?: FlowDispatchOptions) => Promise<FlowDispatchResult>
45
60
 
46
61
  /**
47
62
  * Gets available flow targets
@@ -49,7 +64,25 @@ export interface IFlowSDK {
49
64
  * @param payloadType - Filter by payload type (optional)
50
65
  * @returns List of available targets
51
66
  */
52
- getAvailableTargets(payloadType?: FlowPayloadType): Promise<FlowTargetInfo[]>
67
+ getAvailableTargets: (payloadType?: FlowPayloadType) => Promise<FlowTargetInfo[]>
68
+
69
+ /**
70
+ * Registers a handler for incoming flow transfers
71
+ * This tells the system that your plugin can handle flow data
72
+ *
73
+ * @param handler - Handler function for incoming flows
74
+ * @returns Unsubscribe function
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const unsubscribe = flow.onFlowTransfer(async (payload, sessionId, sender) => {
79
+ * console.log(`Received ${payload.type} from ${sender.senderName}`)
80
+ * // Process the payload...
81
+ * await flow.acknowledge(sessionId, { processed: true })
82
+ * })
83
+ * ```
84
+ */
85
+ onFlowTransfer: (handler: FlowTransferHandler) => () => void
53
86
 
54
87
  /**
55
88
  * Listens for session updates
@@ -58,17 +91,17 @@ export interface IFlowSDK {
58
91
  * @param handler - Update handler
59
92
  * @returns Unsubscribe function
60
93
  */
61
- onSessionUpdate(
94
+ onSessionUpdate: (
62
95
  sessionId: string,
63
- handler: (update: FlowSessionUpdate) => void
64
- ): () => void
96
+ handler: (update: FlowSessionUpdate) => void,
97
+ ) => () => void
65
98
 
66
99
  /**
67
100
  * Cancels a flow session
68
101
  *
69
102
  * @param sessionId - Session to cancel
70
103
  */
71
- cancel(sessionId: string): Promise<void>
104
+ cancel: (sessionId: string) => Promise<void>
72
105
 
73
106
  /**
74
107
  * Acknowledges a received flow (for target plugins)
@@ -76,7 +109,7 @@ export interface IFlowSDK {
76
109
  * @param sessionId - Session to acknowledge
77
110
  * @param ackPayload - Optional acknowledgment data
78
111
  */
79
- acknowledge(sessionId: string, ackPayload?: any): Promise<void>
112
+ acknowledge: (sessionId: string, ackPayload?: any) => Promise<void>
80
113
 
81
114
  /**
82
115
  * Reports an error for a received flow (for target plugins)
@@ -84,40 +117,61 @@ export interface IFlowSDK {
84
117
  * @param sessionId - Session to report error for
85
118
  * @param message - Error message
86
119
  */
87
- reportError(sessionId: string, message: string): Promise<void>
120
+ reportError: (sessionId: string, message: string) => Promise<void>
121
+
122
+ /**
123
+ * Uses native system share functionality
124
+ *
125
+ * @param payload - Data to share
126
+ * @param target - Optional preferred native target (system, airdrop, mail, messages)
127
+ */
128
+ nativeShare: (payload: FlowPayload, target?: string) => Promise<{ success: boolean, target?: string, error?: string }>
88
129
  }
89
130
 
90
131
  /**
91
132
  * Creates a Flow SDK instance
92
133
  *
93
134
  * @param channel - Channel for IPC communication
135
+ * @param channel.send - Send function for IPC communication
94
136
  * @param pluginId - Current plugin ID
95
137
  * @returns Flow SDK instance
96
138
  */
97
139
  export function createFlowSDK(
98
140
  channel: { send: (event: string, data?: any) => Promise<any> },
99
- pluginId: string
141
+ pluginId: string,
100
142
  ): IFlowSDK {
101
143
  const sessionListeners = new Map<string, Set<(update: FlowSessionUpdate) => void>>()
144
+ const flowTransferHandlers = new Set<FlowTransferHandler>()
145
+ let hasRegisteredHandler = false
102
146
 
103
- // Listen for session updates
104
- if (typeof window !== 'undefined') {
105
- window.addEventListener('message', (event) => {
106
- if (event.data?.type === FlowIPCChannel.SESSION_UPDATE) {
107
- const update = event.data.payload as FlowSessionUpdate
108
- const listeners = sessionListeners.get(update.sessionId)
109
- if (listeners) {
110
- for (const listener of listeners) {
111
- try {
112
- listener(update)
113
- } catch (error) {
114
- console.error('[FlowSDK] Error in session listener:', error)
115
- }
116
- }
117
- }
147
+ const transport = createPluginTuffTransport(channel as any)
148
+
149
+ transport.on(FlowEvents.sessionUpdate, (update) => {
150
+ const listeners = sessionListeners.get((update as any)?.sessionId)
151
+ if (!listeners) {
152
+ return
153
+ }
154
+ for (const listener of listeners) {
155
+ try {
156
+ listener(update as any)
118
157
  }
119
- })
120
- }
158
+ catch (error) {
159
+ console.error('[FlowSDK] Error in session listener:', error)
160
+ }
161
+ }
162
+ })
163
+
164
+ transport.on(FlowEvents.deliver, (payload) => {
165
+ const { sessionId, payload: flowPayload, senderId, senderName } = payload as any
166
+ for (const h of flowTransferHandlers) {
167
+ try {
168
+ h(flowPayload, sessionId, { senderId, senderName })
169
+ }
170
+ catch (error) {
171
+ console.error('[FlowSDK] Error in flow transfer handler:', error)
172
+ }
173
+ }
174
+ })
121
175
 
122
176
  return {
123
177
  async dispatch(payload: FlowPayload, options?: FlowDispatchOptions): Promise<FlowDispatchResult> {
@@ -126,26 +180,32 @@ export function createFlowSDK(
126
180
  ...payload,
127
181
  context: {
128
182
  ...payload.context,
129
- sourcePluginId: payload.context?.sourcePluginId || pluginId
130
- }
183
+ sourcePluginId: payload.context?.sourcePluginId || pluginId,
184
+ },
131
185
  }
132
186
 
133
- const response = await channel.send(FlowIPCChannel.DISPATCH, {
187
+ const response = await transport.send(FlowEvents.dispatch, {
134
188
  senderId: pluginId,
135
189
  payload: enrichedPayload,
136
- options
190
+ options,
191
+ _sdkapi: resolveSdkApi(),
137
192
  })
138
193
 
139
194
  if (!response?.success) {
140
195
  throw new Error(response?.error?.message || 'Flow dispatch failed')
141
196
  }
142
197
 
198
+ if (!response.data) {
199
+ throw new Error('Flow dispatch failed')
200
+ }
201
+
143
202
  return response.data
144
203
  },
145
204
 
146
205
  async getAvailableTargets(payloadType?: FlowPayloadType): Promise<FlowTargetInfo[]> {
147
- const response = await channel.send(FlowIPCChannel.GET_TARGETS, {
148
- payloadType
206
+ const response = await transport.send(FlowEvents.getTargets, {
207
+ payloadType,
208
+ _sdkapi: resolveSdkApi(),
149
209
  })
150
210
 
151
211
  if (!response?.success) {
@@ -157,7 +217,7 @@ export function createFlowSDK(
157
217
 
158
218
  onSessionUpdate(
159
219
  sessionId: string,
160
- handler: (update: FlowSessionUpdate) => void
220
+ handler: (update: FlowSessionUpdate) => void,
161
221
  ): () => void {
162
222
  if (!sessionListeners.has(sessionId)) {
163
223
  sessionListeners.set(sessionId, new Set())
@@ -176,8 +236,9 @@ export function createFlowSDK(
176
236
  },
177
237
 
178
238
  async cancel(sessionId: string): Promise<void> {
179
- const response = await channel.send(FlowIPCChannel.CANCEL, {
180
- sessionId
239
+ const response = await transport.send(FlowEvents.cancel, {
240
+ sessionId,
241
+ _sdkapi: resolveSdkApi(),
181
242
  })
182
243
 
183
244
  if (!response?.success) {
@@ -186,9 +247,10 @@ export function createFlowSDK(
186
247
  },
187
248
 
188
249
  async acknowledge(sessionId: string, ackPayload?: any): Promise<void> {
189
- const response = await channel.send(FlowIPCChannel.ACKNOWLEDGE, {
250
+ const response = await transport.send(FlowEvents.acknowledge, {
190
251
  sessionId,
191
- ackPayload
252
+ ackPayload,
253
+ _sdkapi: resolveSdkApi(),
192
254
  })
193
255
 
194
256
  if (!response?.success) {
@@ -197,15 +259,54 @@ export function createFlowSDK(
197
259
  },
198
260
 
199
261
  async reportError(sessionId: string, message: string): Promise<void> {
200
- const response = await channel.send(FlowIPCChannel.REPORT_ERROR, {
262
+ const response = await transport.send(FlowEvents.reportError, {
201
263
  sessionId,
202
- message
264
+ message,
265
+ _sdkapi: resolveSdkApi(),
203
266
  })
204
267
 
205
268
  if (!response?.success) {
206
269
  throw new Error(response?.error?.message || 'Failed to report error')
207
270
  }
208
- }
271
+ },
272
+
273
+ onFlowTransfer(handler: FlowTransferHandler): () => void {
274
+ flowTransferHandlers.add(handler)
275
+
276
+ // Notify the system that this plugin has a flow handler
277
+ if (!hasRegisteredHandler) {
278
+ hasRegisteredHandler = true
279
+ void transport.send(FlowEvents.setPluginHandler, {
280
+ pluginId,
281
+ hasHandler: true,
282
+ _sdkapi: resolveSdkApi(),
283
+ }).catch((err: unknown) => {
284
+ console.warn('[FlowSDK] Failed to register flow handler:', err)
285
+ })
286
+ }
287
+
288
+ return () => {
289
+ flowTransferHandlers.delete(handler)
290
+ if (flowTransferHandlers.size === 0) {
291
+ hasRegisteredHandler = false
292
+ void transport.send(FlowEvents.setPluginHandler, {
293
+ pluginId,
294
+ hasHandler: false,
295
+ _sdkapi: resolveSdkApi(),
296
+ }).catch(() => {})
297
+ }
298
+ }
299
+ },
300
+
301
+ async nativeShare(payload: FlowPayload, target?: string): Promise<{ success: boolean, target?: string, error?: string }> {
302
+ const response = await transport.send(FlowEvents.nativeShare, {
303
+ payload,
304
+ target,
305
+ _sdkapi: resolveSdkApi(),
306
+ })
307
+
308
+ return response || { success: false, error: 'Native share failed' }
309
+ },
209
310
  }
210
311
  }
211
312
 
@@ -231,7 +332,7 @@ export function extractFlowData(query: any): {
231
332
  sessionId: query.flow.sessionId,
232
333
  payload: query.flow.payload,
233
334
  senderId: query.flow.senderId,
234
- senderName: query.flow.senderName
335
+ senderName: query.flow.senderName,
235
336
  }
236
337
  }
237
338