termdock 1.4.80 → 1.4.82
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/.desktop-dist/main.js +92 -29
- package/dist/client/assets/{ModelPreview-CmrDbuMi.js → ModelPreview-uuuW36z9.js} +1 -1
- package/dist/client/assets/{abnfDiagram-VRR7QNED-DvLyxbXc.js → abnfDiagram-VRR7QNED-CgsJAMBN.js} +1 -1
- package/dist/client/assets/{arc-BBX7UdZY.js → arc-nGfONK7j.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-B8sc3j7a.js → architectureDiagram-ZJ3FMSHR-VqIb52ve.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-CNJ9V5V_.js → blockDiagram-677ZJIJ3-Ctycpzj4.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-_CWA1evp.js → c4Diagram-LMCZKHZV-usV6soo_.js} +1 -1
- package/dist/client/assets/channel-ChocsIfv.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-JK-SeEvM.js → chunk-2Q5K7J3B-DU0YamXk.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-Ip39MhTz.js → chunk-32BRIVSS-Dr0nf6sE.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-B097V4OG.js → chunk-5VM5RSS4-DXpxW_1Q.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-CFt4JwXM.js → chunk-EX3LRPZG-TYaJAFK5.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7--b2rZed-.js → chunk-JWPE2WC7-TsyOkOqP.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-CB8NwU4y.js → chunk-MOJQB5TN-Dq96SrYS.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-DWk1jStR.js → chunk-RYQCIY6F-BWCbLxVG.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-C7pHG4Q0.js → chunk-V7JOEXUC-9BO-BnZd.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-DQDgkwN7.js → chunk-VR4S4FIN-Dnk8mDGj.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-BAmAzLw4.js → chunk-XXDRQBXY-BY2NC_da.js} +1 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-CSb1NAjZ.js +1 -0
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-CSb1NAjZ.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CUUTuJWI.js → cose-bilkent-JH36ORCC-dv9Q-6KX.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-DoACWEdr.js → cynefin-VYW2F7L2-BQowae5J.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-ChO4ldDH.js → cynefinDiagram-TSTJHNR4-Kht2p1Fu.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-B5oqmt3P.js → dagre-VKFMJZFB-D6WlMxkv.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-Bl29EkJd.js → diagram-FQU43EPY-BRQ3Qvk1.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-0QJ07hDP.js → diagram-G47NLZAW-DJSX1pNs.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-DprKkmJI.js → diagram-NH7WQ7WH-C2lLIYfP.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-Cd2nXW1M.js → diagram-OA4YK3LP-A5nR5Clr.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-xSveIoHi.js → diagram-WEI45ONY-CcKWLGuJ.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-Dt3QPPjd.js → ebnfDiagram-CCIWWBDH-BP6OGq_z.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-DGZP0mtq.js → erDiagram-Q63AITRT-DbVvfYJp.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-lOGaBg78.js → flowDiagram-23GEKE2U-BMDdbmFd.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-DKWkAbg_.js → ganttDiagram-NO4QXBWP-BBHlRRQ3.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-BRWZParb.js → gitGraphDiagram-IHSO6WYX-DVJge5Bl.js} +1 -1
- package/dist/client/assets/index-5mXeCpce.js +450 -0
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-C4u0Ec0G.js → infoDiagram-FWYZ7A6U-Da5Bl_Tw.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-i9rdc9wP.js → ishikawaDiagram-FXEZZL3T-CZnto3cU.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-gkODicIs.js → journeyDiagram-5HDEW3XC-0lAipTsS.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-B40eexoC.js → kanban-definition-HUTT4EX6-Bg782sFb.js} +1 -1
- package/dist/client/assets/{linear-eiIO6OLs.js → linear-DDTi88pf.js} +1 -1
- package/dist/client/assets/{mermaid.core-kusu3cY5.js → mermaid.core-DTYPyTTt.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-Cjq-EZGN.js → mindmap-definition-LN4V7U3C-gLKNgBCg.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR-B9vTDVR9.js → pegDiagram-2B236MQR-CpcMN29b.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-DvXqNv_I.js → pieDiagram-ENE6RG2P-jEajSB-Q.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-5qZK3-0i.js → quadrantDiagram-ABIIQ3AL-vpcCvATU.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-C9PEApzv.js → railroadDiagram-RFXS5EU6-D4mkWGqR.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-Bj4f-Ch0.js → requirementDiagram-TGXJPOKE-I_Gaa0qM.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-gM0pU976.js → sankeyDiagram-HTMAVEWB-Cm3pnDN5.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-DLvk4N-a.js → sequenceDiagram-DBY2YBRQ-ChAnjdkX.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-jmxK7D0y.js → sizeCapture-X5ZJPWSS-BoYgXCFC.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC-WWPzO4yf.js → stateDiagram-2N3HPSRC-Ix5BDYsA.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-dfMyyi2U.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-BkRglcKT.js → swimlanes-5IMT3BWC-Ch7Tn6tz.js} +2 -2
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-BIIy0a1S.js +8 -0
- package/dist/client/assets/{timeline-definition-FHXFAJF6-86tKYj2E.js → timeline-definition-FHXFAJF6-l2faw2ZI.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-CBuQIAdT.js → vennDiagram-L72KCM5P-B2YA7BKx.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-CLg4pdo2.js → wardleyDiagram-EHGQE667-afTewuti.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-Bk4V779_.js → xychartDiagram-FW5EYKEG-DXuwDpDr.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/agent/registry.js +3 -1
- package/dist/server/notifications/pushService.js +3 -3
- package/dist/server/notifications/pushViewers.js +7 -13
- package/dist/server/routes/terminal.js +25 -3
- package/dist/server/utils/settings.js +15 -0
- package/package.json +1 -1
- package/runtime-manifest.json +2 -2
- package/dist/client/assets/channel-CWvjdrhd.js +0 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-P0m6qC4S.js +0 -1
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-P0m6qC4S.js +0 -1
- package/dist/client/assets/index-PzxPlsI7.js +0 -451
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Dwj1UFsK.js +0 -1
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-C6gnhFd4.js +0 -8
package/.desktop-dist/main.js
CHANGED
|
@@ -6,9 +6,10 @@ import os from 'node:os';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import { checkForDesktopUpdates, configureDesktopUpdater, ensureLatestRuntime, } from './updater.js';
|
|
9
|
+
import { checkForDesktopUpdates, configureDesktopUpdater, ensureLatestRuntime, getDesktopUpdateState, installDownloadedDesktopUpdate, subscribeDesktopUpdateState, } from './updater.js';
|
|
10
10
|
import { resolvePackagedRuntime, rollbackDownloadedRuntime, } from './runtime.js';
|
|
11
11
|
import { isExternalLinkStagingUrl, isSafeExternalUrl } from './externalLinks.js';
|
|
12
|
+
import { shouldThrottleDesktopRenderer } from './windowPolicy.js';
|
|
12
13
|
const execFileAsync = promisify(execFile);
|
|
13
14
|
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
const projectRoot = path.resolve(currentDir, '..');
|
|
@@ -30,13 +31,25 @@ function clearUnreadNotifications() {
|
|
|
30
31
|
let mainWindow = null;
|
|
31
32
|
const serviceWindows = new Map();
|
|
32
33
|
const windowServiceOrigins = new WeakMap();
|
|
34
|
+
let lastFocusedServiceWindow = null;
|
|
33
35
|
let isQuitting = false;
|
|
34
36
|
function focusedWorkspaceWindow() {
|
|
35
37
|
const focused = BrowserWindow.getFocusedWindow();
|
|
36
38
|
if (focused && windowServiceOrigins.has(focused))
|
|
37
39
|
return focused;
|
|
40
|
+
if (lastFocusedServiceWindow && !lastFocusedServiceWindow.isDestroyed()) {
|
|
41
|
+
return lastFocusedServiceWindow;
|
|
42
|
+
}
|
|
38
43
|
return [...serviceWindows.values()].find((window) => !window.isDestroyed()) ?? null;
|
|
39
44
|
}
|
|
45
|
+
function showAndFocusWindow(window) {
|
|
46
|
+
if (windowServiceOrigins.has(window))
|
|
47
|
+
lastFocusedServiceWindow = window;
|
|
48
|
+
if (window.isMinimized())
|
|
49
|
+
window.restore();
|
|
50
|
+
window.show();
|
|
51
|
+
window.focus();
|
|
52
|
+
}
|
|
40
53
|
function serviceLabel(url) {
|
|
41
54
|
const normalized = normalizeServiceUrl(url);
|
|
42
55
|
const saved = readDesktopConfig().connections.find((entry) => entry.url === normalized);
|
|
@@ -599,8 +612,14 @@ async function connectWindow(rawUrl) {
|
|
|
599
612
|
const key = parsed.origin;
|
|
600
613
|
const existingWindow = serviceWindows.get(key);
|
|
601
614
|
if (existingWindow && !existingWindow.isDestroyed()) {
|
|
602
|
-
|
|
603
|
-
|
|
615
|
+
const config = readDesktopConfig();
|
|
616
|
+
config.lastConnectionUrl = probe.url;
|
|
617
|
+
const existing = config.connections.find((entry) => entry.url === probe.url);
|
|
618
|
+
if (existing)
|
|
619
|
+
existing.lastConnectedAt = Date.now();
|
|
620
|
+
writeDesktopConfig(config);
|
|
621
|
+
mainWindow?.hide();
|
|
622
|
+
showAndFocusWindow(existingWindow);
|
|
604
623
|
return probe;
|
|
605
624
|
}
|
|
606
625
|
const workspaceWindow = createDesktopWindow({ serviceOrigin: key, label: serviceLabel(probe.url) });
|
|
@@ -614,8 +633,8 @@ async function connectWindow(rawUrl) {
|
|
|
614
633
|
if (existing)
|
|
615
634
|
existing.lastConnectedAt = Date.now();
|
|
616
635
|
writeDesktopConfig(config);
|
|
617
|
-
|
|
618
|
-
workspaceWindow
|
|
636
|
+
mainWindow?.hide();
|
|
637
|
+
showAndFocusWindow(workspaceWindow);
|
|
619
638
|
return probe;
|
|
620
639
|
}
|
|
621
640
|
catch (error) {
|
|
@@ -646,8 +665,8 @@ async function showConnectionCenter() {
|
|
|
646
665
|
? path.join(process.resourcesPath, 'renderer', 'index.html')
|
|
647
666
|
: path.join(projectRoot, 'desktop', 'renderer', 'index.html');
|
|
648
667
|
await mainWindow?.loadFile(rendererPath);
|
|
649
|
-
mainWindow
|
|
650
|
-
|
|
668
|
+
if (mainWindow)
|
|
669
|
+
showAndFocusWindow(mainWindow);
|
|
651
670
|
}
|
|
652
671
|
function createDesktopWindow(options) {
|
|
653
672
|
const window = new BrowserWindow({
|
|
@@ -668,6 +687,10 @@ function createDesktopWindow(options) {
|
|
|
668
687
|
nodeIntegration: false,
|
|
669
688
|
contextIsolation: true,
|
|
670
689
|
sandbox: true,
|
|
690
|
+
// A service window owns live terminal streams. If Chromium suspends it
|
|
691
|
+
// while macOS places the app in the background, tens of seconds of WS
|
|
692
|
+
// messages can hit the renderer in one burst when the user returns.
|
|
693
|
+
backgroundThrottling: shouldThrottleDesktopRenderer(Boolean(options)),
|
|
671
694
|
},
|
|
672
695
|
});
|
|
673
696
|
if (options)
|
|
@@ -786,12 +809,16 @@ function createDesktopWindow(options) {
|
|
|
786
809
|
window.on('closed', () => {
|
|
787
810
|
if (mainWindow === window)
|
|
788
811
|
mainWindow = null;
|
|
812
|
+
if (lastFocusedServiceWindow === window)
|
|
813
|
+
lastFocusedServiceWindow = null;
|
|
789
814
|
const serviceOrigin = windowServiceOrigins.get(window);
|
|
790
815
|
if (serviceOrigin && serviceWindows.get(serviceOrigin) === window) {
|
|
791
816
|
serviceWindows.delete(serviceOrigin);
|
|
792
817
|
}
|
|
793
818
|
});
|
|
794
819
|
window.on('focus', () => {
|
|
820
|
+
if (windowServiceOrigins.has(window))
|
|
821
|
+
lastFocusedServiceWindow = window;
|
|
795
822
|
// User is looking at the app — the Dock badge has served its purpose.
|
|
796
823
|
clearUnreadNotifications();
|
|
797
824
|
});
|
|
@@ -834,6 +861,9 @@ function installIpcHandlers() {
|
|
|
834
861
|
ipcMain.handle('desktop:connect', (_event, url) => connectWindow(url));
|
|
835
862
|
ipcMain.handle('desktop:start-local', () => startLocalService());
|
|
836
863
|
ipcMain.handle('desktop:install-cli', () => installCli());
|
|
864
|
+
ipcMain.handle('desktop:update-state', () => getDesktopUpdateState());
|
|
865
|
+
ipcMain.handle('desktop:check-update', () => checkForDesktopUpdates());
|
|
866
|
+
ipcMain.handle('desktop:install-update', () => installDownloadedDesktopUpdate());
|
|
837
867
|
ipcMain.handle('desktop:show-connection-center', () => showConnectionCenter());
|
|
838
868
|
ipcMain.handle('desktop:reveal-data-directory', async () => {
|
|
839
869
|
fs.mkdirSync(termdockDir, { recursive: true, mode: 0o700 });
|
|
@@ -841,6 +871,9 @@ function installIpcHandlers() {
|
|
|
841
871
|
if (error)
|
|
842
872
|
throw new Error(error);
|
|
843
873
|
});
|
|
874
|
+
ipcMain.handle('desktop:open-notification-settings', async () => {
|
|
875
|
+
await shell.openExternal('x-apple.systempreferences:com.apple.Notifications-Settings.extension?bundleId=com.jovines.termdock');
|
|
876
|
+
});
|
|
844
877
|
ipcMain.handle('desktop:show-notification', (event, payload) => {
|
|
845
878
|
if (!Notification.isSupported() || typeof payload?.title !== 'string')
|
|
846
879
|
return false;
|
|
@@ -877,15 +910,41 @@ function installIpcHandlers() {
|
|
|
877
910
|
targetWindow?.webContents.send('desktop:focus-session', payload.sessionId);
|
|
878
911
|
}
|
|
879
912
|
});
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
913
|
+
return new Promise((resolve) => {
|
|
914
|
+
let settled = false;
|
|
915
|
+
const finish = (delivered, error) => {
|
|
916
|
+
if (settled)
|
|
917
|
+
return;
|
|
918
|
+
settled = true;
|
|
919
|
+
clearTimeout(timeout);
|
|
920
|
+
if (!delivered) {
|
|
921
|
+
if (tag && activeNotifications.get(tag) === notification) {
|
|
922
|
+
activeNotifications.delete(tag);
|
|
923
|
+
}
|
|
924
|
+
if (error)
|
|
925
|
+
console.error(`[desktop notification] ${error}`);
|
|
926
|
+
resolve(false);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
unreadNotificationCount += 1;
|
|
930
|
+
app.setBadgeCount(unreadNotificationCount);
|
|
931
|
+
// macOS banners auto-dismiss even for "persistent" alerts; bounce the Dock
|
|
932
|
+
// icon so the signal survives until the user looks at the app.
|
|
933
|
+
if (payload.persistent === true && !sourceWindow?.isFocused()) {
|
|
934
|
+
app.dock?.bounce('informational');
|
|
935
|
+
}
|
|
936
|
+
resolve(true);
|
|
937
|
+
};
|
|
938
|
+
notification.once('show', () => finish(true));
|
|
939
|
+
notification.once('failed', (_notificationEvent, error) => finish(false, error));
|
|
940
|
+
// macOS can silently discard notifications when this user disabled the
|
|
941
|
+
// app in System Settings. Do not report success or increment the Dock
|
|
942
|
+
// badge unless Electron confirms delivery.
|
|
943
|
+
const timeout = setTimeout(() => {
|
|
944
|
+
finish(false, 'macOS did not confirm notification delivery');
|
|
945
|
+
}, 2_000);
|
|
946
|
+
notification.show();
|
|
947
|
+
});
|
|
889
948
|
});
|
|
890
949
|
}
|
|
891
950
|
function installMenu() {
|
|
@@ -933,7 +992,7 @@ function installMenu() {
|
|
|
933
992
|
click: () => focusedWorkspaceWindow()?.webContents.send('desktop:open-settings'),
|
|
934
993
|
},
|
|
935
994
|
{
|
|
936
|
-
label: '
|
|
995
|
+
label: '新建连接或切换服务…',
|
|
937
996
|
accelerator: 'CmdOrCtrl+N',
|
|
938
997
|
click: () => void showConnectionCenter(),
|
|
939
998
|
},
|
|
@@ -950,7 +1009,7 @@ function installMenu() {
|
|
|
950
1009
|
},
|
|
951
1010
|
{
|
|
952
1011
|
label: '检查更新…',
|
|
953
|
-
click: () => void checkForDesktopUpdates(true),
|
|
1012
|
+
click: () => void checkForDesktopUpdates({ presentNativeDialogs: true }),
|
|
954
1013
|
},
|
|
955
1014
|
{
|
|
956
1015
|
label: '打开 Termdock 数据目录',
|
|
@@ -1067,25 +1126,29 @@ app.whenReady().then(async () => {
|
|
|
1067
1126
|
configureLocalServiceCertificateTrust();
|
|
1068
1127
|
installIpcHandlers();
|
|
1069
1128
|
installMenu();
|
|
1070
|
-
mainWindow = createDesktopWindow();
|
|
1071
1129
|
configureDesktopUpdater(showDesktopMessageBox);
|
|
1072
|
-
|
|
1130
|
+
subscribeDesktopUpdateState((state) => {
|
|
1131
|
+
for (const window of BrowserWindow.getAllWindows()) {
|
|
1132
|
+
if (!window.isDestroyed())
|
|
1133
|
+
window.webContents.send('desktop:update-state-changed', state);
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1073
1136
|
const lastConnectionUrl = readDesktopConfig().lastConnectionUrl;
|
|
1074
1137
|
if (lastConnectionUrl) {
|
|
1075
1138
|
const probe = await connectWindow(lastConnectionUrl);
|
|
1076
|
-
if (!probe.ok
|
|
1139
|
+
if (!probe.ok)
|
|
1077
1140
|
await showConnectionCenter();
|
|
1078
|
-
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
await showConnectionCenter();
|
|
1079
1144
|
}
|
|
1080
1145
|
app.on('activate', () => {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
else {
|
|
1086
|
-
mainWindow.show();
|
|
1087
|
-
mainWindow.focus();
|
|
1146
|
+
const workspaceWindow = focusedWorkspaceWindow();
|
|
1147
|
+
if (workspaceWindow) {
|
|
1148
|
+
showAndFocusWindow(workspaceWindow);
|
|
1149
|
+
return;
|
|
1088
1150
|
}
|
|
1151
|
+
void showConnectionCenter();
|
|
1089
1152
|
});
|
|
1090
1153
|
}).catch((error) => {
|
|
1091
1154
|
void dialog.showErrorBox('Termdock 启动失败', error instanceof Error ? error.message : String(error));
|