positron.js 1.0.3 → 1.0.5
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/bin/positron.js +6 -1
- package/builder.js +24 -2
- package/core/mac/main.swift +208 -22
- package/core/win/PositronRuntime.csproj +1 -1
- package/core/win/main.cs +196 -19
- package/index.js +239 -42
- package/package.json +9 -2
- package/packager.js +30 -7
package/bin/positron.js
CHANGED
|
@@ -5,6 +5,7 @@ const performPackager = require("../packager");
|
|
|
5
5
|
const { spawn } = require("child_process");
|
|
6
6
|
const [, , command] = process.argv;
|
|
7
7
|
const { info, success, error } = require("../logs");
|
|
8
|
+
const fs = require("fs");
|
|
8
9
|
|
|
9
10
|
switch (command) {
|
|
10
11
|
case "build":
|
|
@@ -15,7 +16,11 @@ switch (command) {
|
|
|
15
16
|
|
|
16
17
|
case "dev":
|
|
17
18
|
info("Starting Positron in development mode...");
|
|
18
|
-
performNativeBuild();
|
|
19
|
+
const buildSuccess = performNativeBuild();
|
|
20
|
+
if (!buildSuccess) {
|
|
21
|
+
error("Development build failed. Please fix the errors and try again.");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
19
24
|
spawn("node", ["."], { stdio: "inherit" });
|
|
20
25
|
break;
|
|
21
26
|
|
package/builder.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const cp = require("child_process");
|
|
4
4
|
const { success, error, info, warn } = require("./logs");
|
|
5
|
+
const semver = require("semver");
|
|
5
6
|
|
|
6
7
|
const arch = process.argv.includes("--x64") ? "x64" : process.argv.includes("--arm64") ? "arm64" : process.arch;
|
|
7
8
|
|
|
@@ -30,6 +31,18 @@ function performNativeBuild() {
|
|
|
30
31
|
if (depPackage.positron) {
|
|
31
32
|
const depDir = path.dirname(depPackagePath);
|
|
32
33
|
|
|
34
|
+
if(depPackage.positron.requiredVersion) {
|
|
35
|
+
const requiredVersion = depPackage.positron.requiredVersion;
|
|
36
|
+
const rootVersion = rootPackage.dependencies["positron.js"];
|
|
37
|
+
if(rootVersion.startsWith("file:")) {
|
|
38
|
+
warn(`[Builder] Dependency "${dep}" specifies a required positron.js version of ${requiredVersion}, but the project is using a local file reference. Skipping version compatibility check for this dependency.`);
|
|
39
|
+
} else {
|
|
40
|
+
if(!semver.satisfies(rootVersion, requiredVersion)) {
|
|
41
|
+
warn(`[Builder] Dependency "${dep}" requires positron.js version ${requiredVersion}, but the project has version ${rootVersion}. This may lead to compatibility issues.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
let missing = [];
|
|
34
47
|
if(!depPackage.positron.className) missing.push("className");
|
|
35
48
|
if(!depPackage.positron.command) missing.push("command");
|
|
@@ -144,10 +157,19 @@ function performNativeBuild() {
|
|
|
144
157
|
});
|
|
145
158
|
|
|
146
159
|
try {
|
|
147
|
-
|
|
160
|
+
let swiftScript = "";
|
|
161
|
+
if(fs.existsSync(path.join(appRoot, "icon.icns"))) {
|
|
162
|
+
const iconPathEscaped = path.join(appRoot, "icon.icns").replace(/"/g, '\\"');
|
|
148
163
|
const binPathEscaped = path.join(outBinaryDir, binaryName).replace(/"/g, '\\"');
|
|
149
|
-
|
|
164
|
+
swiftScript = `import Cocoa; NSWorkspace.shared.setIcon(NSImage(contentsOfFile: "${iconPathEscaped}"), forFile: "${binPathEscaped}", options: []); `;
|
|
150
165
|
cp.execFileSync("swift", ["-e", swiftScript], { stdio: "ignore" });
|
|
166
|
+
return true;
|
|
167
|
+
} else if(fs.existsSync(path.join(__dirname, ['positronicon', 'png'].join('.')))) {
|
|
168
|
+
const iconPathEscaped = path.join(__dirname, ['positronicon', 'png'].join('.')).replace(/"/g, '\\"');
|
|
169
|
+
const binPathEscaped = path.join(outBinaryDir, binaryName).replace(/"/g, '\\"');
|
|
170
|
+
swiftScript = `import Cocoa; NSWorkspace.shared.setIcon(NSImage(contentsOfFile: "${iconPathEscaped}"), forFile: "${binPathEscaped}", options: []);`;
|
|
171
|
+
}
|
|
172
|
+
cp.execFileSync("swift", ["-e", swiftScript], { stdio: "ignore" });
|
|
151
173
|
} catch (err) {
|
|
152
174
|
error("Failed to set custom icon on native binary:", err);
|
|
153
175
|
}
|
package/core/mac/main.swift
CHANGED
|
@@ -3,6 +3,9 @@ import WebKit
|
|
|
3
3
|
import Network
|
|
4
4
|
import Darwin
|
|
5
5
|
import UserNotifications
|
|
6
|
+
import Foundation
|
|
7
|
+
import IOKit.pwr_mgt
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
// MARK: - Globals
|
|
8
11
|
|
|
@@ -20,8 +23,31 @@ let AUTH_TOKEN: String = {
|
|
|
20
23
|
var windowObservations: [Int: NSKeyValueObservation] = [:]
|
|
21
24
|
var navigationDelegates: [Int: WebViewNavigationDelegate] = [:]
|
|
22
25
|
|
|
26
|
+
private var assertionID: IOPMAssertionID?
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
func blockPowerSave() {
|
|
29
|
+
guard assertionID == nil else { return }
|
|
30
|
+
|
|
31
|
+
var id: IOPMAssertionID = 0
|
|
32
|
+
|
|
33
|
+
let result = IOPMAssertionCreateWithName(
|
|
34
|
+
kIOPMAssertionTypePreventUserIdleSystemSleep as CFString,
|
|
35
|
+
IOPMAssertionLevel(kIOPMAssertionLevelOn),
|
|
36
|
+
"Power Save Blocked" as CFString,
|
|
37
|
+
&id
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if result == kIOReturnSuccess {
|
|
41
|
+
assertionID = id
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func unblockPowerSave() {
|
|
46
|
+
guard let id = assertionID else { return }
|
|
47
|
+
|
|
48
|
+
IOPMAssertionRelease(id)
|
|
49
|
+
assertionID = nil
|
|
50
|
+
}
|
|
25
51
|
|
|
26
52
|
final class PositronWebView: WKWebView {
|
|
27
53
|
override func rightMouseDown(with event: NSEvent) {
|
|
@@ -141,13 +167,9 @@ func getBuiltInHandlers() -> [String: (Int, [String]) -> Void] {
|
|
|
141
167
|
return
|
|
142
168
|
}
|
|
143
169
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
webView.perform(selector, with: nil)
|
|
147
|
-
} else {
|
|
148
|
-
printError("openDevTools failed: _showDeveloperTools: selector not found on WKWebView (windowId \(windowId))")
|
|
170
|
+
let inspector = webView.value(forKey: "inspector") as? NSObject
|
|
171
|
+
inspector?.perform(NSSelectorFromString("show"))
|
|
149
172
|
}
|
|
150
|
-
},
|
|
151
173
|
]
|
|
152
174
|
|
|
153
175
|
return baseHandlers + getExtensionRegistry()
|
|
@@ -292,6 +314,13 @@ func handleCommand(windowId: Int, command: String, args: [String]) {
|
|
|
292
314
|
|
|
293
315
|
window.performClose(nil)
|
|
294
316
|
|
|
317
|
+
case "isFullscreen":
|
|
318
|
+
guard let window = windows[windowId] else { return }
|
|
319
|
+
let isFullscreen = window.styleMask.contains(.fullScreen)
|
|
320
|
+
AppDelegate.shared?.ipcClient.send(
|
|
321
|
+
IPCResponse(windowId: windowId, event: args.last ?? "isFullscreen-reply-\(windowId)", data: ["isFullscreen": isFullscreen ? "true" : "false"])
|
|
322
|
+
)
|
|
323
|
+
|
|
295
324
|
case "setSwipeNav":
|
|
296
325
|
guard let window = windows[windowId],
|
|
297
326
|
let webView = window.contentView as? WKWebView else {
|
|
@@ -302,7 +331,7 @@ case "setSwipeNav":
|
|
|
302
331
|
webView.allowsBackForwardNavigationGestures = enable
|
|
303
332
|
|
|
304
333
|
GetIPCClient().send(
|
|
305
|
-
IPCResponse(windowId: windowId, event: "setSwipeNav-reply-\(windowId)", data: ["enabled": enable ? "true" : "false"])
|
|
334
|
+
IPCResponse(windowId: windowId, event: args.last ?? "setSwipeNav-reply-\(windowId)", data: ["enabled": enable ? "true" : "false"])
|
|
306
335
|
)
|
|
307
336
|
|
|
308
337
|
case "forceCloseWindow":
|
|
@@ -399,7 +428,7 @@ case "forceCloseWindow":
|
|
|
399
428
|
let frame = window.frame
|
|
400
429
|
let bounds = ["x": "\(Int(frame.origin.x))", "y": "\(Int(frame.origin.y))", "width": "\(Int(frame.size.width))", "height": "\(Int(frame.size.height))"]
|
|
401
430
|
AppDelegate.shared?.ipcClient.send(
|
|
402
|
-
IPCResponse(windowId: windowId, event: "getBounds-reply-\(windowId)", data: bounds)
|
|
431
|
+
IPCResponse(windowId: windowId, event: args.last ?? "getBounds-reply-\(windowId)", data: bounds)
|
|
403
432
|
)
|
|
404
433
|
|
|
405
434
|
case "setResizable":
|
|
@@ -458,16 +487,64 @@ case "forceCloseWindow":
|
|
|
458
487
|
guard let window = windows[windowId] else { return }
|
|
459
488
|
let canGoBack = (window.contentView as? WKWebView)?.canGoBack ?? false
|
|
460
489
|
AppDelegate.shared?.ipcClient.send(
|
|
461
|
-
IPCResponse(windowId: windowId, event: "canGoBack-reply-\(windowId)", data: ["canGoBack": canGoBack ? "true" : "false"])
|
|
490
|
+
IPCResponse(windowId: windowId, event: args.last ?? "canGoBack-reply-\(windowId)", data: ["canGoBack": canGoBack ? "true" : "false"])
|
|
462
491
|
)
|
|
463
492
|
|
|
464
493
|
case "canGoForward":
|
|
465
494
|
guard let window = windows[windowId] else { return }
|
|
466
495
|
let canGoForward = (window.contentView as? WKWebView)?.canGoForward ?? false
|
|
467
496
|
AppDelegate.shared?.ipcClient.send(
|
|
468
|
-
IPCResponse(windowId: windowId, event: "canGoForward-reply-\(windowId)", data: ["canGoForward": canGoForward ? "true" : "false"])
|
|
497
|
+
IPCResponse(windowId: windowId, event: args.last ?? "canGoForward-reply-\(windowId)", data: ["canGoForward": canGoForward ? "true" : "false"])
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
case "showFileOpenDialog":
|
|
501
|
+
guard let window = windows[windowId] else { return }
|
|
502
|
+
let panel = NSOpenPanel()
|
|
503
|
+
panel.allowsMultipleSelection = false
|
|
504
|
+
panel.canChooseDirectories = false
|
|
505
|
+
panel.canChooseFiles = true
|
|
506
|
+
panel.beginSheetModal(for: window) { response in
|
|
507
|
+
if response == .OK, let url = panel.url {
|
|
508
|
+
AppDelegate.shared?.ipcClient.send(
|
|
509
|
+
IPCResponse(windowId: windowId, event: args.last ?? "showFileOpenDialog-reply-\(windowId)", data: ["filePath": url.path])
|
|
510
|
+
)
|
|
511
|
+
} else {
|
|
512
|
+
AppDelegate.shared?.ipcClient.send(
|
|
513
|
+
IPCResponse(windowId: windowId, event: args.last ?? "showFileOpenDialog-reply-\(windowId)", data: ["filePath": ""])
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
case "readFromClipboard":
|
|
519
|
+
let pasteboard = NSPasteboard.general
|
|
520
|
+
let clipboardText = pasteboard.string(forType: .string) ?? "notext"
|
|
521
|
+
AppDelegate.shared?.ipcClient.send(
|
|
522
|
+
IPCResponse(windowId: windowId, event: args.last ?? "readFromClipboard-reply-\(windowId)", data: ["text": clipboardText])
|
|
469
523
|
)
|
|
470
524
|
|
|
525
|
+
case "blockPowerSave":
|
|
526
|
+
blockPowerSave()
|
|
527
|
+
|
|
528
|
+
case "unblockPowerSave":
|
|
529
|
+
unblockPowerSave()
|
|
530
|
+
|
|
531
|
+
case "isDarkMode":
|
|
532
|
+
guard let window = windows[windowId] else { return }
|
|
533
|
+
let isDarkMode = window.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
|
|
534
|
+
AppDelegate.shared?.ipcClient.send(
|
|
535
|
+
IPCResponse(windowId: windowId, event: args.last ?? "isDarkMode-reply-\(windowId)", data: ["isDarkMode": isDarkMode ? "true" : "false"])
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
case "writeToClipboard":
|
|
540
|
+
guard let text = args.first else {
|
|
541
|
+
printError("writeToClipboard — missing text argument")
|
|
542
|
+
return
|
|
543
|
+
}
|
|
544
|
+
let pasteboard = NSPasteboard.general
|
|
545
|
+
pasteboard.clearContents()
|
|
546
|
+
pasteboard.setString(text, forType: .string)
|
|
547
|
+
|
|
471
548
|
case "showNotification":
|
|
472
549
|
|
|
473
550
|
UNUserNotificationCenter.current().requestAuthorization(
|
|
@@ -498,14 +575,14 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
498
575
|
guard let window = windows[windowId] else { return }
|
|
499
576
|
let url = (window.contentView as? WKWebView)?.url?.absoluteString ?? ""
|
|
500
577
|
AppDelegate.shared?.ipcClient.send(
|
|
501
|
-
IPCResponse(windowId: windowId, event: "getURL-reply-\(windowId)", data: ["url": url])
|
|
578
|
+
IPCResponse(windowId: windowId, event: args.last ?? "getURL-reply-\(windowId)", data: ["url": url])
|
|
502
579
|
)
|
|
503
580
|
|
|
504
581
|
case "getTitle":
|
|
505
582
|
guard let window = windows[windowId] else { return }
|
|
506
583
|
let title = window.title
|
|
507
584
|
AppDelegate.shared?.ipcClient.send(
|
|
508
|
-
IPCResponse(windowId: windowId, event: "getTitle-reply-\(windowId)", data: ["title": title])
|
|
585
|
+
IPCResponse(windowId: windowId, event: args.last ?? "getTitle-reply-\(windowId)", data: ["title": title])
|
|
509
586
|
)
|
|
510
587
|
|
|
511
588
|
case "executeAppleScript":
|
|
@@ -521,6 +598,97 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
521
598
|
printError("executeAppleScript failed: \(errorMessage)")
|
|
522
599
|
}
|
|
523
600
|
|
|
601
|
+
case "isVisible":
|
|
602
|
+
guard let window = windows[windowId] else { return }
|
|
603
|
+
let isVisible = window.isVisible
|
|
604
|
+
AppDelegate.shared?.ipcClient.send(
|
|
605
|
+
IPCResponse(windowId: windowId, event: args.last ?? "isVisible-reply-\(windowId)", data: ["isVisible": isVisible ? "true" : "false"])
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
case "addToContentBlocker":
|
|
609
|
+
guard let window = windows[windowId],
|
|
610
|
+
let webView = window.contentView as? WKWebView,
|
|
611
|
+
let input = args.first
|
|
612
|
+
else {
|
|
613
|
+
printError("addToContentBlocker — missing rules")
|
|
614
|
+
return
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
let jsonStr: String
|
|
618
|
+
|
|
619
|
+
if FileManager.default.fileExists(atPath: input) {
|
|
620
|
+
do {
|
|
621
|
+
jsonStr = try String(contentsOfFile: input, encoding: .utf8)
|
|
622
|
+
} catch {
|
|
623
|
+
printError("Failed to read rule file: \(error.localizedDescription)")
|
|
624
|
+
return
|
|
625
|
+
}
|
|
626
|
+
} else {
|
|
627
|
+
jsonStr = input
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
guard let data = jsonStr.data(using: .utf8),
|
|
631
|
+
(try? JSONSerialization.jsonObject(with: data)) != nil
|
|
632
|
+
else {
|
|
633
|
+
printError("addToContentBlocker — invalid JSON rules")
|
|
634
|
+
return
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
let reload = args.count > 1
|
|
638
|
+
? args[1].lowercased() == "true"
|
|
639
|
+
: true
|
|
640
|
+
|
|
641
|
+
let clearAll = args.count > 2
|
|
642
|
+
? args[2].lowercased() == "true"
|
|
643
|
+
: false
|
|
644
|
+
|
|
645
|
+
let identifier = "dynamicRules-\(windowId)-\(UUID().uuidString)"
|
|
646
|
+
|
|
647
|
+
WKContentRuleListStore.default().compileContentRuleList(
|
|
648
|
+
forIdentifier: identifier,
|
|
649
|
+
encodedContentRuleList: jsonStr
|
|
650
|
+
) { [weak webView] ruleList, error in
|
|
651
|
+
|
|
652
|
+
guard let webView else { return }
|
|
653
|
+
|
|
654
|
+
if let error {
|
|
655
|
+
printError("Failed to compile content blocker rules: \(error.localizedDescription)")
|
|
656
|
+
return
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
guard let ruleList else {
|
|
660
|
+
printError("Failed to compile content blocker rules: no rule list returned")
|
|
661
|
+
return
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
let controller = webView.configuration.userContentController
|
|
665
|
+
|
|
666
|
+
if clearAll {
|
|
667
|
+
controller.removeAllContentRuleLists()
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
controller.add(ruleList)
|
|
671
|
+
|
|
672
|
+
if reload {
|
|
673
|
+
webView.reload()
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
GetIPCClient().send(
|
|
678
|
+
IPCResponse(windowId: windowId, event: args.last ?? "addToContentBlocker-reply-\(windowId)", data: ["status": "success"])
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
case "isSwipeNavEnabled":
|
|
682
|
+
guard let window = windows[windowId],
|
|
683
|
+
let webView = window.contentView as? WKWebView else {
|
|
684
|
+
printError("isSwipeNavEnabled — webview not found for window \(windowId)")
|
|
685
|
+
return
|
|
686
|
+
}
|
|
687
|
+
let enabled = webView.allowsBackForwardNavigationGestures
|
|
688
|
+
AppDelegate.shared?.ipcClient.send(
|
|
689
|
+
IPCResponse(windowId: windowId, event: args.last ?? "isSwipeNavEnabled-reply-\(windowId)", data: ["enabled": enabled ? "true" : "false"])
|
|
690
|
+
)
|
|
691
|
+
|
|
524
692
|
case "forward":
|
|
525
693
|
guard let window = windows[windowId] else { return }
|
|
526
694
|
(window.contentView as? WKWebView)?.goForward()
|
|
@@ -604,7 +772,7 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
604
772
|
resultStr = "null"
|
|
605
773
|
}
|
|
606
774
|
AppDelegate.shared?.ipcClient.send(
|
|
607
|
-
IPCResponse(windowId: windowId, event: "evaluateJS-reply-\(windowId)", data: ["result": resultStr])
|
|
775
|
+
IPCResponse(windowId: windowId, event: args.last ?? "evaluateJS-reply-\(windowId)", data: ["result": resultStr])
|
|
608
776
|
)
|
|
609
777
|
}
|
|
610
778
|
|
|
@@ -630,11 +798,11 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
630
798
|
if response == .alertFirstButtonReturn {
|
|
631
799
|
let userInput = inputField.stringValue
|
|
632
800
|
AppDelegate.shared?.ipcClient.send(
|
|
633
|
-
IPCResponse(windowId: windowId, event: "prompt-reply-\(windowId)", data: ["input": userInput])
|
|
801
|
+
IPCResponse(windowId: windowId, event: args.last ?? "prompt-reply-\(windowId)", data: ["input": userInput])
|
|
634
802
|
)
|
|
635
803
|
} else {
|
|
636
804
|
AppDelegate.shared?.ipcClient.send(
|
|
637
|
-
IPCResponse(windowId: windowId, event: "prompt-reply-\(windowId)", data: ["input": ""])
|
|
805
|
+
IPCResponse(windowId: windowId, event: args.last ?? "prompt-reply-\(windowId)", data: ["input": ""])
|
|
638
806
|
)
|
|
639
807
|
}
|
|
640
808
|
}
|
|
@@ -653,7 +821,7 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
653
821
|
alert.beginSheetModal(for: window) { response in
|
|
654
822
|
let confirmed = (response == .alertFirstButtonReturn)
|
|
655
823
|
AppDelegate.shared?.ipcClient.send(
|
|
656
|
-
IPCResponse(windowId: windowId, event: "confirm-reply-\(windowId)", data: ["confirmed": confirmed ? "true" : "false"])
|
|
824
|
+
IPCResponse(windowId: windowId, event: args.last ?? "confirm-reply-\(windowId)", data: ["confirmed": confirmed ? "true" : "false"])
|
|
657
825
|
)
|
|
658
826
|
}
|
|
659
827
|
|
|
@@ -661,7 +829,7 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
661
829
|
guard let window = windows[windowId] else { return }
|
|
662
830
|
let isFocused = window.isKeyWindow
|
|
663
831
|
AppDelegate.shared?.ipcClient.send(
|
|
664
|
-
IPCResponse(windowId: windowId, event: "isFocused-reply-\(windowId)", data: ["isFocused": isFocused ? "true" : "false"])
|
|
832
|
+
IPCResponse(windowId: windowId, event: args.last ?? "isFocused-reply-\(windowId)", data: ["isFocused": isFocused ? "true" : "false"])
|
|
665
833
|
)
|
|
666
834
|
|
|
667
835
|
case "emitToRenderer":
|
|
@@ -749,7 +917,7 @@ final class WebViewNavigationDelegate: NSObject, WKNavigationDelegate {
|
|
|
749
917
|
let isFile = webView.url?.isFileURL ?? false
|
|
750
918
|
let eventName = isFile ? "loadFile-reply-\(windowId)" : "loadURL-reply-\(windowId)"
|
|
751
919
|
AppDelegate.shared?.ipcClient.send(
|
|
752
|
-
IPCResponse(windowId: windowId, event: eventName, data: [:])
|
|
920
|
+
IPCResponse(windowId: windowId, event: eventName, data: ["url": webView.url?.absoluteString ?? "", "title": webView.title ?? "", "canGoBack": (webView.canGoBack ? "true" : "false"), "canGoForward": (webView.canGoForward ? "true" : "false")])
|
|
753
921
|
)
|
|
754
922
|
}
|
|
755
923
|
}
|
|
@@ -994,8 +1162,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|
|
994
1162
|
|
|
995
1163
|
cd "\(resourcePath)"
|
|
996
1164
|
|
|
997
|
-
|
|
998
|
-
|
|
1165
|
+
BACKEND_BIN=$(find . -maxdepth 1 -name "*-backend" | head -n 1)
|
|
1166
|
+
if [ -n "$BACKEND_BIN" ] && [ -f "$BACKEND_BIN" ]; then
|
|
1167
|
+
exec "$BACKEND_BIN"
|
|
999
1168
|
else
|
|
1000
1169
|
exec "node" "."
|
|
1001
1170
|
fi
|
|
@@ -1067,6 +1236,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|
|
1067
1236
|
editMenu.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v")
|
|
1068
1237
|
editMenu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
|
|
1069
1238
|
|
|
1239
|
+
// Window menu
|
|
1240
|
+
let windowMenuItem = NSMenuItem()
|
|
1241
|
+
mainMenu.addItem(windowMenuItem)
|
|
1242
|
+
let windowMenu = NSMenu(title: "Window")
|
|
1243
|
+
windowMenuItem.submenu = windowMenu
|
|
1244
|
+
windowMenu.addItem(withTitle: "Minimize", action: #selector(NSWindow.miniaturize(_:)), keyEquivalent: "m")
|
|
1245
|
+
windowMenu.addItem(withTitle: "Zoom", action: #selector(NSWindow.zoom(_:)), keyEquivalent: "")
|
|
1246
|
+
NSApp.windowsMenu = windowMenu
|
|
1247
|
+
|
|
1070
1248
|
NSApp.mainMenu = mainMenu
|
|
1071
1249
|
}
|
|
1072
1250
|
}
|
|
@@ -1145,6 +1323,12 @@ func buildMenu(from descriptor: [[String: Any]], windowId: Int) -> NSMenu {
|
|
|
1145
1323
|
let sub = NSMenu(title: topItem.title)
|
|
1146
1324
|
topItem.submenu = sub
|
|
1147
1325
|
|
|
1326
|
+
if let role = topLevel["role"] as? String, role.lowercased() == "window" {
|
|
1327
|
+
NSApp.windowsMenu = sub
|
|
1328
|
+
} else if topItem.title.lowercased() == "window" {
|
|
1329
|
+
NSApp.windowsMenu = sub
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1148
1332
|
if let items = topLevel["items"] as? [[String: Any]] {
|
|
1149
1333
|
populateMenu(sub, with: items, windowId: windowId, isContextMenu: false)
|
|
1150
1334
|
}
|
|
@@ -1202,20 +1386,22 @@ setbuf(__stdoutp, nil)
|
|
|
1202
1386
|
setbuf(__stderrp, nil)
|
|
1203
1387
|
|
|
1204
1388
|
signal(SIGINT) { _ in
|
|
1205
|
-
printError("INFO: Received SIGINT, shutting down…")
|
|
1206
1389
|
AppDelegate.shared?.nodeProcess?.terminate()
|
|
1390
|
+
unblockPowerSave()
|
|
1207
1391
|
exit(0)
|
|
1208
1392
|
}
|
|
1209
1393
|
|
|
1210
1394
|
signal(SIGTERM) { _ in
|
|
1211
1395
|
printError("INFO: Received SIGTERM, shutting down…")
|
|
1212
1396
|
AppDelegate.shared?.nodeProcess?.terminate()
|
|
1397
|
+
unblockPowerSave()
|
|
1213
1398
|
exit(0)
|
|
1214
1399
|
}
|
|
1215
1400
|
|
|
1216
1401
|
signal(SIGSEGV) { _ in
|
|
1217
1402
|
printError("ERROR: Caught SIGSEGV (segmentation fault). This likely indicates a bug in the native code. Attempting to shut down gracefully…")
|
|
1218
1403
|
AppDelegate.shared?.nodeProcess?.terminate()
|
|
1404
|
+
unblockPowerSave()
|
|
1219
1405
|
signal(SIGSEGV, SIG_DFL)
|
|
1220
1406
|
raise(SIGSEGV)
|
|
1221
1407
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
2
|
<PropertyGroup>
|
|
3
3
|
<OutputType>WinExe</OutputType>
|
|
4
|
-
<TargetFramework>
|
|
4
|
+
<TargetFramework>net10.0-windows</TargetFramework>
|
|
5
5
|
<Nullable>enable</Nullable>
|
|
6
6
|
<UseWpf>true</UseWpf>
|
|
7
7
|
<UseWindowsForms>true</UseWindowsForms>
|