diff --git a/CHANGELOG.md b/CHANGELOG.md index 071bbff..207704f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v0.4.50 (2026-05-16) + +## Fixes +- Fix duplicate tray icon on macOS when hiding to tray +- Fix tray not showing in background mode on macOS +- Fix hide to tray broken on Windows/Linux +- Fix Shutdown button in web UI not working + # v0.4.49 (2026-05-16) ## Features diff --git a/cli/cli.js b/cli/cli.js index a049c09..06cd979 100755 --- a/cli/cli.js +++ b/cli/cli.js @@ -694,29 +694,48 @@ function startServer(latestVersion) { await startTerminalUI(port); // Loop continues, show menu again } else if (choice === "hide") { - // Hide to tray: keep the CURRENT process alive (it already owns a - // macOS GUI session so NSStatusItem works). Spawning a detached child - // puts it outside the login session → systray silently fails on macOS. const { clearScreen } = require("./src/cli/utils/display"); clearScreen(); - // Survive terminal close — SIGHUP is sent when the launching shell exits - process.removeAllListeners("SIGHUP"); - process.on("SIGHUP", () => {}); - // Enable auto startup on OS boot try { const { enableAutoStart } = require("./src/cli/tray/autostart"); enableAutoStart(__filename); } catch (e) { } - console.log(`\n🔔 9Router is running in tray (PID: ${process.pid})`); + if (process.platform === "darwin") { + // macOS: keep current process alive — spawning a detached child puts + // it outside the login session so NSStatusItem silently fails. + process.removeAllListeners("SIGHUP"); + process.on("SIGHUP", () => {}); + + console.log(`\n⏳ Switching to tray mode... (icon already visible in menu bar)`); + console.log(`🔔 9Router is running in tray (PID: ${process.pid})`); + console.log(` Server: http://${displayHost}:${port}`); + console.log(`\n💡 You can close this terminal. Right-click tray icon to quit.\n`); + + // Tray already init'd at startup — just keep event loop alive. + return; + } + + // Windows/Linux: spawn detached bgProcess (systray works fine in child) + console.log(`\n⏳ Starting background process... (tray icon will appear in ~3s)`); + + const bgProcess = spawn(process.execPath, [__filename, "--tray", "--skip-update", "-p", port.toString()], { + detached: true, + stdio: "ignore", + windowsHide: true, + env: { ...process.env } + }); + bgProcess.unref(); + + console.log(`🔔 9Router is now running in background (PID: ${bgProcess.pid})`); console.log(` Server: http://${displayHost}:${port}`); console.log(`\n💡 You can close this terminal. Right-click tray icon to quit.\n`); - // Tray icon already running (initTrayIcon was called above at startup). - // Nothing more to do — event loop keeps process alive via tray + server. - return; + // cleanup() kills server so bgProcess can claim the port fresh + cleanup(); + process.exit(0); } else if (choice === "exit") { isShuttingDown = true; console.log("\nExiting..."); diff --git a/cli/package.json b/cli/package.json index b244149..e7bd1c1 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "9router", - "version": "0.4.49", + "version": "0.4.50", "description": "9Router CLI - Start and manage 9Router server", "bin": { "9router": "./cli.js" diff --git a/package.json b/package.json index b468d5e..585d487 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "9router-app", - "version": "0.4.49", + "version": "0.4.50", "description": "9Router web dashboard", "private": true, "scripts": { diff --git a/public/i18n/literals/vi.json b/public/i18n/literals/vi.json index 9ca42c4..1b03b6e 100644 --- a/public/i18n/literals/vi.json +++ b/public/i18n/literals/vi.json @@ -98,13 +98,13 @@ "Console Log": "Nhật ký Console", "System": "Hệ thống", "Debug": "Gỡ lỗi", - "Shutdown": "Tắt máy", + "Shutdown": "Tắt ứng dụng", "Close Proxy": "Đóng Proxy", "Are you sure you want to close the proxy server?": "Bạn có chắc chắn muốn đóng máy chủ proxy không?", "Server Disconnected": "Máy chủ đã ngắt kết nối", "The proxy server has been stopped.": "Máy chủ proxy đã bị dừng.", "Reload Page": "Tải lại trang", - "Service is running in terminal. You can close this web page. Shutdown will stop the service.": "Dịch vụ đang chạy trong terminal. Bạn có thể đóng trang web này. Tắt máy sẽ dừng dịch vụ.", + "Service is running in terminal. You can close this web page. Shutdown will stop the service.": "Dịch vụ đang chạy trong terminal. Bạn có thể đóng trang web này. Tắt ứng dụng sẽ dừng dịch vụ.", "Manage your AI provider connections": "Quản lý kết nối nhà cung cấp AI của bạn", "Model combos with fallback": "Kết hợp mô hình với dự phòng", "Monitor your API usage, token consumption, and request logs": "Theo dõi việc sử dụng API, tiêu thụ token và nhật ký yêu cầu", diff --git a/src/shared/components/Sidebar.js b/src/shared/components/Sidebar.js index bec3a63..4dc3e66 100644 --- a/src/shared/components/Sidebar.js +++ b/src/shared/components/Sidebar.js @@ -109,7 +109,7 @@ export default function Sidebar({ onClose }) { const handleShutdown = async () => { setIsShuttingDown(true); try { - await fetch("/api/shutdown", { method: "POST" }); + await fetch("/api/version/shutdown", { method: "POST" }); } catch (e) { // Expected to fail as server shuts down; ignore error }