@atom-js-org/runtime 0.5.0-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 +1 -0
- package/package.json +1 -1
- package/src/bridge-script.cjs +62 -0
- package/src/bridge-server.cjs +5 -0
- package/src/browser-window.cjs +11 -0
- package/src/runtime/macos-native-host.m +47 -1
package/index.d.ts
CHANGED
package/package.json
CHANGED
package/src/bridge-script.cjs
CHANGED
|
@@ -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,
|
package/src/bridge-server.cjs
CHANGED
|
@@ -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 : []));
|
package/src/browser-window.cjs
CHANGED
|
@@ -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() {
|
|
@@ -70,6 +70,28 @@ static NSColor *AtomJSColor(NSString *hex) {
|
|
|
70
70
|
return [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha];
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
static void AtomJSConfigureTransparentWebView(WKWebView *webView) {
|
|
74
|
+
if (!webView) return;
|
|
75
|
+
|
|
76
|
+
// `drawsBackground` is not exposed as a public Objective-C property by every
|
|
77
|
+
// macOS SDK. Use guarded KVC so older SDKs still compile, while retaining the
|
|
78
|
+
// WebKit behavior needed for genuinely transparent pages.
|
|
79
|
+
@try {
|
|
80
|
+
[webView setValue:@NO forKey:@"drawsBackground"];
|
|
81
|
+
} @catch (__unused NSException *exception) {}
|
|
82
|
+
|
|
83
|
+
// Newer WebKit versions expose a public under-page color. Set it through the
|
|
84
|
+
// Objective-C runtime so the host also compiles against SDKs that predate it.
|
|
85
|
+
if ([webView respondsToSelector:NSSelectorFromString(@"setUnderPageBackgroundColor:")]) {
|
|
86
|
+
@try {
|
|
87
|
+
[webView setValue:[NSColor clearColor] forKey:@"underPageBackgroundColor"];
|
|
88
|
+
} @catch (__unused NSException *exception) {}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
webView.wantsLayer = YES;
|
|
92
|
+
webView.layer.backgroundColor = [NSColor clearColor].CGColor;
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
static NSImage *AtomJSDefaultApplicationIcon(void) {
|
|
74
96
|
NSSize size = NSMakeSize(512.0, 512.0);
|
|
75
97
|
NSImage *image = [[NSImage alloc] initWithSize:size];
|
|
@@ -118,6 +140,7 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
|
|
|
118
140
|
@property(nonatomic, assign) BOOL modal;
|
|
119
141
|
- (instancetype)initWithWindowId:(NSNumber *)windowId config:(NSDictionary *)config;
|
|
120
142
|
- (void)navigate:(NSString *)urlString;
|
|
143
|
+
- (void)beginWindowDrag;
|
|
121
144
|
@end
|
|
122
145
|
|
|
123
146
|
@implementation AtomJSWindowController
|
|
@@ -153,6 +176,8 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
|
|
|
153
176
|
defer:NO];
|
|
154
177
|
_window.releasedWhenClosed = NO;
|
|
155
178
|
_window.delegate = self;
|
|
179
|
+
_window.movable = YES;
|
|
180
|
+
_window.movableByWindowBackground = NO;
|
|
156
181
|
_window.title = AtomJSString(config[@"title"], atomAppName);
|
|
157
182
|
_window.backgroundColor = AtomJSColor(config[@"backgroundColor"]);
|
|
158
183
|
_window.tabbingMode = NSWindowTabbingModeDisallowed;
|
|
@@ -208,7 +233,7 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
|
|
|
208
233
|
_webView = [[WKWebView alloc] initWithFrame:frame configuration:webConfiguration];
|
|
209
234
|
_webView.navigationDelegate = self;
|
|
210
235
|
_webView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
211
|
-
if (AtomJSBoolean(config[@"transparent"], NO)) _webView
|
|
236
|
+
if (AtomJSBoolean(config[@"transparent"], NO)) AtomJSConfigureTransparentWebView(_webView);
|
|
212
237
|
_window.contentView = _webView;
|
|
213
238
|
|
|
214
239
|
NSNumber *parentWindowId = AtomJSNumber(config[@"parentWindowId"], nil);
|
|
@@ -248,6 +273,25 @@ static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
|
|
|
248
273
|
return self;
|
|
249
274
|
}
|
|
250
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
|
+
|
|
251
295
|
- (void)navigate:(NSString *)urlString {
|
|
252
296
|
NSURL *url = [NSURL URLWithString:urlString ?: @"about:blank"];
|
|
253
297
|
if (!url) {
|
|
@@ -539,6 +583,8 @@ static void AtomJSHandleMessage(NSDictionary *message) {
|
|
|
539
583
|
[controller.window makeKeyAndOrderFront:nil];
|
|
540
584
|
[controller.window orderFrontRegardless];
|
|
541
585
|
[NSApp activateIgnoringOtherApps:YES];
|
|
586
|
+
} else if ([command isEqualToString:@"start-drag"]) {
|
|
587
|
+
[controller beginWindowDrag];
|
|
542
588
|
} else if ([command isEqualToString:@"close"] || [command isEqualToString:@"destroy"]) {
|
|
543
589
|
[controller.window close];
|
|
544
590
|
} else if ([command isEqualToString:@"set-title"]) {
|