@talex-touch/utils 1.0.40 → 1.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintcache +1 -0
- package/__tests__/cloud-sync-sdk.test.ts +442 -0
- package/__tests__/icons/icons.test.ts +84 -0
- package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
- package/__tests__/power-sdk.test.ts +143 -0
- package/__tests__/preset-export-types.test.ts +108 -0
- package/__tests__/search/fuzzy-match.test.ts +137 -0
- package/__tests__/transport/port-policy.test.ts +44 -0
- package/__tests__/transport-domain-sdks.test.ts +152 -0
- package/__tests__/types/update.test.ts +67 -0
- package/account/account-sdk.ts +915 -0
- package/account/index.ts +2 -0
- package/account/types.ts +321 -0
- package/analytics/client.ts +136 -0
- package/analytics/index.ts +2 -0
- package/analytics/types.ts +156 -0
- package/animation/auto-resize.ts +322 -0
- package/animation/window-node.ts +26 -19
- package/auth/clerk-types.ts +12 -30
- package/auth/index.ts +0 -2
- package/auth/useAuthState.ts +6 -14
- package/base/index.ts +2 -0
- package/base/log-level.ts +105 -0
- package/channel/index.ts +170 -69
- package/cloud-sync/cloud-sync-sdk.ts +450 -0
- package/cloud-sync/index.ts +1 -0
- package/common/file-scan-utils.ts +17 -9
- package/common/index.ts +4 -0
- package/common/logger/index.ts +46 -0
- package/common/logger/logger-manager.ts +303 -0
- package/common/logger/module-logger.ts +270 -0
- package/common/logger/transport-logger.ts +234 -0
- package/common/logger/types.ts +93 -0
- package/common/search/gather.ts +48 -6
- package/common/search/index.ts +8 -0
- package/common/storage/constants.ts +13 -0
- package/common/storage/entity/app-settings.ts +245 -0
- package/common/storage/entity/index.ts +3 -0
- package/common/storage/entity/layout-atom-types.ts +147 -0
- package/common/storage/entity/openers.ts +1 -0
- package/common/storage/entity/preset-cloud-api.ts +132 -0
- package/common/storage/entity/preset-export-types.ts +256 -0
- package/common/storage/entity/shortcut-settings.ts +1 -0
- package/common/storage/shortcut-storage.ts +11 -0
- package/common/utils/clone-diagnostics.ts +105 -0
- package/common/utils/file.ts +16 -8
- package/common/utils/index.ts +6 -2
- package/common/utils/payload-preview.ts +173 -0
- package/common/utils/polling.ts +167 -13
- package/common/utils/safe-path.ts +103 -0
- package/common/utils/safe-shell.ts +115 -0
- package/common/utils/task-queue.ts +4 -1
- package/core-box/builder/tuff-builder.ts +0 -1
- package/core-box/index.ts +1 -1
- package/core-box/recommendation.ts +38 -1
- package/core-box/tuff/tuff-dsl.ts +97 -0
- package/electron/download-manager.ts +10 -7
- package/electron/env-tool.ts +42 -40
- package/electron/index.ts +0 -1
- package/env/index.ts +156 -0
- package/eslint.config.js +55 -0
- package/i18n/index.ts +62 -0
- package/i18n/locales/en.json +226 -0
- package/i18n/locales/zh.json +226 -0
- package/i18n/message-keys.ts +236 -0
- package/i18n/resolver.ts +181 -0
- package/icons/index.ts +257 -0
- package/icons/svg.ts +69 -0
- package/index.ts +9 -1
- package/intelligence/client.ts +72 -42
- package/market/constants.ts +21 -3
- package/market/index.ts +1 -1
- package/market/types.ts +20 -5
- package/package.json +15 -5
- package/permission/index.ts +143 -46
- package/permission/legacy.ts +26 -0
- package/permission/registry.ts +304 -0
- package/permission/types.ts +164 -0
- package/plugin/channel.ts +68 -39
- package/plugin/index.ts +82 -8
- package/plugin/install.ts +3 -0
- package/plugin/log/types.ts +22 -5
- package/plugin/node/logger-manager.ts +11 -3
- package/plugin/node/logger.ts +24 -17
- package/plugin/preload.ts +25 -2
- package/plugin/providers/index.ts +4 -0
- package/plugin/providers/market-client.ts +218 -0
- package/plugin/providers/npm-provider.ts +228 -0
- package/plugin/providers/tpex-provider.ts +297 -0
- package/plugin/providers/tpex-types.ts +34 -0
- package/plugin/sdk/box-items.ts +14 -0
- package/plugin/sdk/box-sdk.ts +64 -0
- package/plugin/sdk/channel.ts +119 -4
- package/plugin/sdk/clipboard.ts +26 -12
- package/plugin/sdk/cloud-sync.ts +113 -0
- package/plugin/sdk/common.ts +19 -11
- package/plugin/sdk/core-box.ts +6 -15
- package/plugin/sdk/division-box.ts +160 -65
- package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
- package/plugin/sdk/feature-sdk.ts +111 -76
- package/plugin/sdk/flow.ts +146 -45
- package/plugin/sdk/hooks/bridge.ts +113 -49
- package/plugin/sdk/hooks/life-cycle.ts +35 -16
- package/plugin/sdk/index.ts +14 -3
- package/plugin/sdk/intelligence.ts +87 -0
- package/plugin/sdk/meta/README.md +179 -0
- package/plugin/sdk/meta-sdk.ts +244 -0
- package/plugin/sdk/notification.ts +9 -0
- package/plugin/sdk/performance.ts +1 -16
- package/plugin/sdk/plugin-info.ts +64 -0
- package/plugin/sdk/power.ts +155 -0
- package/plugin/sdk/recommend.ts +21 -0
- package/plugin/sdk/service/index.ts +12 -8
- package/plugin/sdk/sqlite.ts +141 -0
- package/plugin/sdk/storage.ts +2 -6
- package/plugin/sdk/system.ts +2 -9
- package/plugin/sdk/temp-files.ts +41 -0
- package/plugin/sdk/touch-sdk.ts +18 -0
- package/plugin/sdk/types.ts +44 -4
- package/plugin/sdk/window/index.ts +12 -9
- package/plugin/sdk-version.ts +231 -0
- package/preload/renderer.ts +3 -2
- package/renderer/hooks/arg-mapper.ts +34 -6
- package/renderer/hooks/index.ts +13 -0
- package/renderer/hooks/initialize.ts +2 -1
- package/renderer/hooks/use-agent-market-sdk.ts +7 -0
- package/renderer/hooks/use-agent-market.ts +106 -0
- package/renderer/hooks/use-agents-sdk.ts +7 -0
- package/renderer/hooks/use-app-sdk.ts +7 -0
- package/renderer/hooks/use-channel.ts +33 -4
- package/renderer/hooks/use-download-sdk.ts +21 -0
- package/renderer/hooks/use-intelligence-sdk.ts +7 -0
- package/renderer/hooks/use-intelligence-stats.ts +290 -0
- package/renderer/hooks/use-intelligence.ts +202 -104
- package/renderer/hooks/use-market-sdk.ts +16 -0
- package/renderer/hooks/use-notification-sdk.ts +7 -0
- package/renderer/hooks/use-permission-sdk.ts +7 -0
- package/renderer/hooks/use-permission.ts +325 -0
- package/renderer/hooks/use-platform-sdk.ts +7 -0
- package/renderer/hooks/use-plugin-sdk.ts +16 -0
- package/renderer/hooks/use-settings-sdk.ts +7 -0
- package/renderer/hooks/use-update-sdk.ts +21 -0
- package/renderer/index.ts +1 -0
- package/renderer/ref.ts +19 -10
- package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
- package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
- package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
- package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
- package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
- package/renderer/shared/components/index.ts +5 -0
- package/renderer/shared/components/shims-vue.d.ts +5 -0
- package/renderer/shared/index.ts +2 -0
- package/renderer/shared/plugin-detail.ts +62 -0
- package/renderer/storage/app-settings.ts +3 -1
- package/renderer/storage/base-storage.ts +508 -82
- package/renderer/storage/intelligence-storage.ts +37 -46
- package/renderer/storage/openers.ts +3 -1
- package/renderer/storage/storage-subscription.ts +126 -42
- package/renderer/touch-sdk/env.ts +10 -10
- package/renderer/touch-sdk/index.ts +114 -18
- package/renderer/touch-sdk/terminal.ts +24 -13
- package/search/feature-matcher.ts +279 -0
- package/search/fuzzy-match.ts +64 -34
- package/search/index.ts +10 -0
- package/search/levenshtein-utils.ts +17 -11
- package/transport/errors.ts +310 -0
- package/transport/event/builder.ts +378 -0
- package/transport/event/index.ts +7 -0
- package/transport/event/types.ts +292 -0
- package/transport/events/index.ts +2670 -0
- package/transport/events/meta-overlay.ts +79 -0
- package/transport/events/types/agents.ts +177 -0
- package/transport/events/types/app-index.ts +9 -0
- package/transport/events/types/app.ts +475 -0
- package/transport/events/types/box-item.ts +222 -0
- package/transport/events/types/clipboard.ts +80 -0
- package/transport/events/types/core-box.ts +534 -0
- package/transport/events/types/device-idle.ts +7 -0
- package/transport/events/types/division-box.ts +99 -0
- package/transport/events/types/download.ts +115 -0
- package/transport/events/types/file-index.ts +73 -0
- package/transport/events/types/flow.ts +149 -0
- package/transport/events/types/index.ts +70 -0
- package/transport/events/types/market.ts +39 -0
- package/transport/events/types/meta-overlay.ts +184 -0
- package/transport/events/types/notification.ts +140 -0
- package/transport/events/types/permission.ts +90 -0
- package/transport/events/types/platform.ts +8 -0
- package/transport/events/types/plugin.ts +620 -0
- package/transport/events/types/sentry.ts +20 -0
- package/transport/events/types/storage.ts +208 -0
- package/transport/events/types/transport.ts +60 -0
- package/transport/events/types/tray.ts +16 -0
- package/transport/events/types/update.ts +78 -0
- package/transport/index.ts +139 -0
- package/transport/main.ts +2 -0
- package/transport/sdk/constants.ts +29 -0
- package/transport/sdk/domains/agents-market.ts +47 -0
- package/transport/sdk/domains/agents.ts +62 -0
- package/transport/sdk/domains/app.ts +48 -0
- package/transport/sdk/domains/disposable.ts +35 -0
- package/transport/sdk/domains/download.ts +139 -0
- package/transport/sdk/domains/index.ts +13 -0
- package/transport/sdk/domains/intelligence.ts +616 -0
- package/transport/sdk/domains/market.ts +35 -0
- package/transport/sdk/domains/notification.ts +62 -0
- package/transport/sdk/domains/permission.ts +85 -0
- package/transport/sdk/domains/platform.ts +19 -0
- package/transport/sdk/domains/plugin.ts +144 -0
- package/transport/sdk/domains/settings.ts +92 -0
- package/transport/sdk/domains/update.ts +64 -0
- package/transport/sdk/index.ts +60 -0
- package/transport/sdk/main-transport.ts +710 -0
- package/transport/sdk/main.ts +9 -0
- package/transport/sdk/plugin-transport.ts +654 -0
- package/transport/sdk/port-policy.ts +38 -0
- package/transport/sdk/renderer-transport.ts +1165 -0
- package/transport/types.ts +605 -0
- package/types/agent.ts +399 -0
- package/types/cloud-sync.ts +157 -0
- package/types/division-box.ts +47 -27
- package/types/download.ts +1 -0
- package/types/flow.ts +63 -12
- package/types/icon.ts +2 -1
- package/types/index.ts +5 -0
- package/types/intelligence.ts +1492 -81
- package/types/modules/base.ts +2 -0
- package/types/path-browserify.d.ts +5 -0
- package/types/platform.ts +12 -0
- package/types/startup-info.ts +32 -0
- package/types/touch-app-core.ts +8 -8
- package/types/update.ts +94 -1
- package/vitest.config.ts +25 -0
- package/auth/useClerkConfig.ts +0 -40
- package/auth/useClerkProvider.ts +0 -52
|
@@ -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
|
-
|
|
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
|
|
74
|
+
private transport: ITuffTransport | null
|
|
75
|
+
private channel: ITouchClientChannel | null
|
|
26
76
|
|
|
27
77
|
constructor(options: TouchSDKOptions) {
|
|
28
|
-
this.
|
|
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.
|
|
102
|
+
return this.sendEvent(legacyCloseEvent)
|
|
36
103
|
}
|
|
37
104
|
|
|
38
105
|
async hideApp(): Promise<void> {
|
|
39
|
-
return this.
|
|
106
|
+
return this.sendEvent(legacyHideEvent)
|
|
40
107
|
}
|
|
41
108
|
|
|
42
109
|
async minimizeApp(): Promise<void> {
|
|
43
|
-
return this.
|
|
110
|
+
return this.sendEvent(legacyMinimizeEvent)
|
|
44
111
|
}
|
|
45
112
|
|
|
46
113
|
async openDevTools(): Promise<void> {
|
|
47
|
-
return this.
|
|
114
|
+
return this.sendEvent(legacyDevToolsEvent)
|
|
48
115
|
}
|
|
49
116
|
|
|
50
117
|
async getCurrentWorkingDirectory(): Promise<string> {
|
|
51
|
-
return this.
|
|
118
|
+
return this.sendEvent(legacyCwdEvent)
|
|
52
119
|
}
|
|
53
120
|
|
|
54
121
|
async getPackageInfo(): Promise<any> {
|
|
55
|
-
return this.
|
|
122
|
+
return this.sendEvent(legacyPackageEvent)
|
|
56
123
|
}
|
|
57
124
|
|
|
58
125
|
async getOSInfo(): Promise<any> {
|
|
59
|
-
return this.
|
|
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.
|
|
133
|
+
return this.sendEvent(legacyFolderOpenEvent, options)
|
|
67
134
|
}
|
|
68
135
|
|
|
69
136
|
async executeCommand(options: ExecuteCommandOptions): Promise<void> {
|
|
70
|
-
return this.
|
|
137
|
+
return this.sendEvent(legacyExecuteCmdEvent, options)
|
|
71
138
|
}
|
|
72
139
|
|
|
73
140
|
async openApp(options: AppOpenOptions): Promise<void> {
|
|
74
|
-
return this.
|
|
141
|
+
return this.sendEvent(legacyAppOpenEvent, options)
|
|
75
142
|
}
|
|
76
143
|
|
|
77
144
|
async openExternalUrl(options: ExternalUrlOptions): Promise<void> {
|
|
78
|
-
return this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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 {
|
|
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
|
|
21
|
+
private transport: ITuffTransport
|
|
11
22
|
|
|
12
|
-
constructor(
|
|
13
|
-
this.
|
|
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.
|
|
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.
|
|
34
|
-
if (this.id ===
|
|
35
|
-
this.onDataCallback(
|
|
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.
|
|
40
|
-
if (this.id ===
|
|
41
|
-
this.onExitCallback(
|
|
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.
|
|
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.
|
|
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
|
+
}
|