plusui-native-core 0.1.103 → 0.1.104

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,2 +1,13 @@
1
+ export {
2
+ fileDrop,
3
+ createDropZone,
4
+ formatFileSize,
5
+ filterFilesByExtension,
6
+ filterFilesByMimeType,
7
+ isImageFile,
8
+ isVideoFile,
9
+ isAudioFile,
10
+ isTextFile
11
+ } from '../FileDrop/filedrop';
12
+
1
13
  export type { FileInfo, DropZone } from '../FileDrop/filedrop';
2
- export { fileDrop, formatFileSize, createDropZone } from '../FileDrop/filedrop';
@@ -10,7 +10,7 @@ export { router } from './router-api';
10
10
  export { keyboard, KeyCode, KeyMod } from './keyboard-api';
11
11
  export { tray } from './tray-api';
12
12
  export { display } from './display-api';
13
- export { fileDrop, formatFileSize, createDropZone } from './filedrop-api';
13
+ export { fileDrop, formatFileSize, createDropZone, filterFilesByExtension, filterFilesByMimeType, isImageFile, isVideoFile, isAudioFile, isTextFile } from './filedrop-api';
14
14
  export { menu } from './menu-api';
15
15
  export { gpu, GPUBufferUsage, GPUTextureUsage, GPUMapMode, GPUShaderStage, GPUColorWrite } from './webgpu-api';
16
16
 
@@ -77,38 +77,38 @@ App::Builder &App::Builder::transparent(bool transparent) {
77
77
  App::Builder &App::Builder::decorations(bool decorations) {
78
78
  config.decorations = decorations;
79
79
  return *this;
80
- }
81
- App::Builder &App::Builder::skipTaskbar(bool skip) {
82
- config.skipTaskbar = skip;
83
- return *this;
84
- }
85
- App::Builder &App::Builder::scrollbars(bool show) {
86
- config.scrollbars = show;
87
- return *this;
88
- }
89
- App::Builder &App::Builder::enableFileDrop(bool enable) {
90
- config.enableFileDrop = enable;
91
- return *this;
92
- }
93
-
94
- Window App::Builder::build() {
95
- WindowConfig winConfig;
96
- winConfig.title = config.title;
97
- winConfig.width = config.width;
98
- winConfig.height = config.height;
99
- winConfig.resizable = config.resizable;
100
- winConfig.alwaysOnTop = config.alwaysOnTop;
101
- winConfig.center = config.centered;
102
- winConfig.transparent = config.transparent;
103
- winConfig.decorations = config.decorations;
104
- winConfig.skipTaskbar = config.skipTaskbar;
105
- winConfig.enableFileDrop = config.enableFileDrop;
106
-
107
- // WebView configuration (now part of WindowConfig)
108
- winConfig.devtools = config.devtools;
109
- winConfig.scrollbars = config.scrollbars;
110
- winConfig.disableWebviewDragDrop =
111
- config.enableFileDrop; // Auto-disable webview drag when FileDrop enabled
80
+ }
81
+ App::Builder &App::Builder::skipTaskbar(bool skip) {
82
+ config.skipTaskbar = skip;
83
+ return *this;
84
+ }
85
+ App::Builder &App::Builder::scrollbars(bool show) {
86
+ config.scrollbars = show;
87
+ return *this;
88
+ }
89
+ App::Builder &App::Builder::fileDrop(bool enable) {
90
+ config.fileDrop = enable;
91
+ return *this;
92
+ }
93
+
94
+ Window App::Builder::build() {
95
+ WindowConfig winConfig;
96
+ winConfig.title = config.title;
97
+ winConfig.width = config.width;
98
+ winConfig.height = config.height;
99
+ winConfig.resizable = config.resizable;
100
+ winConfig.alwaysOnTop = config.alwaysOnTop;
101
+ winConfig.center = config.centered;
102
+ winConfig.transparent = config.transparent;
103
+ winConfig.decorations = config.decorations;
104
+ winConfig.skipTaskbar = config.skipTaskbar;
105
+ winConfig.fileDrop = config.fileDrop;
106
+
107
+ // WebView configuration (now part of WindowConfig)
108
+ winConfig.devtools = config.devtools;
109
+ winConfig.scrollbars = config.scrollbars;
110
+ winConfig.disableWebviewDragDrop =
111
+ config.fileDrop; // Auto-disable browser drag-drop when OS file drop enabled
112
112
 
113
113
  // Create native window
114
114
  auto nativeWinPtr = std::make_shared<Window>(Window::create(winConfig));
@@ -1,3 +1,24 @@
1
+ /**
2
+ * PlusUI FileDrop API
3
+ *
4
+ * Simple API for handling OS file drops in your app.
5
+ * Enable in config: .fileDrop(true) - enabled by default
6
+ *
7
+ * Usage:
8
+ * import plusui from 'plusui';
9
+ * const { fileDrop } = plusui;
10
+ *
11
+ * // Create a drop zone
12
+ * const zone = fileDrop.createDropZone('myZone', element);
13
+ * zone.onFiles((files) => {
14
+ * console.log('Dropped:', files.map(f => f.name));
15
+ * });
16
+ *
17
+ * // Or use data-dropzone attribute
18
+ * // <div data-dropzone="upload"></div>
19
+ * fileDrop.onFiles('upload', (files) => { ... });
20
+ */
21
+
1
22
  export interface FileInfo {
2
23
  path: string;
3
24
  name: string;
@@ -5,9 +26,22 @@ export interface FileInfo {
5
26
  size: number;
6
27
  }
7
28
 
29
+ export interface DropZone {
30
+ name: string;
31
+ onFiles(callback: (files: FileInfo[]) => void): () => void;
32
+ onDragEnter(callback: () => void): () => void;
33
+ onDragLeave(callback: () => void): () => void;
34
+ element: HTMLElement | null;
35
+ }
36
+
37
+ interface PendingCall {
38
+ resolve: (v: any) => void;
39
+ reject: (e: any) => void;
40
+ }
41
+
8
42
  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 }>();
43
+ const pending = new Map<string, PendingCall>();
44
+ const zones = new Map<string, DropZone>();
11
45
 
12
46
  function getGlobal(): any {
13
47
  if (typeof window !== 'undefined') return window;
@@ -25,18 +59,18 @@ g.__response__ = function(id: string, result: any, error?: any) {
25
59
  }
26
60
  };
27
61
 
28
- // Called by C++ when files are dropped on a named zone
29
62
  g.__plusui_fileDrop__ = function(zoneName: string, files: FileInfo[]) {
30
- const zone = zoneCallbacks.get(zoneName);
31
- if (zone && zone.onFiles) zone.onFiles(files);
63
+ const zone = zones.get(zoneName);
64
+ if (zone) {
65
+ const cb = (zone as any).__onFiles;
66
+ if (cb) cb(files);
67
+ }
32
68
  };
33
69
 
34
- // Called by C++ when the drop point doesn't hit any named zone — deliver to
35
- // all registered zones so a single-zone app always works regardless of DPI
36
- // scaling or hit-test accuracy.
37
70
  g.__plusui_fileDrop_default__ = function(files: FileInfo[]) {
38
- for (const zone of zoneCallbacks.values()) {
39
- if (zone && zone.onFiles) zone.onFiles(files);
71
+ for (const zone of zones.values()) {
72
+ const cb = (zone as any).__onFiles;
73
+ if (cb) cb(files);
40
74
  }
41
75
  };
42
76
 
@@ -52,49 +86,40 @@ async function invoke<T>(method: string, params: any[] = []): Promise<T> {
52
86
  g.chrome.webview.postMessage(payload);
53
87
  } else {
54
88
  pending.delete(id);
55
- console.warn('[PlusUI] ' + method + ' - native bridge not ready');
89
+ console.warn('[PlusUI] Native bridge not ready');
56
90
  resolve(null as T);
57
91
  }
58
92
  });
59
93
  }
60
94
 
61
- export interface DropZone {
62
- onFiles: (callback: (files: FileInfo[]) => void) => () => void;
63
- element: HTMLElement | null;
64
- }
65
-
66
- export function createDropZone(name: string, el?: HTMLElement | null): DropZone {
67
- const element = el || document.querySelector(`[data-dropzone="${name}"]`) as HTMLElement;
68
-
69
- if (element) {
70
- element.setAttribute('data-dropzone', name);
71
-
72
- // Prevent browser default on drop (file navigation).
73
- // Actual file delivery comes from C++ via WM_DROPFILES → __plusui_fileDrop__.
74
- element.addEventListener('drop', (e: DragEvent) => {
95
+ export function createDropZone(name: string, element?: HTMLElement | null): DropZone {
96
+ const el = element || document.querySelector(`[data-dropzone="${name}"]`) as HTMLElement;
97
+
98
+ if (el) {
99
+ el.setAttribute('data-dropzone', name);
100
+ el.classList.add('plusui-dropzone');
101
+
102
+ el.addEventListener('drop', (e: DragEvent) => {
75
103
  e.preventDefault();
76
- e.stopPropagation();
77
- element.classList.remove('dropzone-active');
104
+ el.classList.remove('dropzone-active');
78
105
  });
79
106
 
80
- element.addEventListener('dragleave', (e: DragEvent) => {
107
+ el.addEventListener('dragleave', (e: DragEvent) => {
81
108
  const related = e.relatedTarget as Node | null;
82
- if (!related || !element.contains(related)) {
83
- element.classList.remove('dropzone-active');
109
+ if (!related || !el.contains(related)) {
110
+ el.classList.remove('dropzone-active');
84
111
  }
85
112
  });
86
113
 
87
- element.addEventListener('dragenter', (e: DragEvent) => {
114
+ el.addEventListener('dragenter', (e: DragEvent) => {
88
115
  e.preventDefault();
89
- element.classList.add('dropzone-active');
116
+ el.classList.add('dropzone-active');
90
117
  if (e.dataTransfer) {
91
- // 'move' matches the native OS dropzone cursor (arrow + file icon)
92
- // rather than the browser's '+ Copy' cursor
93
118
  try { e.dataTransfer.dropEffect = 'move'; } catch (_) {}
94
119
  }
95
120
  });
96
121
 
97
- element.addEventListener('dragover', (e: DragEvent) => {
122
+ el.addEventListener('dragover', (e: DragEvent) => {
98
123
  e.preventDefault();
99
124
  if (e.dataTransfer) {
100
125
  try { e.dataTransfer.dropEffect = 'move'; } catch (_) {}
@@ -102,27 +127,88 @@ export function createDropZone(name: string, el?: HTMLElement | null): DropZone
102
127
  });
103
128
  }
104
129
 
105
- return {
106
- element,
130
+ const zone: DropZone = {
131
+ name,
132
+ element: el,
107
133
  onFiles: (callback: (files: FileInfo[]) => void) => {
108
- zoneCallbacks.set(name, { onFiles: callback });
109
- return () => zoneCallbacks.delete(name);
134
+ (zone as any).__onFiles = callback;
135
+ zones.set(name, zone);
136
+ return () => {
137
+ (zone as any).__onFiles = undefined;
138
+ zones.delete(name);
139
+ };
140
+ },
141
+ onDragEnter: (callback: () => void) => {
142
+ (zone as any).__onDragEnter = callback;
143
+ return () => { (zone as any).__onDragEnter = undefined; };
144
+ },
145
+ onDragLeave: (callback: () => void) => {
146
+ (zone as any).__onDragLeave = callback;
147
+ return () => { (zone as any).__onDragLeave = undefined; };
110
148
  }
111
149
  };
150
+
151
+ zones.set(name, zone);
152
+ return zone;
112
153
  }
113
154
 
114
155
  export const fileDrop = {
115
- setEnabled: (enabled: boolean) => invoke<void>('fileDrop.setEnabled', [enabled]),
116
- isEnabled: () => invoke<boolean>('fileDrop.isEnabled'),
117
156
  createDropZone,
157
+
158
+ onFiles: (name: string, callback: (files: FileInfo[]) => void): (() => void) => {
159
+ const zone = zones.get(name);
160
+ if (zone) {
161
+ return zone.onFiles(callback);
162
+ }
163
+ const newZone = createDropZone(name);
164
+ return newZone.onFiles(callback);
165
+ },
166
+
167
+ setEnabled: (enabled: boolean): Promise<void> =>
168
+ invoke<void>('fileDrop.setEnabled', [enabled]),
169
+
170
+ isEnabled: (): Promise<boolean> =>
171
+ invoke<boolean>('fileDrop.isEnabled'),
172
+
173
+ startDrag: (filePaths: string[]): Promise<void> =>
174
+ invoke<void>('fileDrop.startDrag', [filePaths]),
118
175
  };
119
176
 
120
177
  export function formatFileSize(bytes: number): string {
121
178
  if (bytes === 0) return '0 B';
122
179
  const k = 1024;
123
- const sizes = ['B', 'KB', 'MB', 'GB'];
180
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
124
181
  const i = Math.floor(Math.log(bytes) / Math.log(k));
125
182
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
126
183
  }
127
184
 
185
+ export function filterFilesByExtension(files: FileInfo[], extensions: string[]): FileInfo[] {
186
+ const exts = extensions.map(e => e.toLowerCase().replace(/^\./, ''));
187
+ return files.filter(f => {
188
+ const ext = f.name.split('.').pop()?.toLowerCase() || '';
189
+ return exts.includes(ext);
190
+ });
191
+ }
192
+
193
+ export function filterFilesByMimeType(files: FileInfo[], mimeTypes: string[]): FileInfo[] {
194
+ return files.filter(f => mimeTypes.includes(f.type));
195
+ }
196
+
197
+ export function isImageFile(file: FileInfo): boolean {
198
+ return file.type.startsWith('image/');
199
+ }
200
+
201
+ export function isVideoFile(file: FileInfo): boolean {
202
+ return file.type.startsWith('video/');
203
+ }
204
+
205
+ export function isAudioFile(file: FileInfo): boolean {
206
+ return file.type.startsWith('audio/');
207
+ }
208
+
209
+ export function isTextFile(file: FileInfo): boolean {
210
+ return file.type.startsWith('text/') || file.name.endsWith('.txt') ||
211
+ file.name.endsWith('.md') || file.name.endsWith('.json');
212
+ }
213
+
128
214
  export default fileDrop;
@@ -167,7 +167,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
167
167
  // while still showing visual feedback on drop zones.
168
168
  // File delivery is handled by WM_DROPFILES in wndProc.
169
169
  if (pImpl->config.disableWebviewDragDrop ||
170
- pImpl->config.enableFileDrop) {
170
+ pImpl->config.fileDrop) {
171
171
  std::string disableDragDropScript = R"(
172
172
  (function() {
173
173
  if (window.__plusui_dropzone_init) return;
@@ -213,7 +213,10 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
213
213
  document.addEventListener('dragleave', function(e) {
214
214
  e.preventDefault();
215
215
  var zone = findDropZone(e);
216
- updateActiveZone(zone);
216
+ // Only remove if actually leaving the zone (relatedTarget is outside)
217
+ if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {
218
+ updateActiveZone(null);
219
+ }
217
220
  }, true);
218
221
 
219
222
  document.addEventListener('drop', function(e) {
@@ -679,7 +682,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
679
682
  script = "(function() { delete window.__plusui_dropzone_init; })();";
680
683
  } else {
681
684
  // Re-install the dropzone blocker script
682
- script = "(function() { if (window.__plusui_dropzone_init) return; window.__plusui_dropzone_init = true; var activeZone = null; var findDropZone = function(e) { var target = null; if (e && typeof e.clientX === 'number' && document.elementFromPoint) { target = document.elementFromPoint(e.clientX, e.clientY); } if (!target && e && e.target) target = e.target; if (!target || !target.closest) return null; return target.closest('[data-dropzone]'); }; var updateActiveZone = function(zone) { if (activeZone === zone) return; if (activeZone) activeZone.classList.remove('dropzone-active'); activeZone = zone; if (activeZone) activeZone.classList.add('dropzone-active'); }; document.addEventListener('dragenter', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragover', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragleave', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); }, true); document.addEventListener('drop', function(e) { e.preventDefault(); updateActiveZone(null); }, true); window.addEventListener('dragover', function(e) { e.preventDefault(); }, true); window.addEventListener('drop', function(e) { e.preventDefault(); }, true); })();";
685
+ script = "(function() { if (window.__plusui_dropzone_init) return; window.__plusui_dropzone_init = true; var activeZone = null; var findDropZone = function(e) { var target = null; if (e && typeof e.clientX === 'number' && document.elementFromPoint) { target = document.elementFromPoint(e.clientX, e.clientY); } if (!target && e && e.target) target = e.target; if (!target || !target.closest) return null; return target.closest('[data-dropzone]'); }; var updateActiveZone = function(zone) { if (activeZone === zone) return; if (activeZone) activeZone.classList.remove('dropzone-active'); activeZone = zone; if (activeZone) activeZone.classList.add('dropzone-active'); }; document.addEventListener('dragenter', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragover', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragleave', function(e) { e.preventDefault(); var zone = findDropZone(e); if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) { updateActiveZone(null); } }, true); document.addEventListener('drop', function(e) { e.preventDefault(); updateActiveZone(null); }, true); window.addEventListener('dragover', function(e) { e.preventDefault(); }, true); window.addEventListener('drop', function(e) { e.preventDefault(); }, true); })();";
683
686
  }
684
687
  pImpl->webview->ExecuteScript(
685
688
  std::wstring(script.begin(), script.end()).c_str(),
@@ -806,7 +809,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
806
809
  // Block browser default drag-drop while allowing drop zone visual feedback.
807
810
  // File delivery is handled natively by macOS drag APIs, not browser events.
808
811
  if (win.pImpl->config.disableWebviewDragDrop ||
809
- win.pImpl->config.enableFileDrop) {
812
+ win.pImpl->config.fileDrop) {
810
813
  NSString *disableDragDropScript = @"(function() {"
811
814
  "if (window.__plusui_dropzone_init) return;"
812
815
  "window.__plusui_dropzone_init = true;"
@@ -841,7 +844,9 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
841
844
  "document.addEventListener('dragleave', function(e) {"
842
845
  "e.preventDefault();"
843
846
  "var zone = findDropZone(e);"
844
- "updateActiveZone(zone);"
847
+ "if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {"
848
+ "updateActiveZone(null);"
849
+ "}"
845
850
  "}, true);"
846
851
  "document.addEventListener('drop', function(e) {"
847
852
  "e.preventDefault();"
@@ -20,7 +20,7 @@ export interface WebViewConfig {
20
20
  allowRemoteContent?: boolean;
21
21
  dataPath?: string;
22
22
  cacheSize?: number; // MB
23
- disableWebviewDragDrop?: boolean; // Disable webview drag-drop (auto-set by WindowConfig.enableFileDrop)
23
+ disableWebviewDragDrop?: boolean; // Disable browser HTML drag-drop (auto-set by WindowConfig.fileDrop)
24
24
  }
25
25
 
26
26
  export type WindowId = number;
@@ -270,7 +270,7 @@ struct Window::Impl {
270
270
  }
271
271
  }
272
272
 
273
- if (!targetImpl || !targetImpl->config.enableFileDrop ||
273
+ if (!targetImpl || !targetImpl->config.fileDrop ||
274
274
  !targetImpl->webview) {
275
275
  DragFinish(hDrop);
276
276
  return 0;
@@ -504,7 +504,7 @@ Window Window::create(const WindowConfig &config) {
504
504
  w.pImpl->state.width = config.width;
505
505
  w.pImpl->state.height = config.height;
506
506
 
507
- DragAcceptFiles(w.pImpl->hwnd, config.enableFileDrop ? TRUE : FALSE);
507
+ DragAcceptFiles(w.pImpl->hwnd, config.fileDrop ? TRUE : FALSE);
508
508
 
509
509
  if (config.center) {
510
510
  RECT screen;
@@ -1069,7 +1069,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1069
1069
 
1070
1070
  // Pure native file-drop mode: when native FileDrop is enabled,
1071
1071
  // fully disable browser/WebView drag-drop handling.
1072
- if (win.pImpl->config.enableFileDrop) {
1072
+ if (win.pImpl->config.fileDrop) {
1073
1073
  win.pImpl->config.disableWebviewDragDrop = true;
1074
1074
  }
1075
1075
 
@@ -1619,7 +1619,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1619
1619
  msg.substr(p1 + 1, p2 - p1 - 1);
1620
1620
  bool enabled = params.find("true") !=
1621
1621
  std::string::npos;
1622
- pImpl->config.enableFileDrop = enabled;
1622
+ pImpl->config.fileDrop = enabled;
1623
1623
 
1624
1624
  HWND targetHwnd = nullptr;
1625
1625
  if (pImpl->window) {
@@ -1652,7 +1652,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1652
1652
  }
1653
1653
  success = true;
1654
1654
  } else if (fileDropMethod == "isEnabled") {
1655
- result = pImpl->config.enableFileDrop
1655
+ result = pImpl->config.fileDrop
1656
1656
  ? "true"
1657
1657
  : "false";
1658
1658
  success = true;
@@ -1758,7 +1758,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1758
1758
  // Block browser default drag-drop while allowing drop zone visual feedback.
1759
1759
  // File delivery is handled natively by macOS drag APIs, not browser events.
1760
1760
  if (win.pImpl->config.disableWebviewDragDrop ||
1761
- win.pImpl->config.enableFileDrop) {
1761
+ win.pImpl->config.fileDrop) {
1762
1762
  NSString *disableDragDropScript =
1763
1763
  @"(function() {"
1764
1764
  "if (window.__plusui_dropzone_init) return;"
@@ -1794,7 +1794,9 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1794
1794
  "document.addEventListener('dragleave', function(e) {"
1795
1795
  "e.preventDefault();"
1796
1796
  "var zone = findDropZone(e);"
1797
- "updateActiveZone(zone);"
1797
+ "if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {"
1798
+ "updateActiveZone(null);"
1799
+ "}"
1798
1800
  "}, true);"
1799
1801
  "document.addEventListener('drop', function(e) {"
1800
1802
  "e.preventDefault();"
@@ -74,7 +74,7 @@
74
74
  * .width(1200) .width(1200)
75
75
  * .height(800) .height(800)
76
76
  * .devtools(true) .devtools(true)
77
- * .enableFileDrop(true); .enableFileDrop(true)
77
+ * .fileDrop(true); .fileDrop(true)
78
78
  * .build() ← returns Window
79
79
  * TS: app.quit() C++: app.quit()
80
80
  * TS: app.onReady(cb) C++: (start after build())
@@ -201,7 +201,7 @@
201
201
  * .title("My App")
202
202
  * .width(1200)
203
203
  * .height(800)
204
- * .enableFileDrop(true)
204
+ * .fileDrop(true)
205
205
  * .build();
206
206
  *
207
207
  * MyApp connect;
@@ -25,47 +25,47 @@ private:
25
25
  std::unique_ptr<Impl> pImpl;
26
26
  };
27
27
 
28
- class App::Builder {
29
- public:
30
- struct Config {
31
- std::string title = "PlusUI App";
32
- int width = 1200;
33
- int height = 800;
34
- bool resizable = true;
35
- bool devtools = true;
36
- std::string trayIconPath;
37
- std::string trayTooltip;
38
- bool alwaysOnTop = false;
39
- bool centered = true;
40
- bool transparent = false;
41
- bool decorations = true;
42
- bool skipTaskbar = false;
43
- bool scrollbars = true;
44
- bool enableFileDrop = true;
45
- };
46
-
47
- Builder();
48
-
49
- Builder &title(const std::string &t);
50
- Builder &width(int w);
51
- Builder &height(int h);
52
- Builder &resizable(bool r);
53
- Builder &devtools(bool d);
54
- Builder &trayIcon(const std::string &icon);
55
- Builder &trayTooltip(const std::string &tooltip);
56
- Builder &alwaysOnTop(bool top);
57
- Builder &centered(bool center);
58
- Builder &transparent(bool transparent);
59
- Builder &decorations(bool decorations);
60
- Builder &skipTaskbar(bool skip);
61
- Builder &scrollbars(bool show);
62
- Builder &enableFileDrop(bool enable);
63
-
64
- Window build();
65
-
66
- private:
67
- Config config;
68
- };
28
+ class App::Builder {
29
+ public:
30
+ struct Config {
31
+ std::string title = "PlusUI App";
32
+ int width = 1200;
33
+ int height = 800;
34
+ bool resizable = true;
35
+ bool devtools = true;
36
+ std::string trayIconPath;
37
+ std::string trayTooltip;
38
+ bool alwaysOnTop = false;
39
+ bool centered = true;
40
+ bool transparent = false;
41
+ bool decorations = true;
42
+ bool skipTaskbar = false;
43
+ bool scrollbars = true;
44
+ bool fileDrop = true; // Enable OS file drop
45
+ };
46
+
47
+ Builder();
48
+
49
+ Builder &title(const std::string &t);
50
+ Builder &width(int w);
51
+ Builder &height(int h);
52
+ Builder &resizable(bool r);
53
+ Builder &devtools(bool d);
54
+ Builder &trayIcon(const std::string &icon);
55
+ Builder &trayTooltip(const std::string &tooltip);
56
+ Builder &alwaysOnTop(bool top);
57
+ Builder &centered(bool center);
58
+ Builder &transparent(bool transparent);
59
+ Builder &decorations(bool decorations);
60
+ Builder &skipTaskbar(bool skip);
61
+ Builder &scrollbars(bool show);
62
+ Builder &fileDrop(bool enable);
63
+
64
+ Window build();
65
+
66
+ private:
67
+ Config config;
68
+ };
69
69
 
70
70
  App::Builder createApp();
71
71
 
@@ -11,46 +11,44 @@ namespace plusui {
11
11
 
12
12
  class TrayManager;
13
13
 
14
- struct WindowConfig {
15
- // Window properties
16
- std::string title = "PlusUI Window";
17
- int x = -1;
18
- int y = -1;
19
- int width = 800;
20
- int height = 600;
21
- int minWidth = 100;
22
- int minHeight = 100;
23
- int maxWidth = -1;
24
- int maxHeight = -1;
25
- bool resizable = true;
26
- bool minimizable = true;
27
- bool maximizable = true;
28
- bool closable = true;
29
- bool alwaysOnTop = false;
30
- bool center = true;
31
- bool frame = true;
32
- bool transparent = false;
33
- bool decorations = true;
34
- bool skipTaskbar = false;
35
- double opacity = 1.0;
36
- bool fullscreen = false;
37
- bool enableFileDrop =
38
- true; // Enable native FileDrop API (auto-disables webview drag-drop)
39
-
40
- // WebView properties
41
- std::string userAgent = "";
42
- bool devtools = true;
43
- bool contextMenu = true;
44
- bool javascript = true;
45
- bool webSecurity = true;
46
- bool allowFileAccess = false;
47
- bool allowRemoteContent = true;
48
- std::string dataPath = "";
49
- int cacheSize = 100; // MB
50
- bool scrollbars = true; // Show/hide scrollbars
51
- bool disableWebviewDragDrop =
52
- true; // Disable webview drag-drop (usually set by enableFileDrop)
53
- };
14
+ struct WindowConfig {
15
+ // Window properties
16
+ std::string title = "PlusUI Window";
17
+ int x = -1;
18
+ int y = -1;
19
+ int width = 800;
20
+ int height = 600;
21
+ int minWidth = 100;
22
+ int minHeight = 100;
23
+ int maxWidth = -1;
24
+ int maxHeight = -1;
25
+ bool resizable = true;
26
+ bool minimizable = true;
27
+ bool maximizable = true;
28
+ bool closable = true;
29
+ bool alwaysOnTop = false;
30
+ bool center = true;
31
+ bool frame = true;
32
+ bool transparent = false;
33
+ bool decorations = true;
34
+ bool skipTaskbar = false;
35
+ double opacity = 1.0;
36
+ bool fullscreen = false;
37
+ bool fileDrop = true; // Enable OS file drop (drag files from Explorer/Finder into window)
38
+ bool disableWebviewDragDrop = true; // Disable browser HTML drag-drop (auto-set by fileDrop)
39
+
40
+ // WebView properties
41
+ std::string userAgent = "";
42
+ bool devtools = true;
43
+ bool contextMenu = true;
44
+ bool javascript = true;
45
+ bool webSecurity = true;
46
+ bool allowFileAccess = false;
47
+ bool allowRemoteContent = true;
48
+ std::string dataPath = "";
49
+ int cacheSize = 100; // MB
50
+ bool scrollbars = true; // Show/hide scrollbars
51
+ };
54
52
 
55
53
  struct WindowState {
56
54
  int x = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plusui-native-core",
3
- "version": "0.1.103",
3
+ "version": "0.1.104",
4
4
  "description": "PlusUI Core framework (frontend + backend implementations)",
5
5
  "type": "module",
6
6
  "main": "./Core/Features/API/index.ts",