Compare commits

...
15 changed files with 256 additions and 26 deletions

No files matched your search

+5 -2
View File
@@ -312,8 +312,11 @@ export const enCoreMessages: Messages = {
'settings.globalHotkey.toggleWindowDesc': 'Press a key combination to set a global shortcut for showing/hiding the window.',
'settings.globalHotkey.notSet': 'Not set',
'settings.globalHotkey.reset': 'Reset to default',
'settings.globalHotkey.closeToTray': 'Close to System Tray',
'settings.globalHotkey.closeToTrayDesc': 'When enabled, closing the window will minimize to the system tray instead of quitting.',
'settings.globalHotkey.closeToTray': 'Keep Running When Window Closes',
'settings.globalHotkey.closeToTrayDesc': 'When enabled, closing the window hides it while the app keeps running. Tray icon visibility is set separately.',
'settings.globalHotkey.showTrayIcon': 'Show System Tray Icon',
'settings.globalHotkey.showTrayIconDesc': 'When disabled, the tray icon is hidden but the app keeps running in the background.',
'settings.globalHotkey.showTrayIconHiddenHint': 'Tray icon hidden. Launch the app again or use the global toggle hotkey to bring the window back, then re-enable this setting to restore the icon.',
'settings.globalHotkey.enabled': 'Enable Global Hotkey',
'settings.globalHotkey.enabledDesc': 'Register system-wide keyboard shortcuts. When disabled, all global hotkeys are unregistered.',
'settings.globalHotkey.hint': 'Global hotkey works system-wide to quickly show or hide the window (Quake-style terminal).',
+5 -2
View File
@@ -227,8 +227,11 @@ export const esCoreMessages: Messages = {
'settings.globalHotkey.toggleWindowDesc': 'Presiona una combinación de teclas para definir un atajo global que muestre u oculte la ventana.',
'settings.globalHotkey.notSet': 'No configurada',
'settings.globalHotkey.reset': 'Restablecer al valor predeterminado',
'settings.globalHotkey.closeToTray': 'Cerrar a la bandeja del sistema',
'settings.globalHotkey.closeToTrayDesc': 'Cuando está activado, cerrar la ventana la minimiza a la bandeja del sistema en lugar de salir.',
'settings.globalHotkey.closeToTray': 'Seguir en segundo plano al cerrar la ventana',
'settings.globalHotkey.closeToTrayDesc': 'Al cerrar la ventana, se oculta y la aplicación sigue activa. La visibilidad del icono de la bandeja se configura aparte.',
'settings.globalHotkey.showTrayIcon': 'Mostrar icono de la bandeja del sistema',
'settings.globalHotkey.showTrayIconDesc': 'Cuando está desactivado, el icono de la bandeja se oculta, pero la aplicación sigue ejecutándose en segundo plano.',
'settings.globalHotkey.showTrayIconHiddenHint': 'Icono de la bandeja oculto. Vuelve a iniciar la aplicación o usa el atajo global para recuperar la ventana y vuelve a activar este ajuste para restaurar el icono.',
'settings.globalHotkey.enabled': 'Activar tecla de acceso global',
'settings.globalHotkey.enabledDesc': 'Registra atajos de teclado a nivel de todo el sistema. Cuando está desactivado, todas las teclas de acceso global se desregistran.',
'settings.globalHotkey.hint': 'La tecla de acceso global funciona en todo el sistema para mostrar u ocultar la ventana rápidamente (terminal estilo Quake).',
+5 -2
View File
@@ -296,8 +296,11 @@ export const ruCoreMessages: Messages = {
'settings.globalHotkey.toggleWindowDesc': 'Нажмите сочетание клавиш, чтобы задать глобальную горячую клавишу для показа или скрытия окна.',
'settings.globalHotkey.notSet': 'Не задано',
'settings.globalHotkey.reset': 'Сбросить по умолчанию',
'settings.globalHotkey.closeToTray': 'Сворачивать в системный трей',
'settings.globalHotkey.closeToTrayDesc': 'Если включено, при закрытии окно будет сворачиваться в системный трей вместо выхода из приложения.',
'settings.globalHotkey.closeToTray': 'Оставлять приложение в фоне при закрытии окна',
'settings.globalHotkey.closeToTrayDesc': 'При закрытии окно скрывается, а приложение продолжает работать. Отображение значка в трее настраивается отдельно.',
'settings.globalHotkey.showTrayIcon': 'Показывать значок в системном трее',
'settings.globalHotkey.showTrayIconDesc': 'Если отключено, значок в трее скрыт, но приложение продолжает работать в фоне.',
'settings.globalHotkey.showTrayIconHiddenHint': 'Значок в трее скрыт. Запустите приложение снова или используйте глобальную горячую клавишу, чтобы вернуть окно, затем снова включите этот параметр, чтобы восстановить значок.',
'settings.globalHotkey.enabled': 'Включить глобальную горячую клавишу',
'settings.globalHotkey.enabledDesc': 'Регистрировать системные сочетания клавиш. Когда отключено, все глобальные горячие клавиши снимаются с регистрации.',
'settings.globalHotkey.hint': 'Глобальная горячая клавиша работает на уровне всей системы и позволяет быстро показывать или скрывать окно (терминал в стиле Quake).',
+5 -2
View File
@@ -316,8 +316,11 @@ export const zhCNCoreMessages: Messages = {
'settings.globalHotkey.toggleWindowDesc': '按下组合键以设置显示/隐藏窗口的全局快捷键。',
'settings.globalHotkey.notSet': '未设置',
'settings.globalHotkey.reset': '恢复默认',
'settings.globalHotkey.closeToTray': '关闭时最小化到托盘',
'settings.globalHotkey.closeToTrayDesc': '启用后,关闭窗口将最小化到系统托盘而不是退出程序。',
'settings.globalHotkey.closeToTray': '关闭窗口后在后台运行',
'settings.globalHotkey.closeToTrayDesc': '启用后,关闭窗口会隐藏窗口,应用继续在后台运行。托盘图标可单独开关。',
'settings.globalHotkey.showTrayIcon': '显示系统托盘图标',
'settings.globalHotkey.showTrayIconDesc': '关闭后,托盘图标将被隐藏,但应用仍会在后台正常运行。',
'settings.globalHotkey.showTrayIconHiddenHint': '托盘图标已隐藏。再次启动应用或使用全局显示/隐藏快捷键即可找回窗口,然后重新打开此设置即可恢复图标。',
'settings.globalHotkey.enabled': '启用全局快捷键',
'settings.globalHotkey.enabledDesc': '注册系统级键盘快捷键。禁用后将取消所有全局快捷键注册。',
'settings.globalHotkey.hint': '全局快捷键在系统范围内工作,可快速显示或隐藏窗口(下拉式终端风格)。',
+5 -2
View File
@@ -318,8 +318,11 @@ export const zhTWCoreMessages: Messages = {
'settings.globalHotkey.toggleWindowDesc': '按下組合鍵以設定顯示/隱藏視窗的全域快速鍵。',
'settings.globalHotkey.notSet': '未設定',
'settings.globalHotkey.reset': '恢復預設',
'settings.globalHotkey.closeToTray': '關閉時最小化到系統匣',
'settings.globalHotkey.closeToTrayDesc': '啟用後,關閉視窗將最小化到系統匣而不是結束程式。',
'settings.globalHotkey.closeToTray': '關閉視窗後在背景執行',
'settings.globalHotkey.closeToTrayDesc': '啟用後,關閉視窗會隱藏視窗,應用程式繼續在背景執行。系統匣圖示可另外開關。',
'settings.globalHotkey.showTrayIcon': '顯示系統匣圖示',
'settings.globalHotkey.showTrayIconDesc': '關閉後,系統匣圖示將被隱藏,但應用程式仍會在背景正常執行。',
'settings.globalHotkey.showTrayIconHiddenHint': '系統匣圖示已隱藏。再次啟動應用程式或使用全域顯示/隱藏快捷鍵即可找回視窗,然後重新開啟此設定即可恢復圖示。',
'settings.globalHotkey.enabled': '啟用全域快速鍵',
'settings.globalHotkey.enabledDesc': '註冊系統級鍵盤快速鍵。停用後將取消所有全域快速鍵註冊。',
'settings.globalHotkey.hint': '全域快速鍵在系統範圍內工作,可快速顯示或隱藏視窗(下拉式終端風格)。',
@@ -2,6 +2,7 @@ import { useEffect, useRef, type MutableRefObject } from 'react';
import {
STORAGE_KEY_AUTO_UPDATE_ENABLED,
STORAGE_KEY_CLOSE_TO_TRAY,
STORAGE_KEY_SHOW_TRAY_ICON,
STORAGE_KEY_AUTO_LAUNCH_ENABLED,
STORAGE_KEY_GLOBAL_HOTKEY_ENABLED,
STORAGE_KEY_TOGGLE_WINDOW_HOTKEY,
@@ -42,6 +43,7 @@ interface UseSystemSettingsEffectsParams {
toggleWindowHotkey: string;
globalHotkeyEnabled: boolean;
closeToTray: boolean;
showTrayIcon: boolean;
autoLaunchEnabled: boolean;
windowOpacityRecord: WindowOpacityRecord;
windowOpacityMutationSourceRef: MutableRefObject<WindowOpacityMutationSource>;
@@ -62,6 +64,7 @@ export function useSystemSettingsEffects({
toggleWindowHotkey,
globalHotkeyEnabled,
closeToTray,
showTrayIcon,
autoLaunchEnabled,
windowOpacityRecord,
windowOpacityMutationSourceRef,
@@ -173,6 +176,24 @@ export function useSystemSettingsEffects({
notifySettingsChanged(STORAGE_KEY_CLOSE_TO_TRAY, closeToTray);
}, [enabled, closeToTray, notifySettingsChanged, persistMountedRef]);
// Persist and sync the show-tray-icon setting. Runs after the close-to-tray
// effect above (declaration order) so a hidden icon destroys the tray that
// close-to-tray's mount-time push may have just created.
useEffect(() => {
if (!enabled) return;
// Update main process tray visibility (needed on mount)
const bridge = netcattyBridge.get();
if (bridge?.setShowTrayIcon) {
bridge.setShowTrayIcon(showTrayIcon).catch((err) => {
console.warn('[SystemTray] Failed to set show-tray-icon:', err);
});
}
localStorageAdapter.writeString(STORAGE_KEY_SHOW_TRAY_ICON, showTrayIcon ? 'true' : 'false');
// Skip IPC on initial mount
if (!persistMountedRef.current) return;
notifySettingsChanged(STORAGE_KEY_SHOW_TRAY_ICON, showTrayIcon);
}, [enabled, showTrayIcon, notifySettingsChanged, persistMountedRef]);
// Hydrate auto-launch from the main process on mount — the OS login item
// is the real source of truth (the user may have toggled it outside the
// app), localStorage is only an optimistic cache for first paint.
+11
View File
@@ -67,6 +67,7 @@ import {
STORAGE_KEY_EXPLORER_CONTEXT_MENU_ENABLED,
STORAGE_KEY_TOGGLE_WINDOW_HOTKEY,
STORAGE_KEY_CLOSE_TO_TRAY,
STORAGE_KEY_SHOW_TRAY_ICON,
STORAGE_KEY_AUTO_LAUNCH_ENABLED,
STORAGE_KEY_HTTP_NETWORK_PROXY,
STORAGE_KEY_GLOBAL_HOTKEY_ENABLED,
@@ -550,6 +551,13 @@ export const useSettingsState = (options: { enableSettingsSync?: boolean; enable
if (stored === null) return true;
return stored === 'true';
});
// Preserve the old tray visibility for existing users until they set this
// independently. Fresh installs still default to showing the icon.
const [showTrayIcon, setShowTrayIcon] = useState<boolean>(() => {
const stored = readStoredString(STORAGE_KEY_SHOW_TRAY_ICON);
if (stored === null) return closeToTray;
return stored === 'true';
});
// Optimistic cache for first paint; useSystemSettingsEffects hydrates this
// from app.getLoginItemSettings() (the real source of truth) on mount.
const [autoLaunchEnabled, setAutoLaunchEnabled] = useState<boolean>(() => {
@@ -1791,6 +1799,7 @@ export const useSettingsState = (options: { enableSettingsSync?: boolean; enable
toggleWindowHotkey,
globalHotkeyEnabled,
closeToTray,
showTrayIcon,
autoLaunchEnabled,
windowOpacityRecord,
windowOpacityMutationSourceRef,
@@ -2156,6 +2165,8 @@ export const useSettingsState = (options: { enableSettingsSync?: boolean; enable
setToggleWindowHotkey,
closeToTray,
setCloseToTray,
showTrayIcon,
setShowTrayIcon,
autoLaunchEnabled,
setAutoLaunchEnabled,
autoLaunchSupported,
+2
View File
@@ -677,6 +677,8 @@ const SettingsPageContent: React.FC<{ settings: SettingsState; appLock?: AppLock
setToggleWindowHotkey={settings.setToggleWindowHotkey}
closeToTray={settings.closeToTray}
setCloseToTray={settings.setCloseToTray}
showTrayIcon={settings.showTrayIcon}
setShowTrayIcon={settings.setShowTrayIcon}
autoLaunchEnabled={settings.autoLaunchEnabled}
setAutoLaunchEnabled={settings.setAutoLaunchEnabled}
autoLaunchSupported={settings.autoLaunchSupported}
@@ -121,6 +121,8 @@ interface SettingsSystemTabProps {
setToggleWindowHotkey: (hotkey: string) => void;
closeToTray: boolean;
setCloseToTray: (enabled: boolean) => void;
showTrayIcon: boolean;
setShowTrayIcon: (enabled: boolean) => void;
autoLaunchEnabled: boolean;
setAutoLaunchEnabled: (enabled: boolean) => void;
autoLaunchSupported: boolean;
@@ -173,6 +175,8 @@ const SettingsSystemTab: React.FC<SettingsSystemTabProps> = ({
setToggleWindowHotkey,
closeToTray,
setCloseToTray,
showTrayIcon,
setShowTrayIcon,
autoLaunchEnabled,
setAutoLaunchEnabled,
autoLaunchSupported,
@@ -1313,6 +1317,23 @@ const SettingsSystemTab: React.FC<SettingsSystemTabProps> = ({
onChange={setCloseToTray}
/>
</SettingRow>
{/* Show System Tray Icon */}
<SettingRow
anchorId="system-show-tray-icon"
label={t("settings.globalHotkey.showTrayIcon")}
description={t("settings.globalHotkey.showTrayIconDesc")}
>
<Toggle
checked={showTrayIcon}
onChange={setShowTrayIcon}
/>
</SettingRow>
{!showTrayIcon && (
<p className="text-sm text-muted-foreground">
{t("settings.globalHotkey.showTrayIconHiddenHint")}
</p>
)}
</SettingCard>
<SettingHint>
+7
View File
@@ -961,6 +961,13 @@ export const SETTINGS_SEARCH_CATALOG: readonly SettingsSearchEntry[] = [
descriptionKey: "settings.globalHotkey.closeToTrayDesc",
sectionKey: "settings.globalHotkey.title",
},
{
id: "system-show-tray-icon",
tab: "system",
labelKey: "settings.globalHotkey.showTrayIcon",
descriptionKey: "settings.globalHotkey.showTrayIconDesc",
sectionKey: "settings.globalHotkey.title",
},
// Plugins
{
+52 -13
View File
@@ -19,6 +19,11 @@ let sendWhenRendererReady = null;
let getSystemMenuMainWindow = null;
let tray = null;
let closeToTray = false;
// User preference for whether the tray/menu bar icon is shown at all.
// Independent of close-to-tray: the app keeps running in the background with
// the icon hidden, and window close still hides (not quits) when close-to-tray
// was explicitly enabled.
let showTrayIcon = true;
let windowsTrayScaleListener = null;
let currentHotkey = null;
let hotkeyEnabled = false;
@@ -1292,18 +1297,12 @@ function setCloseToTray(enabled) {
closeToTray = !!enabled;
if (closeToTray) {
// Create tray if it doesn't exist
if (!tray) {
// Tray visibility is controlled separately by showTrayIcon.
if (!tray && showTrayIcon) {
createTray();
}
} else {
clearPendingFullscreenHide(getMainWindow());
// A hidden auto-launch cold start pins the tray regardless of this
// preference until its window is actually shown once — otherwise a user
// with close-to-tray off would get a windowless, trayless zombie process.
if (!hiddenLaunchTrayPinned) {
destroyTray();
}
}
return { success: true, enabled: closeToTray };
@@ -1321,18 +1320,45 @@ function pinTrayForHiddenLaunch() {
}
/**
* Release the hidden-launch tray pin once its window has been shown. If the
* user's close-to-tray preference is off, the tray is destroyed now instead
* of lingering until the next close-to-tray toggle.
* Release the hidden-launch tray pin once its window has been shown. The
* hidden icon preference can now take effect safely.
*/
function releaseHiddenLaunchTrayPin() {
if (!hiddenLaunchTrayPinned) return;
hiddenLaunchTrayPinned = false;
if (!closeToTray) {
if (!showTrayIcon) {
destroyTray();
}
}
/**
* Show or hide the tray icon without changing close-to-tray behavior. The
* app keeps running in the background while the icon is hidden; close-to-tray
* still hides the window on close.
*/
function setShowTrayIcon(enabled) {
showTrayIcon = !!enabled;
if (showTrayIcon) {
if (!tray) {
createTray();
}
} else if (!hiddenLaunchTrayPinned) {
// A hidden auto-launch cold start keeps its safety pin until its window
// is shown once, so a trayless zombie never appears without consent.
destroyTray();
}
return { success: true, enabled: showTrayIcon };
}
/**
* Check if the tray icon is currently meant to be shown
*/
function isShowTrayIconEnabled() {
return showTrayIcon;
}
/**
* Check if close-to-tray is enabled
*/
@@ -1354,7 +1380,10 @@ function getHotkeyStatus() {
* Handle window close event - hide to tray instead of closing
*/
function handleWindowClose(event, win) {
if (closeToTray && tray) {
// With the tray icon hidden by preference there is no `tray` object, but a
// user who enabled close-to-tray still expects the window to hide (app
// stays in the background), not to quit.
if (closeToTray && (tray || !showTrayIcon)) {
event.preventDefault();
hideWindowRespectingMacFullscreen(win);
return true; // Prevented close
@@ -1392,6 +1421,16 @@ function registerHandlers(ipcMain) {
return { enabled: closeToTray };
});
// Show/hide the tray icon itself (independent of close-to-tray)
ipcMain.handle("netcatty:tray:setShowTrayIcon", async (_event, { enabled }) => {
return setShowTrayIcon(enabled);
});
// Get show-tray-icon status
ipcMain.handle("netcatty:tray:isShowTrayIcon", async () => {
return { enabled: showTrayIcon };
});
// Update tray menu data
ipcMain.handle("netcatty:tray:updateMenuData", async (_event, data) => {
setTrayMenuData(data);
+109 -3
View File
@@ -1705,7 +1705,7 @@ test("a hidden-launch tray pin survives close-to-tray being turned off", async (
}
});
test("releasing the hidden-launch tray pin destroys the tray if close-to-tray is off", async () => {
test("releasing the hidden-launch tray pin keeps the visible tray when close-to-tray is off", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
bridge.init({ electronModule, getMainWindow: () => null });
@@ -1719,7 +1719,27 @@ test("releasing the hidden-launch tray pin destroys the tray if close-to-tray is
bridge.releaseHiddenLaunchTrayPin();
assert.equal(bridge.getTray(), null, "once the window is shown, close-to-tray=off should win");
assert.notEqual(bridge.getTray(), null, "show-tray-icon=on keeps the tray visible");
} finally {
bridge.cleanup();
}
});
test("releasing the hidden-launch tray pin hides the icon when requested", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
bridge.init({ electronModule, getMainWindow: () => null });
const ipcMain = createIpcMainStub();
bridge.registerHandlers(ipcMain);
try {
bridge.pinTrayForHiddenLaunch();
await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: false });
assert.notEqual(bridge.getTray(), null, "the hidden launch keeps its recovery icon");
bridge.releaseHiddenLaunchTrayPin();
assert.equal(bridge.getTray(), null);
} finally {
bridge.cleanup();
}
@@ -1757,7 +1777,7 @@ test("releaseHiddenLaunchTrayPin is a no-op when the pin was never set", () => {
}
});
test("setCloseToTray(false) still destroys an unpinned tray as before", async () => {
test("setCloseToTray(false) keeps a visible tray without hiding the window on close", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
const { ipcMain } = await enableCloseToTray(bridge, electronModule);
@@ -1765,6 +1785,92 @@ test("setCloseToTray(false) still destroys an unpinned tray as before", async ()
try {
assert.notEqual(bridge.getTray(), null);
await ipcMain.handlers.get("netcatty:tray:setCloseToTray")(null, { enabled: false });
assert.notEqual(bridge.getTray(), null);
assert.equal(bridge.handleWindowClose({ preventDefault() { assert.fail("close was prevented"); } }, new FakeWindow()), false);
} finally {
bridge.cleanup();
}
});
test("setShowTrayIcon(false) destroys the tray even when close-to-tray stays on", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
const { ipcMain } = await enableCloseToTray(bridge, electronModule);
try {
assert.notEqual(bridge.getTray(), null);
const result = await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: false });
assert.deepEqual(result, { success: true, enabled: false });
assert.equal(bridge.getTray(), null);
} finally {
bridge.cleanup();
}
});
test("handleWindowClose still hides to tray after the icon was hidden by preference", async () => {
await withPlatform("darwin", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
const { ipcMain } = await enableCloseToTray(bridge, electronModule);
try {
await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: false });
assert.equal(bridge.getTray(), null);
const win = new FakeWindow({ fullscreen: false });
let prevented = false;
const result = bridge.handleWindowClose({ preventDefault() { prevented = true; } }, win);
assert.equal(result, true);
assert.equal(prevented, true);
assert.equal(win.hideCalls, 1);
} finally {
bridge.cleanup();
}
});
});
test("setShowTrayIcon(true) restores the tray when close-to-tray is on", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
const { ipcMain } = await enableCloseToTray(bridge, electronModule);
try {
await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: false });
assert.equal(bridge.getTray(), null);
await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: true });
assert.notEqual(bridge.getTray(), null);
} finally {
bridge.cleanup();
}
});
test("setShowTrayIcon(true) creates a tray when close-to-tray is off", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
bridge.init({ electronModule, getMainWindow: () => null });
const ipcMain = createIpcMainStub();
bridge.registerHandlers(ipcMain);
try {
await ipcMain.handlers.get("netcatty:tray:setCloseToTray")(null, { enabled: false });
await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: true });
assert.notEqual(bridge.getTray(), null);
} finally {
bridge.cleanup();
}
});
test("setCloseToTray(true) does not recreate the tray while the icon is hidden by preference", async () => {
const bridge = loadBridge();
const electronModule = createElectronStub();
bridge.init({ electronModule, getMainWindow: () => null });
const ipcMain = createIpcMainStub();
bridge.registerHandlers(ipcMain);
try {
await ipcMain.handlers.get("netcatty:tray:setShowTrayIcon")(null, { enabled: false });
await ipcMain.handlers.get("netcatty:tray:setCloseToTray")(null, { enabled: true });
assert.equal(bridge.getTray(), null);
} finally {
bridge.cleanup();
+4
View File
@@ -1557,6 +1557,10 @@ function createPreloadApi(ctx) {
ipcRenderer.invoke("netcatty:tray:setCloseToTray", { enabled }),
isCloseToTray: () =>
ipcRenderer.invoke("netcatty:tray:isCloseToTray"),
setShowTrayIcon: (enabled) =>
ipcRenderer.invoke("netcatty:tray:setShowTrayIcon", { enabled }),
isShowTrayIcon: () =>
ipcRenderer.invoke("netcatty:tray:isShowTrayIcon"),
// Auto Launch at system login (hidden to tray)
getAutoLaunch: () =>
+2
View File
@@ -171,6 +171,8 @@ export const STORAGE_KEY_MANAGED_SOURCES = 'netcatty_managed_sources_v1';
// Global Toggle Window Settings (Quake Mode)
export const STORAGE_KEY_TOGGLE_WINDOW_HOTKEY = 'netcatty_toggle_window_hotkey_v1';
export const STORAGE_KEY_CLOSE_TO_TRAY = 'netcatty_close_to_tray_v1';
/** Whether the system tray / menu bar icon is shown. Default: shown. */
export const STORAGE_KEY_SHOW_TRAY_ICON = 'netcatty_show_tray_icon_v1';
/** Optimistic cache only — the source of truth is app.getLoginItemSettings() in the main process. */
export const STORAGE_KEY_AUTO_LAUNCH_ENABLED = 'netcatty_auto_launch_enabled_v1';
/** App-level HTTP(S) proxy for cloud sync / AI (not SSH ProxyJump). */
+2
View File
@@ -65,6 +65,8 @@ declare global {
// System Tray / Close to Tray
setCloseToTray?(enabled: boolean): Promise<{ success: boolean; enabled: boolean }>;
isCloseToTray?(): Promise<{ enabled: boolean }>;
setShowTrayIcon?(enabled: boolean): Promise<{ success: boolean; enabled: boolean }>;
isShowTrayIcon?(): Promise<{ enabled: boolean }>;
// Auto Launch at system login (hidden to tray)
getAutoLaunch?(): Promise<{ success: boolean; enabled: boolean; supported: boolean }>;