@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,7 +1,32 @@
1
1
  import type { ITouchClientChannel } from '@talex-touch/utils/channel'
2
+ import type { ITuffTransport, TuffEvent } from '@talex-touch/utils/transport'
3
+ import { defineRawEvent } from '@talex-touch/utils/transport/event/builder'
4
+
5
+ const legacyCloseEvent = defineRawEvent<void, void>('close')
6
+ const legacyHideEvent = defineRawEvent<void, void>('hide')
7
+ const legacyMinimizeEvent = defineRawEvent<void, void>('minimize')
8
+ const legacyDevToolsEvent = defineRawEvent<void, void>('dev-tools')
9
+ const legacyCwdEvent = defineRawEvent<void, string>('common:cwd')
10
+ const legacyPackageEvent = defineRawEvent<void, any>('get-package')
11
+ const legacyOsEvent = defineRawEvent<void, any>('get-os')
12
+ const legacyFolderOpenEvent = defineRawEvent<FolderOpenOptions, void>('folder:open')
13
+ const legacyExecuteCmdEvent = defineRawEvent<ExecuteCommandOptions, void>('execute:cmd')
14
+ const legacyAppOpenEvent = defineRawEvent<AppOpenOptions, void>('app:open')
15
+ const legacyOpenExternalEvent = defineRawEvent<ExternalUrlOptions, void>('open-external')
16
+ const legacyTempFileCreateEvent = defineRawEvent<TempFileCreateOptions, TempFileCreateResult>(
17
+ 'temp-file:create',
18
+ )
19
+ const legacyTempFileDeleteEvent = defineRawEvent<TempFileDeleteOptions, TempFileDeleteResult>(
20
+ 'temp-file:delete',
21
+ )
22
+ const legacyPluginFolderEvent = defineRawEvent<string, void>('plugin:explorer')
23
+ const legacyPluginDevToolsEvent = defineRawEvent<string, void>('plugin:open-devtools')
24
+ const legacyPluginReloadEvent = defineRawEvent<{ name: string }, void>('reload-plugin')
25
+ const legacyModuleFolderEvent = defineRawEvent<{ name?: string }, void>('module:folder')
2
26
 
3
27
  export interface TouchSDKOptions {
4
- channel: ITouchClientChannel
28
+ transport?: ITuffTransport
29
+ channel?: ITouchClientChannel
5
30
  }
6
31
 
7
32
  export interface FolderOpenOptions {
@@ -21,68 +46,130 @@ export interface ExternalUrlOptions {
21
46
  url: string
22
47
  }
23
48
 
49
+ export interface TempFileCreateOptions {
50
+ namespace: string
51
+ ext?: string
52
+ text?: string
53
+ base64?: string
54
+ prefix?: string
55
+ retentionMs?: number
56
+ }
57
+
58
+ export interface TempFileCreateResult {
59
+ url: string
60
+ sizeBytes: number
61
+ createdAt: number
62
+ }
63
+
64
+ export interface TempFileDeleteOptions {
65
+ url?: string
66
+ path?: string
67
+ }
68
+
69
+ export interface TempFileDeleteResult {
70
+ success: boolean
71
+ }
72
+
24
73
  export class TouchSDK {
25
- private channel: ITouchClientChannel
74
+ private transport: ITuffTransport | null
75
+ private channel: ITouchClientChannel | null
26
76
 
27
77
  constructor(options: TouchSDKOptions) {
28
- this.channel = options.channel
78
+ this.transport = options.transport ?? null
79
+ this.channel = options.channel ?? null
80
+ }
81
+
82
+ private async sendEvent<TReq, TRes>(event: TuffEvent<TReq, TRes>, payload?: TReq): Promise<TRes> {
83
+ if (this.transport) {
84
+ const shouldPassPayload = payload !== undefined
85
+ if (shouldPassPayload) {
86
+ return this.transport.send(event, payload)
87
+ }
88
+ return this.transport.send(event as TuffEvent<void, TRes>)
89
+ }
90
+
91
+ if (this.channel) {
92
+ return this.channel.send(event.toEventName(), payload as any) as Promise<TRes>
93
+ }
94
+
95
+ throw new Error('[TouchSDK] Transport or channel not initialized.')
29
96
  }
30
97
 
31
98
  /**
32
99
  * System Operations
33
100
  */
34
101
  async closeApp(): Promise<void> {
35
- return this.channel.send('close')
102
+ return this.sendEvent(legacyCloseEvent)
36
103
  }
37
104
 
38
105
  async hideApp(): Promise<void> {
39
- return this.channel.send('hide')
106
+ return this.sendEvent(legacyHideEvent)
40
107
  }
41
108
 
42
109
  async minimizeApp(): Promise<void> {
43
- return this.channel.send('minimize')
110
+ return this.sendEvent(legacyMinimizeEvent)
44
111
  }
45
112
 
46
113
  async openDevTools(): Promise<void> {
47
- return this.channel.send('dev-tools')
114
+ return this.sendEvent(legacyDevToolsEvent)
48
115
  }
49
116
 
50
117
  async getCurrentWorkingDirectory(): Promise<string> {
51
- return this.channel.send('common:cwd')
118
+ return this.sendEvent(legacyCwdEvent)
52
119
  }
53
120
 
54
121
  async getPackageInfo(): Promise<any> {
55
- return this.channel.send('get-package')
122
+ return this.sendEvent(legacyPackageEvent)
56
123
  }
57
124
 
58
125
  async getOSInfo(): Promise<any> {
59
- return this.channel.send('get-os')
126
+ return this.sendEvent(legacyOsEvent)
60
127
  }
61
128
 
62
129
  /**
63
130
  * File & Folder Operations
64
131
  */
65
132
  async openFolder(options: FolderOpenOptions): Promise<void> {
66
- return this.channel.send('folder:open', options)
133
+ return this.sendEvent(legacyFolderOpenEvent, options)
67
134
  }
68
135
 
69
136
  async executeCommand(options: ExecuteCommandOptions): Promise<void> {
70
- return this.channel.send('execute:cmd', options)
137
+ return this.sendEvent(legacyExecuteCmdEvent, options)
71
138
  }
72
139
 
73
140
  async openApp(options: AppOpenOptions): Promise<void> {
74
- return this.channel.send('app:open', options)
141
+ return this.sendEvent(legacyAppOpenEvent, options)
75
142
  }
76
143
 
77
144
  async openExternalUrl(options: ExternalUrlOptions): Promise<void> {
78
- return this.channel.send('open-external', options)
145
+ return this.sendEvent(legacyOpenExternalEvent, options)
146
+ }
147
+
148
+ /**
149
+ * Temp file operations
150
+ */
151
+ async createTempFile(options: TempFileCreateOptions): Promise<TempFileCreateResult> {
152
+ return this.sendEvent(legacyTempFileCreateEvent, options)
153
+ }
154
+
155
+ async deleteTempFile(options: TempFileDeleteOptions): Promise<TempFileDeleteResult> {
156
+ return this.sendEvent(legacyTempFileDeleteEvent, options)
79
157
  }
80
158
 
81
159
  /**
82
160
  * Plugin Operations
83
161
  */
84
162
  async openPluginFolder(pluginName: string): Promise<void> {
85
- return this.channel.send('plugin:explorer', pluginName)
163
+ return this.sendEvent(legacyPluginFolderEvent, pluginName)
164
+ }
165
+
166
+ /**
167
+ * Opens the DevTools for a plugin's Surface WebContents
168
+ * @param pluginName - The name of the plugin to open DevTools for
169
+ * @returns Promise that resolves when DevTools is opened
170
+ */
171
+ async openPluginDevTools(pluginName: string): Promise<void> {
172
+ return this.sendEvent(legacyPluginDevToolsEvent, pluginName)
86
173
  }
87
174
 
88
175
  /**
@@ -91,27 +178,36 @@ export class TouchSDK {
91
178
  * @returns Promise that resolves when the reload operation completes
92
179
  */
93
180
  async reloadPlugin(pluginName: string): Promise<void> {
94
- return this.channel.send('reload-plugin', { name: pluginName })
181
+ return this.sendEvent(legacyPluginReloadEvent, { name: pluginName })
95
182
  }
96
183
 
97
184
  /**
98
185
  * Module Operations
99
186
  */
100
187
  async openModuleFolder(moduleName?: string): Promise<void> {
101
- return this.channel.send('module:folder', { name: moduleName })
188
+ return this.sendEvent(legacyModuleFolderEvent, { name: moduleName })
102
189
  }
103
190
 
104
191
  /**
105
192
  * Event Registration
106
193
  */
107
194
  onChannelEvent(eventName: string, callback: (data: any) => void): () => void {
108
- return this.channel.regChannel(eventName, callback)
195
+ if (this.transport) {
196
+ return this.transport.on(defineRawEvent<any, any>(eventName), callback)
197
+ }
198
+ if (this.channel) {
199
+ return this.channel.regChannel(eventName, callback)
200
+ }
201
+ throw new Error('[TouchSDK] Transport or channel not initialized.')
109
202
  }
110
203
 
111
204
  /**
112
205
  * Raw channel access for advanced usage
113
206
  */
114
207
  get rawChannel(): ITouchClientChannel {
208
+ if (!this.channel) {
209
+ throw new Error('[TouchSDK] Channel not initialized.')
210
+ }
115
211
  return this.channel
116
212
  }
117
213
  }
@@ -1,4 +1,15 @@
1
- import type { ITouchClientChannel } from '@talex-touch/utils/channel'
1
+ import type { ITuffTransport } from '@talex-touch/utils/transport'
2
+ import { defineRawEvent } from '@talex-touch/utils/transport/event/builder'
3
+
4
+ const terminalCreateEvent = defineRawEvent<{ command: string, args?: string[] }, { id: string }>(
5
+ 'terminal:create',
6
+ )
7
+ const terminalWriteEvent = defineRawEvent<{ id: string, data: string }, void>('terminal:write')
8
+ const terminalKillEvent = defineRawEvent<{ id: string }, void>('terminal:kill')
9
+ const terminalDataEvent = defineRawEvent<{ id: string, data: string }, void>('terminal:data')
10
+ const terminalExitEvent = defineRawEvent<{ id: string, exitCode: number | null }, void>(
11
+ 'terminal:exit',
12
+ )
2
13
 
3
14
  type DataCallback = (data: string) => void
4
15
  type ExitCallback = (exitCode: number | null) => void
@@ -7,10 +18,10 @@ export class Terminal {
7
18
  private id: string | null = null
8
19
  private onDataCallback: DataCallback | null = null
9
20
  private onExitCallback: ExitCallback | null = null
10
- private channel: ITouchClientChannel
21
+ private transport: ITuffTransport
11
22
 
12
- constructor(channel: ITouchClientChannel) {
13
- this.channel = channel
23
+ constructor(transport: ITuffTransport) {
24
+ this.transport = transport
14
25
  }
15
26
 
16
27
  /**
@@ -26,19 +37,19 @@ export class Terminal {
26
37
  // However, for simplicity in this refactor, we'll assume exec is called for a new, independent command.
27
38
  // A more robust implementation might track multiple concurrent processes.
28
39
 
29
- const { id } = await this.channel.send('terminal:create', { command, args })
40
+ const { id } = await this.transport.send(terminalCreateEvent, { command, args })
30
41
  this.id = id
31
42
 
32
43
  // Re-register listeners for the new process ID
33
- this.channel.regChannel('terminal:data', (channelData) => {
34
- if (this.id === channelData.data.id && this.onDataCallback) {
35
- this.onDataCallback(channelData.data.data)
44
+ this.transport.on(terminalDataEvent, (payload) => {
45
+ if (this.id === payload.id && this.onDataCallback) {
46
+ this.onDataCallback(payload.data)
36
47
  }
37
48
  })
38
49
 
39
- this.channel.regChannel('terminal:exit', (channelData) => {
40
- if (this.id === channelData.data.id && this.onExitCallback) {
41
- this.onExitCallback(channelData.data.exitCode)
50
+ this.transport.on(terminalExitEvent, (payload) => {
51
+ if (this.id === payload.id && this.onExitCallback) {
52
+ this.onExitCallback(payload.exitCode)
42
53
  this.id = null
43
54
  }
44
55
  })
@@ -53,7 +64,7 @@ export class Terminal {
53
64
  */
54
65
  public write(data: string): void {
55
66
  if (this.id) {
56
- this.channel.send('terminal:write', { id: this.id, data })
67
+ void this.transport.send(terminalWriteEvent, { id: this.id, data })
57
68
  }
58
69
  }
59
70
 
@@ -62,7 +73,7 @@ export class Terminal {
62
73
  */
63
74
  public kill(): void {
64
75
  if (this.id) {
65
- this.channel.send('terminal:kill', { id: this.id })
76
+ void this.transport.send(terminalKillEvent, { id: this.id })
66
77
  this.id = null
67
78
  }
68
79
  }
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Feature Matching Utilities
3
+ *
4
+ * Provides enhanced search matching for plugin features with:
5
+ * - Pinyin/English token matching
6
+ * - Fuzzy match support
7
+ * - Match range generation for UI highlighting
8
+ */
9
+
10
+ import { fuzzyMatch, indicesToRanges } from './fuzzy-match'
11
+
12
+ /**
13
+ * Match range for highlighting
14
+ */
15
+ export interface MatchRange {
16
+ start: number
17
+ end: number
18
+ }
19
+
20
+ /**
21
+ * Feature match result with score and highlight ranges
22
+ */
23
+ export interface FeatureMatchResult {
24
+ /** Whether the feature matches the query */
25
+ matched: boolean
26
+ /** Match score (0-1000, higher is better) */
27
+ score: number
28
+ /** Match type for debugging */
29
+ matchType: 'exact' | 'token' | 'prefix' | 'contains' | 'fuzzy' | 'none'
30
+ /** Match ranges for highlighting in title */
31
+ matchRanges: MatchRange[]
32
+ /** Which token matched (for debugging) */
33
+ matchedToken?: string
34
+ }
35
+
36
+ /**
37
+ * Options for feature matching
38
+ */
39
+ export interface FeatureMatchOptions {
40
+ /** Feature title/name */
41
+ title: string
42
+ /** Feature description */
43
+ desc?: string
44
+ /** Pre-computed search tokens (pinyin, initials, etc.) */
45
+ searchTokens?: string[]
46
+ /** Search query */
47
+ query: string
48
+ /** Enable fuzzy matching (default: true) */
49
+ enableFuzzy?: boolean
50
+ /** Maximum fuzzy errors (default: 2) */
51
+ maxFuzzyErrors?: number
52
+ }
53
+
54
+ /**
55
+ * Find substring match and return range
56
+ */
57
+ function findSubstringMatch(text: string, query: string): MatchRange | null {
58
+ const lowerText = text.toLowerCase()
59
+ const lowerQuery = query.toLowerCase()
60
+ const index = lowerText.indexOf(lowerQuery)
61
+
62
+ if (index === -1)
63
+ return null
64
+
65
+ return { start: index, end: index + query.length }
66
+ }
67
+
68
+ /**
69
+ * Match query against a single token and return match info
70
+ */
71
+ function matchToken(
72
+ token: string,
73
+ query: string,
74
+ ): { matched: boolean, score: number, type: 'exact' | 'prefix' | 'contains' | 'none' } {
75
+ const lowerToken = token.toLowerCase()
76
+ const lowerQuery = query.toLowerCase()
77
+
78
+ // Exact match
79
+ if (lowerToken === lowerQuery) {
80
+ return { matched: true, score: 1000, type: 'exact' }
81
+ }
82
+
83
+ // Prefix match (token starts with query)
84
+ if (lowerToken.startsWith(lowerQuery)) {
85
+ // Score based on how much of the token is matched
86
+ const coverage = lowerQuery.length / lowerToken.length
87
+ return { matched: true, score: 800 + Math.round(coverage * 100), type: 'prefix' }
88
+ }
89
+
90
+ // Contains match
91
+ if (lowerToken.includes(lowerQuery)) {
92
+ const coverage = lowerQuery.length / lowerToken.length
93
+ return { matched: true, score: 600 + Math.round(coverage * 50), type: 'contains' }
94
+ }
95
+
96
+ return { matched: false, score: 0, type: 'none' }
97
+ }
98
+
99
+ /**
100
+ * Match feature against search query
101
+ *
102
+ * This is the main matching function that:
103
+ * 1. Tries exact/prefix/contains match against title
104
+ * 2. Tries token matching (pinyin, initials, keywords)
105
+ * 3. Falls back to fuzzy matching
106
+ *
107
+ * @returns FeatureMatchResult with score and highlight ranges
108
+ */
109
+ export function matchFeature(options: FeatureMatchOptions): FeatureMatchResult {
110
+ const {
111
+ title,
112
+ desc,
113
+ searchTokens = [],
114
+ query,
115
+ enableFuzzy = true,
116
+ maxFuzzyErrors = 2,
117
+ } = options
118
+
119
+ const trimmedQuery = query.trim()
120
+
121
+ // Empty query - no match
122
+ if (!trimmedQuery) {
123
+ return { matched: false, score: 0, matchType: 'none', matchRanges: [] }
124
+ }
125
+
126
+ const lowerQuery = trimmedQuery.toLowerCase()
127
+ const lowerTitle = title.toLowerCase()
128
+
129
+ // 1. Exact title match
130
+ if (lowerTitle === lowerQuery) {
131
+ return {
132
+ matched: true,
133
+ score: 1000,
134
+ matchType: 'exact',
135
+ matchRanges: [{ start: 0, end: title.length }],
136
+ }
137
+ }
138
+
139
+ // 2. Title prefix match
140
+ if (lowerTitle.startsWith(lowerQuery)) {
141
+ return {
142
+ matched: true,
143
+ score: 900,
144
+ matchType: 'prefix',
145
+ matchRanges: [{ start: 0, end: trimmedQuery.length }],
146
+ }
147
+ }
148
+
149
+ // 3. Title contains match
150
+ const titleMatch = findSubstringMatch(title, trimmedQuery)
151
+ if (titleMatch) {
152
+ return {
153
+ matched: true,
154
+ score: 700,
155
+ matchType: 'contains',
156
+ matchRanges: [titleMatch],
157
+ }
158
+ }
159
+
160
+ // 4. Search tokens matching (pinyin, initials, keywords)
161
+ // This enables searching "fanyi" to match "翻译"
162
+ if (searchTokens.length > 0) {
163
+ let bestTokenMatch: {
164
+ score: number
165
+ type: 'exact' | 'prefix' | 'contains'
166
+ token: string
167
+ } | null = null
168
+
169
+ for (const token of searchTokens) {
170
+ if (!token)
171
+ continue
172
+
173
+ const result = matchToken(token, trimmedQuery)
174
+ if (result.matched && (!bestTokenMatch || result.score > bestTokenMatch.score)) {
175
+ bestTokenMatch = {
176
+ score: result.score,
177
+ type: result.type as 'exact' | 'prefix' | 'contains',
178
+ token,
179
+ }
180
+ }
181
+ }
182
+
183
+ if (bestTokenMatch) {
184
+ // Token matched - highlight entire title since we can't map token back to characters
185
+ // For pinyin matches, the full Chinese title is relevant
186
+ return {
187
+ matched: true,
188
+ score: bestTokenMatch.score - 50, // Slightly lower than direct title match
189
+ matchType: 'token',
190
+ matchRanges: [{ start: 0, end: title.length }],
191
+ matchedToken: bestTokenMatch.token,
192
+ }
193
+ }
194
+ }
195
+
196
+ // 5. Description matching (lower priority)
197
+ if (desc) {
198
+ const descMatch = findSubstringMatch(desc, trimmedQuery)
199
+ if (descMatch) {
200
+ return {
201
+ matched: true,
202
+ score: 400,
203
+ matchType: 'contains',
204
+ matchRanges: [], // No title highlight for desc matches
205
+ }
206
+ }
207
+ }
208
+
209
+ // 6. Fuzzy matching on title
210
+ if (enableFuzzy) {
211
+ const fuzzyResult = fuzzyMatch(title, trimmedQuery, maxFuzzyErrors)
212
+ if (fuzzyResult.matched && fuzzyResult.score > 0.5) {
213
+ return {
214
+ matched: true,
215
+ score: Math.round(fuzzyResult.score * 500), // Scale to 0-500 range
216
+ matchType: 'fuzzy',
217
+ matchRanges: indicesToRanges(fuzzyResult.matchedIndices),
218
+ }
219
+ }
220
+
221
+ // Try fuzzy on tokens
222
+ for (const token of searchTokens) {
223
+ if (!token || token.length < 2)
224
+ continue
225
+
226
+ const tokenFuzzy = fuzzyMatch(token, trimmedQuery, maxFuzzyErrors)
227
+ if (tokenFuzzy.matched && tokenFuzzy.score > 0.6) {
228
+ return {
229
+ matched: true,
230
+ score: Math.round(tokenFuzzy.score * 400),
231
+ matchType: 'fuzzy',
232
+ matchRanges: [{ start: 0, end: title.length }],
233
+ matchedToken: token,
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ return { matched: false, score: 0, matchType: 'none', matchRanges: [] }
240
+ }
241
+
242
+ /**
243
+ * Batch match multiple features and return sorted results
244
+ *
245
+ * @param features Array of features with their search metadata
246
+ * @param query Search query
247
+ * @returns Features sorted by match score (highest first), with match metadata
248
+ */
249
+ export function matchFeatures<T extends { searchTokens?: string[] }>(
250
+ features: Array<{
251
+ feature: T
252
+ title: string
253
+ desc?: string
254
+ }>,
255
+ query: string,
256
+ ): Array<{
257
+ feature: T
258
+ result: FeatureMatchResult
259
+ }> {
260
+ const results: Array<{ feature: T, result: FeatureMatchResult }> = []
261
+
262
+ for (const { feature, title, desc } of features) {
263
+ const result = matchFeature({
264
+ title,
265
+ desc,
266
+ searchTokens: feature.searchTokens,
267
+ query,
268
+ })
269
+
270
+ if (result.matched) {
271
+ results.push({ feature, result })
272
+ }
273
+ }
274
+
275
+ // Sort by score descending
276
+ results.sort((a, b) => b.result.score - a.result.score)
277
+
278
+ return results
279
+ }