plusui-native-core 0.1.98 → 0.1.101
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.
|
@@ -1,43 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Handle files dragged and dropped onto the application window.
|
|
5
|
-
* Also supports initiating drag operations from the app.
|
|
6
|
-
*
|
|
7
|
-
* Actions:
|
|
8
|
-
* fileDrop.setEnabled(enabled) - Enable or disable file drop
|
|
9
|
-
* fileDrop.isEnabled() - Check if file drop is enabled
|
|
10
|
-
* fileDrop.startDrag(filePaths) - Start a drag operation with the given files
|
|
11
|
-
* fileDrop.clearCallbacks() - Remove all registered drop callbacks
|
|
12
|
-
*
|
|
13
|
-
* Events:
|
|
14
|
-
* fileDrop.onFilesDropped(cb) - Fires when files are dropped onto the window
|
|
15
|
-
* fileDrop.onDragEnter(cb) - Fires when a drag enters the window
|
|
16
|
-
* fileDrop.onDragLeave(cb) - Fires when a drag leaves the window
|
|
17
|
-
*
|
|
18
|
-
* Helpers (pure functions):
|
|
19
|
-
* readFileAsText(path) - Read a file path as a text string
|
|
20
|
-
* readFileAsDataUrl(path) - Read a file path as a base64 data URL
|
|
21
|
-
* filterFilesByExtension(files, ext) - Filter FileInfo[] by extension(s)
|
|
22
|
-
* filterFilesByMimeType(files, mime) - Filter FileInfo[] by MIME type(s)
|
|
23
|
-
* isImageFile(file) - Check if FileInfo is an image
|
|
24
|
-
* isVideoFile(file) - Check if FileInfo is a video
|
|
25
|
-
* isAudioFile(file) - Check if FileInfo is audio
|
|
26
|
-
* isTextFile(file) - Check if FileInfo is a text file
|
|
27
|
-
* formatFileSize(bytes) - Format byte count as a human-readable string
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
export type { FileInfo, DragEvent, FileDropAPI } from '../FileDrop/filedrop';
|
|
31
|
-
|
|
32
|
-
export {
|
|
33
|
-
fileDrop,
|
|
34
|
-
readFileAsText,
|
|
35
|
-
readFileAsDataUrl,
|
|
36
|
-
filterFilesByExtension,
|
|
37
|
-
filterFilesByMimeType,
|
|
38
|
-
isImageFile,
|
|
39
|
-
isVideoFile,
|
|
40
|
-
isAudioFile,
|
|
41
|
-
isTextFile,
|
|
42
|
-
formatFileSize,
|
|
43
|
-
} from '../FileDrop/filedrop';
|
|
1
|
+
export type { FileInfo, DropZone } from '../FileDrop/filedrop';
|
|
2
|
+
export { fileDrop, formatFileSize, createDropZone } from '../FileDrop/filedrop';
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PlusUI API
|
|
3
|
-
*
|
|
4
|
-
* window.show(), window.hide(), window.close()
|
|
5
|
-
* window.maximize(), window.minimize(), window.restore()
|
|
6
|
-
* window.setSize(w, h), window.setPosition(x, y)
|
|
7
3
|
*/
|
|
8
4
|
|
|
9
5
|
export { window, win } from './window-api';
|
|
@@ -14,7 +10,7 @@ export { router } from './router-api';
|
|
|
14
10
|
export { keyboard, KeyCode, KeyMod } from './keyboard-api';
|
|
15
11
|
export { tray } from './tray-api';
|
|
16
12
|
export { display } from './display-api';
|
|
17
|
-
export { fileDrop, formatFileSize } from './filedrop-api';
|
|
13
|
+
export { fileDrop, formatFileSize, createDropZone } from './filedrop-api';
|
|
18
14
|
export { menu } from './menu-api';
|
|
19
15
|
export { gpu, GPUBufferUsage, GPUTextureUsage, GPUMapMode, GPUShaderStage, GPUColorWrite } from './webgpu-api';
|
|
20
16
|
|
|
@@ -22,19 +18,17 @@ export { connect, createChannel, type Channel } from './Connect_API';
|
|
|
22
18
|
export { _client } from '../Connection/connect';
|
|
23
19
|
|
|
24
20
|
export type { WindowSize, WindowPosition, WindowRect } from './window-api';
|
|
21
|
+
export type { FileInfo, DropZone } from './filedrop-api';
|
|
25
22
|
export type { ClipboardAPI } from './clipboard-api';
|
|
26
23
|
export type { KeyEvent, Shortcut } from './keyboard-api';
|
|
27
24
|
export type { TrayMenuItem, TrayIconData } from './tray-api';
|
|
28
25
|
export type { Display, DisplayMode, DisplayBounds, DisplayResolution } from './display-api';
|
|
29
|
-
export type { FileInfo, FileDropAPI } from './filedrop-api';
|
|
30
26
|
export type { MenuItem, MenuItemType, MenuBarData, ContextMenuOptions, ContextInfo } from './menu-api';
|
|
31
27
|
export type { AppConfig } from './app-api';
|
|
32
28
|
export type { GPUAdapter, GPUDevice, GPUBuffer, GPUTexture, GPUShaderModule, GPURenderPipeline, GPUComputePipeline, GPUQueue, GPUCommandEncoder } from './webgpu-api';
|
|
33
29
|
export type { BrowserState, NavigateCallback, StateCallback, LoadCallback, ErrorCallback } from './browser-api';
|
|
34
30
|
export type { RouteMap, RouteChangeCallback, RouteConfig } from './router-api';
|
|
35
31
|
|
|
36
|
-
export { readFileAsText, readFileAsDataUrl, filterFilesByExtension, filterFilesByMimeType, isImageFile, isVideoFile, isAudioFile, isTextFile } from './filedrop-api';
|
|
37
|
-
|
|
38
32
|
import { window, win } from './window-api';
|
|
39
33
|
import { clipboard } from './clipboard-api';
|
|
40
34
|
import { app } from './app-api';
|
|
@@ -43,7 +37,7 @@ import { router } from './router-api';
|
|
|
43
37
|
import { keyboard, KeyCode, KeyMod } from './keyboard-api';
|
|
44
38
|
import { tray } from './tray-api';
|
|
45
39
|
import { display } from './display-api';
|
|
46
|
-
import { fileDrop, formatFileSize } from './filedrop-api';
|
|
40
|
+
import { fileDrop, formatFileSize, createDropZone } from './filedrop-api';
|
|
47
41
|
import { menu } from './menu-api';
|
|
48
42
|
import { gpu, GPUBufferUsage, GPUTextureUsage, GPUMapMode, GPUShaderStage, GPUColorWrite } from './webgpu-api';
|
|
49
43
|
import { connect, createChannel } from './Connect_API';
|
|
@@ -60,6 +54,7 @@ const plusui = {
|
|
|
60
54
|
tray,
|
|
61
55
|
display,
|
|
62
56
|
fileDrop,
|
|
57
|
+
createDropZone,
|
|
63
58
|
menu,
|
|
64
59
|
gpu,
|
|
65
60
|
connect,
|
|
@@ -1,15 +1,3 @@
|
|
|
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
1
|
export interface FileInfo {
|
|
14
2
|
path: string;
|
|
15
3
|
name: string;
|
|
@@ -17,147 +5,107 @@ export interface FileInfo {
|
|
|
17
5
|
size: number;
|
|
18
6
|
}
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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;
|
|
8
|
+
let callId = 0;
|
|
9
|
+
const pending = new Map<string, { resolve: (v: any) => void; reject: (e: any) => void }>();
|
|
10
|
+
const zoneCallbacks = new Map<string, { onFiles: (files: FileInfo[]) => void }>();
|
|
37
11
|
|
|
38
|
-
|
|
39
|
-
|
|
12
|
+
function getGlobal(): any {
|
|
13
|
+
if (typeof window !== 'undefined') return window;
|
|
14
|
+
if (typeof globalThis !== 'undefined') return globalThis;
|
|
15
|
+
return {};
|
|
40
16
|
}
|
|
41
17
|
|
|
42
|
-
|
|
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
|
-
}
|
|
18
|
+
const g = getGlobal();
|
|
56
19
|
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return () => {};
|
|
63
|
-
}
|
|
20
|
+
g.__response__ = function(id: string, result: any, error?: any) {
|
|
21
|
+
const p = pending.get(id);
|
|
22
|
+
if (p) {
|
|
23
|
+
pending.delete(id);
|
|
24
|
+
error ? p.reject(new Error(error)) : p.resolve(result);
|
|
64
25
|
}
|
|
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
26
|
};
|
|
100
27
|
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
}
|
|
28
|
+
g.__plusui_fileDrop__ = function(zoneName: string, files: FileInfo[]) {
|
|
29
|
+
const zone = zoneCallbacks.get(zoneName);
|
|
30
|
+
if (zone && zone.onFiles) zone.onFiles(files);
|
|
31
|
+
};
|
|
105
32
|
|
|
106
|
-
|
|
107
|
-
const response = await fetch(`file://${filePath}`);
|
|
108
|
-
const blob = await response.blob();
|
|
33
|
+
async function invoke<T>(method: string, params: any[] = []): Promise<T> {
|
|
109
34
|
return new Promise((resolve, reject) => {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const ext = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();
|
|
124
|
-
return lowerExtensions.includes(ext);
|
|
35
|
+
const id = String(++callId);
|
|
36
|
+
pending.set(id, { resolve, reject });
|
|
37
|
+
const payload = JSON.stringify({ id, method, params });
|
|
38
|
+
|
|
39
|
+
if (g.__native_invoke__) {
|
|
40
|
+
g.__native_invoke__(payload);
|
|
41
|
+
} else if (g.chrome && g.chrome.webview && g.chrome.webview.postMessage) {
|
|
42
|
+
g.chrome.webview.postMessage(payload);
|
|
43
|
+
} else {
|
|
44
|
+
pending.delete(id);
|
|
45
|
+
console.warn('[PlusUI] ' + method + ' - native bridge not ready');
|
|
46
|
+
resolve(null as T);
|
|
47
|
+
}
|
|
125
48
|
});
|
|
126
49
|
}
|
|
127
50
|
|
|
128
|
-
export
|
|
129
|
-
files: FileInfo[]
|
|
130
|
-
|
|
131
|
-
): FileInfo[] {
|
|
132
|
-
return files.filter((file) => mimeTypes.includes(file.type));
|
|
51
|
+
export interface DropZone {
|
|
52
|
+
onFiles: (callback: (files: FileInfo[]) => void) => () => void;
|
|
53
|
+
element: HTMLElement | null;
|
|
133
54
|
}
|
|
134
55
|
|
|
135
|
-
export function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
56
|
+
export function createDropZone(name: string, el?: HTMLElement | null): DropZone {
|
|
57
|
+
const element = el || document.querySelector(`[data-dropzone="${name}"]`) as HTMLElement;
|
|
58
|
+
|
|
59
|
+
if (element) {
|
|
60
|
+
element.setAttribute('data-dropzone', name);
|
|
61
|
+
|
|
62
|
+
// Prevent browser default on drop (file navigation)
|
|
63
|
+
// The C++ backend handles actual file delivery via WM_DROPFILES
|
|
64
|
+
element.addEventListener('drop', (e: DragEvent) => {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
e.stopPropagation();
|
|
67
|
+
element.classList.remove('dropzone-active');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Visual feedback: the injected C++ script manages dropzone-active
|
|
71
|
+
// via document-level listeners, but we also handle cleanup here
|
|
72
|
+
element.addEventListener('dragleave', (e: DragEvent) => {
|
|
73
|
+
// Only remove if actually leaving the element (not entering a child)
|
|
74
|
+
const related = e.relatedTarget as Node | null;
|
|
75
|
+
if (!related || !element.contains(related)) {
|
|
76
|
+
element.classList.remove('dropzone-active');
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
element.addEventListener('dragover', (e: DragEvent) => {
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
if (e.dataTransfer) {
|
|
83
|
+
try { e.dataTransfer.dropEffect = 'copy'; } catch (_) {}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
142
87
|
|
|
143
|
-
|
|
144
|
-
|
|
88
|
+
return {
|
|
89
|
+
element,
|
|
90
|
+
onFiles: (callback: (files: FileInfo[]) => void) => {
|
|
91
|
+
zoneCallbacks.set(name, { onFiles: callback });
|
|
92
|
+
return () => zoneCallbacks.delete(name);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
145
95
|
}
|
|
146
96
|
|
|
147
|
-
export
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
97
|
+
export const fileDrop = {
|
|
98
|
+
setEnabled: (enabled: boolean) => invoke<void>('fileDrop.setEnabled', [enabled]),
|
|
99
|
+
isEnabled: () => invoke<boolean>('fileDrop.isEnabled'),
|
|
100
|
+
createDropZone,
|
|
101
|
+
};
|
|
152
102
|
|
|
153
103
|
export function formatFileSize(bytes: number): string {
|
|
154
|
-
if (bytes === 0) return '0
|
|
155
|
-
|
|
104
|
+
if (bytes === 0) return '0 B';
|
|
156
105
|
const k = 1024;
|
|
157
|
-
const sizes = ['
|
|
106
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
158
107
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
159
|
-
|
|
160
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
108
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
161
109
|
}
|
|
162
110
|
|
|
163
111
|
export default fileDrop;
|