@talex-touch/utils 1.0.39 → 1.0.42
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/core-box/tuff/tuff-dsl.ts +133 -54
- package/intelligence/client.ts +8 -8
- package/market/constants.ts +14 -0
- package/market/types.ts +1 -1
- package/package.json +1 -1
- package/plugin/index.ts +7 -1
- package/plugin/providers/index.ts +4 -0
- package/plugin/providers/market-client.ts +215 -0
- package/plugin/providers/npm-provider.ts +213 -0
- package/plugin/providers/tpex-provider.ts +283 -0
- package/plugin/providers/tpex-types.ts +34 -0
- package/plugin/sdk/README.md +54 -6
- package/plugin/sdk/clipboard.ts +196 -23
- package/plugin/sdk/enum/bridge-event.ts +1 -0
- package/plugin/sdk/feature-sdk.ts +85 -6
- package/plugin/sdk/flow.ts +246 -0
- package/plugin/sdk/hooks/bridge.ts +113 -39
- package/plugin/sdk/index.ts +2 -0
- package/plugin/sdk/performance.ts +186 -0
- package/plugin/widget.ts +5 -0
- package/renderer/hooks/arg-mapper.ts +20 -6
- package/renderer/hooks/use-intelligence.ts +291 -34
- package/renderer/storage/base-storage.ts +98 -15
- package/renderer/storage/intelligence-storage.ts +9 -9
- package/renderer/storage/storage-subscription.ts +17 -9
- package/search/fuzzy-match.ts +254 -0
- package/types/division-box.ts +20 -0
- package/types/flow.ts +283 -0
- package/types/index.ts +1 -0
- package/types/intelligence.ts +1496 -78
|
@@ -14,6 +14,7 @@ class StorageSubscriptionManager {
|
|
|
14
14
|
private subscribers = new Map<string, Set<StorageSubscriptionCallback>>()
|
|
15
15
|
private channelListenerRegistered = false
|
|
16
16
|
private pendingUpdates = new Map<string, NodeJS.Timeout>()
|
|
17
|
+
private configVersions = new Map<string, number>()
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Initialize the subscription manager with a channel
|
|
@@ -24,8 +25,15 @@ class StorageSubscriptionManager {
|
|
|
24
25
|
if (!this.channelListenerRegistered) {
|
|
25
26
|
// Listen to storage:update events from main process
|
|
26
27
|
this.channel.regChannel('storage:update', ({ data }) => {
|
|
27
|
-
const { name } = data as { name: string }
|
|
28
|
-
|
|
28
|
+
const { name, version } = data as { name: string, version?: number }
|
|
29
|
+
// Only handle update if version is newer or unknown
|
|
30
|
+
const currentVersion = this.configVersions.get(name) ?? 0
|
|
31
|
+
if (version === undefined || version > currentVersion) {
|
|
32
|
+
if (version !== undefined) {
|
|
33
|
+
this.configVersions.set(name, version)
|
|
34
|
+
}
|
|
35
|
+
this.handleStorageUpdate(name)
|
|
36
|
+
}
|
|
29
37
|
})
|
|
30
38
|
this.channelListenerRegistered = true
|
|
31
39
|
}
|
|
@@ -36,13 +44,13 @@ class StorageSubscriptionManager {
|
|
|
36
44
|
* @param configName - The configuration file name (e.g., 'app-setting.ini')
|
|
37
45
|
* @param callback - Callback function to receive updates
|
|
38
46
|
* @returns Unsubscribe function
|
|
39
|
-
*
|
|
47
|
+
*
|
|
40
48
|
* @example
|
|
41
49
|
* ```typescript
|
|
42
50
|
* const unsubscribe = subscribeStorage('app-setting.ini', (data) => {
|
|
43
51
|
* console.log('Config updated:', data)
|
|
44
52
|
* })
|
|
45
|
-
*
|
|
53
|
+
*
|
|
46
54
|
* // Later:
|
|
47
55
|
* unsubscribe()
|
|
48
56
|
* ```
|
|
@@ -155,7 +163,7 @@ const subscriptionManager = new StorageSubscriptionManager()
|
|
|
155
163
|
/**
|
|
156
164
|
* Initialize storage subscription system with channel
|
|
157
165
|
* Must be called before using subscribeStorage
|
|
158
|
-
*
|
|
166
|
+
*
|
|
159
167
|
* @param channel - The storage channel
|
|
160
168
|
*/
|
|
161
169
|
export function initStorageSubscription(channel: IStorageChannel): void {
|
|
@@ -164,19 +172,19 @@ export function initStorageSubscription(channel: IStorageChannel): void {
|
|
|
164
172
|
|
|
165
173
|
/**
|
|
166
174
|
* Subscribe to storage configuration changes
|
|
167
|
-
*
|
|
175
|
+
*
|
|
168
176
|
* @param configName - Configuration file name (e.g., 'app-setting.ini')
|
|
169
177
|
* @param callback - Callback function that receives updated data
|
|
170
178
|
* @returns Unsubscribe function
|
|
171
|
-
*
|
|
179
|
+
*
|
|
172
180
|
* @example
|
|
173
181
|
* ```typescript
|
|
174
182
|
* import { subscribeStorage } from '@talex-touch/utils/renderer/storage/storage-subscription'
|
|
175
|
-
*
|
|
183
|
+
*
|
|
176
184
|
* const unsubscribe = subscribeStorage('app-setting.ini', (data) => {
|
|
177
185
|
* console.log('Settings updated:', data)
|
|
178
186
|
* })
|
|
179
|
-
*
|
|
187
|
+
*
|
|
180
188
|
* // Clean up when no longer needed
|
|
181
189
|
* unsubscribe()
|
|
182
190
|
* ```
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy matching utilities for typo-tolerant search
|
|
3
|
+
* Supports matching queries like "helol" to "hello"
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface FuzzyMatchResult {
|
|
7
|
+
/** Whether the match was successful */
|
|
8
|
+
matched: boolean
|
|
9
|
+
/** Match score (0-1, higher is better) */
|
|
10
|
+
score: number
|
|
11
|
+
/** Indices of matched characters in the target string */
|
|
12
|
+
matchedIndices: number[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Performs fuzzy matching with typo tolerance
|
|
17
|
+
* Uses a combination of subsequence matching and edit distance
|
|
18
|
+
*
|
|
19
|
+
* @param target - The string to match against (e.g., "hello")
|
|
20
|
+
* @param query - The search query (e.g., "helol")
|
|
21
|
+
* @param maxErrors - Maximum allowed errors (default: 2)
|
|
22
|
+
* @returns FuzzyMatchResult with match info and indices
|
|
23
|
+
*/
|
|
24
|
+
export function fuzzyMatch(
|
|
25
|
+
target: string,
|
|
26
|
+
query: string,
|
|
27
|
+
maxErrors = 2
|
|
28
|
+
): FuzzyMatchResult {
|
|
29
|
+
if (!query || !target) {
|
|
30
|
+
return { matched: false, score: 0, matchedIndices: [] }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const targetLower = target.toLowerCase()
|
|
34
|
+
const queryLower = query.toLowerCase()
|
|
35
|
+
|
|
36
|
+
// Exact match - highest score
|
|
37
|
+
if (targetLower === queryLower) {
|
|
38
|
+
return {
|
|
39
|
+
matched: true,
|
|
40
|
+
score: 1,
|
|
41
|
+
matchedIndices: Array.from({ length: target.length }, (_, i) => i)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Substring match
|
|
46
|
+
const substringIndex = targetLower.indexOf(queryLower)
|
|
47
|
+
if (substringIndex !== -1) {
|
|
48
|
+
return {
|
|
49
|
+
matched: true,
|
|
50
|
+
score: 0.95,
|
|
51
|
+
matchedIndices: Array.from({ length: query.length }, (_, i) => substringIndex + i)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Try subsequence matching first (for cases like "vsc" -> "Visual Studio Code")
|
|
56
|
+
const subsequenceResult = subsequenceMatch(targetLower, queryLower)
|
|
57
|
+
if (subsequenceResult.matched && subsequenceResult.matchedIndices.length === queryLower.length) {
|
|
58
|
+
return {
|
|
59
|
+
matched: true,
|
|
60
|
+
score: 0.8 + (subsequenceResult.matchedIndices.length / target.length) * 0.1,
|
|
61
|
+
matchedIndices: subsequenceResult.matchedIndices
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fuzzy match with edit distance for typo tolerance
|
|
66
|
+
const fuzzyResult = fuzzyMatchWithErrors(targetLower, queryLower, maxErrors)
|
|
67
|
+
if (fuzzyResult.matched) {
|
|
68
|
+
return fuzzyResult
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { matched: false, score: 0, matchedIndices: [] }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Subsequence matching - matches characters in order but not necessarily consecutive
|
|
76
|
+
* e.g., "vsc" matches "Visual Studio Code" at indices [0, 7, 14]
|
|
77
|
+
*/
|
|
78
|
+
function subsequenceMatch(
|
|
79
|
+
target: string,
|
|
80
|
+
query: string
|
|
81
|
+
): { matched: boolean; matchedIndices: number[] } {
|
|
82
|
+
const matchedIndices: number[] = []
|
|
83
|
+
let queryIdx = 0
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < target.length && queryIdx < query.length; i++) {
|
|
86
|
+
if (target[i] === query[queryIdx]) {
|
|
87
|
+
matchedIndices.push(i)
|
|
88
|
+
queryIdx++
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
matched: queryIdx === query.length,
|
|
94
|
+
matchedIndices
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Fuzzy matching with error tolerance using dynamic programming
|
|
100
|
+
* Finds the best matching substring allowing for insertions, deletions, and substitutions
|
|
101
|
+
*/
|
|
102
|
+
function fuzzyMatchWithErrors(
|
|
103
|
+
target: string,
|
|
104
|
+
query: string,
|
|
105
|
+
maxErrors: number
|
|
106
|
+
): FuzzyMatchResult {
|
|
107
|
+
const m = query.length
|
|
108
|
+
const n = target.length
|
|
109
|
+
|
|
110
|
+
if (m === 0) return { matched: true, score: 1, matchedIndices: [] }
|
|
111
|
+
if (n === 0) return { matched: false, score: 0, matchedIndices: [] }
|
|
112
|
+
|
|
113
|
+
// Allow more errors for longer queries
|
|
114
|
+
const allowedErrors = Math.min(maxErrors, Math.floor(m / 3) + 1)
|
|
115
|
+
|
|
116
|
+
// Find best matching window using sliding window with edit distance
|
|
117
|
+
let bestScore = 0
|
|
118
|
+
let bestStart = -1
|
|
119
|
+
let bestMatchedIndices: number[] = []
|
|
120
|
+
|
|
121
|
+
// Try different window sizes around query length
|
|
122
|
+
const minWindowSize = Math.max(1, m - allowedErrors)
|
|
123
|
+
const maxWindowSize = Math.min(n, m + allowedErrors)
|
|
124
|
+
|
|
125
|
+
for (let windowSize = minWindowSize; windowSize <= maxWindowSize; windowSize++) {
|
|
126
|
+
for (let start = 0; start <= n - windowSize; start++) {
|
|
127
|
+
const window = target.substring(start, start + windowSize)
|
|
128
|
+
const { distance, matchedIndices } = editDistanceWithPath(window, query)
|
|
129
|
+
|
|
130
|
+
if (distance <= allowedErrors) {
|
|
131
|
+
// Calculate score based on edit distance and position
|
|
132
|
+
const score = calculateFuzzyScore(distance, m, start, n)
|
|
133
|
+
|
|
134
|
+
if (score > bestScore) {
|
|
135
|
+
bestScore = score
|
|
136
|
+
bestStart = start
|
|
137
|
+
// Adjust indices to be relative to the full target string
|
|
138
|
+
bestMatchedIndices = matchedIndices.map((i) => start + i)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (bestStart !== -1) {
|
|
145
|
+
return {
|
|
146
|
+
matched: true,
|
|
147
|
+
score: bestScore,
|
|
148
|
+
matchedIndices: bestMatchedIndices
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { matched: false, score: 0, matchedIndices: [] }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Computes edit distance and tracks which characters matched
|
|
157
|
+
*/
|
|
158
|
+
function editDistanceWithPath(
|
|
159
|
+
s1: string,
|
|
160
|
+
s2: string
|
|
161
|
+
): { distance: number; matchedIndices: number[] } {
|
|
162
|
+
const m = s1.length
|
|
163
|
+
const n = s2.length
|
|
164
|
+
|
|
165
|
+
// DP table
|
|
166
|
+
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))
|
|
167
|
+
|
|
168
|
+
// Initialize
|
|
169
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i
|
|
170
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j
|
|
171
|
+
|
|
172
|
+
// Fill DP table
|
|
173
|
+
for (let i = 1; i <= m; i++) {
|
|
174
|
+
for (let j = 1; j <= n; j++) {
|
|
175
|
+
if (s1[i - 1] === s2[j - 1]) {
|
|
176
|
+
dp[i][j] = dp[i - 1][j - 1]
|
|
177
|
+
} else {
|
|
178
|
+
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Backtrack to find matched indices
|
|
184
|
+
const matchedIndices: number[] = []
|
|
185
|
+
let i = m
|
|
186
|
+
let j = n
|
|
187
|
+
|
|
188
|
+
while (i > 0 && j > 0) {
|
|
189
|
+
if (s1[i - 1] === s2[j - 1]) {
|
|
190
|
+
matchedIndices.unshift(i - 1)
|
|
191
|
+
i--
|
|
192
|
+
j--
|
|
193
|
+
} else if (dp[i - 1][j - 1] <= dp[i - 1][j] && dp[i - 1][j - 1] <= dp[i][j - 1]) {
|
|
194
|
+
// Substitution
|
|
195
|
+
i--
|
|
196
|
+
j--
|
|
197
|
+
} else if (dp[i - 1][j] <= dp[i][j - 1]) {
|
|
198
|
+
// Deletion from s1
|
|
199
|
+
i--
|
|
200
|
+
} else {
|
|
201
|
+
// Insertion into s1
|
|
202
|
+
j--
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { distance: dp[m][n], matchedIndices }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Calculate fuzzy match score based on various factors
|
|
211
|
+
*/
|
|
212
|
+
function calculateFuzzyScore(
|
|
213
|
+
editDistance: number,
|
|
214
|
+
queryLength: number,
|
|
215
|
+
matchStart: number,
|
|
216
|
+
targetLength: number
|
|
217
|
+
): number {
|
|
218
|
+
// Base score from edit distance (0.5 - 0.7 range for fuzzy matches)
|
|
219
|
+
const distanceScore = Math.max(0, 1 - editDistance / queryLength) * 0.3 + 0.4
|
|
220
|
+
|
|
221
|
+
// Bonus for matching at the start
|
|
222
|
+
const positionBonus = matchStart === 0 ? 0.15 : 0
|
|
223
|
+
|
|
224
|
+
// Bonus for shorter targets (more specific matches)
|
|
225
|
+
const lengthBonus = Math.min(0.1, queryLength / targetLength * 0.1)
|
|
226
|
+
|
|
227
|
+
return Math.min(0.75, distanceScore + positionBonus + lengthBonus)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Convert matched indices to Range array for highlighting
|
|
232
|
+
*/
|
|
233
|
+
export function indicesToRanges(indices: number[]): Array<{ start: number; end: number }> {
|
|
234
|
+
if (!indices.length) return []
|
|
235
|
+
|
|
236
|
+
const sorted = Array.from(new Set(indices)).sort((a, b) => a - b)
|
|
237
|
+
const ranges: Array<{ start: number; end: number }> = []
|
|
238
|
+
|
|
239
|
+
let start = sorted[0]
|
|
240
|
+
let end = sorted[0] + 1
|
|
241
|
+
|
|
242
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
243
|
+
if (sorted[i] === end) {
|
|
244
|
+
end++
|
|
245
|
+
} else {
|
|
246
|
+
ranges.push({ start, end })
|
|
247
|
+
start = sorted[i]
|
|
248
|
+
end = sorted[i] + 1
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
ranges.push({ start, end })
|
|
252
|
+
|
|
253
|
+
return ranges
|
|
254
|
+
}
|
package/types/division-box.ts
CHANGED
|
@@ -93,6 +93,23 @@ export interface HeaderConfig {
|
|
|
93
93
|
*/
|
|
94
94
|
export type DivisionBoxSize = 'compact' | 'medium' | 'expanded'
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* UI configuration for DivisionBox CoreBox header
|
|
98
|
+
*/
|
|
99
|
+
export interface DivisionBoxUIConfig {
|
|
100
|
+
/** Show the search input in header */
|
|
101
|
+
showInput?: boolean
|
|
102
|
+
|
|
103
|
+
/** Placeholder text for search input */
|
|
104
|
+
inputPlaceholder?: string
|
|
105
|
+
|
|
106
|
+
/** Show result list area */
|
|
107
|
+
showResults?: boolean
|
|
108
|
+
|
|
109
|
+
/** Initial input value */
|
|
110
|
+
initialInput?: string
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
/**
|
|
97
114
|
* Configuration for creating a DivisionBox instance
|
|
98
115
|
*/
|
|
@@ -118,6 +135,9 @@ export interface DivisionBoxConfig {
|
|
|
118
135
|
/** Header configuration */
|
|
119
136
|
header?: HeaderConfig
|
|
120
137
|
|
|
138
|
+
/** UI configuration for CoreBox header display */
|
|
139
|
+
ui?: DivisionBoxUIConfig
|
|
140
|
+
|
|
121
141
|
/** WebContentsView preferences (main process only) */
|
|
122
142
|
webPreferences?: any
|
|
123
143
|
}
|
package/types/flow.ts
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow Transfer Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core types for the plugin-to-plugin data flow transfer system.
|
|
5
|
+
* Enables plugins to share structured data with other plugins' features.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TuffQuery } from '../core-box/tuff/tuff-dsl'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Flow payload types
|
|
12
|
+
*/
|
|
13
|
+
export type FlowPayloadType = 'text' | 'image' | 'files' | 'json' | 'html' | 'custom'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Flow session states
|
|
17
|
+
*/
|
|
18
|
+
export type FlowSessionState =
|
|
19
|
+
| 'INIT'
|
|
20
|
+
| 'TARGET_SELECTING'
|
|
21
|
+
| 'TARGET_SELECTED'
|
|
22
|
+
| 'DELIVERING'
|
|
23
|
+
| 'DELIVERED'
|
|
24
|
+
| 'PROCESSING'
|
|
25
|
+
| 'ACKED'
|
|
26
|
+
| 'FAILED'
|
|
27
|
+
| 'CANCELLED'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Flow error codes
|
|
31
|
+
*/
|
|
32
|
+
export enum FlowErrorCode {
|
|
33
|
+
SENDER_NOT_ALLOWED = 'SENDER_NOT_ALLOWED',
|
|
34
|
+
TARGET_NOT_FOUND = 'TARGET_NOT_FOUND',
|
|
35
|
+
TARGET_OFFLINE = 'TARGET_OFFLINE',
|
|
36
|
+
PAYLOAD_INVALID = 'PAYLOAD_INVALID',
|
|
37
|
+
PAYLOAD_TOO_LARGE = 'PAYLOAD_TOO_LARGE',
|
|
38
|
+
TYPE_NOT_SUPPORTED = 'TYPE_NOT_SUPPORTED',
|
|
39
|
+
PERMISSION_DENIED = 'PERMISSION_DENIED',
|
|
40
|
+
TIMEOUT = 'TIMEOUT',
|
|
41
|
+
CANCELLED = 'CANCELLED',
|
|
42
|
+
INTERNAL_ERROR = 'INTERNAL_ERROR'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Flow error
|
|
47
|
+
*/
|
|
48
|
+
export interface FlowError {
|
|
49
|
+
code: FlowErrorCode
|
|
50
|
+
message: string
|
|
51
|
+
details?: Record<string, any>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Flow payload context
|
|
56
|
+
*/
|
|
57
|
+
export interface FlowPayloadContext {
|
|
58
|
+
/** Source plugin ID */
|
|
59
|
+
sourcePluginId: string
|
|
60
|
+
|
|
61
|
+
/** Source feature ID (optional) */
|
|
62
|
+
sourceFeatureId?: string
|
|
63
|
+
|
|
64
|
+
/** Original query (if triggered from CoreBox) */
|
|
65
|
+
originalQuery?: TuffQuery
|
|
66
|
+
|
|
67
|
+
/** Custom metadata */
|
|
68
|
+
metadata?: Record<string, any>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Flow payload - data being transferred
|
|
73
|
+
*/
|
|
74
|
+
export interface FlowPayload {
|
|
75
|
+
/** Payload type */
|
|
76
|
+
type: FlowPayloadType
|
|
77
|
+
|
|
78
|
+
/** Main data content */
|
|
79
|
+
data: string | object
|
|
80
|
+
|
|
81
|
+
/** MIME type (optional) */
|
|
82
|
+
mimeType?: string
|
|
83
|
+
|
|
84
|
+
/** Context information */
|
|
85
|
+
context?: FlowPayloadContext
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Flow target capabilities
|
|
90
|
+
*/
|
|
91
|
+
export interface FlowTargetCapabilities {
|
|
92
|
+
/** Whether authentication is required */
|
|
93
|
+
requiresAuth?: boolean
|
|
94
|
+
|
|
95
|
+
/** Allowed sender plugin IDs (whitelist) */
|
|
96
|
+
allowedSenders?: string[]
|
|
97
|
+
|
|
98
|
+
/** Maximum payload size in bytes */
|
|
99
|
+
maxPayloadSize?: number
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Flow target - declared in plugin manifest
|
|
104
|
+
*/
|
|
105
|
+
export interface FlowTarget {
|
|
106
|
+
/** Target unique ID (unique within plugin) */
|
|
107
|
+
id: string
|
|
108
|
+
|
|
109
|
+
/** Display name */
|
|
110
|
+
name: string
|
|
111
|
+
|
|
112
|
+
/** Description */
|
|
113
|
+
description?: string
|
|
114
|
+
|
|
115
|
+
/** Supported payload types */
|
|
116
|
+
supportedTypes: FlowPayloadType[]
|
|
117
|
+
|
|
118
|
+
/** Icon (iconify format) */
|
|
119
|
+
icon?: string
|
|
120
|
+
|
|
121
|
+
/** Associated feature ID (optional) */
|
|
122
|
+
featureId?: string
|
|
123
|
+
|
|
124
|
+
/** Associated action ID (optional) */
|
|
125
|
+
actionId?: string
|
|
126
|
+
|
|
127
|
+
/** Whether user confirmation is required */
|
|
128
|
+
requireConfirm?: boolean
|
|
129
|
+
|
|
130
|
+
/** Capability requirements */
|
|
131
|
+
capabilities?: FlowTargetCapabilities
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Flow target info - runtime information
|
|
136
|
+
*/
|
|
137
|
+
export interface FlowTargetInfo extends FlowTarget {
|
|
138
|
+
/** Full target ID (pluginId.targetId) */
|
|
139
|
+
fullId: string
|
|
140
|
+
|
|
141
|
+
/** Plugin ID */
|
|
142
|
+
pluginId: string
|
|
143
|
+
|
|
144
|
+
/** Plugin name */
|
|
145
|
+
pluginName?: string
|
|
146
|
+
|
|
147
|
+
/** Plugin icon */
|
|
148
|
+
pluginIcon?: string
|
|
149
|
+
|
|
150
|
+
/** Whether the plugin is currently enabled */
|
|
151
|
+
isEnabled: boolean
|
|
152
|
+
|
|
153
|
+
/** Recent usage count */
|
|
154
|
+
usageCount?: number
|
|
155
|
+
|
|
156
|
+
/** Last used timestamp */
|
|
157
|
+
lastUsed?: number
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Flow session - a complete flow operation
|
|
162
|
+
*/
|
|
163
|
+
export interface FlowSession {
|
|
164
|
+
/** Session unique ID */
|
|
165
|
+
sessionId: string
|
|
166
|
+
|
|
167
|
+
/** Session state */
|
|
168
|
+
state: FlowSessionState
|
|
169
|
+
|
|
170
|
+
/** Sender plugin ID */
|
|
171
|
+
senderId: string
|
|
172
|
+
|
|
173
|
+
/** Target plugin ID */
|
|
174
|
+
targetPluginId: string
|
|
175
|
+
|
|
176
|
+
/** Target endpoint ID */
|
|
177
|
+
targetId: string
|
|
178
|
+
|
|
179
|
+
/** Full target ID (pluginId.targetId) */
|
|
180
|
+
fullTargetId: string
|
|
181
|
+
|
|
182
|
+
/** Payload data */
|
|
183
|
+
payload: FlowPayload
|
|
184
|
+
|
|
185
|
+
/** Creation timestamp */
|
|
186
|
+
createdAt: number
|
|
187
|
+
|
|
188
|
+
/** Update timestamp */
|
|
189
|
+
updatedAt: number
|
|
190
|
+
|
|
191
|
+
/** Acknowledgment payload (if any) */
|
|
192
|
+
ackPayload?: any
|
|
193
|
+
|
|
194
|
+
/** Error information (if failed) */
|
|
195
|
+
error?: FlowError
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Flow dispatch options
|
|
200
|
+
*/
|
|
201
|
+
export interface FlowDispatchOptions {
|
|
202
|
+
/** Display title (for selector panel) */
|
|
203
|
+
title?: string
|
|
204
|
+
|
|
205
|
+
/** Display description */
|
|
206
|
+
description?: string
|
|
207
|
+
|
|
208
|
+
/** Preferred target (bundleId.targetId or tuffItemId) */
|
|
209
|
+
preferredTarget?: string
|
|
210
|
+
|
|
211
|
+
/** Skip selector panel (requires preferredTarget) */
|
|
212
|
+
skipSelector?: boolean
|
|
213
|
+
|
|
214
|
+
/** Timeout in milliseconds (default: 30000) */
|
|
215
|
+
timeout?: number
|
|
216
|
+
|
|
217
|
+
/** Fallback action on failure */
|
|
218
|
+
fallbackAction?: 'copy' | 'none'
|
|
219
|
+
|
|
220
|
+
/** Whether acknowledgment is required */
|
|
221
|
+
requireAck?: boolean
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Flow dispatch result
|
|
226
|
+
*/
|
|
227
|
+
export interface FlowDispatchResult {
|
|
228
|
+
/** Session ID */
|
|
229
|
+
sessionId: string
|
|
230
|
+
|
|
231
|
+
/** Final state */
|
|
232
|
+
state: FlowSessionState
|
|
233
|
+
|
|
234
|
+
/** Acknowledgment payload */
|
|
235
|
+
ackPayload?: any
|
|
236
|
+
|
|
237
|
+
/** Error (if failed) */
|
|
238
|
+
error?: FlowError
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Flow session update event
|
|
243
|
+
*/
|
|
244
|
+
export interface FlowSessionUpdate {
|
|
245
|
+
/** Session ID */
|
|
246
|
+
sessionId: string
|
|
247
|
+
|
|
248
|
+
/** Previous state */
|
|
249
|
+
previousState: FlowSessionState
|
|
250
|
+
|
|
251
|
+
/** Current state */
|
|
252
|
+
currentState: FlowSessionState
|
|
253
|
+
|
|
254
|
+
/** Timestamp */
|
|
255
|
+
timestamp: number
|
|
256
|
+
|
|
257
|
+
/** Additional data */
|
|
258
|
+
data?: any
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* IPC channels for Flow operations
|
|
263
|
+
*/
|
|
264
|
+
export enum FlowIPCChannel {
|
|
265
|
+
DISPATCH = 'flow:dispatch',
|
|
266
|
+
GET_TARGETS = 'flow:get-targets',
|
|
267
|
+
CANCEL = 'flow:cancel',
|
|
268
|
+
ACKNOWLEDGE = 'flow:acknowledge',
|
|
269
|
+
REPORT_ERROR = 'flow:report-error',
|
|
270
|
+
SESSION_UPDATE = 'flow:session-update',
|
|
271
|
+
DELIVER = 'flow:deliver'
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Flow manifest configuration (in plugin manifest.json)
|
|
276
|
+
*/
|
|
277
|
+
export interface FlowManifestConfig {
|
|
278
|
+
/** Whether this plugin can send flows */
|
|
279
|
+
flowSender?: boolean
|
|
280
|
+
|
|
281
|
+
/** Flow targets this plugin can receive */
|
|
282
|
+
flowTargets?: FlowTarget[]
|
|
283
|
+
}
|