termdock 1.4.81 → 1.4.83

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.
Files changed (89) hide show
  1. package/.desktop-dist/main.js +92 -29
  2. package/dist/client/assets/{ModelPreview-DAtTGHFe.js → ModelPreview-9COKuh0b.js} +1 -1
  3. package/dist/client/assets/{abnfDiagram-VRR7QNED-C2ZqXQ0z.js → abnfDiagram-VRR7QNED-CoYbhzM5.js} +1 -1
  4. package/dist/client/assets/{arc-RQoszOFf.js → arc-D5kE-Va-.js} +1 -1
  5. package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-DmWIn5JC.js → architectureDiagram-ZJ3FMSHR-DtvN_aTb.js} +1 -1
  6. package/dist/client/assets/{blockDiagram-677ZJIJ3-dczRSE05.js → blockDiagram-677ZJIJ3-eKU689Jl.js} +1 -1
  7. package/dist/client/assets/{c4Diagram-LMCZKHZV-C1ld89_q.js → c4Diagram-LMCZKHZV-DbA_Q3EV.js} +1 -1
  8. package/dist/client/assets/channel-BYvp3FXi.js +1 -0
  9. package/dist/client/assets/{chunk-2Q5K7J3B-B-F4uVIc.js → chunk-2Q5K7J3B-D_KqdWRv.js} +1 -1
  10. package/dist/client/assets/{chunk-32BRIVSS-BmLUD_wF.js → chunk-32BRIVSS-BE_aJAAt.js} +1 -1
  11. package/dist/client/assets/{chunk-5VM5RSS4-Du0KD_wr.js → chunk-5VM5RSS4-BRA7AHYm.js} +1 -1
  12. package/dist/client/assets/{chunk-EX3LRPZG-EkUNLIBW.js → chunk-EX3LRPZG-DUXfIHPs.js} +1 -1
  13. package/dist/client/assets/{chunk-JWPE2WC7-CgvTkhj6.js → chunk-JWPE2WC7-CIdYJx6T.js} +1 -1
  14. package/dist/client/assets/{chunk-MOJQB5TN-CM53hB9p.js → chunk-MOJQB5TN-f8Q5Hue8.js} +1 -1
  15. package/dist/client/assets/{chunk-RYQCIY6F-C9_sGa-Y.js → chunk-RYQCIY6F-C-uQJpqV.js} +1 -1
  16. package/dist/client/assets/{chunk-V7JOEXUC-B-aMy-AU.js → chunk-V7JOEXUC-CVxxacvm.js} +1 -1
  17. package/dist/client/assets/{chunk-VR4S4FIN-DHjoue2k.js → chunk-VR4S4FIN-CpUTeOjD.js} +1 -1
  18. package/dist/client/assets/{chunk-XXDRQBXY-Czdw7Vmf.js → chunk-XXDRQBXY-jJaHuuq6.js} +1 -1
  19. package/dist/client/assets/classDiagram-OUVF2IWQ-CVEent4o.js +1 -0
  20. package/dist/client/assets/classDiagram-v2-EOCWNBFH-CVEent4o.js +1 -0
  21. package/dist/client/assets/{cose-bilkent-JH36ORCC-YlTECujp.js → cose-bilkent-JH36ORCC-B8tFnjCk.js} +1 -1
  22. package/dist/client/assets/{cynefin-VYW2F7L2-i5oAoFyj.js → cynefin-VYW2F7L2-Dv_ibKvJ.js} +1 -1
  23. package/dist/client/assets/{cynefinDiagram-TSTJHNR4-DKY2SyDs.js → cynefinDiagram-TSTJHNR4-Cx22KETU.js} +1 -1
  24. package/dist/client/assets/{dagre-VKFMJZFB-ncQZHOId.js → dagre-VKFMJZFB-g9JYugts.js} +1 -1
  25. package/dist/client/assets/{diagram-FQU43EPY-BaAv7XVR.js → diagram-FQU43EPY-EfeuXr3A.js} +1 -1
  26. package/dist/client/assets/{diagram-G47NLZAW-B9PP-5Cc.js → diagram-G47NLZAW-BAXOYF1Y.js} +1 -1
  27. package/dist/client/assets/{diagram-NH7WQ7WH-3Dz7jdZP.js → diagram-NH7WQ7WH-NPRLmrs2.js} +1 -1
  28. package/dist/client/assets/{diagram-OA4YK3LP-C9vAtESs.js → diagram-OA4YK3LP-NvTkzKuy.js} +1 -1
  29. package/dist/client/assets/{diagram-WEI45ONY-AJCv9G5V.js → diagram-WEI45ONY-CTBDafBn.js} +1 -1
  30. package/dist/client/assets/{ebnfDiagram-CCIWWBDH-CgjtoK6d.js → ebnfDiagram-CCIWWBDH-xgFOuT_O.js} +1 -1
  31. package/dist/client/assets/{erDiagram-Q63AITRT-lSUVyLyC.js → erDiagram-Q63AITRT-DL3Y9k5A.js} +1 -1
  32. package/dist/client/assets/{flowDiagram-23GEKE2U-DJDWJtSL.js → flowDiagram-23GEKE2U-BA0jWtlM.js} +1 -1
  33. package/dist/client/assets/{ganttDiagram-NO4QXBWP-Dm2nj8eN.js → ganttDiagram-NO4QXBWP-r8QdM4lR.js} +1 -1
  34. package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-DOqw97P9.js → gitGraphDiagram-IHSO6WYX-Cql9WEYT.js} +1 -1
  35. package/dist/client/assets/index-CJZxf0L2.css +1 -0
  36. package/dist/client/assets/index-Cj_Ek_vv.js +450 -0
  37. package/dist/client/assets/{infoDiagram-FWYZ7A6U-BHYJYwz6.js → infoDiagram-FWYZ7A6U-P0vxoZbn.js} +1 -1
  38. package/dist/client/assets/{ishikawaDiagram-FXEZZL3T--sQESh_B.js → ishikawaDiagram-FXEZZL3T-Be2BMer_.js} +1 -1
  39. package/dist/client/assets/{journeyDiagram-5HDEW3XC-BhvterIf.js → journeyDiagram-5HDEW3XC-fp0E8yKC.js} +1 -1
  40. package/dist/client/assets/{kanban-definition-HUTT4EX6-BPqkXX-q.js → kanban-definition-HUTT4EX6-C0j5JSap.js} +1 -1
  41. package/dist/client/assets/{linear-DN-0yadd.js → linear-Dq2UM4lp.js} +1 -1
  42. package/dist/client/assets/{mermaid.core-BaudTVV-.js → mermaid.core-3ETrswZb.js} +4 -4
  43. package/dist/client/assets/{mindmap-definition-LN4V7U3C-7Y0PTkdd.js → mindmap-definition-LN4V7U3C-lG7_7QaK.js} +1 -1
  44. package/dist/client/assets/{pegDiagram-2B236MQR-Fz3TN5dz.js → pegDiagram-2B236MQR-DzfokPIe.js} +1 -1
  45. package/dist/client/assets/{pieDiagram-ENE6RG2P-DvbQwNkV.js → pieDiagram-ENE6RG2P-DcuLg42m.js} +1 -1
  46. package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-n1ozIMS5.js → quadrantDiagram-ABIIQ3AL-Bozzexso.js} +1 -1
  47. package/dist/client/assets/{railroadDiagram-RFXS5EU6-BuMUK6nr.js → railroadDiagram-RFXS5EU6-BQZIDbGg.js} +1 -1
  48. package/dist/client/assets/{requirementDiagram-TGXJPOKE-DN7nUBKP.js → requirementDiagram-TGXJPOKE-DAixTBwf.js} +1 -1
  49. package/dist/client/assets/{sankeyDiagram-HTMAVEWB-Dty_NVwg.js → sankeyDiagram-HTMAVEWB-CcRKh_Bu.js} +1 -1
  50. package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-DljFmSUS.js → sequenceDiagram-DBY2YBRQ-irTBAgTu.js} +1 -1
  51. package/dist/client/assets/{sizeCapture-X5ZJPWSS-B11ck3fI.js → sizeCapture-X5ZJPWSS-Cg5clH15.js} +1 -1
  52. package/dist/client/assets/{stateDiagram-2N3HPSRC-Dien2PD0.js → stateDiagram-2N3HPSRC-BOpbCMCJ.js} +1 -1
  53. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-IkryvtvJ.js +1 -0
  54. package/dist/client/assets/{swimlanes-5IMT3BWC-DB8moKvp.js → swimlanes-5IMT3BWC-D2ypATVq.js} +2 -2
  55. package/dist/client/assets/swimlanesDiagram-G3AALYLV-BQpZfz7G.js +8 -0
  56. package/dist/client/assets/{timeline-definition-FHXFAJF6-BYECv_z8.js → timeline-definition-FHXFAJF6-Dx3xnVku.js} +1 -1
  57. package/dist/client/assets/{vennDiagram-L72KCM5P-N_4upiZI.js → vennDiagram-L72KCM5P-zvGXwai8.js} +1 -1
  58. package/dist/client/assets/{wardleyDiagram-EHGQE667-D7piAaqU.js → wardleyDiagram-EHGQE667-CuxYlYen.js} +1 -1
  59. package/dist/client/assets/{xychartDiagram-FW5EYKEG-D4f5RRCh.js → xychartDiagram-FW5EYKEG-BmCUYY1J.js} +1 -1
  60. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-Bold.woff2 +0 -0
  61. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-BoldItalic.woff2 +0 -0
  62. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-Italic.woff2 +0 -0
  63. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-Regular.woff2 +0 -0
  64. package/dist/client/index.html +8 -8
  65. package/dist/client/sw.js +1 -1
  66. package/dist/server/agent/registry.js +3 -1
  67. package/dist/server/notifications/pushService.js +3 -3
  68. package/dist/server/notifications/pushViewers.js +7 -13
  69. package/dist/server/routes/terminal.js +12 -2
  70. package/dist/server/utils/settings.js +145 -25
  71. package/package.json +1 -1
  72. package/runtime-manifest.json +2 -2
  73. package/scripts/patch-termdock-font.py +1 -1
  74. package/dist/client/assets/channel-y7BJNECq.js +0 -1
  75. package/dist/client/assets/classDiagram-OUVF2IWQ-DVKuq-aF.js +0 -1
  76. package/dist/client/assets/classDiagram-v2-EOCWNBFH-DVKuq-aF.js +0 -1
  77. package/dist/client/assets/index-DnOydypX.css +0 -1
  78. package/dist/client/assets/index-Dw6JjobI.js +0 -451
  79. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-DJwzWamA.js +0 -1
  80. package/dist/client/assets/swimlanesDiagram-G3AALYLV-DGvQqrEj.js +0 -8
  81. package/dist/client/fonts/JetBrainsMonoNL-Bold.woff2 +0 -0
  82. package/dist/client/fonts/JetBrainsMonoNL-BoldItalic.woff2 +0 -0
  83. package/dist/client/fonts/JetBrainsMonoNL-Italic.woff2 +0 -0
  84. package/dist/client/fonts/JetBrainsMonoNL-Regular.woff2 +0 -0
  85. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-Bold.ttf +0 -0
  86. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-BoldItalic.ttf +0 -0
  87. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-Italic.ttf +0 -0
  88. package/dist/client/fonts/JetBrainsMonoNLNerdFontMono-Regular.ttf +0 -0
  89. package/dist/client/fonts/SymbolsNerdFontMono-Regular.woff2 +0 -0
@@ -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
- existingWindow.show();
603
- existingWindow.focus();
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
- workspaceWindow.show();
618
- workspaceWindow.focus();
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?.show();
650
- mainWindow?.focus();
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
- notification.show();
881
- unreadNotificationCount += 1;
882
- app.setBadgeCount(unreadNotificationCount);
883
- // macOS banners auto-dismiss even for "persistent" alerts; bounce the Dock
884
- // icon so the signal survives until the user looks at the app.
885
- if (payload.persistent === true && !sourceWindow?.isFocused()) {
886
- app.dock?.bounce('informational');
887
- }
888
- return true;
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
- await showConnectionCenter();
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 && mainWindow?.webContents.getURL().startsWith('file:') === false) {
1139
+ if (!probe.ok)
1077
1140
  await showConnectionCenter();
1078
- }
1141
+ }
1142
+ else {
1143
+ await showConnectionCenter();
1079
1144
  }
1080
1145
  app.on('activate', () => {
1081
- if (!mainWindow) {
1082
- mainWindow = createDesktopWindow();
1083
- void showConnectionCenter();
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));