@talex-touch/utils 1.0.13 → 1.0.15
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/base/index.ts +181 -181
- package/channel/index.ts +108 -99
- package/common/index.ts +2 -39
- package/common/storage/constants.ts +3 -0
- package/common/storage/entity/app-settings.ts +47 -0
- package/common/storage/entity/index.ts +1 -0
- package/common/storage/index.ts +3 -0
- package/common/utils.ts +160 -0
- package/core-box/README.md +218 -0
- package/core-box/index.ts +7 -0
- package/core-box/search.ts +536 -0
- package/core-box/types.ts +384 -0
- package/electron/download-manager.ts +118 -0
- package/{common → electron}/env-tool.ts +56 -56
- package/electron/touch-core.ts +167 -0
- package/electron/window.ts +71 -0
- package/eventbus/index.ts +86 -87
- package/index.ts +5 -0
- package/package.json +55 -30
- package/permission/index.ts +48 -48
- package/plugin/channel.ts +203 -193
- package/plugin/index.ts +216 -121
- package/plugin/log/logger-manager.ts +60 -0
- package/plugin/log/logger.ts +75 -0
- package/plugin/log/types.ts +27 -0
- package/plugin/preload.ts +39 -39
- package/plugin/sdk/common.ts +27 -27
- package/plugin/sdk/hooks/life-cycle.ts +95 -95
- package/plugin/sdk/index.ts +18 -13
- package/plugin/sdk/service/index.ts +29 -29
- package/plugin/sdk/types.ts +578 -0
- package/plugin/sdk/window/index.ts +40 -40
- package/renderer/index.ts +2 -0
- package/renderer/ref.ts +54 -54
- package/renderer/slots.ts +124 -0
- package/renderer/storage/app-settings.ts +34 -0
- package/renderer/storage/base-storage.ts +335 -0
- package/renderer/storage/index.ts +1 -0
- package/search/types.ts +726 -0
- package/service/index.ts +67 -67
- package/service/protocol/index.ts +77 -77
package/common/utils.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delays execution for a given amount of milliseconds.
|
|
3
|
+
*
|
|
4
|
+
* @param time - Time to sleep in milliseconds.
|
|
5
|
+
* @returns A promise that resolves with the same time value after the delay.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* await sleep(1000); // Waits for 1 second
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export async function sleep(time: number): Promise<number> {
|
|
13
|
+
return new Promise(resolve => setTimeout(() => resolve(time), time))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Encodes any string into a `bigint` by converting each character's char code
|
|
18
|
+
* into a relative offset based on the minimum char code in the string.
|
|
19
|
+
*
|
|
20
|
+
* Format: `{minCharCode}000{paddedOffsetPairs}`
|
|
21
|
+
*
|
|
22
|
+
* @param str - Any input string to encode.
|
|
23
|
+
* @returns A BigInt representation of the input string.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const encoded = anyStr2Num("abc"); // e.g., 970000000102
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function anyStr2Num(str: string): bigint {
|
|
31
|
+
const codes = Array.from(str).map(ch => ch.charCodeAt(0))
|
|
32
|
+
const minCode = Math.min(...codes)
|
|
33
|
+
|
|
34
|
+
const encoded = codes
|
|
35
|
+
.map(code => (code - minCode).toString().padStart(2, '0'))
|
|
36
|
+
.join('')
|
|
37
|
+
|
|
38
|
+
return BigInt(`${minCode}000${encoded}`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Decodes a BigInt back into the original string.
|
|
43
|
+
* Assumes the format produced by `anyStr2Num`.
|
|
44
|
+
*
|
|
45
|
+
* @param num - The BigInt to decode.
|
|
46
|
+
* @returns The original decoded string.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const decoded = num2anyStr(970000000102n); // returns "abc"
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function num2anyStr(num: bigint): string {
|
|
54
|
+
const [baseStr, encoded] = num.toString().split('000')
|
|
55
|
+
const base = Number(baseStr)
|
|
56
|
+
|
|
57
|
+
let result = ''
|
|
58
|
+
for (let i = 0; i < encoded.length; i += 2) {
|
|
59
|
+
const offset = Number(encoded.slice(i, i + 2))
|
|
60
|
+
result += String.fromCharCode(base + offset)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const LOCALHOST_KEYS = ['localhost', '127.0.0.1']
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Determines whether a given URL string points to localhost.
|
|
70
|
+
*
|
|
71
|
+
* @param urlStr - The full URL string to check.
|
|
72
|
+
* @returns `true` if the URL hostname is `localhost` or `127.0.0.1`, otherwise `false`.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* isLocalhostUrl("http://localhost:3000"); // true
|
|
77
|
+
* isLocalhostUrl("https://example.com"); // false
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function isLocalhostUrl(urlStr: string): boolean {
|
|
81
|
+
return LOCALHOST_KEYS.includes(new URL(urlStr).hostname)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Serializes a value to JSON, throwing a detailed error on the first unsupported value (like a function, DOM node, symbol, etc.).
|
|
86
|
+
* Reports the exact path to the error.
|
|
87
|
+
*
|
|
88
|
+
* @param value - Any JS value.
|
|
89
|
+
* @returns The JSON string if serialization succeeds.
|
|
90
|
+
* @throws Error if an unsupported value is found (reports path and type).
|
|
91
|
+
*/
|
|
92
|
+
export function structuredStrictStringify(value: unknown): string {
|
|
93
|
+
const seen = new WeakMap<object, string>();
|
|
94
|
+
const badTypes = [
|
|
95
|
+
'symbol'
|
|
96
|
+
];
|
|
97
|
+
// Support Map/Set, but not Error, DOM, Proxy, WeakMap, WeakSet
|
|
98
|
+
function getType(val: any): string {
|
|
99
|
+
if (val === null) return 'null';
|
|
100
|
+
if (Array.isArray(val)) return 'Array';
|
|
101
|
+
if (typeof Document !== 'undefined') {
|
|
102
|
+
if (val instanceof Node) return 'DOMNode';
|
|
103
|
+
}
|
|
104
|
+
if (val instanceof Error) return 'Error';
|
|
105
|
+
if (val instanceof WeakMap) return 'WeakMap';
|
|
106
|
+
if (val instanceof WeakSet) return 'WeakSet';
|
|
107
|
+
if (typeof val === 'object' && val !== null && val.constructor?.name === 'Proxy') return 'Proxy';
|
|
108
|
+
if (typeof val === 'bigint') return 'BigInt';
|
|
109
|
+
return typeof val;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function serialize(val: any, path: string): any {
|
|
113
|
+
const type = getType(val);
|
|
114
|
+
// Block disallowed/unsafe types and edge cases for structured-clone
|
|
115
|
+
if (badTypes.includes(typeof val) || type === 'DOMNode' || type === 'Error' || type === 'Proxy' || type === 'WeakMap' || type === 'WeakSet' || type === 'BigInt') {
|
|
116
|
+
throw new Error(`Cannot serialize property at path "${path}": type "${type}"`);
|
|
117
|
+
}
|
|
118
|
+
// JSON doesn't support undefined, skip it for values in objects, preserve in arrays as null
|
|
119
|
+
if (typeof val === 'undefined') return null;
|
|
120
|
+
// Simple value
|
|
121
|
+
if (
|
|
122
|
+
val === null ||
|
|
123
|
+
typeof val === 'number' ||
|
|
124
|
+
typeof val === 'boolean' ||
|
|
125
|
+
typeof val === 'string'
|
|
126
|
+
) return val;
|
|
127
|
+
// Cycle check
|
|
128
|
+
if (typeof val === 'object') {
|
|
129
|
+
if (seen.has(val)) {
|
|
130
|
+
return `[Circular ~${seen.get(val)}]`; // You could just throw if you dislike this fallback!
|
|
131
|
+
}
|
|
132
|
+
seen.set(val, path);
|
|
133
|
+
if (Array.isArray(val)) {
|
|
134
|
+
return val.map((item, idx) => serialize(item, `${path}[${idx}]`));
|
|
135
|
+
}
|
|
136
|
+
if (val instanceof Date) {
|
|
137
|
+
return val.toISOString();
|
|
138
|
+
}
|
|
139
|
+
if (val instanceof Map) {
|
|
140
|
+
const obj: Record<string, any> = {};
|
|
141
|
+
for (const [k, v] of val.entries()) {
|
|
142
|
+
obj[typeof k === 'string' ? k : JSON.stringify(k)] = serialize(v, `${path}[Map(${typeof k === 'string' ? k : JSON.stringify(k)})]`);
|
|
143
|
+
}
|
|
144
|
+
return obj;
|
|
145
|
+
}
|
|
146
|
+
if (val instanceof Set) {
|
|
147
|
+
return Array.from(val).map((item, idx) => serialize(item, `${path}[SetEntry${idx}]`));
|
|
148
|
+
}
|
|
149
|
+
// General object
|
|
150
|
+
const res: any = {};
|
|
151
|
+
for (const key of Object.keys(val)) {
|
|
152
|
+
res[key] = serialize(val[key], `${path}.${key}`);
|
|
153
|
+
}
|
|
154
|
+
return res;
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`Cannot serialize property at path "${path}": unknown type`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return JSON.stringify(serialize(value, 'root'));
|
|
160
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Core Box Package
|
|
2
|
+
|
|
3
|
+
The Core Box package provides unified type definitions and utility functions for the Talex Touch search box system. This package contains the foundational types and tools used across the entire project for search result management and plugin integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### 🎯 Unified Type System
|
|
8
|
+
- **ISearchItem**: Universal search result item interface
|
|
9
|
+
- **IRenderConfig**: Flexible rendering configuration with multiple modes
|
|
10
|
+
- **IDataItem**: Extended interface for data processing results
|
|
11
|
+
- **IAppItem**: Specialized interface for application results
|
|
12
|
+
- **IFileItem**: Specialized interface for file results
|
|
13
|
+
- **IFeatureItem**: Specialized interface for plugin feature results
|
|
14
|
+
|
|
15
|
+
### 🎨 Render Modes
|
|
16
|
+
The package supports multiple rendering modes for search results:
|
|
17
|
+
|
|
18
|
+
- **STANDARD**: Default text-based rendering
|
|
19
|
+
- **URL**: URL rendering - loads and displays remote URL content
|
|
20
|
+
- **HTML**: Rich HTML content rendering
|
|
21
|
+
- **JAVASCRIPT**: JavaScript code with syntax highlighting
|
|
22
|
+
- **JSX**: React JSX component rendering
|
|
23
|
+
- **VUE_SFC**: Vue Single File Component rendering
|
|
24
|
+
|
|
25
|
+
### 🛠️ Utility Functions
|
|
26
|
+
Comprehensive set of utility functions for creating and managing search results:
|
|
27
|
+
|
|
28
|
+
- `SearchUtils.createSearchItem()`: Create basic search items
|
|
29
|
+
- `SearchUtils.createDataItem()`: Create data processing results
|
|
30
|
+
- `SearchUtils.createAppItem()`: Create application results
|
|
31
|
+
- `SearchUtils.createFileItem()`: Create file results
|
|
32
|
+
- `SearchUtils.createFeatureItem()`: Create plugin feature results
|
|
33
|
+
- `SearchUtils.createErrorItem()`: Create error state items
|
|
34
|
+
- `SearchUtils.sortByPriority()`: Sort results by relevance
|
|
35
|
+
- `SearchUtils.removeDuplicates()`: Remove duplicate results
|
|
36
|
+
- `SearchUtils.filterByConfidence()`: Filter by confidence score
|
|
37
|
+
- `SearchUtils.groupByPluginType()`: Group results by type
|
|
38
|
+
|
|
39
|
+
## Usage Examples
|
|
40
|
+
|
|
41
|
+
### Basic Search Item
|
|
42
|
+
```typescript
|
|
43
|
+
import { SearchUtils, RenderMode } from '@talex-touch/utils';
|
|
44
|
+
|
|
45
|
+
const basicItem = SearchUtils.createSearchItem({
|
|
46
|
+
name: "My Feature",
|
|
47
|
+
desc: "A useful plugin feature",
|
|
48
|
+
pluginName: "my-plugin",
|
|
49
|
+
keyWords: ["feature", "utility"],
|
|
50
|
+
render: {
|
|
51
|
+
mode: RenderMode.STANDARD
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Data Processing Result
|
|
57
|
+
```typescript
|
|
58
|
+
const dataItem = SearchUtils.createDataItem({
|
|
59
|
+
name: "Translation Result",
|
|
60
|
+
desc: "English to Chinese translation",
|
|
61
|
+
pluginName: "translator",
|
|
62
|
+
source: "google-translate",
|
|
63
|
+
dataType: "translation",
|
|
64
|
+
originalData: "Hello World",
|
|
65
|
+
processedData: "你好世界",
|
|
66
|
+
confidence: 95,
|
|
67
|
+
duration: 120,
|
|
68
|
+
render: {
|
|
69
|
+
mode: RenderMode.STANDARD
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### HTML Rendering
|
|
75
|
+
```typescript
|
|
76
|
+
const htmlItem = SearchUtils.createSearchItem({
|
|
77
|
+
name: "Rich Content",
|
|
78
|
+
desc: "HTML formatted result",
|
|
79
|
+
pluginName: "content-plugin",
|
|
80
|
+
render: {
|
|
81
|
+
mode: RenderMode.HTML,
|
|
82
|
+
content: "<div><h3>Title</h3><p>Content</p></div>",
|
|
83
|
+
options: {
|
|
84
|
+
trusted: true,
|
|
85
|
+
className: "custom-content"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Code Rendering
|
|
92
|
+
```typescript
|
|
93
|
+
const codeItem = SearchUtils.createDataItem({
|
|
94
|
+
name: "JavaScript Function",
|
|
95
|
+
desc: "Generated code snippet",
|
|
96
|
+
pluginName: "code-generator",
|
|
97
|
+
dataType: "javascript",
|
|
98
|
+
processedData: "function hello() { return 'world'; }",
|
|
99
|
+
render: {
|
|
100
|
+
mode: RenderMode.JAVASCRIPT,
|
|
101
|
+
content: "function hello() { return 'world'; }",
|
|
102
|
+
options: {
|
|
103
|
+
syntaxHighlight: true,
|
|
104
|
+
showLineNumbers: true,
|
|
105
|
+
theme: "dark"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### URL Rendering with Preview
|
|
112
|
+
```typescript
|
|
113
|
+
const urlItem = SearchUtils.createSearchItem({
|
|
114
|
+
name: "GitHub Repository",
|
|
115
|
+
desc: "Talex Touch project",
|
|
116
|
+
pluginName: "web-search",
|
|
117
|
+
render: {
|
|
118
|
+
mode: RenderMode.URL,
|
|
119
|
+
content: "https://github.com/talex-touch/talex-touch", // The actual URL to load
|
|
120
|
+
preview: {
|
|
121
|
+
enabled: true,
|
|
122
|
+
title: "Talex Touch", // Preview metadata
|
|
123
|
+
description: "Modern desktop application framework", // Preview description
|
|
124
|
+
image: "https://github.com/talex-touch.png" // Preview image
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Note**:
|
|
131
|
+
- `content`: The remote URL that will be loaded and displayed
|
|
132
|
+
- `preview`: Metadata shown before/alongside the loaded URL content
|
|
133
|
+
|
|
134
|
+
## Type Definitions
|
|
135
|
+
|
|
136
|
+
### ISearchItem
|
|
137
|
+
The core interface for all search results:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface ISearchItem {
|
|
141
|
+
name: string; // Display name
|
|
142
|
+
desc: string; // Description
|
|
143
|
+
icon: IPluginIcon; // Icon configuration
|
|
144
|
+
push: boolean; // Push mode support
|
|
145
|
+
names: string[]; // Searchable names
|
|
146
|
+
keyWords: string[]; // Search keywords
|
|
147
|
+
pluginType: string; // Plugin type
|
|
148
|
+
type: string; // General type
|
|
149
|
+
value: string; // Associated value
|
|
150
|
+
render?: IRenderConfig; // Render configuration
|
|
151
|
+
// ... additional properties
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### IRenderConfig
|
|
156
|
+
Configuration for custom rendering:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
interface IRenderConfig {
|
|
160
|
+
mode: RenderMode; // Rendering mode
|
|
161
|
+
content?: string; // Content to render
|
|
162
|
+
options?: { // Render options
|
|
163
|
+
syntaxHighlight?: boolean;
|
|
164
|
+
theme?: string;
|
|
165
|
+
showLineNumbers?: boolean;
|
|
166
|
+
className?: string;
|
|
167
|
+
style?: Record<string, string>;
|
|
168
|
+
trusted?: boolean;
|
|
169
|
+
};
|
|
170
|
+
preview?: { // URL preview config
|
|
171
|
+
enabled?: boolean;
|
|
172
|
+
image?: string;
|
|
173
|
+
title?: string;
|
|
174
|
+
description?: string;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Integration
|
|
180
|
+
|
|
181
|
+
This package is automatically exported from `@talex-touch/utils`:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import {
|
|
185
|
+
ISearchItem,
|
|
186
|
+
IDataItem,
|
|
187
|
+
SearchUtils,
|
|
188
|
+
RenderMode
|
|
189
|
+
} from '@talex-touch/utils';
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Best Practices
|
|
193
|
+
|
|
194
|
+
1. **Always specify render mode**: Even for standard rendering, explicitly set the render mode
|
|
195
|
+
2. **Use appropriate specialized interfaces**: Use IDataItem for data processing, IAppItem for applications, etc.
|
|
196
|
+
3. **Include confidence scores**: For data processing results, always include confidence levels
|
|
197
|
+
4. **Sanitize HTML content**: When using HTML render mode, ensure content is safe
|
|
198
|
+
5. **Provide meaningful keywords**: Include relevant search terms in the keyWords array
|
|
199
|
+
6. **Use consistent naming**: Follow established naming conventions for plugin types
|
|
200
|
+
|
|
201
|
+
## Migration Guide
|
|
202
|
+
|
|
203
|
+
If you're migrating from the old type system:
|
|
204
|
+
|
|
205
|
+
1. Replace `ITranslationItem` with `IDataItem`
|
|
206
|
+
2. Update render configurations to use the new `IRenderConfig` interface
|
|
207
|
+
3. Use `SearchUtils` functions instead of manual object creation
|
|
208
|
+
4. Update import paths to use `@talex-touch/utils`
|
|
209
|
+
|
|
210
|
+
## Contributing
|
|
211
|
+
|
|
212
|
+
When adding new types or utilities:
|
|
213
|
+
|
|
214
|
+
1. Add comprehensive TSDoc comments in English
|
|
215
|
+
2. Include usage examples in documentation
|
|
216
|
+
3. Follow the established naming conventions
|
|
217
|
+
4. Add appropriate unit tests
|
|
218
|
+
5. Update this README with new features
|