desktop-pet-app 0.2.5 → 0.2.9
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/README.md
CHANGED
package/bin/cli.js
CHANGED
|
@@ -25,7 +25,8 @@ const appPath = path.join(__dirname, '..')
|
|
|
25
25
|
|
|
26
26
|
const child = spawn(electron, [appPath, ...process.argv.slice(2)], {
|
|
27
27
|
stdio: 'ignore',
|
|
28
|
-
detached: true
|
|
28
|
+
detached: true,
|
|
29
|
+
env: { ...process.env, DESKTOP_PET_NPM_DISTRIBUTION: '1' }
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
child.unref()
|
|
Binary file
|
|
Binary file
|
package/out/main/index.js
CHANGED
|
@@ -23,11 +23,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
const electron = require("electron");
|
|
25
25
|
const node_path = require("node:path");
|
|
26
|
+
const node_fs = require("node:fs");
|
|
26
27
|
const path = require("path");
|
|
27
28
|
const os = require("os");
|
|
28
29
|
const node_crypto = require("node:crypto");
|
|
29
30
|
const node_http = require("node:http");
|
|
30
|
-
const node_fs = require("node:fs");
|
|
31
31
|
const node_os = require("node:os");
|
|
32
32
|
const crypto = require("crypto");
|
|
33
33
|
const WebSocket = require("ws");
|
|
@@ -66,7 +66,20 @@ function levelFromEnv() {
|
|
|
66
66
|
const v = (process.env.DESKTOP_PET_LOG_LEVEL ?? "info").toLowerCase();
|
|
67
67
|
return v === "debug" || v === "warn" || v === "error" ? v : "info";
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
function mainLogDirectory() {
|
|
70
|
+
return node_path.join(electron.app.getPath("userData"), "logs");
|
|
71
|
+
}
|
|
72
|
+
function writeMainLog(line) {
|
|
73
|
+
console.log(line);
|
|
74
|
+
try {
|
|
75
|
+
const directory = mainLogDirectory();
|
|
76
|
+
node_fs.mkdirSync(directory, { recursive: true });
|
|
77
|
+
node_fs.appendFileSync(node_path.join(directory, "main.log"), `${line}
|
|
78
|
+
`, "utf8");
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const logger = new Logger(levelFromEnv(), writeMainLog);
|
|
70
83
|
const PORT_FILE = process.env.DESKTOP_PET_PORT_FILE?.trim() || path.join(os.homedir(), ".desktop-pet-port");
|
|
71
84
|
const LOCAL_PET_PROTOCOL_VERSION = "1.0";
|
|
72
85
|
const LOCAL_PET_MAX_BODY_BYTES = 64 * 1024;
|
|
@@ -268,9 +281,11 @@ class PetManager {
|
|
|
268
281
|
pets = /* @__PURE__ */ new Map();
|
|
269
282
|
lastGreetAt = /* @__PURE__ */ new Map();
|
|
270
283
|
timer;
|
|
284
|
+
petsVisible = true;
|
|
271
285
|
register(id, win, skin, identity) {
|
|
272
286
|
this.pets.set(id, { id, win, skin, identity });
|
|
273
287
|
win.on("closed", () => this.pets.delete(id));
|
|
288
|
+
if (!this.petsVisible) win.hide();
|
|
274
289
|
if (!this.timer) this.timer = setInterval(() => this.checkProximity(), PROXIMITY_SCAN_MS);
|
|
275
290
|
logger.info("pet registered", { id, skin, total: this.pets.size });
|
|
276
291
|
}
|
|
@@ -282,6 +297,17 @@ class PetManager {
|
|
|
282
297
|
(pet) => !pet.win.isDestroyed() && pet.identity.kind === "primary"
|
|
283
298
|
);
|
|
284
299
|
}
|
|
300
|
+
arePetsVisible() {
|
|
301
|
+
return this.petsVisible;
|
|
302
|
+
}
|
|
303
|
+
setPetsVisible(visible) {
|
|
304
|
+
this.petsVisible = visible;
|
|
305
|
+
for (const pet of this.pets.values()) {
|
|
306
|
+
if (pet.win.isDestroyed()) continue;
|
|
307
|
+
if (visible) pet.win.showInactive();
|
|
308
|
+
else pet.win.hide();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
285
311
|
findRemoteFriend(username) {
|
|
286
312
|
const normalized = username.trim().toLowerCase();
|
|
287
313
|
return [...this.pets.values()].find(
|
|
@@ -1653,6 +1679,7 @@ async function fallbackChat(operationId) {
|
|
|
1653
1679
|
const REQUEST_TIMEOUT_MS = 6e3;
|
|
1654
1680
|
const DOWNLOAD_TIMEOUT_MS = 10 * 6e4;
|
|
1655
1681
|
const LEGACY_OFFICIAL_HTTP_HOST = "47.94.20.104";
|
|
1682
|
+
const MAC_BUNDLE_ID = "com.xinshu.desktoppet";
|
|
1656
1683
|
let status = { kind: "idle" };
|
|
1657
1684
|
function updateCheckURL() {
|
|
1658
1685
|
const override = process.env.DESKTOP_PET_UPDATE_API?.trim();
|
|
@@ -1669,6 +1696,7 @@ function getUpdateStatus() {
|
|
|
1669
1696
|
return status;
|
|
1670
1697
|
}
|
|
1671
1698
|
async function checkForUpdates(currentVersion = electron.app.getVersion()) {
|
|
1699
|
+
if (status.kind === "downloading") return status;
|
|
1672
1700
|
status = { kind: "checking" };
|
|
1673
1701
|
try {
|
|
1674
1702
|
const endpoint = new URL(updateCheckURL());
|
|
@@ -1713,7 +1741,7 @@ function updateCheckHeaders(endpoint) {
|
|
|
1713
1741
|
async function downloadAvailableUpdate() {
|
|
1714
1742
|
const available = status.kind === "available" ? status.update : void 0;
|
|
1715
1743
|
if (!available) return status;
|
|
1716
|
-
status = { kind: "downloading", update: available };
|
|
1744
|
+
status = { kind: "downloading", update: available, downloadedBytes: 0 };
|
|
1717
1745
|
const updateDirectory = node_path.join(electron.app.getPath("userData"), "updates");
|
|
1718
1746
|
const artifactPath = node_path.join(updateDirectory, `desktop-pet-${available.version}${installerExtension(available.releaseUrl)}`);
|
|
1719
1747
|
const temporaryPath = `${artifactPath}.${node_crypto.randomUUID()}.download`;
|
|
@@ -1730,7 +1758,13 @@ async function downloadAvailableUpdate() {
|
|
|
1730
1758
|
if (response.url && new URL(response.url).protocol !== "https:") {
|
|
1731
1759
|
throw new Error("更新下载地址必须使用 HTTPS");
|
|
1732
1760
|
}
|
|
1733
|
-
const
|
|
1761
|
+
const totalBytes = parseContentLength(response.headers.get("content-length"));
|
|
1762
|
+
let downloadedBytes = 0;
|
|
1763
|
+
status = { kind: "downloading", update: available, downloadedBytes, totalBytes };
|
|
1764
|
+
const digest = await saveAndHash(response.body, temporaryPath, (chunkBytes) => {
|
|
1765
|
+
downloadedBytes += chunkBytes;
|
|
1766
|
+
status = { kind: "downloading", update: available, downloadedBytes, totalBytes };
|
|
1767
|
+
});
|
|
1734
1768
|
if (digest !== available.sha256) throw new Error("更新包校验失败,文件未安装");
|
|
1735
1769
|
await promises.rm(artifactPath, { force: true });
|
|
1736
1770
|
await moveFile(temporaryPath, artifactPath);
|
|
@@ -1747,7 +1781,7 @@ async function downloadAvailableUpdate() {
|
|
|
1747
1781
|
}
|
|
1748
1782
|
async function installDownloadedUpdate() {
|
|
1749
1783
|
if (status.kind !== "downloaded") return void 0;
|
|
1750
|
-
if (!
|
|
1784
|
+
if (!supportsInAppInstall()) throw new Error("当前运行方式不支持应用内安装,请使用正式发布版");
|
|
1751
1785
|
if (process.platform !== "darwin" && process.platform !== "win32") {
|
|
1752
1786
|
throw new Error("当前平台暂不支持应用内安装,请下载对应安装包更新");
|
|
1753
1787
|
}
|
|
@@ -1759,7 +1793,8 @@ async function installDownloadedUpdate() {
|
|
|
1759
1793
|
downloaded.artifactPath,
|
|
1760
1794
|
downloaded.update.sha256,
|
|
1761
1795
|
process.platform,
|
|
1762
|
-
process.execPath
|
|
1796
|
+
process.execPath,
|
|
1797
|
+
JSON.stringify(restartArgumentsAfterUpdate())
|
|
1763
1798
|
], {
|
|
1764
1799
|
detached: true,
|
|
1765
1800
|
stdio: "ignore",
|
|
@@ -1771,11 +1806,18 @@ async function installDownloadedUpdate() {
|
|
|
1771
1806
|
electron.app.quit();
|
|
1772
1807
|
return downloaded.update;
|
|
1773
1808
|
}
|
|
1774
|
-
|
|
1809
|
+
function supportsInAppInstall() {
|
|
1810
|
+
return electron.app.isPackaged || process.env.DESKTOP_PET_NPM_DISTRIBUTION === "1";
|
|
1811
|
+
}
|
|
1812
|
+
function restartArgumentsAfterUpdate() {
|
|
1813
|
+
return !electron.app.isPackaged && process.argv[1] ? [process.argv[1]] : [];
|
|
1814
|
+
}
|
|
1815
|
+
async function saveAndHash(body, destination, onChunk) {
|
|
1775
1816
|
const hash = node_crypto.createHash("sha256");
|
|
1776
1817
|
const meter = new node_stream.Transform({
|
|
1777
1818
|
transform(chunk, _encoding, done) {
|
|
1778
1819
|
hash.update(chunk);
|
|
1820
|
+
onChunk(chunk.length);
|
|
1779
1821
|
done(null, chunk);
|
|
1780
1822
|
}
|
|
1781
1823
|
});
|
|
@@ -1786,6 +1828,23 @@ async function saveAndHash(body, destination) {
|
|
|
1786
1828
|
);
|
|
1787
1829
|
return hash.digest("hex");
|
|
1788
1830
|
}
|
|
1831
|
+
function parseContentLength(value) {
|
|
1832
|
+
if (!value) return void 0;
|
|
1833
|
+
const bytes = Number(value);
|
|
1834
|
+
return Number.isSafeInteger(bytes) && bytes > 0 ? bytes : void 0;
|
|
1835
|
+
}
|
|
1836
|
+
function downloadProgressText(progress) {
|
|
1837
|
+
if (progress.totalBytes) {
|
|
1838
|
+
const percent = Math.min(100, Math.floor(progress.downloadedBytes / progress.totalBytes * 100));
|
|
1839
|
+
return `${percent}%`;
|
|
1840
|
+
}
|
|
1841
|
+
return formatBytes(progress.downloadedBytes);
|
|
1842
|
+
}
|
|
1843
|
+
function formatBytes(bytes) {
|
|
1844
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1845
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1846
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1847
|
+
}
|
|
1789
1848
|
async function moveFile(source, destination) {
|
|
1790
1849
|
const { rename } = await import("node:fs/promises");
|
|
1791
1850
|
await rename(source, destination);
|
|
@@ -1802,8 +1861,9 @@ const { dirname, join } = require('node:path')
|
|
|
1802
1861
|
const { execFile, spawn } = require('node:child_process')
|
|
1803
1862
|
const { promisify } = require('node:util')
|
|
1804
1863
|
|
|
1805
|
-
const [, , parentPidText, sourcePath, expectedSHA256, platform,
|
|
1864
|
+
const [, , parentPidText, sourcePath, expectedSHA256, platform, fallbackExecutable, fallbackArgsJSON] = process.argv
|
|
1806
1865
|
const parentPid = Number(parentPidText)
|
|
1866
|
+
const fallbackArgs = JSON.parse(fallbackArgsJSON || '[]')
|
|
1807
1867
|
const run = promisify(execFile)
|
|
1808
1868
|
|
|
1809
1869
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
@@ -1817,7 +1877,22 @@ const sha256 = (path) => new Promise((resolve, reject) => {
|
|
|
1817
1877
|
input.on('error', reject)
|
|
1818
1878
|
input.on('end', () => resolve(hash.digest('hex')))
|
|
1819
1879
|
})
|
|
1820
|
-
const
|
|
1880
|
+
const restartFallback = () => {
|
|
1881
|
+
const env = { ...process.env }
|
|
1882
|
+
delete env.ELECTRON_RUN_AS_NODE
|
|
1883
|
+
const child = spawn(fallbackExecutable, fallbackArgs, { detached: true, stdio: 'ignore', env })
|
|
1884
|
+
child.on('error', () => undefined)
|
|
1885
|
+
child.unref()
|
|
1886
|
+
}
|
|
1887
|
+
const restartInstalledApp = async () => {
|
|
1888
|
+
// 安装器可能会保留已有应用的位置(开发构建尤其如此),不能假设它在 /Applications。
|
|
1889
|
+
// 让 LaunchServices 按 bundle ID 查找刚安装的实际应用位置。
|
|
1890
|
+
if (platform === 'darwin') {
|
|
1891
|
+
await run('/usr/bin/open', ['-b', '${MAC_BUNDLE_ID}'])
|
|
1892
|
+
return
|
|
1893
|
+
}
|
|
1894
|
+
restartFallback()
|
|
1895
|
+
}
|
|
1821
1896
|
const shellQuote = (value) => "'" + value.replace(/'/g, "'\\''") + "'"
|
|
1822
1897
|
|
|
1823
1898
|
async function install() {
|
|
@@ -1837,17 +1912,28 @@ async function install() {
|
|
|
1837
1912
|
async function main() {
|
|
1838
1913
|
while (parentIsRunning()) await sleep(200)
|
|
1839
1914
|
|
|
1915
|
+
let installed = false
|
|
1840
1916
|
try {
|
|
1841
1917
|
if (await sha256(sourcePath) !== expectedSHA256) throw new Error('downloaded update hash changed')
|
|
1842
1918
|
await install()
|
|
1919
|
+
installed = true
|
|
1843
1920
|
} catch (error) {
|
|
1844
1921
|
await fs.writeFile(join(dirname(sourcePath), 'last-install-error.log'), String(error)).catch(() => undefined)
|
|
1845
1922
|
} finally {
|
|
1846
|
-
|
|
1923
|
+
if (installed) {
|
|
1924
|
+
try {
|
|
1925
|
+
await restartInstalledApp()
|
|
1926
|
+
} catch (error) {
|
|
1927
|
+
await fs.writeFile(join(dirname(sourcePath), 'last-restart-error.log'), String(error)).catch(() => undefined)
|
|
1928
|
+
restartFallback()
|
|
1929
|
+
}
|
|
1930
|
+
} else {
|
|
1931
|
+
restartFallback()
|
|
1932
|
+
}
|
|
1847
1933
|
}
|
|
1848
1934
|
}
|
|
1849
1935
|
|
|
1850
|
-
main().catch(() =>
|
|
1936
|
+
main().catch(() => restartFallback())
|
|
1851
1937
|
`;
|
|
1852
1938
|
function installerExtension(releaseUrl) {
|
|
1853
1939
|
const pathname = new URL(releaseUrl).pathname.toLowerCase();
|
|
@@ -1957,6 +2043,7 @@ function createPetWindow(skin, options) {
|
|
|
1957
2043
|
const x = options.near?.x ?? workArea.x + workArea.width - W - 120;
|
|
1958
2044
|
const y = options.near?.y ?? workArea.y + workArea.height - H - 60;
|
|
1959
2045
|
const win = new electron.BrowserWindow({
|
|
2046
|
+
show: petManager.arePetsVisible(),
|
|
1960
2047
|
width: W,
|
|
1961
2048
|
height: H,
|
|
1962
2049
|
x,
|
|
@@ -2090,11 +2177,20 @@ function createPetWindow(skin, options) {
|
|
|
2090
2177
|
dispatch({ type: "bubble", text: result.message, ttl: 6e3 }, id);
|
|
2091
2178
|
}
|
|
2092
2179
|
},
|
|
2180
|
+
{
|
|
2181
|
+
label: "打开日志文件夹",
|
|
2182
|
+
click: () => {
|
|
2183
|
+
void electron.shell.openPath(mainLogDirectory()).then((error) => {
|
|
2184
|
+
if (error) dispatch({ type: "bubble", text: "无法打开日志文件夹", ttl: 6e3 }, id);
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
},
|
|
2093
2188
|
{
|
|
2094
2189
|
label: updateMenuLabel(),
|
|
2095
2190
|
submenu: updateMenuItems(id)
|
|
2096
2191
|
},
|
|
2097
2192
|
{ type: "separator" },
|
|
2193
|
+
{ label: "隐藏所有桌面宠物", click: () => petManager.setPetsVisible(false) },
|
|
2098
2194
|
...canSendAwayPet(identity) ? [{ label: "送走这只宠物", click: () => win.close() }] : [],
|
|
2099
2195
|
{ label: "退出", click: () => electron.app.quit() }
|
|
2100
2196
|
]);
|
|
@@ -2115,7 +2211,9 @@ function createPetWindow(skin, options) {
|
|
|
2115
2211
|
function updateMenuLabel() {
|
|
2116
2212
|
const status2 = getUpdateStatus();
|
|
2117
2213
|
if (status2.kind === "available") return `软件更新(v${status2.update.version} 可下载)`;
|
|
2118
|
-
if (status2.kind === "downloading")
|
|
2214
|
+
if (status2.kind === "downloading") {
|
|
2215
|
+
return `软件更新(v${status2.update.version} 下载 ${downloadProgressText(status2)})`;
|
|
2216
|
+
}
|
|
2119
2217
|
if (status2.kind === "downloaded") return `软件更新(v${status2.update.version} 已下载)`;
|
|
2120
2218
|
return "软件更新";
|
|
2121
2219
|
}
|
|
@@ -2131,6 +2229,12 @@ function updateMenuItems(petId) {
|
|
|
2131
2229
|
dispatch({ type: "bubble", text: `发现 v${result.update.version},可从“软件更新”安装`, ttl: 6e3 }, petId);
|
|
2132
2230
|
} else if (result.kind === "up-to-date") {
|
|
2133
2231
|
dispatch({ type: "bubble", text: "已经是最新版本啦", ttl: 4500 }, petId);
|
|
2232
|
+
} else if (result.kind === "downloading") {
|
|
2233
|
+
dispatch({
|
|
2234
|
+
type: "bubble",
|
|
2235
|
+
text: `v${result.update.version} 正在下载:${downloadProgressText(result)}`,
|
|
2236
|
+
ttl: 4500
|
|
2237
|
+
}, petId);
|
|
2134
2238
|
} else if (result.kind === "error") {
|
|
2135
2239
|
dispatch({ type: "bubble", text: result.message, ttl: 7e3 }, petId);
|
|
2136
2240
|
}
|
|
@@ -2154,7 +2258,10 @@ function updateMenuItems(petId) {
|
|
|
2154
2258
|
});
|
|
2155
2259
|
}
|
|
2156
2260
|
if (status2.kind === "downloading") {
|
|
2157
|
-
items.splice(1, 0, {
|
|
2261
|
+
items.splice(1, 0, {
|
|
2262
|
+
label: `正在下载 v${status2.update.version} · ${downloadProgressText(status2)}`,
|
|
2263
|
+
enabled: false
|
|
2264
|
+
});
|
|
2158
2265
|
}
|
|
2159
2266
|
if (status2.kind === "downloaded") {
|
|
2160
2267
|
items.splice(1, 0, {
|
|
@@ -2993,6 +3100,39 @@ async function dutyLoop(adapter) {
|
|
|
2993
3100
|
}
|
|
2994
3101
|
}
|
|
2995
3102
|
}
|
|
3103
|
+
const trayIcon1xPath = path.join(__dirname, "./chunks/tray-iconTemplate-BIK86F-0.png");
|
|
3104
|
+
const trayIcon2xPath = path.join(__dirname, "./chunks/tray-iconTemplate@2x-B19HpcaE.png");
|
|
3105
|
+
let tray;
|
|
3106
|
+
function createMenuBar() {
|
|
3107
|
+
if (process.platform !== "darwin" || tray) return;
|
|
3108
|
+
tray = new electron.Tray(createPetTemplateImage());
|
|
3109
|
+
tray.setToolTip("Desktop Pet");
|
|
3110
|
+
tray.on("click", showMenu);
|
|
3111
|
+
tray.on("right-click", showMenu);
|
|
3112
|
+
}
|
|
3113
|
+
function destroyMenuBar() {
|
|
3114
|
+
tray?.destroy();
|
|
3115
|
+
tray = void 0;
|
|
3116
|
+
}
|
|
3117
|
+
function showMenu() {
|
|
3118
|
+
tray?.popUpContextMenu(electron.Menu.buildFromTemplate([
|
|
3119
|
+
{ label: `Desktop Pet v${electron.app.getVersion()}`, enabled: false },
|
|
3120
|
+
{ type: "separator" },
|
|
3121
|
+
{
|
|
3122
|
+
label: petManager.arePetsVisible() ? "隐藏所有桌面宠物" : "显示所有桌面宠物",
|
|
3123
|
+
click: () => petManager.setPetsVisible(!petManager.arePetsVisible())
|
|
3124
|
+
},
|
|
3125
|
+
{ type: "separator" },
|
|
3126
|
+
{ label: "退出 Desktop Pet", click: () => electron.app.quit() }
|
|
3127
|
+
]));
|
|
3128
|
+
}
|
|
3129
|
+
function createPetTemplateImage() {
|
|
3130
|
+
let image = electron.nativeImage.createFromBuffer(node_fs.readFileSync(trayIcon2xPath), { scaleFactor: 2 });
|
|
3131
|
+
if (image.isEmpty()) image = electron.nativeImage.createFromPath(trayIcon1xPath);
|
|
3132
|
+
if (image.isEmpty()) throw new Error("无法加载 macOS 菜单栏图标");
|
|
3133
|
+
image.setTemplateImage(true);
|
|
3134
|
+
return image;
|
|
3135
|
+
}
|
|
2996
3136
|
const profileDirectory = process.env.DESKTOP_PET_PROFILE?.trim();
|
|
2997
3137
|
if (profileDirectory) {
|
|
2998
3138
|
electron.app.setPath("userData", node_path.resolve(profileDirectory));
|
|
@@ -3005,6 +3145,7 @@ if (!singleInstance) {
|
|
|
3005
3145
|
electron.app.on("second-instance", (_event, argv) => {
|
|
3006
3146
|
const callback = argv.find((value) => value.startsWith("desktop-pet://"));
|
|
3007
3147
|
if (callback) handleAccountCallback(callback);
|
|
3148
|
+
petManager.setPetsVisible(true);
|
|
3008
3149
|
ensurePrimaryPet();
|
|
3009
3150
|
});
|
|
3010
3151
|
}
|
|
@@ -3015,6 +3156,7 @@ function ensurePrimaryPet() {
|
|
|
3015
3156
|
});
|
|
3016
3157
|
}
|
|
3017
3158
|
electron.app.whenReady().then(async () => {
|
|
3159
|
+
createMenuBar();
|
|
3018
3160
|
await startAccountCallbackRouter();
|
|
3019
3161
|
startEventServer();
|
|
3020
3162
|
const activated = await activateStoredDevice().catch((err) => {
|
|
@@ -3039,9 +3181,10 @@ electron.app.whenReady().then(async () => {
|
|
|
3039
3181
|
logger.info("app started", { firstPet: id });
|
|
3040
3182
|
});
|
|
3041
3183
|
electron.app.on("window-all-closed", () => {
|
|
3042
|
-
electron.app.quit();
|
|
3184
|
+
if (process.platform !== "darwin") electron.app.quit();
|
|
3043
3185
|
});
|
|
3044
3186
|
electron.app.on("quit", () => {
|
|
3187
|
+
destroyMenuBar();
|
|
3045
3188
|
stopAccountCallbackRouter();
|
|
3046
3189
|
stopRelay();
|
|
3047
3190
|
stopP2PWindow();
|