Spaces:
Runtime error
Runtime error
| import { spawn, type ChildProcess } from "node:child_process"; | |
| import { join, dirname } from "node:path"; | |
| import { fileURLToPath } from "node:url"; | |
| const __dirname = dirname(fileURLToPath(import.meta.url)); | |
| const PS1_PATH = join(__dirname, "tray.ps1"); | |
| export interface WinTrayEvent { | |
| type: "click" | "ready" | "error"; | |
| index?: number; | |
| error?: string; | |
| } | |
| export interface WinTrayOptions { | |
| iconPath: string; | |
| tooltip: string; | |
| onEvent: (evt: WinTrayEvent) => void; | |
| } | |
| export interface WinTrayHandle { | |
| update(items: Array<{ title: string; enabled: boolean }>): void; | |
| setTooltip(text: string): void; | |
| destroy(): void; | |
| } | |
| export function initWindowsTray(opts: WinTrayOptions): WinTrayHandle | null { | |
| let child: ChildProcess; | |
| try { | |
| child = spawn( | |
| "powershell.exe", | |
| [ | |
| "-NoLogo", | |
| "-NoProfile", | |
| "-ExecutionPolicy", | |
| "Bypass", | |
| "-File", | |
| PS1_PATH, | |
| "-IconPath", | |
| opts.iconPath, | |
| "-Tooltip", | |
| opts.tooltip, | |
| ], | |
| { stdio: ["pipe", "pipe", "pipe"], windowsHide: true } | |
| ); | |
| } catch (err) { | |
| opts.onEvent({ type: "error", error: String(err) }); | |
| return null; | |
| } | |
| child.stdout?.setEncoding("utf-8"); | |
| let buffer = ""; | |
| child.stdout?.on("data", (chunk: string) => { | |
| buffer += chunk; | |
| let nl: number; | |
| while ((nl = buffer.indexOf("\n")) >= 0) { | |
| const line = buffer.slice(0, nl).trim(); | |
| buffer = buffer.slice(nl + 1); | |
| if (!line) continue; | |
| try { | |
| const evt = JSON.parse(line) as WinTrayEvent; | |
| opts.onEvent(evt); | |
| } catch { | |
| // ignore malformed JSON lines | |
| } | |
| } | |
| }); | |
| child.on("error", (err) => opts.onEvent({ type: "error", error: err.message })); | |
| function send(cmd: object): void { | |
| if (!child.stdin?.writable) return; | |
| child.stdin.write(JSON.stringify(cmd) + "\n"); | |
| } | |
| return { | |
| update(items) { | |
| send({ type: "setMenu", items }); | |
| }, | |
| setTooltip(text) { | |
| send({ type: "setTooltip", text }); | |
| }, | |
| destroy() { | |
| try { | |
| send({ type: "quit" }); | |
| } catch { | |
| // already dead | |
| } | |
| setTimeout(() => child.kill(), 500); | |
| }, | |
| }; | |
| } | |