@atom-js-org/runtime 0.2.0-alpha.0 → 0.3.0-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/package.json +1 -1
- package/src/app.cjs +2 -0
- package/src/browser-window.cjs +144 -50
- package/src/dialog.cjs +32 -28
- package/src/native-host.cjs +356 -0
- package/src/runtime/macos-native-host.m +519 -0
- package/src/runtime/window-host.mjs +28 -72
- package/src/web-contents.cjs +8 -2
- package/src/runtime/macos-window-host.jxa.js +0 -147
package/package.json
CHANGED
package/src/app.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const path = require('node:path');
|
|
|
6
6
|
const fs = require('node:fs');
|
|
7
7
|
const state = require('./state.cjs');
|
|
8
8
|
const { BridgeServer } = require('./bridge-server.cjs');
|
|
9
|
+
const { stopNativeHost } = require('./native-host.cjs');
|
|
9
10
|
|
|
10
11
|
class App extends EventEmitter {
|
|
11
12
|
constructor() {
|
|
@@ -41,6 +42,7 @@ class App extends EventEmitter {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
for (const win of [...state.windows.values()]) win.destroy();
|
|
45
|
+
await stopNativeHost();
|
|
44
46
|
if (state.bridgeServer) await state.bridgeServer.stop();
|
|
45
47
|
this.emit('will-quit');
|
|
46
48
|
this.emit('quit', {}, 0);
|
package/src/browser-window.cjs
CHANGED
|
@@ -9,6 +9,7 @@ const state = require('./state.cjs');
|
|
|
9
9
|
const app = require('./app.cjs');
|
|
10
10
|
const { WebContents } = require('./web-contents.cjs');
|
|
11
11
|
const { generateBridgeScript } = require('./bridge-script.cjs');
|
|
12
|
+
const { getNativeHost } = require('./native-host.cjs');
|
|
12
13
|
|
|
13
14
|
class BrowserWindow extends EventEmitter {
|
|
14
15
|
constructor(options = {}) {
|
|
@@ -17,6 +18,8 @@ class BrowserWindow extends EventEmitter {
|
|
|
17
18
|
this.options = normalizeOptions(options);
|
|
18
19
|
this.webContents = new WebContents(this);
|
|
19
20
|
this._child = null;
|
|
21
|
+
this._nativeHost = null;
|
|
22
|
+
this._hostAttached = false;
|
|
20
23
|
this._destroyed = false;
|
|
21
24
|
this._rendererReady = false;
|
|
22
25
|
this._visible = this.options.show;
|
|
@@ -26,14 +29,20 @@ class BrowserWindow extends EventEmitter {
|
|
|
26
29
|
this._menu = null;
|
|
27
30
|
this._menuBarVisible = true;
|
|
28
31
|
this._lastFinishedLoad = null;
|
|
32
|
+
this._fullScreen = false;
|
|
33
|
+
this._maximized = false;
|
|
34
|
+
this._minimized = false;
|
|
35
|
+
this._bounds = {
|
|
36
|
+
x: 0,
|
|
37
|
+
y: 0,
|
|
38
|
+
width: this.options.width,
|
|
39
|
+
height: this.options.height
|
|
40
|
+
};
|
|
29
41
|
state.windows.set(this.id, this);
|
|
30
42
|
}
|
|
31
43
|
|
|
32
44
|
loadFile(filePath) {
|
|
33
45
|
const task = this._loadFile(filePath);
|
|
34
|
-
// Electron applications commonly call loadFile() without awaiting it. Keep
|
|
35
|
-
// failures observable to callers while preventing a second, unhandled
|
|
36
|
-
// rejection from terminating modern Node.js processes.
|
|
37
46
|
task.catch(() => {});
|
|
38
47
|
return task;
|
|
39
48
|
}
|
|
@@ -61,32 +70,49 @@ class BrowserWindow extends EventEmitter {
|
|
|
61
70
|
|
|
62
71
|
async _load(url) {
|
|
63
72
|
if (this._destroyed) throw new Error('BrowserWindow has been destroyed');
|
|
64
|
-
this._currentUrl = url;
|
|
73
|
+
this._currentUrl = String(url);
|
|
65
74
|
|
|
66
|
-
if (this.
|
|
67
|
-
|
|
75
|
+
if (this._hostAttached) {
|
|
76
|
+
this._sendHostCommand({ command: 'navigate', url: this._currentUrl });
|
|
68
77
|
return;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
const preloadPath = this.options.webPreferences.preload;
|
|
72
81
|
let preloadCode = '';
|
|
73
|
-
if (preloadPath)
|
|
74
|
-
preloadCode = await fs.promises.readFile(path.resolve(preloadPath), 'utf8');
|
|
75
|
-
}
|
|
82
|
+
if (preloadPath) preloadCode = await fs.promises.readFile(path.resolve(preloadPath), 'utf8');
|
|
76
83
|
|
|
77
84
|
const config = {
|
|
78
85
|
title: this.options.title || app.getName(),
|
|
79
86
|
width: this.options.width,
|
|
80
87
|
height: this.options.height,
|
|
81
88
|
resizable: this.options.resizable,
|
|
89
|
+
center: this.options.center,
|
|
90
|
+
frame: this.options.frame,
|
|
91
|
+
show: this.options.show,
|
|
92
|
+
backgroundColor: this.options.backgroundColor,
|
|
82
93
|
debug: Boolean(this.options.webPreferences.devTools || process.env.ATOM_DEV === '1'),
|
|
83
|
-
url,
|
|
94
|
+
url: this._currentUrl,
|
|
84
95
|
bridgeScript: generateBridgeScript({
|
|
85
96
|
websocketUrl: state.bridgeServer.websocketUrl(this.id),
|
|
86
97
|
preloadCode
|
|
87
98
|
})
|
|
88
99
|
};
|
|
89
100
|
|
|
101
|
+
this._pendingLoad = this._createPendingLoad();
|
|
102
|
+
|
|
103
|
+
if (process.platform === 'darwin') {
|
|
104
|
+
this._nativeHost = getNativeHost(app.getName());
|
|
105
|
+
this._hostAttached = true;
|
|
106
|
+
await this._nativeHost.createWindow(this, config);
|
|
107
|
+
} else {
|
|
108
|
+
await this._startLegacyHost(config);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this._pendingLoad.catch(() => {});
|
|
112
|
+
return this._pendingLoad;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async _startLegacyHost(config) {
|
|
90
116
|
const configPath = path.join(os.tmpdir(), `atomjs-window-${process.pid}-${this.id}-${Date.now()}.json`);
|
|
91
117
|
await fs.promises.writeFile(configPath, JSON.stringify(config), { mode: 0o600 });
|
|
92
118
|
|
|
@@ -97,7 +123,7 @@ class BrowserWindow extends EventEmitter {
|
|
|
97
123
|
env: process.env,
|
|
98
124
|
stdio: ['ignore', 'pipe', 'inherit']
|
|
99
125
|
});
|
|
100
|
-
|
|
126
|
+
this._hostAttached = true;
|
|
101
127
|
attachHostOutput(this, this._child);
|
|
102
128
|
|
|
103
129
|
this._child.once('error', (error) => {
|
|
@@ -111,16 +137,22 @@ class BrowserWindow extends EventEmitter {
|
|
|
111
137
|
const failedBeforeReady = !this._rendererReady && code !== 0 && signal == null;
|
|
112
138
|
if (failedBeforeReady) {
|
|
113
139
|
process.exitCode = code || 1;
|
|
114
|
-
this.webContents.emit(
|
|
140
|
+
this.webContents.emit(
|
|
141
|
+
'did-fail-load',
|
|
142
|
+
{},
|
|
143
|
+
code || 1,
|
|
144
|
+
'AtomJS window host exited before the renderer became ready',
|
|
145
|
+
this._currentUrl,
|
|
146
|
+
true
|
|
147
|
+
);
|
|
115
148
|
}
|
|
116
|
-
this.
|
|
117
|
-
state.windows.delete(this.id);
|
|
118
|
-
this.emit('closed', { code, signal });
|
|
119
|
-
if (state.windows.size === 0 && !state.isQuitting) app.emit('window-all-closed');
|
|
149
|
+
this._finalizeClosed({ code, signal });
|
|
120
150
|
}
|
|
121
151
|
});
|
|
152
|
+
}
|
|
122
153
|
|
|
123
|
-
|
|
154
|
+
_createPendingLoad() {
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
124
156
|
const timeout = setTimeout(() => {
|
|
125
157
|
cleanup();
|
|
126
158
|
reject(new Error('Renderer did not become ready within 20 seconds'));
|
|
@@ -141,11 +173,14 @@ class BrowserWindow extends EventEmitter {
|
|
|
141
173
|
this.once('ready-to-show', onReady);
|
|
142
174
|
this.once('closed', onClosed);
|
|
143
175
|
});
|
|
176
|
+
}
|
|
144
177
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
178
|
+
_sendHostCommand(command) {
|
|
179
|
+
if (this._nativeHost) {
|
|
180
|
+
this._nativeHost.send({ ...command, windowId: this.id });
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
149
184
|
}
|
|
150
185
|
|
|
151
186
|
_markRendererReady(details) {
|
|
@@ -154,6 +189,29 @@ class BrowserWindow extends EventEmitter {
|
|
|
154
189
|
|
|
155
190
|
_handleHostEvent(event) {
|
|
156
191
|
if (!event || typeof event !== 'object') return;
|
|
192
|
+
|
|
193
|
+
if (event.type === 'closed') {
|
|
194
|
+
this._finalizeClosed({ code: 0, signal: null });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (event.type === 'focus') {
|
|
198
|
+
this.emit('focus');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (event.type === 'blur') {
|
|
202
|
+
this.emit('blur');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (event.type === 'minimize') {
|
|
206
|
+
this._minimized = true;
|
|
207
|
+
this.emit('minimize');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (event.type === 'restore') {
|
|
211
|
+
this._minimized = false;
|
|
212
|
+
this.emit('restore');
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
157
215
|
if (event.type === 'did-start-loading') {
|
|
158
216
|
if (event.url) this._currentUrl = String(event.url);
|
|
159
217
|
this.webContents.emit('did-start-loading');
|
|
@@ -179,10 +237,12 @@ class BrowserWindow extends EventEmitter {
|
|
|
179
237
|
if (href) this._currentUrl = href;
|
|
180
238
|
|
|
181
239
|
const now = Date.now();
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
240
|
+
if (
|
|
241
|
+
this._lastFinishedLoad &&
|
|
242
|
+
this._lastFinishedLoad.url === this._currentUrl &&
|
|
243
|
+
this._lastFinishedLoad.source !== source &&
|
|
244
|
+
now - this._lastFinishedLoad.time < 300
|
|
245
|
+
) {
|
|
186
246
|
return;
|
|
187
247
|
}
|
|
188
248
|
this._lastFinishedLoad = { url: this._currentUrl, time: now, source };
|
|
@@ -199,14 +259,26 @@ class BrowserWindow extends EventEmitter {
|
|
|
199
259
|
}
|
|
200
260
|
}
|
|
201
261
|
|
|
262
|
+
_finalizeClosed(details) {
|
|
263
|
+
if (this._destroyed) return;
|
|
264
|
+
this._destroyed = true;
|
|
265
|
+
this._hostAttached = false;
|
|
266
|
+
state.windows.delete(this.id);
|
|
267
|
+
this.emit('closed', details);
|
|
268
|
+
if (state.windows.size === 0 && !state.isQuitting) app.emit('window-all-closed');
|
|
269
|
+
}
|
|
270
|
+
|
|
202
271
|
show() {
|
|
203
272
|
this._visible = true;
|
|
273
|
+
this._sendHostCommand({ command: 'show' });
|
|
204
274
|
this.emit('show');
|
|
205
275
|
}
|
|
206
276
|
|
|
207
277
|
hide() {
|
|
208
278
|
this._visible = false;
|
|
209
|
-
|
|
279
|
+
if (!this._sendHostCommand({ command: 'hide' })) {
|
|
280
|
+
console.warn('[AtomJS] Native hide is not supported by the current platform host.');
|
|
281
|
+
}
|
|
210
282
|
this.emit('hide');
|
|
211
283
|
}
|
|
212
284
|
|
|
@@ -215,7 +287,9 @@ class BrowserWindow extends EventEmitter {
|
|
|
215
287
|
}
|
|
216
288
|
|
|
217
289
|
focus() {
|
|
218
|
-
|
|
290
|
+
if (!this._sendHostCommand({ command: 'focus' })) {
|
|
291
|
+
console.warn('[AtomJS] Native focus is not supported by the current platform host.');
|
|
292
|
+
}
|
|
219
293
|
}
|
|
220
294
|
|
|
221
295
|
blur() {}
|
|
@@ -225,6 +299,8 @@ class BrowserWindow extends EventEmitter {
|
|
|
225
299
|
const event = { defaultPrevented: false, preventDefault() { this.defaultPrevented = true; } };
|
|
226
300
|
this.emit('close', event);
|
|
227
301
|
if (event.defaultPrevented) return;
|
|
302
|
+
|
|
303
|
+
if (this._sendHostCommand({ command: 'close' })) return;
|
|
228
304
|
state.bridgeServer && state.bridgeServer.send(this.id, { type: 'system', command: 'close' });
|
|
229
305
|
setTimeout(() => {
|
|
230
306
|
if (!this._destroyed && this._child) this._child.kill();
|
|
@@ -233,13 +309,14 @@ class BrowserWindow extends EventEmitter {
|
|
|
233
309
|
|
|
234
310
|
destroy() {
|
|
235
311
|
if (this._destroyed) return;
|
|
236
|
-
this.
|
|
237
|
-
|
|
312
|
+
if (this._nativeHost) {
|
|
313
|
+
try { this._sendHostCommand({ command: 'destroy' }); } catch {}
|
|
314
|
+
}
|
|
238
315
|
if (this._child) {
|
|
239
316
|
try { this._child.kill(); } catch {}
|
|
240
317
|
this._child = null;
|
|
241
318
|
}
|
|
242
|
-
this.
|
|
319
|
+
this._finalizeClosed({ code: 0, signal: null });
|
|
243
320
|
}
|
|
244
321
|
|
|
245
322
|
isDestroyed() {
|
|
@@ -248,7 +325,7 @@ class BrowserWindow extends EventEmitter {
|
|
|
248
325
|
|
|
249
326
|
setTitle(title) {
|
|
250
327
|
this.options.title = String(title);
|
|
251
|
-
if (state.bridgeServer) {
|
|
328
|
+
if (!this._sendHostCommand({ command: 'set-title', title: this.options.title }) && state.bridgeServer) {
|
|
252
329
|
state.bridgeServer.send(this.id, { type: 'system', command: 'set-title', title: this.options.title });
|
|
253
330
|
}
|
|
254
331
|
}
|
|
@@ -257,7 +334,6 @@ class BrowserWindow extends EventEmitter {
|
|
|
257
334
|
return this.options.title || app.getName();
|
|
258
335
|
}
|
|
259
336
|
|
|
260
|
-
|
|
261
337
|
setMenu(menu) {
|
|
262
338
|
this._menu = menu || null;
|
|
263
339
|
return this;
|
|
@@ -286,51 +362,69 @@ class BrowserWindow extends EventEmitter {
|
|
|
286
362
|
setAutoHideMenuBar() {}
|
|
287
363
|
|
|
288
364
|
isFullScreen() {
|
|
289
|
-
return
|
|
365
|
+
return this._fullScreen;
|
|
290
366
|
}
|
|
291
367
|
|
|
292
|
-
setFullScreen() {
|
|
293
|
-
|
|
368
|
+
setFullScreen(value = true) {
|
|
369
|
+
this._fullScreen = Boolean(value);
|
|
370
|
+
if (!this._sendHostCommand({ command: 'fullscreen', value: this._fullScreen })) {
|
|
371
|
+
console.warn('[AtomJS] Native fullscreen switching is not supported by the current platform host.');
|
|
372
|
+
}
|
|
294
373
|
}
|
|
295
374
|
|
|
296
375
|
maximize() {
|
|
297
|
-
|
|
376
|
+
this._maximized = true;
|
|
377
|
+
if (!this._sendHostCommand({ command: 'maximize' })) {
|
|
378
|
+
console.warn('[AtomJS] Native maximize is not supported by the current platform host.');
|
|
379
|
+
}
|
|
298
380
|
}
|
|
299
381
|
|
|
300
|
-
unmaximize() {
|
|
382
|
+
unmaximize() {
|
|
383
|
+
this._maximized = false;
|
|
384
|
+
this._sendHostCommand({ command: 'unmaximize' });
|
|
385
|
+
}
|
|
301
386
|
|
|
302
387
|
isMaximized() {
|
|
303
|
-
return
|
|
388
|
+
return this._maximized;
|
|
304
389
|
}
|
|
305
390
|
|
|
306
391
|
minimize() {
|
|
307
|
-
|
|
392
|
+
this._minimized = true;
|
|
393
|
+
if (!this._sendHostCommand({ command: 'minimize' })) {
|
|
394
|
+
console.warn('[AtomJS] Native minimize is not supported by the current platform host.');
|
|
395
|
+
}
|
|
308
396
|
}
|
|
309
397
|
|
|
310
|
-
restore() {
|
|
398
|
+
restore() {
|
|
399
|
+
this._minimized = false;
|
|
400
|
+
this._sendHostCommand({ command: 'restore' });
|
|
401
|
+
}
|
|
311
402
|
|
|
312
403
|
isMinimized() {
|
|
313
|
-
return
|
|
404
|
+
return this._minimized;
|
|
314
405
|
}
|
|
315
406
|
|
|
316
407
|
setSize(width, height) {
|
|
317
|
-
this.
|
|
318
|
-
this.options.height = Number(height);
|
|
319
|
-
console.warn('[AtomJS] Changing the native window size after creation is not yet supported.');
|
|
408
|
+
this.setBounds({ width, height });
|
|
320
409
|
}
|
|
321
410
|
|
|
322
411
|
getSize() {
|
|
323
|
-
return [this.
|
|
412
|
+
return [this._bounds.width, this._bounds.height];
|
|
324
413
|
}
|
|
325
414
|
|
|
326
415
|
setBounds(bounds) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
416
|
+
const next = { ...this._bounds };
|
|
417
|
+
for (const key of ['x', 'y', 'width', 'height']) {
|
|
418
|
+
if (Number.isFinite(Number(bounds?.[key]))) next[key] = Number(bounds[key]);
|
|
419
|
+
}
|
|
420
|
+
this._bounds = next;
|
|
421
|
+
if (!this._sendHostCommand({ command: 'set-bounds', bounds: next })) {
|
|
422
|
+
console.warn('[AtomJS] Changing native bounds is not supported by the current platform host.');
|
|
423
|
+
}
|
|
330
424
|
}
|
|
331
425
|
|
|
332
426
|
getBounds() {
|
|
333
|
-
return {
|
|
427
|
+
return { ...this._bounds };
|
|
334
428
|
}
|
|
335
429
|
|
|
336
430
|
reload() {
|
|
@@ -372,7 +466,7 @@ function attachHostOutput(win, child) {
|
|
|
372
466
|
function handleHostLine(win, line) {
|
|
373
467
|
const prefix = '__ATOMJS_EVENT__';
|
|
374
468
|
if (!line.startsWith(prefix)) {
|
|
375
|
-
if (line) process.stdout.write(line
|
|
469
|
+
if (line) process.stdout.write(`${line}\n`);
|
|
376
470
|
return;
|
|
377
471
|
}
|
|
378
472
|
try {
|
package/src/dialog.cjs
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
const { execFile } = require('node:child_process');
|
|
4
4
|
const { promisify } = require('node:util');
|
|
5
|
+
const app = require('./app.cjs');
|
|
6
|
+
const { getNativeHost } = require('./native-host.cjs');
|
|
7
|
+
|
|
5
8
|
const execFileAsync = promisify(execFile);
|
|
6
9
|
|
|
7
|
-
async function showOpenDialog(
|
|
8
|
-
const options = normalizeOptions(
|
|
10
|
+
async function showOpenDialog(browserWindowOrOptions, maybeOptions) {
|
|
11
|
+
const options = normalizeOptions(browserWindowOrOptions, maybeOptions);
|
|
12
|
+
|
|
13
|
+
if (process.platform === 'darwin') {
|
|
14
|
+
return macOSRequest('dialog-open', options);
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
try {
|
|
10
18
|
const filePaths = await openDialogForPlatform(options);
|
|
11
19
|
return { canceled: filePaths.length === 0, filePaths };
|
|
@@ -15,8 +23,17 @@ async function showOpenDialog(_browserWindowOrOptions, maybeOptions) {
|
|
|
15
23
|
}
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
async function showSaveDialog(
|
|
19
|
-
const options = normalizeOptions(
|
|
26
|
+
async function showSaveDialog(browserWindowOrOptions, maybeOptions) {
|
|
27
|
+
const options = normalizeOptions(browserWindowOrOptions, maybeOptions);
|
|
28
|
+
|
|
29
|
+
if (process.platform === 'darwin') {
|
|
30
|
+
const result = await macOSRequest('dialog-save', options);
|
|
31
|
+
return {
|
|
32
|
+
canceled: Boolean(result.canceled),
|
|
33
|
+
filePath: result.filePath || undefined
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
20
37
|
try {
|
|
21
38
|
const filePath = await saveDialogForPlatform(options);
|
|
22
39
|
return { canceled: !filePath, filePath: filePath || undefined };
|
|
@@ -26,19 +43,21 @@ async function showSaveDialog(_browserWindowOrOptions, maybeOptions) {
|
|
|
26
43
|
}
|
|
27
44
|
}
|
|
28
45
|
|
|
29
|
-
async function showMessageBox(
|
|
30
|
-
const options = normalizeOptions(
|
|
46
|
+
async function showMessageBox(browserWindowOrOptions, maybeOptions) {
|
|
47
|
+
const options = normalizeOptions(browserWindowOrOptions, maybeOptions);
|
|
31
48
|
const title = options.title || 'AtomJS';
|
|
32
49
|
const message = options.message || '';
|
|
33
50
|
|
|
51
|
+
if (process.platform === 'darwin') {
|
|
52
|
+
return macOSRequest('dialog-message', { ...options, title, message });
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
if (process.platform === 'win32') {
|
|
35
56
|
const script = [
|
|
36
57
|
'Add-Type -AssemblyName PresentationFramework',
|
|
37
58
|
`[System.Windows.MessageBox]::Show(${psQuote(message)}, ${psQuote(title)}) | Out-Null`
|
|
38
59
|
].join('; ');
|
|
39
60
|
await execFileAsync('powershell.exe', ['-NoProfile', '-STA', '-Command', script]);
|
|
40
|
-
} else if (process.platform === 'darwin') {
|
|
41
|
-
await execFileAsync('osascript', ['-e', `display dialog ${appleQuote(message)} with title ${appleQuote(title)} buttons {"OK"} default button "OK"`]);
|
|
42
61
|
} else {
|
|
43
62
|
await execFileAsync('zenity', ['--info', `--title=${title}`, `--text=${message}`]);
|
|
44
63
|
}
|
|
@@ -46,6 +65,11 @@ async function showMessageBox(_browserWindowOrOptions, maybeOptions) {
|
|
|
46
65
|
return { response: 0, checkboxChecked: false };
|
|
47
66
|
}
|
|
48
67
|
|
|
68
|
+
async function macOSRequest(command, options) {
|
|
69
|
+
const host = getNativeHost(app.getName());
|
|
70
|
+
return host.request({ command, options });
|
|
71
|
+
}
|
|
72
|
+
|
|
49
73
|
async function openDialogForPlatform(options) {
|
|
50
74
|
const multiple = Array.isArray(options.properties) && options.properties.includes('multiSelections');
|
|
51
75
|
|
|
@@ -63,15 +87,6 @@ async function openDialogForPlatform(options) {
|
|
|
63
87
|
return splitLines(stdout);
|
|
64
88
|
}
|
|
65
89
|
|
|
66
|
-
if (process.platform === 'darwin') {
|
|
67
|
-
const prompt = options.title || 'Choose a file';
|
|
68
|
-
const script = multiple
|
|
69
|
-
? `set chosenFiles to choose file with prompt ${appleQuote(prompt)} with multiple selections allowed\nset output to ""\nrepeat with f in chosenFiles\nset output to output & POSIX path of f & linefeed\nend repeat\nreturn output`
|
|
70
|
-
: `POSIX path of (choose file with prompt ${appleQuote(prompt)})`;
|
|
71
|
-
const { stdout } = await execFileAsync('osascript', ['-e', script], { encoding: 'utf8' });
|
|
72
|
-
return splitLines(stdout);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
90
|
const args = ['--file-selection'];
|
|
76
91
|
if (options.title) args.push(`--title=${options.title}`);
|
|
77
92
|
if (options.defaultPath) args.push(`--filename=${options.defaultPath}`);
|
|
@@ -94,13 +109,6 @@ async function saveDialogForPlatform(options) {
|
|
|
94
109
|
return stdout.trim();
|
|
95
110
|
}
|
|
96
111
|
|
|
97
|
-
if (process.platform === 'darwin') {
|
|
98
|
-
const defaultName = options.defaultPath ? String(options.defaultPath).split(/[\\/]/).pop() : 'Untitled';
|
|
99
|
-
const script = `POSIX path of (choose file name with prompt ${appleQuote(options.title || 'Save file')} default name ${appleQuote(defaultName)})`;
|
|
100
|
-
const { stdout } = await execFileAsync('osascript', ['-e', script], { encoding: 'utf8' });
|
|
101
|
-
return stdout.trim();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
112
|
const args = ['--file-selection', '--save', '--confirm-overwrite'];
|
|
105
113
|
if (options.title) args.push(`--title=${options.title}`);
|
|
106
114
|
if (options.defaultPath) args.push(`--filename=${options.defaultPath}`);
|
|
@@ -134,8 +142,4 @@ function psQuote(value) {
|
|
|
134
142
|
return `'${String(value).replace(/'/g, "''")}'`;
|
|
135
143
|
}
|
|
136
144
|
|
|
137
|
-
function appleQuote(value) {
|
|
138
|
-
return JSON.stringify(String(value));
|
|
139
|
-
}
|
|
140
|
-
|
|
141
145
|
module.exports = { showOpenDialog, showSaveDialog, showMessageBox };
|