positron.js 1.0.4 → 1.0.6
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 +25 -2
- package/core/mac/main.swift +104 -21
- package/core/mac/tray.swift +95 -0
- package/core/win/PositronRuntime.csproj +1 -1
- package/core/win/main.cs +176 -22
- package/core/win/tray.cs +142 -0
- package/index.js +165 -42
- package/package.json +9 -2
- package/packager.js +47 -12
- package/tray.js +64 -0
package/core/win/main.cs
CHANGED
|
@@ -10,13 +10,50 @@ using System.Threading;
|
|
|
10
10
|
using System.Threading.Tasks;
|
|
11
11
|
using System.Windows;
|
|
12
12
|
using System.Windows.Controls;
|
|
13
|
-
using System.Windows.Input;
|
|
14
13
|
using Microsoft.Web.WebView2.Core;
|
|
15
14
|
using Microsoft.Web.WebView2.Wpf;
|
|
16
15
|
using System.Text.Json.Serialization;
|
|
17
16
|
using System.Net;
|
|
18
17
|
using System.Net.Sockets;
|
|
19
18
|
using Microsoft.VisualBasic;
|
|
19
|
+
using System.Runtime.InteropServices;
|
|
20
|
+
using Microsoft.Win32;
|
|
21
|
+
using System.Linq;
|
|
22
|
+
|
|
23
|
+
class PowerSaveBlocker
|
|
24
|
+
{
|
|
25
|
+
// Import the Win32 API
|
|
26
|
+
[DllImport("kernel32.dll")]
|
|
27
|
+
private static extern uint SetThreadExecutionState(uint esFlags);
|
|
28
|
+
|
|
29
|
+
// Flags
|
|
30
|
+
private const uint ES_CONTINUOUS = 0x80000000;
|
|
31
|
+
private const uint ES_SYSTEM_REQUIRED = 0x00000001;
|
|
32
|
+
private const uint ES_DISPLAY_REQUIRED = 0x00000002;
|
|
33
|
+
|
|
34
|
+
private static uint currentState = 0;
|
|
35
|
+
|
|
36
|
+
public static void BlockPowerSave(bool keepDisplayOn = false)
|
|
37
|
+
{
|
|
38
|
+
uint flags = ES_CONTINUOUS | ES_SYSTEM_REQUIRED;
|
|
39
|
+
if (keepDisplayOn)
|
|
40
|
+
{
|
|
41
|
+
flags |= ES_DISPLAY_REQUIRED;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
currentState = SetThreadExecutionState(flags);
|
|
45
|
+
if (currentState == 0)
|
|
46
|
+
{
|
|
47
|
+
Console.WriteLine("Failed to set execution state!");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public static void UnblockPowerSave()
|
|
52
|
+
{
|
|
53
|
+
SetThreadExecutionState(ES_CONTINUOUS);
|
|
54
|
+
currentState = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
20
57
|
|
|
21
58
|
|
|
22
59
|
namespace PositronWindows
|
|
@@ -137,10 +174,18 @@ namespace PositronWindows
|
|
|
137
174
|
? Path.Combine(basePath, "resources")
|
|
138
175
|
: basePath;
|
|
139
176
|
|
|
140
|
-
|
|
177
|
+
string backendExeName = "positron-backend.exe";
|
|
178
|
+
if (Directory.Exists(targetDir)) {
|
|
179
|
+
string[] files = Directory.GetFiles(targetDir, "*-backend.exe");
|
|
180
|
+
if (files.Length > 0) {
|
|
181
|
+
backendExeName = Path.GetFileName(files[0]);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (File.Exists(Path.Combine(targetDir, backendExeName)))
|
|
141
186
|
{
|
|
142
187
|
// PACKAGED MODE — C# is the entry point; launch the Node backend
|
|
143
|
-
StartNodeProcess(targetDir);
|
|
188
|
+
StartNodeProcess(targetDir, backendExeName);
|
|
144
189
|
}
|
|
145
190
|
else
|
|
146
191
|
{
|
|
@@ -153,7 +198,7 @@ namespace PositronWindows
|
|
|
153
198
|
}
|
|
154
199
|
else
|
|
155
200
|
{
|
|
156
|
-
error("No
|
|
201
|
+
error($"No {backendExeName} found and POSITRON_IPC_PORT not set. Cannot start.");
|
|
157
202
|
Shutdown();
|
|
158
203
|
return;
|
|
159
204
|
}
|
|
@@ -189,13 +234,13 @@ namespace PositronWindows
|
|
|
189
234
|
|
|
190
235
|
private static int _ipcPort = 9000;
|
|
191
236
|
|
|
192
|
-
private void StartNodeProcess(string workingDirectory)
|
|
237
|
+
private void StartNodeProcess(string workingDirectory, string backendExeName)
|
|
193
238
|
{
|
|
194
239
|
IsPackaged = true;
|
|
195
240
|
|
|
196
241
|
_ipcPort = GetRandomOpenPort();
|
|
197
242
|
|
|
198
|
-
|
|
243
|
+
string backendExe = Path.Combine(workingDirectory, backendExeName);
|
|
199
244
|
|
|
200
245
|
_nodeProcess = new Process
|
|
201
246
|
{
|
|
@@ -335,7 +380,8 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
335
380
|
_ipcClient.Send(new IPCResponse
|
|
336
381
|
{
|
|
337
382
|
windowId = windowId,
|
|
338
|
-
@event = eventName
|
|
383
|
+
@event = eventName,
|
|
384
|
+
data = new() { { "url", webView.Source?.ToString() ?? "" }, { "title", webView.CoreWebView2.DocumentTitle }, { "canGoBack", webView.CoreWebView2.CanGoBack.ToString().ToLower() }, { "canGoForward", webView.CoreWebView2.CanGoForward.ToString().ToLower() } }
|
|
339
385
|
});
|
|
340
386
|
};
|
|
341
387
|
|
|
@@ -359,6 +405,7 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
359
405
|
break;
|
|
360
406
|
}
|
|
361
407
|
|
|
408
|
+
|
|
362
409
|
case "setContextMenu":
|
|
363
410
|
if (!LayoutMap.TryGetValue(windowId, out var layout)) break;
|
|
364
411
|
if (args.Count == 0)
|
|
@@ -394,12 +441,41 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
394
441
|
GetIPCClient().Send(new IPCResponse
|
|
395
442
|
{
|
|
396
443
|
windowId = windowId,
|
|
397
|
-
@event = "setSwipeNav-reply-" + windowId,
|
|
444
|
+
@event = args[^1] ?? "setSwipeNav-reply-" + windowId,
|
|
398
445
|
data = new() { { "enabled", (wvSwipeNav?.CoreWebView2.Settings.IsSwipeNavigationEnabled ?? false).ToString().ToLower() } }
|
|
399
446
|
});
|
|
400
447
|
|
|
401
448
|
break;
|
|
402
449
|
|
|
450
|
+
case "blockPowerSave":
|
|
451
|
+
PowerSaveBlocker.BlockPowerSave();
|
|
452
|
+
break;
|
|
453
|
+
|
|
454
|
+
case "unblockPowerSave":
|
|
455
|
+
PowerSaveBlocker.UnblockPowerSave();
|
|
456
|
+
break;
|
|
457
|
+
|
|
458
|
+
case "isDarkMode":
|
|
459
|
+
bool isLightTheme = true;
|
|
460
|
+
using (RegistryKey? key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
|
|
461
|
+
{
|
|
462
|
+
if (key != null)
|
|
463
|
+
{
|
|
464
|
+
object? value = key?.GetValue("AppsUseLightTheme");
|
|
465
|
+
if (value != null && (int)value == 0)
|
|
466
|
+
{
|
|
467
|
+
isLightTheme = false; // Dark Mode
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
GetIPCClient().Send(new IPCResponse
|
|
472
|
+
{
|
|
473
|
+
windowId = windowId,
|
|
474
|
+
@event = args[^1] ?? "isDarkMode-reply-" + windowId,
|
|
475
|
+
data = new() { { "isDarkMode", (!isLightTheme).ToString().ToLower() } }
|
|
476
|
+
});
|
|
477
|
+
break;
|
|
478
|
+
|
|
403
479
|
case "isSwipeNavEnabled":
|
|
404
480
|
if (!WindowsMap.TryGetValue(windowId, out var winCheckSwipe)) break;
|
|
405
481
|
var wvCheckSwipe = GetWebView(windowId);
|
|
@@ -408,19 +484,75 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
408
484
|
GetIPCClient().Send(new IPCResponse
|
|
409
485
|
{
|
|
410
486
|
windowId = windowId,
|
|
411
|
-
@event = "isSwipeNavEnabled-reply-" + windowId,
|
|
487
|
+
@event = args[^1] ?? "isSwipeNavEnabled-reply-" + windowId,
|
|
412
488
|
data = new() { { "enabled", isEnabled.ToString().ToLower() } }
|
|
413
489
|
}
|
|
414
490
|
);
|
|
415
491
|
}
|
|
416
492
|
break;
|
|
417
493
|
|
|
494
|
+
case "showFileOpenDialog":
|
|
495
|
+
{
|
|
496
|
+
var dialog = new Microsoft.Win32.OpenFileDialog
|
|
497
|
+
{
|
|
498
|
+
Multiselect = args.Count > 0 && args[0].ToLower() == "true"
|
|
499
|
+
};
|
|
500
|
+
bool? result = dialog.ShowDialog();
|
|
501
|
+
if (result == true)
|
|
502
|
+
{
|
|
503
|
+
string[] files = dialog.FileNames;
|
|
504
|
+
GetIPCClient().Send(new IPCResponse
|
|
505
|
+
{
|
|
506
|
+
windowId = windowId,
|
|
507
|
+
@event = args[^1] ?? "showFileOpenDialog-reply-" + windowId,
|
|
508
|
+
data = new() { { "files", JsonSerializer.Serialize(files) } }
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
break;
|
|
513
|
+
|
|
514
|
+
case "readFromClipboard":
|
|
515
|
+
string clipboardText = "";
|
|
516
|
+
Current.Dispatcher.Invoke(() =>
|
|
517
|
+
{
|
|
518
|
+
try
|
|
519
|
+
{
|
|
520
|
+
clipboardText = Clipboard.GetText();
|
|
521
|
+
}
|
|
522
|
+
catch (Exception ex)
|
|
523
|
+
{
|
|
524
|
+
error($"readFromClipboard failed: {ex.Message}");
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
GetIPCClient().Send(new IPCResponse
|
|
528
|
+
{
|
|
529
|
+
windowId = windowId,
|
|
530
|
+
@event = args[^1] ?? "readFromClipboard-reply-" + windowId,
|
|
531
|
+
data = new() { { "text", clipboardText } }
|
|
532
|
+
});
|
|
533
|
+
break;
|
|
534
|
+
|
|
535
|
+
case "writeToClipboard":
|
|
536
|
+
if (args.Count == 0) break;
|
|
537
|
+
Current.Dispatcher.Invoke(() =>
|
|
538
|
+
{
|
|
539
|
+
try
|
|
540
|
+
{
|
|
541
|
+
Clipboard.SetText(args[0]);
|
|
542
|
+
}
|
|
543
|
+
catch (Exception ex)
|
|
544
|
+
{
|
|
545
|
+
error($"writeToClipboard failed: {ex.Message}");
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
break;
|
|
549
|
+
|
|
418
550
|
case "isVisible":
|
|
419
551
|
if (!WindowsMap.TryGetValue(windowId, out var winVisible)) break;
|
|
420
552
|
bool isVisible = winVisible.IsVisible;
|
|
421
553
|
GetIPCClient().Send(new IPCResponse
|
|
422
554
|
{ windowId = windowId,
|
|
423
|
-
@event = "isVisible-reply-" + windowId,
|
|
555
|
+
@event = args[^1] ?? "isVisible-reply-" + windowId,
|
|
424
556
|
data = new() { { "isVisible", isVisible.ToString().ToLower() } }
|
|
425
557
|
});
|
|
426
558
|
break;
|
|
@@ -430,14 +562,17 @@ private void StartNodeProcess(string workingDirectory)
|
|
|
430
562
|
bool isFullscreen = winFullscreen.WindowState == WindowState.Maximized;
|
|
431
563
|
GetIPCClient().Send(new IPCResponse
|
|
432
564
|
{ windowId = windowId,
|
|
433
|
-
@event = "isFullscreen-reply-" + windowId,
|
|
565
|
+
@event = args[^1] ?? "isFullscreen-reply-" + windowId,
|
|
434
566
|
data = new() { { "isFullscreen", isFullscreen.ToString().ToLower() } }
|
|
435
567
|
});
|
|
436
568
|
break;
|
|
437
569
|
|
|
438
570
|
case "closeWindow":
|
|
439
571
|
if (WindowsMap.TryGetValue(windowId, out var winToClose))
|
|
572
|
+
{
|
|
573
|
+
_forceClosing.Add(windowId);
|
|
440
574
|
winToClose.Close(); // Triggers Closed → cleanup above
|
|
575
|
+
}
|
|
441
576
|
else
|
|
442
577
|
error($"closeWindow — no window found with ID {windowId}");
|
|
443
578
|
break;
|
|
@@ -500,6 +635,15 @@ case "forceCloseWindow":
|
|
|
500
635
|
}
|
|
501
636
|
break;
|
|
502
637
|
|
|
638
|
+
case "addToContentBlocker":
|
|
639
|
+
_ipcClient.Send(new IPCResponse
|
|
640
|
+
{
|
|
641
|
+
windowId = windowId,
|
|
642
|
+
@event = args[^1] ?? "addToContentBlocker-reply-" + windowId,
|
|
643
|
+
data = new() { { "status", "success" }, { "warning", "Content blocker not supported on Windows." } }
|
|
644
|
+
});
|
|
645
|
+
break;
|
|
646
|
+
|
|
503
647
|
case "loadFile":
|
|
504
648
|
if (!WindowsMap.TryGetValue(windowId, out _)) break;
|
|
505
649
|
if (args.Count == 0)
|
|
@@ -550,7 +694,7 @@ case "forceCloseWindow":
|
|
|
550
694
|
_ipcClient.Send(new IPCResponse
|
|
551
695
|
{
|
|
552
696
|
windowId = windowId,
|
|
553
|
-
@event = "evaluateJS-reply-" + windowId,
|
|
697
|
+
@event = args[^1] ?? "evaluateJS-reply-" + windowId,
|
|
554
698
|
data = new() { { "result", result ?? "null" } }
|
|
555
699
|
});
|
|
556
700
|
}
|
|
@@ -560,7 +704,7 @@ case "forceCloseWindow":
|
|
|
560
704
|
_ipcClient.Send(new IPCResponse
|
|
561
705
|
{
|
|
562
706
|
windowId = windowId,
|
|
563
|
-
@event = "evaluateJS-reply-" + windowId,
|
|
707
|
+
@event = args[^1] ?? "evaluateJS-reply-" + windowId,
|
|
564
708
|
data = new() { { "error", ex.Message } }
|
|
565
709
|
});
|
|
566
710
|
}
|
|
@@ -574,11 +718,21 @@ case "forceCloseWindow":
|
|
|
574
718
|
_ipcClient.Send(new IPCResponse
|
|
575
719
|
{
|
|
576
720
|
windowId = windowId,
|
|
577
|
-
@event = "isFocused-reply-" + windowId,
|
|
721
|
+
@event = args[^1] ?? "isFocused-reply-" + windowId,
|
|
578
722
|
data = new() { { "isFocused", isFocused.ToString().ToLower() } }
|
|
579
723
|
});
|
|
580
724
|
break;
|
|
581
725
|
|
|
726
|
+
case "getFocusedWindowId":
|
|
727
|
+
int focusedWindowId = WindowsMap.FirstOrDefault(kv => kv.Value.IsActive).Key;
|
|
728
|
+
_ipcClient.Send(new IPCResponse
|
|
729
|
+
{
|
|
730
|
+
windowId = windowId,
|
|
731
|
+
@event = args[^1] ?? "getFocusedWindowId-reply-" + windowId,
|
|
732
|
+
data = new() { { "focusedWindowId", focusedWindowId.ToString() } }
|
|
733
|
+
});
|
|
734
|
+
break;
|
|
735
|
+
|
|
582
736
|
case "showNotification":
|
|
583
737
|
if (args.Count < 2)
|
|
584
738
|
{
|
|
@@ -666,7 +820,7 @@ case "setBounds":
|
|
|
666
820
|
_ipcClient.Send(new IPCResponse
|
|
667
821
|
{
|
|
668
822
|
windowId = windowId,
|
|
669
|
-
@event = "prompt-reply-" + windowId,
|
|
823
|
+
@event = args[^1] ?? "prompt-reply-" + windowId,
|
|
670
824
|
data = new() { { "input", result } }
|
|
671
825
|
});
|
|
672
826
|
}
|
|
@@ -684,7 +838,7 @@ case "setBounds":
|
|
|
684
838
|
_ipcClient.Send(new IPCResponse
|
|
685
839
|
{
|
|
686
840
|
windowId = windowId,
|
|
687
|
-
@event = "confirm-reply-" + windowId,
|
|
841
|
+
@event = args[^1] ?? "confirm-reply-" + windowId,
|
|
688
842
|
data = new() { { "confirmed", result.ToString().ToLower() } }
|
|
689
843
|
});
|
|
690
844
|
}
|
|
@@ -810,7 +964,7 @@ case "setBounds":
|
|
|
810
964
|
_ipcClient.Send(new IPCResponse
|
|
811
965
|
{
|
|
812
966
|
windowId = windowId,
|
|
813
|
-
@event = "capture-page-result-" + windowId,
|
|
967
|
+
@event = args[^1] ?? "capture-page-result-" + windowId,
|
|
814
968
|
data = new() { { "imageData", base64 } }
|
|
815
969
|
});
|
|
816
970
|
}
|
|
@@ -831,7 +985,7 @@ case "setBounds":
|
|
|
831
985
|
_ipcClient.Send(new IPCResponse
|
|
832
986
|
{
|
|
833
987
|
windowId = windowId,
|
|
834
|
-
@event = "canGoBack-reply-" + windowId,
|
|
988
|
+
@event = args[^1] ?? "canGoBack-reply-" + windowId,
|
|
835
989
|
data = new() { { "canGoBack", canGoBack.ToString().ToLower() } }
|
|
836
990
|
});
|
|
837
991
|
}
|
|
@@ -847,7 +1001,7 @@ case "setBounds":
|
|
|
847
1001
|
_ipcClient.Send(new IPCResponse
|
|
848
1002
|
{
|
|
849
1003
|
windowId = windowId,
|
|
850
|
-
@event = "canGoForward-reply-" + windowId,
|
|
1004
|
+
@event = args[^1] ?? "canGoForward-reply-" + windowId,
|
|
851
1005
|
data = new() { { "canGoForward", canGoForward.ToString().ToLower() } }
|
|
852
1006
|
});
|
|
853
1007
|
}
|
|
@@ -863,7 +1017,7 @@ case "setBounds":
|
|
|
863
1017
|
_ipcClient.Send(new IPCResponse
|
|
864
1018
|
{
|
|
865
1019
|
windowId = windowId,
|
|
866
|
-
@event = "getURL-reply-" + windowId,
|
|
1020
|
+
@event = args[^1] ?? "getURL-reply-" + windowId,
|
|
867
1021
|
data = new() { { "url", url } }
|
|
868
1022
|
});
|
|
869
1023
|
}
|
|
@@ -879,7 +1033,7 @@ case "setBounds":
|
|
|
879
1033
|
_ipcClient.Send(new IPCResponse
|
|
880
1034
|
{
|
|
881
1035
|
windowId = windowId,
|
|
882
|
-
@event = "getTitle-reply-" + windowId,
|
|
1036
|
+
@event = args[^1] ?? "getTitle-reply-" + windowId,
|
|
883
1037
|
data = new() { { "title", title } }
|
|
884
1038
|
});
|
|
885
1039
|
}
|
|
@@ -951,7 +1105,7 @@ case "setBounds":
|
|
|
951
1105
|
MenuMap[windowId] = menu;
|
|
952
1106
|
}
|
|
953
1107
|
|
|
954
|
-
|
|
1108
|
+
internal static void PopulateMenu(ItemCollection parentItems, JsonArray items, int windowId, string eventType = "menu-action")
|
|
955
1109
|
{
|
|
956
1110
|
foreach (var item in items)
|
|
957
1111
|
{
|
package/core/win/tray.cs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.Windows.Controls;
|
|
4
|
+
using System.Text.Json;
|
|
5
|
+
using System.Text.Json.Nodes;
|
|
6
|
+
using System.Windows;
|
|
7
|
+
using System.Drawing;
|
|
8
|
+
|
|
9
|
+
namespace PositronWindows
|
|
10
|
+
{
|
|
11
|
+
public class TrayManager
|
|
12
|
+
{
|
|
13
|
+
private static TrayManager? _shared;
|
|
14
|
+
public static TrayManager Shared => _shared ??= new TrayManager();
|
|
15
|
+
|
|
16
|
+
public System.Windows.Forms.NotifyIcon? NotifyIcon { get; private set; }
|
|
17
|
+
private ContextMenu? _wpfContextMenu;
|
|
18
|
+
|
|
19
|
+
public void SetupTray()
|
|
20
|
+
{
|
|
21
|
+
if (NotifyIcon == null)
|
|
22
|
+
{
|
|
23
|
+
NotifyIcon = new System.Windows.Forms.NotifyIcon();
|
|
24
|
+
NotifyIcon.Visible = true;
|
|
25
|
+
NotifyIcon.Text = "App";
|
|
26
|
+
NotifyIcon.Icon = SystemIcons.Application;
|
|
27
|
+
|
|
28
|
+
NotifyIcon.MouseUp += (s, e) =>
|
|
29
|
+
{
|
|
30
|
+
if (e.Button == System.Windows.Forms.MouseButtons.Right || e.Button == System.Windows.Forms.MouseButtons.Left)
|
|
31
|
+
{
|
|
32
|
+
if (_wpfContextMenu != null)
|
|
33
|
+
{
|
|
34
|
+
_wpfContextMenu.IsOpen = true;
|
|
35
|
+
if (Application.Current.MainWindow != null)
|
|
36
|
+
{
|
|
37
|
+
Application.Current.MainWindow.Activate();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public void SetMenu(ContextMenu menu)
|
|
46
|
+
{
|
|
47
|
+
_wpfContextMenu = menu;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public void SetTitle(string title)
|
|
51
|
+
{
|
|
52
|
+
if (NotifyIcon != null && !string.IsNullOrEmpty(title))
|
|
53
|
+
{
|
|
54
|
+
NotifyIcon.Text = title.Length > 63 ? title.Substring(0, 63) : title;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public void SetIcon(string iconPath)
|
|
59
|
+
{
|
|
60
|
+
if (NotifyIcon != null && !string.IsNullOrEmpty(iconPath))
|
|
61
|
+
{
|
|
62
|
+
try
|
|
63
|
+
{
|
|
64
|
+
NotifyIcon.Icon = new Icon(iconPath);
|
|
65
|
+
}
|
|
66
|
+
catch (Exception ex)
|
|
67
|
+
{
|
|
68
|
+
Console.WriteLine($"Failed to set tray icon: {ex.Message}");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public static class TrayExtension
|
|
75
|
+
{
|
|
76
|
+
public static void Handle(int windowId, List<string> args)
|
|
77
|
+
{
|
|
78
|
+
if (args.Count == 0)
|
|
79
|
+
{
|
|
80
|
+
Console.WriteLine("tray:setMenu — missing JSON descriptor");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (args[^1] == "setTitle")
|
|
85
|
+
{
|
|
86
|
+
string title = args[0];
|
|
87
|
+
Application.Current.Dispatcher.Invoke(() =>
|
|
88
|
+
{
|
|
89
|
+
TrayManager.Shared.SetTitle(title);
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (args[^1] == "setIcon")
|
|
95
|
+
{
|
|
96
|
+
string iconPath = args[0];
|
|
97
|
+
Application.Current.Dispatcher.Invoke(() =>
|
|
98
|
+
{
|
|
99
|
+
TrayManager.Shared.SetIcon(iconPath);
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var descString = args[0];
|
|
105
|
+
var ctxDescriptor = JsonSerializer.Deserialize<JsonArray>(descString);
|
|
106
|
+
if (ctxDescriptor == null)
|
|
107
|
+
{
|
|
108
|
+
Console.WriteLine("tray:setMenu — invalid JSON descriptor");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if(args[^1] == "setMenu")
|
|
113
|
+
{
|
|
114
|
+
Application.Current.Dispatcher.Invoke(() =>
|
|
115
|
+
{
|
|
116
|
+
var contextMenu = new ContextMenu();
|
|
117
|
+
App.PopulateMenu(contextMenu.Items, ctxDescriptor, windowId, "context-menu-action");
|
|
118
|
+
TrayManager.Shared.SetMenu(contextMenu);
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Application.Current.Dispatcher.Invoke(() =>
|
|
124
|
+
{
|
|
125
|
+
TrayManager.Shared.SetupTray();
|
|
126
|
+
|
|
127
|
+
string? title = args.Count > 1 ? args[1] : "";
|
|
128
|
+
TrayManager.Shared.SetTitle(title);
|
|
129
|
+
|
|
130
|
+
string? imagePath = args.Count > 2 ? args[2] : null;
|
|
131
|
+
if (imagePath != null)
|
|
132
|
+
{
|
|
133
|
+
TrayManager.Shared.SetIcon(imagePath);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var contextMenu = new ContextMenu();
|
|
137
|
+
App.PopulateMenu(contextMenu.Items, ctxDescriptor, windowId, "context-menu-action");
|
|
138
|
+
TrayManager.Shared.SetMenu(contextMenu);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|