@atom-js-org/runtime 0.5.1-alpha.0 → 0.5.2-alpha.0

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/index.d.ts CHANGED
@@ -61,6 +61,7 @@ export class BrowserWindow extends EventEmitter {
61
61
  hide(): void;
62
62
  isVisible(): boolean;
63
63
  focus(): void;
64
+ startDrag(): void;
64
65
  blur(): void;
65
66
  close(): void;
66
67
  destroy(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atom-js-org/runtime",
3
- "version": "0.5.1-alpha.0",
3
+ "version": "0.5.2-alpha.0",
4
4
  "description": "Electron-like desktop runtime powered by the operating system WebView.",
5
5
  "main": "src/index.cjs",
6
6
  "types": "index.d.ts",
@@ -39,6 +39,68 @@ function generateBridgeScript({ websocketUrl, preloadCode = '' }) {
39
39
  }
40
40
  }
41
41
 
42
+ function normalizeAppRegion(value) {
43
+ const normalized = String(value || '').trim().toLowerCase();
44
+ return normalized === 'drag' || normalized === 'no-drag' ? normalized : '';
45
+ }
46
+
47
+ function appRegionForElement(element) {
48
+ if (!(element instanceof Element)) return '';
49
+ if (element.hasAttribute('data-atom-no-drag')) return 'no-drag';
50
+ if (element.hasAttribute('data-atom-drag-region')) return 'drag';
51
+
52
+ const attribute = normalizeAppRegion(
53
+ element.getAttribute('data-atom-app-region') || element.getAttribute('data-app-region')
54
+ );
55
+ if (attribute) return attribute;
56
+
57
+ const inline = normalizeAppRegion(
58
+ element.style && (
59
+ element.style.getPropertyValue('-webkit-app-region') ||
60
+ element.style.getPropertyValue('app-region')
61
+ )
62
+ );
63
+ if (inline) return inline;
64
+
65
+ try {
66
+ const computed = getComputedStyle(element);
67
+ return normalizeAppRegion(
68
+ computed.getPropertyValue('-webkit-app-region') ||
69
+ computed.getPropertyValue('app-region')
70
+ );
71
+ } catch {
72
+ return '';
73
+ }
74
+ }
75
+
76
+ function isInteractiveDragTarget(element) {
77
+ return element instanceof Element && Boolean(element.closest(
78
+ 'button, input, textarea, select, option, a[href], [contenteditable=""], [contenteditable="true"], [role="button"]'
79
+ ));
80
+ }
81
+
82
+ function shouldStartWindowDrag(event) {
83
+ if (event.button !== 0 || event.defaultPrevented) return false;
84
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : [event.target];
85
+ let draggable = false;
86
+
87
+ for (const entry of path) {
88
+ if (!(entry instanceof Element)) continue;
89
+ const region = appRegionForElement(entry);
90
+ if (region === 'no-drag') return false;
91
+ if (region === 'drag') draggable = true;
92
+ }
93
+
94
+ if (!draggable || isInteractiveDragTarget(event.target)) return false;
95
+ return true;
96
+ }
97
+
98
+ document.addEventListener('mousedown', (event) => {
99
+ if (!shouldStartWindowDrag(event)) return;
100
+ event.preventDefault();
101
+ sendNow({ type: 'system', command: 'start-window-drag' });
102
+ }, true);
103
+
42
104
  function makeEvent(channel) {
43
105
  return Object.freeze({
44
106
  channel,
@@ -163,6 +163,11 @@ class BridgeServer {
163
163
  return;
164
164
  }
165
165
 
166
+ if (message.type === 'system') {
167
+ if (message.command === 'start-window-drag') win._startNativeDrag();
168
+ return;
169
+ }
170
+
166
171
  if (message.type === 'send') {
167
172
  const event = createIpcEvent(win);
168
173
  ipcMain.emit(message.channel, event, ...(Array.isArray(message.args) ? message.args : []));
@@ -219,6 +219,11 @@ class BrowserWindow extends EventEmitter {
219
219
  return false;
220
220
  }
221
221
 
222
+ _startNativeDrag() {
223
+ if (this._destroyed) return false;
224
+ return this._sendHostCommand({ command: 'start-drag' });
225
+ }
226
+
222
227
  _markRendererReady(details) {
223
228
  this._notifyDidFinishLoad(details && details.href ? details.href : this._currentUrl, 'bridge');
224
229
  }
@@ -334,6 +339,12 @@ class BrowserWindow extends EventEmitter {
334
339
  }
335
340
  }
336
341
 
342
+ startDrag() {
343
+ if (!this._startNativeDrag()) {
344
+ console.warn('[AtomJS] Native window dragging is not supported by the current platform host.');
345
+ }
346
+ }
347
+
337
348
  blur() {}
338
349
 
339
350
  close() {
@@ -140,6 +140,7 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
140
140
  @property(nonatomic, assign) BOOL modal;
141
141
  - (instancetype)initWithWindowId:(NSNumber *)windowId config:(NSDictionary *)config;
142
142
  - (void)navigate:(NSString *)urlString;
143
+ - (void)beginWindowDrag;
143
144
  @end
144
145
 
145
146
  @implementation AtomJSWindowController
@@ -175,6 +176,8 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
175
176
  defer:NO];
176
177
  _window.releasedWhenClosed = NO;
177
178
  _window.delegate = self;
179
+ _window.movable = YES;
180
+ _window.movableByWindowBackground = NO;
178
181
  _window.title = AtomJSString(config[@"title"], atomAppName);
179
182
  _window.backgroundColor = AtomJSColor(config[@"backgroundColor"]);
180
183
  _window.tabbingMode = NSWindowTabbingModeDisallowed;
@@ -270,6 +273,25 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
270
273
  return self;
271
274
  }
272
275
 
276
+ - (void)beginWindowDrag {
277
+ if (!self.window || ([NSEvent pressedMouseButtons] & 1) == 0) return;
278
+
279
+ NSPoint screenLocation = [NSEvent mouseLocation];
280
+ NSPoint windowLocation = [self.window convertPointFromScreen:screenLocation];
281
+ NSEvent *mouseDown = [NSEvent
282
+ mouseEventWithType:NSEventTypeLeftMouseDown
283
+ location:windowLocation
284
+ modifierFlags:0
285
+ timestamp:[[NSProcessInfo processInfo] systemUptime]
286
+ windowNumber:self.window.windowNumber
287
+ context:nil
288
+ eventNumber:0
289
+ clickCount:1
290
+ pressure:1.0];
291
+
292
+ if (mouseDown) [self.window performWindowDragWithEvent:mouseDown];
293
+ }
294
+
273
295
  - (void)navigate:(NSString *)urlString {
274
296
  NSURL *url = [NSURL URLWithString:urlString ?: @"about:blank"];
275
297
  if (!url) {
@@ -561,6 +583,8 @@ static void AtomJSHandleMessage(NSDictionary *message) {
561
583
  [controller.window makeKeyAndOrderFront:nil];
562
584
  [controller.window orderFrontRegardless];
563
585
  [NSApp activateIgnoringOtherApps:YES];
586
+ } else if ([command isEqualToString:@"start-drag"]) {
587
+ [controller beginWindowDrag];
564
588
  } else if ([command isEqualToString:@"close"] || [command isEqualToString:@"destroy"]) {
565
589
  [controller.window close];
566
590
  } else if ([command isEqualToString:@"set-title"]) {