positron.js 1.0.1 → 1.0.3
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 +1 -1
- package/builder.js +1 -1
- package/core/mac/main.swift +74 -2
- package/core/win/main.cs +55 -0
- package/index.js +202 -20
- package/package.json +1 -1
package/bin/positron.js
CHANGED
package/builder.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const cp = require("child_process");
|
|
4
|
-
const { success, error, info } = require("./logs");
|
|
4
|
+
const { success, error, info, warn } = require("./logs");
|
|
5
5
|
|
|
6
6
|
const arch = process.argv.includes("--x64") ? "x64" : process.argv.includes("--arm64") ? "arm64" : process.arch;
|
|
7
7
|
|
package/core/mac/main.swift
CHANGED
|
@@ -18,6 +18,7 @@ let AUTH_TOKEN: String = {
|
|
|
18
18
|
}()
|
|
19
19
|
|
|
20
20
|
var windowObservations: [Int: NSKeyValueObservation] = [:]
|
|
21
|
+
var navigationDelegates: [Int: WebViewNavigationDelegate] = [:]
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
import Foundation
|
|
@@ -168,6 +169,26 @@ struct IPCResponse: Codable {
|
|
|
168
169
|
let data: [String: String]
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
func GetIPCClient() -> IPCClient {
|
|
173
|
+
return AppDelegate.shared?.ipcClient ?? IPCClient()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
func GetWebView(windowId: Int) -> WKWebView? {
|
|
177
|
+
guard let window = windows[windowId],
|
|
178
|
+
let webView = window.contentView as? WKWebView else {
|
|
179
|
+
printError("GetWebView failed: no webview found for window \(windowId)")
|
|
180
|
+
return nil
|
|
181
|
+
}
|
|
182
|
+
return webView
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
func GetWindow(windowId: Int) -> NSWindow? {
|
|
186
|
+
guard let window = windows[windowId] else {
|
|
187
|
+
printError("GetWindow failed: no window found with ID \(windowId)")
|
|
188
|
+
return nil
|
|
189
|
+
}
|
|
190
|
+
return window
|
|
191
|
+
}
|
|
171
192
|
|
|
172
193
|
// MARK: - Command Handler
|
|
173
194
|
|
|
@@ -225,6 +246,9 @@ func handleCommand(windowId: Int, command: String, args: [String]) {
|
|
|
225
246
|
config.userContentController.addUserScript(preload)
|
|
226
247
|
|
|
227
248
|
let webView = PositronWebView(frame: NSRect(origin: .zero, size: frame.size), configuration: config)
|
|
249
|
+
let navDelegate = WebViewNavigationDelegate(windowId: windowId)
|
|
250
|
+
webView.navigationDelegate = navDelegate
|
|
251
|
+
navigationDelegates[windowId] = navDelegate
|
|
228
252
|
// Resize webview automatically when the window resizes
|
|
229
253
|
webView.autoresizingMask = [.width, .height]
|
|
230
254
|
newWindow.contentView = webView
|
|
@@ -242,6 +266,7 @@ func handleCommand(windowId: Int, command: String, args: [String]) {
|
|
|
242
266
|
observation.invalidate()
|
|
243
267
|
windowObservations.removeValue(forKey: windowId)
|
|
244
268
|
}
|
|
269
|
+
navigationDelegates.removeValue(forKey: windowId)
|
|
245
270
|
|
|
246
271
|
|
|
247
272
|
windows.removeValue(forKey: windowId)
|
|
@@ -267,6 +292,19 @@ func handleCommand(windowId: Int, command: String, args: [String]) {
|
|
|
267
292
|
|
|
268
293
|
window.performClose(nil)
|
|
269
294
|
|
|
295
|
+
case "setSwipeNav":
|
|
296
|
+
guard let window = windows[windowId],
|
|
297
|
+
let webView = window.contentView as? WKWebView else {
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let enable = args.first?.lowercased() != "false"
|
|
302
|
+
webView.allowsBackForwardNavigationGestures = enable
|
|
303
|
+
|
|
304
|
+
GetIPCClient().send(
|
|
305
|
+
IPCResponse(windowId: windowId, event: "setSwipeNav-reply-\(windowId)", data: ["enabled": enable ? "true" : "false"])
|
|
306
|
+
)
|
|
307
|
+
|
|
270
308
|
case "forceCloseWindow":
|
|
271
309
|
guard let window = windows[windowId] else { return }
|
|
272
310
|
|
|
@@ -305,7 +343,6 @@ case "forceCloseWindow":
|
|
|
305
343
|
}
|
|
306
344
|
(window.contentView as? WKWebView)?.load(URLRequest(url: url))
|
|
307
345
|
|
|
308
|
-
|
|
309
346
|
case "hide":
|
|
310
347
|
guard let window = windows[windowId] else { return }
|
|
311
348
|
window.orderOut(nil)
|
|
@@ -344,7 +381,6 @@ case "forceCloseWindow":
|
|
|
344
381
|
(window.contentView as? WKWebView)?
|
|
345
382
|
.loadFileURL(fileURL, allowingReadAccessTo: fileURL.deletingLastPathComponent())
|
|
346
383
|
|
|
347
|
-
|
|
348
384
|
case "setBounds":
|
|
349
385
|
guard let window = windows[windowId] else { return }
|
|
350
386
|
guard args.count >= 4,
|
|
@@ -603,6 +639,24 @@ UNUserNotificationCenter.current().requestAuthorization(
|
|
|
603
639
|
}
|
|
604
640
|
}
|
|
605
641
|
|
|
642
|
+
case "confirm":
|
|
643
|
+
guard let window = windows[windowId] else { return }
|
|
644
|
+
guard let message = args.first else {
|
|
645
|
+
printError("confirm — missing message argument")
|
|
646
|
+
return
|
|
647
|
+
}
|
|
648
|
+
let alert = NSAlert()
|
|
649
|
+
alert.messageText = message
|
|
650
|
+
alert.addButton(withTitle: "OK")
|
|
651
|
+
alert.addButton(withTitle: "Cancel")
|
|
652
|
+
|
|
653
|
+
alert.beginSheetModal(for: window) { response in
|
|
654
|
+
let confirmed = (response == .alertFirstButtonReturn)
|
|
655
|
+
AppDelegate.shared?.ipcClient.send(
|
|
656
|
+
IPCResponse(windowId: windowId, event: "confirm-reply-\(windowId)", data: ["confirmed": confirmed ? "true" : "false"])
|
|
657
|
+
)
|
|
658
|
+
}
|
|
659
|
+
|
|
606
660
|
case "isFocused":
|
|
607
661
|
guard let window = windows[windowId] else { return }
|
|
608
662
|
let isFocused = window.isKeyWindow
|
|
@@ -682,6 +736,24 @@ case "resetMenu":
|
|
|
682
736
|
}
|
|
683
737
|
}
|
|
684
738
|
|
|
739
|
+
// MARK: - WebView Navigation Delegate
|
|
740
|
+
|
|
741
|
+
final class WebViewNavigationDelegate: NSObject, WKNavigationDelegate {
|
|
742
|
+
let windowId: Int
|
|
743
|
+
|
|
744
|
+
init(windowId: Int) {
|
|
745
|
+
self.windowId = windowId
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
749
|
+
let isFile = webView.url?.isFileURL ?? false
|
|
750
|
+
let eventName = isFile ? "loadFile-reply-\(windowId)" : "loadURL-reply-\(windowId)"
|
|
751
|
+
AppDelegate.shared?.ipcClient.send(
|
|
752
|
+
IPCResponse(windowId: windowId, event: eventName, data: [:])
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
685
757
|
// MARK: - WebView → Swift IPC Handler
|
|
686
758
|
|
|
687
759
|
/// Receives messages from renderer JS: window.webkit.messageHandlers.ipc.postMessage({...})
|
package/core/win/main.cs
CHANGED
|
@@ -329,6 +329,17 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
329
329
|
window.Title = webView.CoreWebView2.DocumentTitle;
|
|
330
330
|
};
|
|
331
331
|
|
|
332
|
+
webView.CoreWebView2.NavigationCompleted += (s, e) =>
|
|
333
|
+
{
|
|
334
|
+
bool isFile = webView.Source != null && webView.Source.IsFile;
|
|
335
|
+
string eventName = isFile ? $"loadFile-reply-{windowId}" : $"loadURL-reply-{windowId}";
|
|
336
|
+
_ipcClient.Send(new IPCResponse
|
|
337
|
+
{
|
|
338
|
+
windowId = windowId,
|
|
339
|
+
@event = eventName
|
|
340
|
+
});
|
|
341
|
+
};
|
|
342
|
+
|
|
332
343
|
webView.CoreWebView2.ContextMenuRequested += (s, e) =>
|
|
333
344
|
{
|
|
334
345
|
if (LayoutMap.TryGetValue(windowId, out var l) && l.ContextMenu != null)
|
|
@@ -371,6 +382,23 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
371
382
|
|
|
372
383
|
// 3. Attach it to the layout
|
|
373
384
|
layout.ContextMenu = contextMenu;
|
|
385
|
+
break;
|
|
386
|
+
|
|
387
|
+
case "setSwipeNav":
|
|
388
|
+
if (!WindowsMap.TryGetValue(windowId, out var winSwipeNav)) break;
|
|
389
|
+
var wvSwipeNav = GetWebView(windowId);
|
|
390
|
+
if (wvSwipeNav != null) {
|
|
391
|
+
bool enable = args.Count == 0 || args[0].ToLower() != "false";
|
|
392
|
+
wvSwipeNav.CoreWebView2.Settings.IsSwipeNavigationEnabled = enable;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
GetIPCClient().Send(new IPCResponse
|
|
396
|
+
{
|
|
397
|
+
windowId = windowId,
|
|
398
|
+
@event = "setSwipeNav-reply-" + windowId,
|
|
399
|
+
data = new() { { "enabled", (wvSwipeNav?.CoreWebView2.Settings.IsSwipeNavigationEnabled ?? false).ToString().ToLower() } }
|
|
400
|
+
});
|
|
401
|
+
|
|
374
402
|
break;
|
|
375
403
|
|
|
376
404
|
case "closeWindow":
|
|
@@ -610,6 +638,24 @@ case "setBounds":
|
|
|
610
638
|
}
|
|
611
639
|
break;
|
|
612
640
|
|
|
641
|
+
case "confirm":
|
|
642
|
+
if (args.Count < 1)
|
|
643
|
+
{
|
|
644
|
+
error("confirm — expected message argument");
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
{
|
|
648
|
+
var message = args[0];
|
|
649
|
+
var result = MessageBox.Show(message, "Confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes;
|
|
650
|
+
_ipcClient.Send(new IPCResponse
|
|
651
|
+
{
|
|
652
|
+
windowId = windowId,
|
|
653
|
+
@event = "confirm-reply-" + windowId,
|
|
654
|
+
data = new() { { "confirmed", result.ToString().ToLower() } }
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
break;
|
|
658
|
+
|
|
613
659
|
case "emitToRenderer":
|
|
614
660
|
if (args.Count < 2)
|
|
615
661
|
{
|
|
@@ -830,6 +876,15 @@ case "setBounds":
|
|
|
830
876
|
return null;
|
|
831
877
|
}
|
|
832
878
|
|
|
879
|
+
public static Window? GetWindow(int windowId)
|
|
880
|
+
{
|
|
881
|
+
if (WindowsMap.TryGetValue(windowId, out var window))
|
|
882
|
+
return window;
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
public static IPCClient GetIPCClient() => _ipcClient;
|
|
887
|
+
|
|
833
888
|
// MARK: - Menu Management
|
|
834
889
|
|
|
835
890
|
private static void BuildAndAttachMenu(int windowId, string jsonStr)
|
package/index.js
CHANGED
|
@@ -294,16 +294,6 @@ class Window extends Events.EventEmitter {
|
|
|
294
294
|
this.emit("title-updated", title);
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
/**
|
|
298
|
-
* Loads a remote URL in the window. Emits "url-loaded" and "navigated" events with the URL as data.
|
|
299
|
-
* @param {string} url The URL to load.
|
|
300
|
-
*/
|
|
301
|
-
loadURL(url) {
|
|
302
|
-
this.sendCommand("loadURL", [url]);
|
|
303
|
-
this.emit("url-loaded", url);
|
|
304
|
-
this.emit("navigated", url);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
297
|
/**
|
|
308
298
|
* Triggers the print dialog for the window. Emits a "print" event. Note that the actual print functionality and dialog is handled by the native layer, so behavior may vary across platforms.
|
|
309
299
|
*/
|
|
@@ -322,13 +312,25 @@ class Window extends Events.EventEmitter {
|
|
|
322
312
|
}
|
|
323
313
|
|
|
324
314
|
/**
|
|
325
|
-
* Loads a
|
|
315
|
+
* Loads a file into the window. The path can be an absolute file path or a relative path from the application's root directory. Emits a "file-loaded" event with the path as data, and a "navigated" event with the path as data.
|
|
326
316
|
* @param {string} path The path to the file to load.
|
|
327
317
|
*/
|
|
328
|
-
loadFile(path) {
|
|
329
|
-
this.
|
|
330
|
-
this.emit("file-loaded", path);
|
|
331
|
-
this.emit("navigated", path);
|
|
318
|
+
async loadFile(path) {
|
|
319
|
+
const res = await this.request("loadFile", `loadFile-reply-${this.id}`, path);
|
|
320
|
+
this.emit("file-loaded", path);
|
|
321
|
+
this.emit("navigated", path);
|
|
322
|
+
return res;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Loads a URL into the window. Emits a "url-loaded" event with the URL as data, and a "navigated" event with the URL as data.
|
|
327
|
+
* @param {string} url The URL to load.
|
|
328
|
+
*/
|
|
329
|
+
async loadURL(url) {
|
|
330
|
+
const res = await this.request("loadURL", `loadURL-reply-${this.id}`, url);
|
|
331
|
+
this.emit("url-loaded", url);
|
|
332
|
+
this.emit("navigated", url);
|
|
333
|
+
return res;
|
|
332
334
|
}
|
|
333
335
|
|
|
334
336
|
/**
|
|
@@ -625,7 +627,7 @@ if (timeoutArg) {
|
|
|
625
627
|
|
|
626
628
|
args = args.filter(arg => arg !== timeoutArg);
|
|
627
629
|
|
|
628
|
-
timeoutDuration = parseInt(timeoutArg.split("=")[1]
|
|
630
|
+
timeoutDuration = parseInt(timeoutArg.split("=")[1]);
|
|
629
631
|
}
|
|
630
632
|
|
|
631
633
|
timeout = setTimeout(() => {
|
|
@@ -763,7 +765,7 @@ async getBounds() {
|
|
|
763
765
|
* @returns {Promise<string>} The current URL loaded in the window.
|
|
764
766
|
*/
|
|
765
767
|
async getURL() {
|
|
766
|
-
return await this.request("getURL", `getURL-reply-${this.id}`);
|
|
768
|
+
return (await this.request("getURL", `getURL-reply-${this.id}`))?.url || "";
|
|
767
769
|
}
|
|
768
770
|
|
|
769
771
|
/**
|
|
@@ -771,7 +773,7 @@ async getURL() {
|
|
|
771
773
|
* @returns {Promise<string>} The current title of the window.
|
|
772
774
|
*/
|
|
773
775
|
async getTitle() {
|
|
774
|
-
return await this.request("getTitle", `getTitle-reply-${this.id}`);
|
|
776
|
+
return (await this.request("getTitle", `getTitle-reply-${this.id}`)).title || "";
|
|
775
777
|
}
|
|
776
778
|
|
|
777
779
|
/**
|
|
@@ -799,7 +801,168 @@ setTitlebarTransparent(isTransparent) {
|
|
|
799
801
|
*/
|
|
800
802
|
async evaluateJavaScript(script) {
|
|
801
803
|
const res = await this.request("evaluateJS", `evaluateJS-reply-${this.id}`, script);
|
|
802
|
-
return res;
|
|
804
|
+
return res.result;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Gets the user agent string of the window. Returns a Promise that resolves to the user agent as a string.
|
|
809
|
+
* @returns {Promise<string>} The user agent string of the window.
|
|
810
|
+
*/
|
|
811
|
+
async getUserAgent() {
|
|
812
|
+
return await this.evaluateJavaScript("navigator.userAgent");
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Sets the style of elements matching a CSS selector. Returns a Promise that resolves when the style has been applied.
|
|
817
|
+
* @param {string} selector The CSS selector for the elements to style.
|
|
818
|
+
* @param {Object} style The style properties to apply.
|
|
819
|
+
* @returns {Promise<void>} A Promise that resolves when the style has been applied.
|
|
820
|
+
*/
|
|
821
|
+
async setStyleOf(selector, style) {
|
|
822
|
+
const styleString = Object.entries(style).map(([key, value]) => `${key}: ${value};`).join(" ");
|
|
823
|
+
const script = `
|
|
824
|
+
(function() {
|
|
825
|
+
const elements = document.querySelectorAll(${JSON.stringify(selector)});
|
|
826
|
+
elements.forEach(el => {
|
|
827
|
+
Object.entries(${JSON.stringify(style)}).forEach(([key, value]) => {
|
|
828
|
+
el.style[key] = value;
|
|
829
|
+
});
|
|
830
|
+
});
|
|
831
|
+
})();
|
|
832
|
+
`;
|
|
833
|
+
await this.evaluateJavaScript(script);
|
|
834
|
+
this.emit("style-updated", { selector, style });
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* Sets an attribute of elements matching a CSS selector. Returns a Promise that resolves when the attribute has been set.
|
|
839
|
+
* @param {string} selector The CSS selector for the elements to update.
|
|
840
|
+
* @param {string} attribute The name of the attribute to set.
|
|
841
|
+
* @param {string} value The value to set for the attribute.
|
|
842
|
+
* @returns {Promise<void>} A Promise that resolves when the attribute has been set.
|
|
843
|
+
*/
|
|
844
|
+
async setAttributeOf(selector, attribute, value) {
|
|
845
|
+
const script = `
|
|
846
|
+
(function() {
|
|
847
|
+
const elements = document.querySelectorAll(${JSON.stringify(selector)});
|
|
848
|
+
elements.forEach(el => {
|
|
849
|
+
el.setAttribute(${JSON.stringify(attribute)}, ${JSON.stringify(value)});
|
|
850
|
+
});
|
|
851
|
+
})();
|
|
852
|
+
`;
|
|
853
|
+
await this.evaluateJavaScript(script);
|
|
854
|
+
this.emit("attribute-updated", { selector, attribute, value });
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Removes an attribute from elements matching a CSS selector. Returns a Promise that resolves when the attribute has been removed.
|
|
859
|
+
* @param {string} selector The CSS selector for the elements to update.
|
|
860
|
+
* @param {string} attribute The name of the attribute to remove.
|
|
861
|
+
* @returns {Promise<void>} A Promise that resolves when the attribute has been removed.
|
|
862
|
+
*/
|
|
863
|
+
async removeAttributeOf(selector, attribute) {
|
|
864
|
+
const script = `
|
|
865
|
+
(function() {
|
|
866
|
+
const elements = document.querySelectorAll(${JSON.stringify(selector)});
|
|
867
|
+
elements.forEach(el => {
|
|
868
|
+
el.removeAttribute(${JSON.stringify(attribute)});
|
|
869
|
+
});
|
|
870
|
+
})();
|
|
871
|
+
`;
|
|
872
|
+
await this.evaluateJavaScript(script);
|
|
873
|
+
this.emit("attribute-removed", { selector, attribute });
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Removes specific style properties from elements matching a CSS selector. Returns a Promise that resolves when the styles have been removed.
|
|
878
|
+
* @param {string} selector The CSS selector for the elements to update.
|
|
879
|
+
* @param {string[]} styleProperties The style properties to remove.
|
|
880
|
+
* @returns {Promise<void>} A Promise that resolves when the styles have been removed.
|
|
881
|
+
*/
|
|
882
|
+
async removeStyleOf(selector, styleProperties) {
|
|
883
|
+
const propertiesString = styleProperties.map(prop => `${prop}:`).join("|");
|
|
884
|
+
const script = `
|
|
885
|
+
(function() {
|
|
886
|
+
const elements = document.querySelectorAll(${JSON.stringify(selector)});
|
|
887
|
+
elements.forEach(el => {
|
|
888
|
+
el.style.cssText = el.style.cssText.split(";").filter(rule => {
|
|
889
|
+
return !${JSON.stringify(propertiesString)}.includes(rule.trim().split(":")[0] + ":");
|
|
890
|
+
}).join(";");
|
|
891
|
+
});
|
|
892
|
+
})();
|
|
893
|
+
`;
|
|
894
|
+
await this.evaluateJavaScript(script);
|
|
895
|
+
this.emit("style-removed", { selector, styleProperties });
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Adds or replaces click handlers that emit IPC events.
|
|
900
|
+
*
|
|
901
|
+
* @param {string} selector - The CSS selector for the elements to attach the click handlers to.
|
|
902
|
+
* @param {string} channel - The IPC channel to emit events on when the elements are clicked.
|
|
903
|
+
* @param {{ replace?: boolean }} [options] - Optional settings for the click handlers. If `replace` is true, any existing IPC click handlers on the elements will be removed before adding the new handler. If false or omitted, the new handler will be added alongside existing handlers without removing them.
|
|
904
|
+
* @returns {Promise<void>} A Promise that resolves when the click handlers have been added.
|
|
905
|
+
*/
|
|
906
|
+
async onClick(selector, channel, { replace = true } = {}) {
|
|
907
|
+
const script = `
|
|
908
|
+
(function() {
|
|
909
|
+
const selector = ${JSON.stringify(selector)};
|
|
910
|
+
const channel = ${JSON.stringify(channel)};
|
|
911
|
+
const replace = ${replace};
|
|
912
|
+
|
|
913
|
+
const elements = document.querySelectorAll(selector);
|
|
914
|
+
|
|
915
|
+
elements.forEach(el => {
|
|
916
|
+
if (replace) {
|
|
917
|
+
el.onclick = () => {
|
|
918
|
+
window.ipc.send(channel, { selector });
|
|
919
|
+
}
|
|
920
|
+
} else {
|
|
921
|
+
el.addEventListener("click", () => {
|
|
922
|
+
window.ipc.send(channel, { selector });
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
})
|
|
926
|
+
})();
|
|
927
|
+
`;
|
|
928
|
+
|
|
929
|
+
await this.evaluateJavaScript(script);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Removes click handlers that emit IPC events from elements matching the specified CSS selector. This will remove all click handlers that were added via the onClick method for the given selector, regardless of the channel or whether they were set to replace existing handlers.
|
|
934
|
+
* @param {string} selector The CSS selector for the elements to remove click handlers from.
|
|
935
|
+
* @returns {Promise<void>} A Promise that resolves when the click handlers have been removed.
|
|
936
|
+
*/
|
|
937
|
+
async removeOnClick(selector) {
|
|
938
|
+
const script = `
|
|
939
|
+
const elements = document.querySelectorAll(${JSON.stringify(selector)});
|
|
940
|
+
elements.forEach(el => {
|
|
941
|
+
el.onclick = null;
|
|
942
|
+
});
|
|
943
|
+
`;
|
|
944
|
+
await this.evaluateJavaScript(script);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Displays a confirmation dialog with the given message. Returns a Promise that resolves to true if the user confirmed, or false if the user cancelled. Emits a "confirm" event with the message as data when done.
|
|
949
|
+
* @param {string} message The message to display in the confirmation dialog.
|
|
950
|
+
* @returns {Promise<boolean>} True if the user confirmed, false if the user cancelled.
|
|
951
|
+
*/
|
|
952
|
+
async confirm(message) {
|
|
953
|
+
const res = await this.request("confirm", `confirm-reply-${this.id}`, message);
|
|
954
|
+
this.emit("confirm", message);
|
|
955
|
+
return res?.confirmed === "true";
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Enables or disables swipe navigation for the window. When enabled, users can navigate back and forward through their history by swiping left or right on a trackpad or touchscreen. Emits a "swipe-navigation-updated" event with the new value when done.
|
|
960
|
+
* @param {boolean} enabled Whether swipe navigation should be enabled.
|
|
961
|
+
* @returns {Promise<void>} A Promise that resolves when the swipe navigation setting has been updated.
|
|
962
|
+
*/
|
|
963
|
+
async setSwipeNavigation(enabled) {
|
|
964
|
+
const res = await this.request("setSwipeNav", `setSwipeNav-reply-${this.id}`, String(enabled));
|
|
965
|
+
this.emit("swipe-navigation-updated", enabled);
|
|
803
966
|
}
|
|
804
967
|
|
|
805
968
|
}
|
|
@@ -832,7 +995,7 @@ const app = {
|
|
|
832
995
|
},
|
|
833
996
|
|
|
834
997
|
/**
|
|
835
|
-
* Adds an event listener for application-level events.
|
|
998
|
+
* Adds an event listener for application-level events.
|
|
836
999
|
* @param {string} event The name of the event to listen for.
|
|
837
1000
|
* @param {Function} listener The callback function to invoke when the event is emitted.
|
|
838
1001
|
*/
|
|
@@ -858,11 +1021,19 @@ const app = {
|
|
|
858
1021
|
this.events.once(event, listener);
|
|
859
1022
|
},
|
|
860
1023
|
|
|
1024
|
+
/**
|
|
1025
|
+
* Gets the currently focused window. Returns a Promise that resolves to the focused Window instance, or null if no windows are currently focused. Emits a "focused-window-retrieved" event with the focused window as data when done.
|
|
1026
|
+
* @returns {Promise<Window|null>} The currently focused window, or null if no windows are focused.
|
|
1027
|
+
*/
|
|
861
1028
|
async getFocusedWindow() {
|
|
862
1029
|
const results = await Promise.all([...activeWindows].map(win => win.isFocused().then(isFocused => ({ win, isFocused }))));
|
|
863
1030
|
return results.find(({ isFocused }) => isFocused)?.win || null;
|
|
864
1031
|
},
|
|
865
1032
|
|
|
1033
|
+
/**
|
|
1034
|
+
* Sets the application name, which is used for things like the user data directory and may be used by the native layer for other purposes.
|
|
1035
|
+
* @param {*} name The new name for the application. This will be converted to a string before being used.
|
|
1036
|
+
*/
|
|
866
1037
|
setName(name) {
|
|
867
1038
|
process.env.POSITRON_APP_NAME = name;
|
|
868
1039
|
this.events.emit("name-updated", name);
|
|
@@ -874,6 +1045,10 @@ const app = {
|
|
|
874
1045
|
},
|
|
875
1046
|
|
|
876
1047
|
userData: {
|
|
1048
|
+
/**
|
|
1049
|
+
* Gets the path to the user data directory for the application. The path is determined based on the operating system and the application name. If the directory does not exist, it will be created. Emits a "user-data-path-retrieved" event with the path as data when done.
|
|
1050
|
+
* @returns {string} The path to the user data directory.
|
|
1051
|
+
*/
|
|
877
1052
|
getPath() {
|
|
878
1053
|
let userPath = null;
|
|
879
1054
|
|
|
@@ -902,6 +1077,9 @@ userData: {
|
|
|
902
1077
|
return userPath;
|
|
903
1078
|
},
|
|
904
1079
|
|
|
1080
|
+
/**
|
|
1081
|
+
* Creates the user data directory if it does not already exist. Emits a "user-data-created" event when the directory is created successfully.
|
|
1082
|
+
*/
|
|
905
1083
|
create() {
|
|
906
1084
|
const userPath = this.getPath();
|
|
907
1085
|
|
|
@@ -911,6 +1089,10 @@ userData: {
|
|
|
911
1089
|
}
|
|
912
1090
|
},
|
|
913
1091
|
|
|
1092
|
+
/**
|
|
1093
|
+
* Deletes the user data directory and all of its contents. Use with caution, as this will permanently remove all user data for the application. Emits a "user-data-deleted" event when the directory is deleted successfully.
|
|
1094
|
+
*/
|
|
1095
|
+
|
|
914
1096
|
delete() {
|
|
915
1097
|
const userPath = this.getPath();
|
|
916
1098
|
|