diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs
index 816ef34a..70fa534b 100644
--- a/packages/electron/main.mjs
+++ b/packages/electron/main.mjs
@@ -29,10 +29,21 @@ const DEV_APP_USER_MODEL_ID = 'dev.openchamber.desktop.dev';
const APP_USER_MODEL_ID = app.isPackaged ? PACKAGED_APP_USER_MODEL_ID : DEV_APP_USER_MODEL_ID;
const BACKGROUND_START_ARG = '--background';
+const getLoginItemOptions = () => {
+ if (process.platform === 'win32') {
+ return {
+ path: process.execPath,
+ args: [BACKGROUND_START_ARG],
+ name: APP_USER_MODEL_ID,
+ };
+ }
+ return {};
+};
+
const readLoginItemSettings = () => {
- if (process.platform !== 'darwin') return null;
+ if (process.platform !== 'darwin' && process.platform !== 'win32') return null;
try {
- return app.getLoginItemSettings();
+ return app.getLoginItemSettings(getLoginItemOptions());
} catch {
return null;
}
@@ -225,6 +236,22 @@ const readDesktopKeepAwakeStatus = () => {
return { supported: true, enabled, active };
};
+const readDesktopMinimizeToTrayStatus = () => {
+ const supported = process.platform === 'win32';
+ return {
+ supported,
+ enabled: supported && readSettingsRoot().desktopMinimizeToTrayEnabled === true,
+ };
+};
+
+const shouldHideMainWindowToTray = (browserWindow) => {
+ if (process.platform !== 'win32') return false;
+ if (!state.trayController) return false;
+ if (!browserWindow || browserWindow.isDestroyed()) return false;
+ if (browserWindow.__ocMiniChat === true) return false;
+ return readSettingsRoot().desktopMinimizeToTrayEnabled === true;
+};
+
const quitRisk = {
hasActiveTunnel: false,
hasRunningScheduledTasks: false,
@@ -2278,7 +2305,20 @@ const createBrowserWindow = ({ label, restoreGeometry, url, runtimeConfig = {} }
browserWindow.on('move', () => {
debounceWindowStatePersist(browserWindow, false);
});
+ browserWindow.on('minimize', (event) => {
+ if (!shouldHideMainWindowToTray(browserWindow)) return;
+ debounceWindowStatePersist(browserWindow, true);
+ event.preventDefault();
+ browserWindow.hide();
+ });
browserWindow.on('close', (event) => {
+ if (!state.quitRequested && shouldHideMainWindowToTray(browserWindow)) {
+ debounceWindowStatePersist(browserWindow, true);
+ event.preventDefault();
+ browserWindow.hide();
+ return;
+ }
+
if (process.platform === 'darwin' && !state.quitRequested) {
const remainingVisible = BrowserWindow.getAllWindows().filter(
(window) => !window.isDestroyed() && window.isVisible(),
@@ -3389,23 +3429,39 @@ const handleInvoke = async (browserWindow, command, args = {}) => {
return APP_VERSION;
case 'desktop_get_launch_at_login': {
- if (process.platform !== 'darwin') return { supported: false, enabled: false };
- const settings = app.getLoginItemSettings();
+ if (process.platform !== 'darwin' && process.platform !== 'win32') return { supported: false, enabled: false };
+ const settings = app.getLoginItemSettings(getLoginItemOptions());
return { supported: true, enabled: settings.openAtLogin === true };
}
case 'desktop_set_launch_at_login': {
- if (process.platform !== 'darwin') return { supported: false, enabled: false };
+ if (process.platform !== 'darwin' && process.platform !== 'win32') return { supported: false, enabled: false };
const enabled = args.enabled === true;
- app.setLoginItemSettings({
+ const settingsArgs = {
openAtLogin: enabled,
- openAsHidden: enabled,
- args: enabled ? [BACKGROUND_START_ARG] : [],
- });
- const settings = app.getLoginItemSettings();
+ ...(process.platform === 'darwin' ? { openAsHidden: enabled } : {}),
+ ...(process.platform === 'win32' ? getLoginItemOptions() : { args: enabled ? [BACKGROUND_START_ARG] : [] }),
+ ...(process.platform === 'win32' ? { enabled } : {}),
+ };
+ app.setLoginItemSettings(settingsArgs);
+ const settings = app.getLoginItemSettings(getLoginItemOptions());
return { supported: true, enabled: settings.openAtLogin === true };
}
+ case 'desktop_get_minimize_to_tray': {
+ return readDesktopMinimizeToTrayStatus();
+ }
+
+ case 'desktop_set_minimize_to_tray': {
+ if (process.platform !== 'win32') return { supported: false, enabled: false };
+ const enabled = args.enabled === true;
+ await mutateSettingsRoot((root) => {
+ root.desktopMinimizeToTrayEnabled = enabled;
+ });
+ setupTray();
+ return readDesktopMinimizeToTrayStatus();
+ }
+
case 'desktop_get_keep_awake': {
return readDesktopKeepAwakeStatus();
}
@@ -4525,8 +4581,8 @@ ipcMain.handle('openchamber:file:grant-existing', async (event, filePath) => {
};
});
-// --- macOS menu bar (status bar) ---------------------------------------------
-// Tray lives only on macOS; the renderer streams a compact state snapshot via
+// --- Native tray / menu bar ---------------------------------------------------
+// Tray lives on macOS and Windows; the renderer streams a compact state snapshot via
// the `desktop_tray_update` IPC command (see the command switch). Tray clicks
// flow back through dispatchTrayAction → renderer (focus/respond) or native
// handlers (show window / quit).
@@ -4558,6 +4614,21 @@ const resolveTraySurface = () => {
const trayIconAssets = () => {
const dir = path.join(resourceRoot(), 'icons', 'tray');
const statusDir = path.join(dir, 'status');
+ if (process.platform === 'win32') {
+ const iconPath = getWindowIconPath() || path.join(resourceRoot(), 'icons', 'icon.ico');
+ return {
+ idleIconPath: iconPath,
+ unseenIconPath: iconPath,
+ breathIconPaths: [iconPath],
+ statusIconPaths: {
+ busy: path.join(statusDir, 'busy.png'),
+ retry: path.join(statusDir, 'retry.png'),
+ error: path.join(statusDir, 'error.png'),
+ unseen: path.join(statusDir, 'unseen.png'),
+ blank: path.join(statusDir, 'blank.png'),
+ },
+ };
+ }
return {
idleIconPath: path.join(dir, 'trayTemplate-idle.png'),
unseenIconPath: path.join(dir, 'trayTemplate-unseen.png'),
@@ -4576,7 +4647,7 @@ const trayIconAssets = () => {
};
const setupTray = () => {
- if (process.platform !== 'darwin' || state.trayController) return;
+ if (!['darwin', 'win32'].includes(process.platform) || state.trayController) return;
const assets = trayIconAssets();
if (!fs.existsSync(assets.idleIconPath)) {
log.warn('[electron] tray icon missing, skipping tray setup', { iconPath: assets.idleIconPath });
@@ -4778,17 +4849,17 @@ app.whenReady().then(async () => {
if (process.platform === 'darwin') {
Menu.setApplicationMenu(buildMacMenu());
- setupTray();
} else {
Menu.setApplicationMenu(buildAutoHiddenMenu());
}
+ setupTray();
- if (process.platform === 'darwin' && app.isPackaged) {
+ if ((process.platform === 'darwin' || process.platform === 'win32') && app.isPackaged) {
const openAtLogin = loginItemSettings?.openAtLogin === true;
app.setLoginItemSettings({
openAtLogin,
- openAsHidden: openAtLogin,
- args: openAtLogin ? [BACKGROUND_START_ARG] : [],
+ ...(process.platform === 'darwin' ? { openAsHidden: openAtLogin, args: openAtLogin ? [BACKGROUND_START_ARG] : [] } : {}),
+ ...(process.platform === 'win32' ? { ...getLoginItemOptions(), enabled: openAtLogin } : {}),
});
}
diff --git a/packages/electron/tray.mjs b/packages/electron/tray.mjs
index 721c891c..51cc251b 100644
--- a/packages/electron/tray.mjs
+++ b/packages/electron/tray.mjs
@@ -1,4 +1,4 @@
-// macOS menu bar (status bar) controller.
+// Native tray/menu bar controller.
//
// Surfaces a glanceable, always-visible view of OpenChamber's live state:
// 1. an aggregate activity indicator (idle / busy / error+retry) in the icon
@@ -17,6 +17,8 @@
import { Tray, Menu, nativeImage } from 'electron';
+const isMac = process.platform === 'darwin';
+
const MAX_SESSIONS = 8;
const MAX_APPROVALS = 10;
@@ -87,7 +89,7 @@ const ANIM_INTERVAL_MS = 75;
const toTemplateImage = (p) => {
const image = nativeImage.createFromPath(p);
- image.setTemplateImage(true);
+ if (isMac) image.setTemplateImage(true);
return image;
};
@@ -99,6 +101,7 @@ export const createTrayController = ({ idleIconPath, unseenIconPath, breathIconP
let lastTitle = null;
// macOS auto-picks the @2x file next to each path and tints the alpha.
+ // Windows uses the regular app icon and ignores template tinting.
const idleFrame = toTemplateImage(idleIconPath);
const unseenFrame = toTemplateImage(unseenIconPath);
const breathFrames = breathIconPaths.map(toTemplateImage);
@@ -122,6 +125,7 @@ export const createTrayController = ({ idleIconPath, unseenIconPath, breathIconP
const startAnim = () => {
if (animTimer || !tray || tray.isDestroyed?.()) return;
+ if (breathFrames.length < 2) return;
animIndex = 0;
animDir = 1;
animTimer = setInterval(() => {
@@ -139,7 +143,8 @@ export const createTrayController = ({ idleIconPath, unseenIconPath, breathIconP
iconState = nextState;
if (!tray || tray.isDestroyed?.()) return;
if (nextState === 'busy') {
- startAnim();
+ if (breathFrames.length > 1) startAnim();
+ else tray.setImage(breathFrames[0] || idleFrame);
} else if (nextState === 'unseen') {
stopAnim();
tray.setImage(unseenFrame);
@@ -153,6 +158,9 @@ export const createTrayController = ({ idleIconPath, unseenIconPath, breathIconP
if (tray && !tray.isDestroyed?.()) return tray;
tray = new Tray(idleFrame);
tray.setIgnoreDoubleClickEvents(true);
+ if (!isMac) {
+ tray.on('click', () => onAction({ type: 'show-main-window' }));
+ }
return tray;
};
diff --git a/packages/ui/src/components/sections/openchamber/DesktopNetworkSettings.tsx b/packages/ui/src/components/sections/openchamber/DesktopNetworkSettings.tsx
index 661d492f..df480fcc 100644
--- a/packages/ui/src/components/sections/openchamber/DesktopNetworkSettings.tsx
+++ b/packages/ui/src/components/sections/openchamber/DesktopNetworkSettings.tsx
@@ -7,11 +7,13 @@ import {
getDesktopLanAddress,
getDesktopKeepAwake,
getDesktopLaunchAtLogin,
+ getDesktopMinimizeToTray,
isDesktopLocalOriginActive,
isDesktopShell,
restartDesktopApp,
setDesktopKeepAwake,
setDesktopLaunchAtLogin,
+ setDesktopMinimizeToTray,
} from '@/lib/desktop';
import { useI18n } from '@/lib/i18n';
import { runtimeFetch } from '@/lib/runtime-fetch';
@@ -31,6 +33,9 @@ export const DesktopNetworkSettings: React.FC = () => {
const [launchAtLoginSupported, setLaunchAtLoginSupported] = React.useState(false);
const [launchAtLoginEnabled, setLaunchAtLoginEnabled] = React.useState(false);
const [isSavingLaunchAtLogin, setIsSavingLaunchAtLogin] = React.useState(false);
+ const [minimizeToTraySupported, setMinimizeToTraySupported] = React.useState(false);
+ const [minimizeToTrayEnabled, setMinimizeToTrayEnabled] = React.useState(false);
+ const [isSavingMinimizeToTray, setIsSavingMinimizeToTray] = React.useState(false);
const [keepAwakeSupported, setKeepAwakeSupported] = React.useState(false);
const [keepAwakeEnabled, setKeepAwakeEnabled] = React.useState(false);
const [isSavingKeepAwake, setIsSavingKeepAwake] = React.useState(false);
@@ -112,6 +117,27 @@ export const DesktopNetworkSettings: React.FC = () => {
};
}, [isLocalDesktop]);
+ React.useEffect(() => {
+ if (!isLocalDesktop) {
+ setMinimizeToTraySupported(false);
+ return;
+ }
+
+ let cancelled = false;
+ void (async () => {
+ const status = await getDesktopMinimizeToTray();
+ if (cancelled) {
+ return;
+ }
+ setMinimizeToTraySupported(status?.supported === true);
+ setMinimizeToTrayEnabled(status?.enabled === true);
+ })();
+
+ return () => {
+ cancelled = true;
+ };
+ }, [isLocalDesktop]);
+
React.useEffect(() => {
if (!isLocalDesktop) {
setKeepAwakeSupported(false);
@@ -209,6 +235,33 @@ export const DesktopNetworkSettings: React.FC = () => {
}
}, [isSavingLaunchAtLogin, launchAtLoginEnabled, launchAtLoginSupported, t]);
+ const handleMinimizeToTrayToggle = React.useCallback(async () => {
+ if (!minimizeToTraySupported || isSavingMinimizeToTray) {
+ return;
+ }
+
+ const nextValue = !minimizeToTrayEnabled;
+ setMinimizeToTrayEnabled(nextValue);
+ setIsSavingMinimizeToTray(true);
+ setError(null);
+
+ try {
+ const status = await setDesktopMinimizeToTray(nextValue);
+ if (!status) {
+ throw new Error(t('settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed'));
+ }
+ if (!status.supported) {
+ throw new Error(t('settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported'));
+ }
+ setMinimizeToTrayEnabled(status.enabled);
+ } catch (cause) {
+ setMinimizeToTrayEnabled(!nextValue);
+ setError(cause instanceof Error ? cause.message : t('settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed'));
+ } finally {
+ setIsSavingMinimizeToTray(false);
+ }
+ }, [isSavingMinimizeToTray, minimizeToTrayEnabled, minimizeToTraySupported, t]);
+
const handleKeepAwakeToggle = React.useCallback(async () => {
if (!keepAwakeSupported || isSavingKeepAwake) {
return;
@@ -311,6 +364,35 @@ export const DesktopNetworkSettings: React.FC = () => {
) : null}
+ {minimizeToTraySupported ? (
+
{
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ handleMinimizeToTrayToggle();
+ }
+ }}
+ >
+
+
+
{t('settings.openchamber.desktopNetwork.field.minimizeToTray')}
+
+ {t('settings.openchamber.desktopNetwork.field.minimizeToTrayDescription')}
+
+
+
+ ) : null}
+
{keepAwakeSupported ? (
= ({ onClose, forceMobile
return isDesktopShell() && typeof window !== 'undefined'
&& (window as unknown as { __OPENCHAMBER_PLATFORM__?: string }).__OPENCHAMBER_PLATFORM__ === 'darwin';
}, []);
+ const isWindows = React.useMemo(() => {
+ return isDesktopShell() && typeof window !== 'undefined'
+ && (window as unknown as { __OPENCHAMBER_PLATFORM__?: string }).__OPENCHAMBER_PLATFORM__ === 'win32';
+ }, []);
// keep platform check available for future window chrome tweaks
@@ -536,12 +540,12 @@ export const SettingsView: React.FC = ({ onClose, forceMobile
const settingsSearchResults = React.useMemo(() => {
return buildSettingsSearchResults({
query: settingsSearchQuery,
- runtimeCtx: { ...runtimeCtx, isDesktopLocalOrigin, isMac },
+ runtimeCtx: { ...runtimeCtx, isDesktopLocalOrigin, isMac, isWindows },
visiblePageSlugs,
t,
getPageTitle,
});
- }, [getPageTitle, isDesktopLocalOrigin, isMac, runtimeCtx, settingsSearchQuery, t, visiblePageSlugs]);
+ }, [getPageTitle, isDesktopLocalOrigin, isMac, isWindows, runtimeCtx, settingsSearchQuery, t, visiblePageSlugs]);
const prepareSettingsSearchTarget = React.useCallback((result: SettingsSearchResult): string => {
if (result.id.startsWith('agents.')) {
diff --git a/packages/ui/src/hooks/useTraySync.ts b/packages/ui/src/hooks/useTraySync.ts
index 0a10a32e..25864f76 100644
--- a/packages/ui/src/hooks/useTraySync.ts
+++ b/packages/ui/src/hooks/useTraySync.ts
@@ -27,12 +27,12 @@ import { toast } from '@/components/ui';
import type { PermissionRequest } from '@/types/permission';
import type { QuestionRequest } from '@/types/question';
-// macOS menu bar bridge. The Electron main process owns the Tray UI; this hook
+// Native tray/menu bar bridge. The Electron main process owns the Tray UI; this hook
// streams a compact snapshot of live session/approval state to it via the
// `desktop_tray_update` IPC command, and routes tray clicks back into the app.
//
-// Only meaningful on the macOS desktop shell — main.mjs no-ops the command on
-// other platforms, but we still gate here to avoid pointless work.
+// Only meaningful on desktop platforms with a native tray/menu bar — main.mjs
+// no-ops the command elsewhere, but we still gate here to avoid pointless work.
const TRAY_ACTION_EVENT = 'openchamber:tray-action';
// Event-driven updates do the real work; this is just a slow safety net.
@@ -99,9 +99,10 @@ type DesktopBridgeGlobal = {
) => Promise<() => void>;
};
-const isMac = (): boolean => {
+const isTrayPlatform = (): boolean => {
if (typeof window === 'undefined') return false;
- return (window as unknown as { __OPENCHAMBER_PLATFORM__?: string }).__OPENCHAMBER_PLATFORM__ === 'darwin';
+ const platform = (window as unknown as { __OPENCHAMBER_PLATFORM__?: string }).__OPENCHAMBER_PLATFORM__;
+ return platform === 'darwin' || platform === 'win32';
};
const permissionLabel = (request: PermissionRequest): string => {
@@ -417,7 +418,7 @@ const buildSnapshot = (instanceName: string): TraySnapshot => {
export const useTraySync = (): void => {
React.useEffect(() => {
- if (!isMac() || !canUseElectronDesktopIPC()) return;
+ if (!isTrayPlatform() || !canUseElectronDesktopIPC()) return;
let disposed = false;
let lastSerialized = '';
@@ -577,7 +578,7 @@ export const useTraySync = (): void => {
}, []);
React.useEffect(() => {
- if (!isMac() || typeof window === 'undefined') return;
+ if (!isTrayPlatform() || typeof window === 'undefined') return;
const bridge = (window as unknown as { __OPENCHAMBER_DESKTOP__?: DesktopBridgeGlobal }).__OPENCHAMBER_DESKTOP__;
const listen = bridge?.listen;
if (typeof listen !== 'function') return;
diff --git a/packages/ui/src/lib/desktop.ts b/packages/ui/src/lib/desktop.ts
index 593d27b9..2d483992 100644
--- a/packages/ui/src/lib/desktop.ts
+++ b/packages/ui/src/lib/desktop.ts
@@ -54,6 +54,7 @@ export type DesktopSettings = {
opencodeBinary?: string;
desktopLanAccessEnabled?: boolean;
desktopKeepAwakeEnabled?: boolean;
+ desktopMinimizeToTrayEnabled?: boolean;
desktopUiPassword?: string;
projects?: ProjectEntry[];
activeProjectId?: string;
@@ -247,6 +248,11 @@ type KeepAwakeStatus = {
active: boolean;
};
+type MinimizeToTrayStatus = {
+ supported: boolean;
+ enabled: boolean;
+};
+
export const getDesktopLaunchAtLogin = async (): Promise => {
if (!canUseElectronDesktopIPC() || !isDesktopLocalOriginActive()) {
return null;
@@ -281,6 +287,40 @@ export const setDesktopLaunchAtLogin = async (enabled: boolean): Promise => {
+ if (!canUseElectronDesktopIPC() || !isDesktopLocalOriginActive()) {
+ return null;
+ }
+
+ try {
+ const result = await invokeDesktop('desktop_get_minimize_to_tray');
+ if (!result || typeof result.supported !== 'boolean' || typeof result.enabled !== 'boolean') {
+ return null;
+ }
+ return result;
+ } catch (error) {
+ console.warn('Failed to get minimize to tray status', error);
+ return null;
+ }
+};
+
+export const setDesktopMinimizeToTray = async (enabled: boolean): Promise => {
+ if (!canUseElectronDesktopIPC() || !isDesktopLocalOriginActive()) {
+ return null;
+ }
+
+ try {
+ const result = await invokeDesktop('desktop_set_minimize_to_tray', { enabled });
+ if (!result || typeof result.supported !== 'boolean' || typeof result.enabled !== 'boolean') {
+ return null;
+ }
+ return result;
+ } catch (error) {
+ console.warn('Failed to set minimize to tray status', error);
+ return null;
+ }
+};
+
export const getDesktopKeepAwake = async (): Promise => {
if (!canUseElectronDesktopIPC() || !isDesktopLocalOriginActive()) {
return null;
diff --git a/packages/ui/src/lib/i18n/messages/en.settings.ts b/packages/ui/src/lib/i18n/messages/en.settings.ts
index 6e33123a..8733b129 100644
--- a/packages/ui/src/lib/i18n/messages/en.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/en.settings.ts
@@ -877,7 +877,10 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.title': 'Desktop Network Access',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Start OpenChamber at login',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Start OpenChamber when you log in',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Starts the app in the background without opening a window. Click the Dock icon to open it.',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Starts the app in the background without opening a window. Use the desktop status icon to open it.',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': 'Minimize and close OpenChamber to the system tray',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': 'Minimize and close to the system tray',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': 'Keeps OpenChamber running in the Windows system tray when the main window is minimized or closed.',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'Keep computer awake while OpenChamber is running',
'settings.openchamber.desktopNetwork.field.keepAwake': 'Keep computer awake while OpenChamber is running',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': 'Prevents system sleep so phones can keep reaching this app. The screen can still turn off.',
@@ -897,6 +900,8 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.error.savedRestartFailed': 'Saved, but failed to restart app',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': 'Launch at login is not supported on this system',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': 'Failed to update launch at login setting',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': 'System tray background mode is not supported on this system',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': 'Failed to update system tray setting',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': 'Preventing sleep is not supported on this system',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': 'Failed to update keep awake setting',
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
diff --git a/packages/ui/src/lib/i18n/messages/es.settings.ts b/packages/ui/src/lib/i18n/messages/es.settings.ts
index b05ce65e..75a93be9 100644
--- a/packages/ui/src/lib/i18n/messages/es.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/es.settings.ts
@@ -844,7 +844,10 @@ export const settingsDict = {
"settings.openchamber.desktopNetwork.title": "Acceso de red de escritorio",
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Iniciar OpenChamber al iniciar sesión",
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Iniciar OpenChamber al iniciar sesión",
- "settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia la app en segundo plano sin abrir una ventana. Haz clic en el icono del Dock para abrirla.",
+ "settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia la app en segundo plano sin abrir una ventana. Usa el icono de estado del escritorio para abrirla.",
+ "settings.openchamber.desktopNetwork.field.minimizeToTrayAria": "Minimizar y cerrar OpenChamber a la bandeja del sistema",
+ "settings.openchamber.desktopNetwork.field.minimizeToTray": "Minimizar y cerrar a la bandeja del sistema",
+ "settings.openchamber.desktopNetwork.field.minimizeToTrayDescription": "Mantiene OpenChamber en ejecución en la bandeja del sistema de Windows cuando la ventana principal se minimiza o se cierra.",
"settings.openchamber.desktopNetwork.field.keepAwakeAria": "Mantener el ordenador activo mientras OpenChamber está en ejecución",
"settings.openchamber.desktopNetwork.field.keepAwake": "Mantener el ordenador activo mientras OpenChamber está en ejecución",
"settings.openchamber.desktopNetwork.field.keepAwakeDescription": "Para que los teléfonos puedan seguir abriendo esta app. La pantalla aún puede apagarse.",
@@ -864,6 +867,8 @@ export const settingsDict = {
"settings.openchamber.desktopNetwork.error.savedRestartFailed": "Guardado, pero no se pudo reiniciar la aplicación",
"settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported": "El inicio al iniciar sesión no es compatible con este sistema",
"settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed": "No se pudo actualizar la opción de inicio al iniciar sesión",
+ "settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported": "El modo en segundo plano de la bandeja del sistema no es compatible con este sistema",
+ "settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed": "No se pudo actualizar la opción de bandeja del sistema",
"settings.openchamber.desktopNetwork.error.keepAwakeUnsupported": "Evitar la suspensión no es compatible con este sistema",
"settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed": "No se pudo actualizar la opción de mantener activo",
"settings.openchamber.opencodeCli.title": "CLI de OpenCode",
diff --git a/packages/ui/src/lib/i18n/messages/fr.settings.ts b/packages/ui/src/lib/i18n/messages/fr.settings.ts
index 56ca828f..b3603c35 100644
--- a/packages/ui/src/lib/i18n/messages/fr.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/fr.settings.ts
@@ -772,7 +772,10 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.title': 'Accès au réseau de bureau',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Démarrez OpenChamber lors de la connexion',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Démarrez OpenChamber lorsque vous vous connectez',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Démarre l\'application en arrière-plan sans ouvrir de fenêtre. Cliquez sur l\'icône Dock pour l\'ouvrir.',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Démarre l\'application en arrière-plan sans ouvrir de fenêtre. Utilisez l\'icône d\'état du bureau pour l\'ouvrir.',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': 'Réduire et fermer OpenChamber dans la zone de notification',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': 'Réduire et fermer dans la zone de notification',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': 'Garde OpenChamber actif dans la zone de notification Windows lorsque la fenêtre principale est réduite ou fermée.',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'Garder l\'ordinateur éveillé pendant qu\'OpenChamber fonctionne',
'settings.openchamber.desktopNetwork.field.keepAwake': 'Garder l\'ordinateur éveillé pendant qu\'OpenChamber fonctionne',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': 'Pour que les téléphones puissent continuer à ouvrir cette application. L\'écran peut toujours s\'éteindre.',
@@ -792,6 +795,8 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.error.savedRestartFailed': 'Enregistré, mais échec du redémarrage de l\'application',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': 'Le lancement à la connexion n\'est pas pris en charge sur ce système',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': 'Échec de la mise à jour du paramètre de lancement lors de la connexion',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': 'Le mode d\'arrière-plan dans la zone de notification n\'est pas pris en charge sur ce système',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': 'Échec de la mise à jour du paramètre de zone de notification',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': 'La prévention de la veille n\'est pas prise en charge sur ce système',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': 'Échec de la mise à jour du paramètre de maintien en éveil',
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
diff --git a/packages/ui/src/lib/i18n/messages/ja.settings.ts b/packages/ui/src/lib/i18n/messages/ja.settings.ts
index 701768f5..40cd2e0e 100644
--- a/packages/ui/src/lib/i18n/messages/ja.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/ja.settings.ts
@@ -877,7 +877,10 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.title': 'Desktop ネットワークアクセス',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'ログイン時に OpenChamber を起動',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'ログイン時に OpenChamber を起動',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'ウィンドウを開かずにバックグラウンドでアプリを起動します。Dock アイコンをクリックして開きます。',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'ウィンドウを開かずにバックグラウンドでアプリを起動します。デスクトップのステータスアイコンから開けます。',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': 'OpenChamber をシステムトレイへ最小化または閉じる',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': 'システムトレイへ最小化または閉じる',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': 'メインウィンドウを最小化または閉じたときも、OpenChamber を Windows のシステムトレイで実行し続けます。',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'OpenChamber の実行中はコンピューターのスリープを防止',
'settings.openchamber.desktopNetwork.field.keepAwake': 'OpenChamber の実行中はコンピューターのスリープを防止',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': 'スマートフォンがこのアプリを開き続けられるようにします。ディスプレイはオフにできます。',
@@ -897,6 +900,8 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.error.savedRestartFailed': '保存しましたが、アプリの再起動に失敗しました',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': 'このシステムではログイン時の起動はサポートされていません',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': 'ログイン時起動設定の更新に失敗しました',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': 'このシステムではシステムトレイのバックグラウンドモードはサポートされていません',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': 'システムトレイ設定の更新に失敗しました',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': 'このシステムではスリープ防止はサポートされていません',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': 'スリープ防止設定の更新に失敗しました',
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
diff --git a/packages/ui/src/lib/i18n/messages/ko.settings.ts b/packages/ui/src/lib/i18n/messages/ko.settings.ts
index f2652802..9aceeaf7 100644
--- a/packages/ui/src/lib/i18n/messages/ko.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/ko.settings.ts
@@ -844,7 +844,10 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.title': 'Desktop 네트워크 접속',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '로그인 시 OpenChamber 시작',
'settings.openchamber.desktopNetwork.field.launchAtLogin': '로그인 시 OpenChamber 시작',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '창을 열지 않고 백그라운드에서 앱을 시작합니다. Dock 아이콘을 클릭하면 열립니다.',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '창을 열지 않고 백그라운드에서 앱을 시작합니다. 데스크톱 상태 아이콘으로 열 수 있습니다.',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': 'OpenChamber를 시스템 트레이로 최소화하거나 닫기',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': '시스템 트레이로 최소화하고 닫기',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': '메인 창을 최소화하거나 닫아도 OpenChamber가 Windows 시스템 트레이에서 계속 실행됩니다.',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'OpenChamber가 실행되는 동안 컴퓨터 절전 방지',
'settings.openchamber.desktopNetwork.field.keepAwake': 'OpenChamber가 실행되는 동안 컴퓨터 절전 방지',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': '휴대폰이 이 앱을 계속 열 수 있도록 합니다. 디스플레이는 꺼질 수 있습니다.',
@@ -864,6 +867,8 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.error.savedRestartFailed': '저장했지만 앱을 다시 시작하지 못했습니다',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': '이 시스템에서는 로그인 시 시작을 지원하지 않습니다',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': '로그인 시 시작 설정을 업데이트하지 못했습니다',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': '이 시스템에서는 시스템 트레이 백그라운드 모드를 지원하지 않습니다',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': '시스템 트레이 설정을 업데이트하지 못했습니다',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': '이 시스템에서는 절전 방지를 지원하지 않습니다',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': '절전 방지 설정을 업데이트하지 못했습니다',
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
diff --git a/packages/ui/src/lib/i18n/messages/pl.settings.ts b/packages/ui/src/lib/i18n/messages/pl.settings.ts
index 0e566a08..acdecd69 100644
--- a/packages/ui/src/lib/i18n/messages/pl.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/pl.settings.ts
@@ -692,6 +692,8 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.actions.saveAndRestart': 'Zapisz + Zrestartuj',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': 'Uruchamianie przy logowaniu nie jest obsługiwane w tym systemie',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': 'Nie udało się zaktualizować ustawienia uruchamiania przy logowaniu',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': 'Tryb działania w tle w zasobniku systemowym nie jest obsługiwany w tym systemie',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': 'Nie udało się zaktualizować ustawienia zasobnika systemowego',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': 'Zapobieganie uśpieniu nie jest obsługiwane w tym systemie',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': 'Nie udało się zaktualizować ustawienia utrzymywania aktywności',
'settings.openchamber.desktopNetwork.error.loadFailed': 'Nie udało się załadować ustawień pulpitu',
@@ -702,7 +704,10 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.field.allowLanAccessDescription': 'Restartuje aplikację, aby telefony, tablety i inne komputery w Twojej sieci Wi-Fi mogły ją otworzyć.',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Uruchamiaj OpenChamber przy logowaniu',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Uruchamiaj OpenChamber przy logowaniu',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Uruchamia aplikację w tle bez otwierania okna. Kliknij ikonę w Docku, aby ją otworzyć.',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Uruchamia aplikację w tle bez otwierania okna. Otworzysz ją z ikony stanu na pulpicie.',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': 'Minimalizuj i zamykaj OpenChamber do zasobnika systemowego',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': 'Minimalizuj i zamykaj do zasobnika systemowego',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': 'Utrzymuje OpenChamber w zasobniku systemowym Windows, gdy główne okno jest zminimalizowane lub zamknięte.',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'Utrzymuj komputer aktywny, gdy OpenChamber jest uruchomiony',
'settings.openchamber.desktopNetwork.field.keepAwake': 'Utrzymuj komputer aktywny, gdy OpenChamber jest uruchomiony',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': 'Aby telefony nadal mogły otwierać tę aplikację. Ekran nadal może się wyłączyć.',
diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts
index db017d39..ea273353 100644
--- a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts
@@ -844,7 +844,10 @@ export const settingsDict = {
"settings.openchamber.desktopNetwork.title": "Acesso de rede do desktop",
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Iniciar o OpenChamber ao fazer login",
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Iniciar o OpenChamber ao fazer login",
- "settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia o app em segundo plano sem abrir uma janela. Clique no ícone do Dock para abrir.",
+ "settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia o app em segundo plano sem abrir uma janela. Use o ícone de status da área de trabalho para abrir.",
+ "settings.openchamber.desktopNetwork.field.minimizeToTrayAria": "Minimizar e fechar o OpenChamber na bandeja do sistema",
+ "settings.openchamber.desktopNetwork.field.minimizeToTray": "Minimizar e fechar na bandeja do sistema",
+ "settings.openchamber.desktopNetwork.field.minimizeToTrayDescription": "Mantém o OpenChamber em execução na bandeja do sistema do Windows quando a janela principal é minimizada ou fechada.",
"settings.openchamber.desktopNetwork.field.keepAwakeAria": "Manter o computador ativo enquanto o OpenChamber estiver em execução",
"settings.openchamber.desktopNetwork.field.keepAwake": "Manter o computador ativo enquanto o OpenChamber estiver em execução",
"settings.openchamber.desktopNetwork.field.keepAwakeDescription": "Para que telefones continuem abrindo este app. A tela ainda pode desligar.",
@@ -864,6 +867,8 @@ export const settingsDict = {
"settings.openchamber.desktopNetwork.error.savedRestartFailed": "Salvo, mas não foi possível reiniciar o aplicativo",
"settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported": "Iniciar ao fazer login não é compatível com este sistema",
"settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed": "Não foi possível atualizar a opção de iniciar ao fazer login",
+ "settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported": "O modo em segundo plano na bandeja do sistema não é compatível com este sistema",
+ "settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed": "Não foi possível atualizar a opção da bandeja do sistema",
"settings.openchamber.desktopNetwork.error.keepAwakeUnsupported": "Impedir suspensão não é compatível com este sistema",
"settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed": "Não foi possível atualizar a opção de manter ativo",
"settings.openchamber.opencodeCli.title": "CLI do OpenCode",
diff --git a/packages/ui/src/lib/i18n/messages/uk.settings.ts b/packages/ui/src/lib/i18n/messages/uk.settings.ts
index 5b01f330..8bca1b7d 100644
--- a/packages/ui/src/lib/i18n/messages/uk.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/uk.settings.ts
@@ -844,7 +844,10 @@ export const settingsDict = {
"settings.openchamber.desktopNetwork.title": "Мережевий доступ десктопного застосунку",
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Запускати OpenChamber під час входу в систему",
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Запускати OpenChamber під час входу в систему",
- "settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Запускає застосунок у фоні без відкриття вікна. Щоб відкрити його, натисніть іконку в Dock.",
+ "settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Запускає застосунок у фоні без відкриття вікна. Відкрийте його через піктограму стану на робочому столі.",
+ "settings.openchamber.desktopNetwork.field.minimizeToTrayAria": "Згортати й закривати OpenChamber у системний трей",
+ "settings.openchamber.desktopNetwork.field.minimizeToTray": "Згортати й закривати в системний трей",
+ "settings.openchamber.desktopNetwork.field.minimizeToTrayDescription": "Залишає OpenChamber запущеним у системному треї Windows, коли головне вікно згорнуто або закрито.",
"settings.openchamber.desktopNetwork.field.keepAwakeAria": "Не давати комп’ютеру засинати, поки OpenChamber запущено",
"settings.openchamber.desktopNetwork.field.keepAwake": "Не давати комп’ютеру засинати, поки OpenChamber запущено",
"settings.openchamber.desktopNetwork.field.keepAwakeDescription": "Щоб телефон і далі міг відкривати застосунок. Екран усе одно може вимикатися.",
@@ -864,6 +867,8 @@ export const settingsDict = {
"settings.openchamber.desktopNetwork.error.savedRestartFailed": "Збережено, але не вдалося перезапустити застосунок",
"settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported": "Запуск під час входу в систему не підтримується на цій системі",
"settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed": "Не вдалося оновити налаштування запуску під час входу в систему",
+ "settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported": "Фоновий режим у системному треї не підтримується на цій системі",
+ "settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed": "Не вдалося оновити налаштування системного трея",
"settings.openchamber.desktopNetwork.error.keepAwakeUnsupported": "Запобігання сну не підтримується на цій системі",
"settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed": "Не вдалося оновити налаштування утримання комп’ютера активним",
"settings.openchamber.opencodeCli.title": "OpenCode CLI",
diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts
index 86945ca1..28ba6c53 100644
--- a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts
@@ -844,7 +844,10 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.title': '桌面端网络访问',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '登录时启动 OpenChamber',
'settings.openchamber.desktopNetwork.field.launchAtLogin': '登录时启动 OpenChamber',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '在后台启动应用且不打开窗口。点击 Dock 图标即可打开。',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '在后台启动应用且不打开窗口。可通过桌面状态图标打开。',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': '将 OpenChamber 最小化和关闭到系统托盘',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': '最小化和关闭到系统托盘',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': '主窗口最小化或关闭时,让 OpenChamber 继续在 Windows 系统托盘中运行。',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'OpenChamber 运行时保持电脑唤醒',
'settings.openchamber.desktopNetwork.field.keepAwake': 'OpenChamber 运行时保持电脑唤醒',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': '让手机可以继续打开此应用。屏幕仍可关闭。',
@@ -864,6 +867,8 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.error.savedRestartFailed': '已保存,但重启应用失败',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': '此系统不支持登录时启动',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': '更新登录时启动设置失败',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': '此系统不支持系统托盘后台模式',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': '更新系统托盘设置失败',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': '此系统不支持防止睡眠',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': '更新保持唤醒设置失败',
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts b/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts
index b58047db..999e545d 100644
--- a/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts
+++ b/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts
@@ -1817,11 +1817,16 @@
'settings.promptTemplates.page.toast.saveUnexpectedError': '儲存時發生意外錯誤',
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': '無法更新登入時自動啟動設定',
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': '此系統不支援登入時自動啟動',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTrayUnsupported': '此系統不支援系統匣背景模式',
+ 'settings.openchamber.desktopNetwork.error.minimizeToTraySaveFailed': '無法更新系統匣設定',
'settings.openchamber.desktopNetwork.error.keepAwakeUnsupported': '此系統不支援防止睡眠',
'settings.openchamber.desktopNetwork.error.keepAwakeSaveFailed': '無法更新保持喚醒設定',
'settings.openchamber.desktopNetwork.field.launchAtLogin': '登入時自動啟動 OpenChamber',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '登入時自動啟動 OpenChamber',
- 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '在背景啟動應用程式,不開啟視窗。按一下 Dock 圖示即可開啟。',
+ 'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '在背景啟動應用程式,不開啟視窗。可透過桌面狀態圖示開啟。',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayAria': '將 OpenChamber 最小化和關閉到系統匣',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTray': '最小化和關閉到系統匣',
+ 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription': '主視窗最小化或關閉時,讓 OpenChamber 繼續在 Windows 系統匣中執行。',
'settings.openchamber.desktopNetwork.field.keepAwakeAria': 'OpenChamber 執行時保持電腦喚醒',
'settings.openchamber.desktopNetwork.field.keepAwake': 'OpenChamber 執行時保持電腦喚醒',
'settings.openchamber.desktopNetwork.field.keepAwakeDescription': '讓手機可以持續開啟此應用程式。螢幕仍可關閉。',
diff --git a/packages/ui/src/lib/persistence.ts b/packages/ui/src/lib/persistence.ts
index 1d760f08..02afee54 100644
--- a/packages/ui/src/lib/persistence.ts
+++ b/packages/ui/src/lib/persistence.ts
@@ -747,6 +747,12 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
if (typeof candidate.desktopLanAccessEnabled === 'boolean') {
result.desktopLanAccessEnabled = candidate.desktopLanAccessEnabled;
}
+ if (typeof candidate.desktopKeepAwakeEnabled === 'boolean') {
+ result.desktopKeepAwakeEnabled = candidate.desktopKeepAwakeEnabled;
+ }
+ if (typeof candidate.desktopMinimizeToTrayEnabled === 'boolean') {
+ result.desktopMinimizeToTrayEnabled = candidate.desktopMinimizeToTrayEnabled;
+ }
const projects = sanitizeProjects(candidate.projects);
if (projects) {
diff --git a/packages/ui/src/lib/settings/search.ts b/packages/ui/src/lib/settings/search.ts
index f57ad5d2..f25486ce 100644
--- a/packages/ui/src/lib/settings/search.ts
+++ b/packages/ui/src/lib/settings/search.ts
@@ -22,6 +22,8 @@ interface SettingsSearchAvailabilityContext extends SettingsRuntimeContext {
isDesktopLocalOrigin: boolean;
// macOS desktop shell — for controls that only render on darwin (e.g. dock badge).
isMac: boolean;
+ // Windows desktop shell — for controls that only render on win32.
+ isWindows: boolean;
}
const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
@@ -312,6 +314,14 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
keywords: ['desktop', 'startup', 'login'],
isAvailable: (ctx) => ctx.isDesktopLocalOrigin,
},
+ {
+ id: 'sessions.desktop-minimize-to-tray',
+ page: 'sessions',
+ titleKey: 'settings.openchamber.desktopNetwork.field.minimizeToTray',
+ descriptionKey: 'settings.openchamber.desktopNetwork.field.minimizeToTrayDescription',
+ keywords: ['desktop', 'tray', 'system tray', 'minimize', 'close', 'background', 'windows'],
+ isAvailable: (ctx) => ctx.isDesktopLocalOrigin && ctx.isWindows,
+ },
{
id: 'sessions.desktop-keep-awake',
page: 'sessions',
diff --git a/packages/web/server/lib/opencode/settings-helpers.js b/packages/web/server/lib/opencode/settings-helpers.js
index 056a5f04..49abc179 100644
--- a/packages/web/server/lib/opencode/settings-helpers.js
+++ b/packages/web/server/lib/opencode/settings-helpers.js
@@ -181,6 +181,9 @@ export const createSettingsHelpers = (dependencies) => {
if (typeof candidate.desktopKeepAwakeEnabled === 'boolean') {
result.desktopKeepAwakeEnabled = candidate.desktopKeepAwakeEnabled;
}
+ if (typeof candidate.desktopMinimizeToTrayEnabled === 'boolean') {
+ result.desktopMinimizeToTrayEnabled = candidate.desktopMinimizeToTrayEnabled;
+ }
if (typeof candidate.desktopUiPassword === 'string') {
result.desktopUiPassword = candidate.desktopUiPassword.trim();
}
diff --git a/packages/web/server/lib/opencode/settings-helpers.test.js b/packages/web/server/lib/opencode/settings-helpers.test.js
index d030d1f0..7560f857 100644
--- a/packages/web/server/lib/opencode/settings-helpers.test.js
+++ b/packages/web/server/lib/opencode/settings-helpers.test.js
@@ -100,6 +100,17 @@ describe('settings helpers', () => {
});
});
+ it('accepts desktopMinimizeToTrayEnabled as a persisted shared setting', () => {
+ const helpers = createTestHelpers();
+
+ expect(helpers.sanitizeSettingsUpdate({ desktopMinimizeToTrayEnabled: true })).toEqual({
+ desktopMinimizeToTrayEnabled: true,
+ });
+ expect(helpers.sanitizeSettingsUpdate({ desktopMinimizeToTrayEnabled: false })).toEqual({
+ desktopMinimizeToTrayEnabled: false,
+ });
+ });
+
it('accepts desktopUiPassword as a persisted shared setting', () => {
const helpers = createTestHelpers();