返回 DeepSeek-Reasonix
tray.ts
根目录 / desktop / electron / src / main / tray.ts
1 import { Menu, nativeImage, Tray } from "electron";
2 import type { TrayLabels } from "./hostCalls.js";
3 import { errorText, type Logger } from "./log.js";
4
5 export interface TrayDeps {
6 platform: NodeJS.Platform;
7 iconPath: string | null;
8 onOpen(): void;
9 onQuit(): void;
10 log: Logger;
11 }
12
13 export class TrayHost {
14 private tray: Tray | null = null;
15
16 constructor(private readonly deps: TrayDeps) {}
17
18 ensure(labels: TrayLabels): { ready: boolean; reason: string } {
19 const { deps } = this;
20 if (!deps.iconPath) return { ready: false, reason: "tray icon asset missing" };
21 try {
22 if (!this.tray) {
23 let image = nativeImage.createFromPath(deps.iconPath);
24 if (image.isEmpty()) return { ready: false, reason: `tray icon could not be decoded: ${deps.iconPath}` };
25 if (deps.platform === "darwin") {
26 image = image.resize({ width: 18, height: 18 });
27 image.setTemplateImage(true);
28 } else if (deps.platform === "win32") {
29 image = image.resize({ width: 16, height: 16 });
30 }
31 const tray = new Tray(image);
32 tray.on("click", () => deps.onOpen());
33 this.tray = tray;
34 }
35 this.tray.setToolTip(labels.tooltip || "Reasonix");
36 this.tray.setContextMenu(Menu.buildFromTemplate([
37 { label: labels.openTitle, toolTip: labels.openTooltip, click: () => deps.onOpen() },
38 { label: labels.quitTitle, toolTip: labels.quitTooltip, click: () => deps.onQuit() },
39 ]));
40 return { ready: true, reason: "" };
41 } catch (error) {
42 deps.log.warn(`tray unavailable: ${errorText(error)}`);
43 this.destroy();
44 return { ready: false, reason: errorText(error) };
45 }
46 }
47
48 destroy(): void {
49 try {
50 this.tray?.destroy();
51 } catch {
52 // Already gone.
53 }
54 this.tray = null;
55 }
56 }
57
57 lines TYPESCRIPT