plusui-native-core 0.1.66 → 0.1.69
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/API/Connect_API.ts +160 -0
- package/Core/API/index.ts +127 -0
- package/Core/Features/App/app.ts +192 -131
- package/Core/Features/Clipboard/clipboard.ts +92 -127
- package/Core/Features/Connection/README.md +111 -214
- package/Core/Features/Connection/connect.ts +190 -272
- package/Core/Features/Display/display.ts +142 -116
- package/Core/Features/FileDrop/filedrop.ts +163 -283
- package/Core/Features/Keyboard/keyboard.ts +194 -186
- package/Core/Features/Menu/menu.ts +233 -420
- package/Core/Features/Router/router.ts +125 -0
- package/Core/Features/Tray/tray.ts +102 -80
- package/Core/Features/WebGPU/webgpu.ts +283 -1024
- package/Core/Features/Window/window.ts +207 -156
- package/package.json +5 -1
|
@@ -1,283 +1,163 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export interface FileInfo {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
* @param callback - Function called when drag leaves the window
|
|
165
|
-
* @returns Unsubscribe function
|
|
166
|
-
*/
|
|
167
|
-
onDragLeave(callback: (event: DragEvent) => void): () => void {
|
|
168
|
-
return this.onFn('fileDrop.dragLeave', (data) => {
|
|
169
|
-
callback((data as DragEvent) || {});
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Helper functions for common use cases
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Read dropped file as text
|
|
178
|
-
* @param filePath - Path to the file
|
|
179
|
-
* @returns File contents as text
|
|
180
|
-
*/
|
|
181
|
-
export async function readFileAsText(filePath: string): Promise<string> {
|
|
182
|
-
const response = await fetch(`file://${filePath}`);
|
|
183
|
-
return await response.text();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Read dropped file as data URL (useful for images)
|
|
188
|
-
* @param filePath - Path to the file
|
|
189
|
-
* @returns File contents as data URL
|
|
190
|
-
*/
|
|
191
|
-
export async function readFileAsDataUrl(filePath: string): Promise<string> {
|
|
192
|
-
const response = await fetch(`file://${filePath}`);
|
|
193
|
-
const blob = await response.blob();
|
|
194
|
-
return new Promise((resolve, reject) => {
|
|
195
|
-
const reader = new FileReader();
|
|
196
|
-
reader.onload = () => resolve(reader.result as string);
|
|
197
|
-
reader.onerror = reject;
|
|
198
|
-
reader.readAsDataURL(blob);
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Filter files by extension
|
|
204
|
-
* @param files - Array of file info
|
|
205
|
-
* @param extensions - Array of extensions to filter (e.g., ['.png', '.jpg'])
|
|
206
|
-
* @returns Filtered array of files
|
|
207
|
-
*/
|
|
208
|
-
export function filterFilesByExtension(
|
|
209
|
-
files: FileInfo[],
|
|
210
|
-
extensions: string[]
|
|
211
|
-
): FileInfo[] {
|
|
212
|
-
const lowerExtensions = extensions.map((ext) => ext.toLowerCase());
|
|
213
|
-
return files.filter((file) => {
|
|
214
|
-
const ext = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();
|
|
215
|
-
return lowerExtensions.includes(ext);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Filter files by MIME type
|
|
221
|
-
* @param files - Array of file info
|
|
222
|
-
* @param mimeTypes - Array of MIME types to filter (e.g., ['image/png', 'image/jpeg'])
|
|
223
|
-
* @returns Filtered array of files
|
|
224
|
-
*/
|
|
225
|
-
export function filterFilesByMimeType(
|
|
226
|
-
files: FileInfo[],
|
|
227
|
-
mimeTypes: string[]
|
|
228
|
-
): FileInfo[] {
|
|
229
|
-
return files.filter((file) => mimeTypes.includes(file.type));
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Check if file is an image
|
|
234
|
-
* @param file - File info
|
|
235
|
-
* @returns true if file is an image
|
|
236
|
-
*/
|
|
237
|
-
export function isImageFile(file: FileInfo): boolean {
|
|
238
|
-
return file.type.startsWith('image/');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Check if file is a video
|
|
243
|
-
* @param file - File info
|
|
244
|
-
* @returns true if file is a video
|
|
245
|
-
*/
|
|
246
|
-
export function isVideoFile(file: FileInfo): boolean {
|
|
247
|
-
return file.type.startsWith('video/');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Check if file is audio
|
|
252
|
-
* @param file - File info
|
|
253
|
-
* @returns true if file is audio
|
|
254
|
-
*/
|
|
255
|
-
export function isAudioFile(file: FileInfo): boolean {
|
|
256
|
-
return file.type.startsWith('audio/');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Check if file is text
|
|
261
|
-
* @param file - File info
|
|
262
|
-
* @returns true if file is text
|
|
263
|
-
*/
|
|
264
|
-
export function isTextFile(file: FileInfo): boolean {
|
|
265
|
-
return file.type.startsWith('text/') ||
|
|
266
|
-
file.type === 'application/json' ||
|
|
267
|
-
file.type === 'application/xml';
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Format file size for display
|
|
272
|
-
* @param bytes - File size in bytes
|
|
273
|
-
* @returns Formatted string (e.g., "1.5 MB")
|
|
274
|
-
*/
|
|
275
|
-
export function formatFileSize(bytes: number): string {
|
|
276
|
-
if (bytes === 0) return '0 Bytes';
|
|
277
|
-
|
|
278
|
-
const k = 1024;
|
|
279
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
280
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
281
|
-
|
|
282
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
283
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* FileDrop API - Cross-platform drag & drop file handling
|
|
3
|
+
*
|
|
4
|
+
* Direct method calls - no .on/.emit
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { fileDrop } from '@plusui/api';
|
|
8
|
+
*
|
|
9
|
+
* await fileDrop.setEnabled(true);
|
|
10
|
+
* fileDrop.onFilesDropped((files) => { ... });
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface FileInfo {
|
|
14
|
+
path: string;
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
size: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DragEvent {
|
|
21
|
+
x?: number;
|
|
22
|
+
y?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface FileDropAPI {
|
|
26
|
+
setEnabled(enabled: boolean): Promise<void>;
|
|
27
|
+
isEnabled(): Promise<boolean>;
|
|
28
|
+
startDrag(filePaths: string[]): Promise<boolean>;
|
|
29
|
+
clearCallbacks(): Promise<void>;
|
|
30
|
+
onFilesDropped(callback: (files: FileInfo[]) => void): () => void;
|
|
31
|
+
onDragEnter(callback: (event: DragEvent) => void): () => void;
|
|
32
|
+
onDragLeave(callback: (event: DragEvent) => void): () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let _invoke: ((name: string, args?: unknown[]) => Promise<unknown>) | null = null;
|
|
36
|
+
let _event: ((event: string, callback: (data: unknown) => void) => () => void) | null = null;
|
|
37
|
+
|
|
38
|
+
export function setInvokeFn(fn: (name: string, args?: unknown[]) => Promise<unknown>) {
|
|
39
|
+
_invoke = fn;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function setEventFn(fn: (event: string, callback: (data: unknown) => void) => () => void) {
|
|
43
|
+
_event = fn;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function invoke<T = unknown>(name: string, args?: unknown[]): Promise<T> {
|
|
47
|
+
if (!_invoke) {
|
|
48
|
+
if (typeof window !== 'undefined' && (window as any).__invoke__) {
|
|
49
|
+
_invoke = (window as any).__invoke__;
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error('FileDrop API not initialized');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return _invoke!(name, args) as Promise<T>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function eventHandler(event: string, callback: (data: unknown) => void): () => void {
|
|
58
|
+
if (!_event) {
|
|
59
|
+
if (typeof window !== 'undefined' && (window as any).__on__) {
|
|
60
|
+
_event = (window as any).__on__;
|
|
61
|
+
} else {
|
|
62
|
+
return () => {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return _event!(event, callback);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const fileDrop: FileDropAPI = {
|
|
69
|
+
async setEnabled(enabled: boolean): Promise<void> {
|
|
70
|
+
await invoke('fileDrop.setEnabled', [enabled]);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async isEnabled(): Promise<boolean> {
|
|
74
|
+
return invoke<boolean>('fileDrop.isEnabled');
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
async startDrag(filePaths: string[]): Promise<boolean> {
|
|
78
|
+
if (!filePaths || filePaths.length === 0) {
|
|
79
|
+
throw new Error('filePaths must be a non-empty array');
|
|
80
|
+
}
|
|
81
|
+
return invoke<boolean>('fileDrop.startDrag', [filePaths]);
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
async clearCallbacks(): Promise<void> {
|
|
85
|
+
await invoke('fileDrop.clearCallbacks');
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
onFilesDropped(callback: (files: FileInfo[]) => void): () => void {
|
|
89
|
+
return eventHandler('fileDrop:filesDropped', callback as (data: unknown) => void);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
onDragEnter(callback: (event: DragEvent) => void): () => void {
|
|
93
|
+
return eventHandler('fileDrop:dragEnter', callback as (data: unknown) => void);
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
onDragLeave(callback: (event: DragEvent) => void): () => void {
|
|
97
|
+
return eventHandler('fileDrop:dragLeave', callback as (data: unknown) => void);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export async function readFileAsText(filePath: string): Promise<string> {
|
|
102
|
+
const response = await fetch(`file://${filePath}`);
|
|
103
|
+
return await response.text();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function readFileAsDataUrl(filePath: string): Promise<string> {
|
|
107
|
+
const response = await fetch(`file://${filePath}`);
|
|
108
|
+
const blob = await response.blob();
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
const reader = new FileReader();
|
|
111
|
+
reader.onload = () => resolve(reader.result as string);
|
|
112
|
+
reader.onerror = reject;
|
|
113
|
+
reader.readAsDataURL(blob);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function filterFilesByExtension(
|
|
118
|
+
files: FileInfo[],
|
|
119
|
+
extensions: string[]
|
|
120
|
+
): FileInfo[] {
|
|
121
|
+
const lowerExtensions = extensions.map((ext) => ext.toLowerCase());
|
|
122
|
+
return files.filter((file) => {
|
|
123
|
+
const ext = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();
|
|
124
|
+
return lowerExtensions.includes(ext);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function filterFilesByMimeType(
|
|
129
|
+
files: FileInfo[],
|
|
130
|
+
mimeTypes: string[]
|
|
131
|
+
): FileInfo[] {
|
|
132
|
+
return files.filter((file) => mimeTypes.includes(file.type));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function isImageFile(file: FileInfo): boolean {
|
|
136
|
+
return file.type.startsWith('image/');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function isVideoFile(file: FileInfo): boolean {
|
|
140
|
+
return file.type.startsWith('video/');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function isAudioFile(file: FileInfo): boolean {
|
|
144
|
+
return file.type.startsWith('audio/');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function isTextFile(file: FileInfo): boolean {
|
|
148
|
+
return file.type.startsWith('text/') ||
|
|
149
|
+
file.type === 'application/json' ||
|
|
150
|
+
file.type === 'application/xml';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function formatFileSize(bytes: number): string {
|
|
154
|
+
if (bytes === 0) return '0 Bytes';
|
|
155
|
+
|
|
156
|
+
const k = 1024;
|
|
157
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
158
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
159
|
+
|
|
160
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export default fileDrop;
|