- {(launchAtLoginSupported || isMacDesktop || minimizeToTraySupported || keepAwakeSupported) ? (
-
- {launchAtLoginSupported ? (
- {
- if (checked === launchAtLoginEnabled) return;
- void handleLaunchAtLoginToggle();
- }}
- disabled={isSavingLaunchAtLogin}
- label={t('settings.openchamber.desktopNetwork.field.launchAtLogin')}
- info={t('settings.openchamber.desktopNetwork.field.launchAtLoginDescription')}
- ariaLabel={t('settings.openchamber.desktopNetwork.field.launchAtLoginAria')}
- />
- ) : null}
-
- {isMacDesktop ? (
-
- ) : null}
-
- {minimizeToTraySupported ? (
- {
- if (checked === minimizeToTrayEnabled) return;
- void handleMinimizeToTrayToggle();
- }}
- disabled={isSavingMinimizeToTray}
- label={t('settings.openchamber.desktopNetwork.field.minimizeToTray')}
- info={t('settings.openchamber.desktopNetwork.field.minimizeToTrayDescription')}
- ariaLabel={t('settings.openchamber.desktopNetwork.field.minimizeToTrayAria')}
- />
- ) : null}
-
- {keepAwakeSupported ? (
- {
- if (checked === keepAwakeEnabled) return;
- void handleKeepAwakeToggle();
- }}
- disabled={isSavingKeepAwake}
- label={t('settings.openchamber.desktopNetwork.field.keepAwake')}
- info={t('settings.openchamber.desktopNetwork.field.keepAwakeDescription')}
- ariaLabel={t('settings.openchamber.desktopNetwork.field.keepAwakeAria')}
- />
- ) : null}
-
- ) : null}
-
-
- {t('settings.openchamber.desktopPassword.field.password')}
-
- )}
- info={t('settings.openchamber.desktopPassword.field.passwordDescription')}
- >
- handlePasswordChange(event.target.value)}
- placeholder={t('settings.openchamber.desktopPassword.field.passwordPlaceholder')}
- disabled={isLoading || isSaving}
- required={draftValue}
- aria-invalid={lanRequiresPassword}
- />
-
-
-
-
+
+
+ {(launchAtLoginSupported || isMacDesktop || minimizeToTraySupported || keepAwakeSupported) ? (
+
+ {launchAtLoginSupported ? (
-
- {t('settings.openchamber.desktopNetwork.field.warning')}
-
- {lanRequiresPassword || lanBlockedByMissingPassword ? (
-
- {t('settings.openchamber.desktopNetwork.field.passwordRequiredWarning')}
-
- ) : null}
- >
- )}
- ariaLabel={t('settings.openchamber.desktopNetwork.field.allowLanAccessAria')}
+ settingsItem="sessions.desktop-launch-at-login"
+ checked={launchAtLoginEnabled}
+ onChange={(checked) => {
+ if (checked === launchAtLoginEnabled) return;
+ void handleLaunchAtLoginToggle();
+ }}
+ disabled={isSavingLaunchAtLogin}
+ label={t('settings.openchamber.desktopNetwork.field.launchAtLogin')}
+ info={t('settings.openchamber.desktopNetwork.field.launchAtLoginDescription')}
+ ariaLabel={t('settings.openchamber.desktopNetwork.field.launchAtLoginAria')}
/>
-
-
- {error ? (
-
{error}
) : null}
- {lanUrl ? (
-
- {isDirty && !savedValue
- ? t('settings.openchamber.desktopNetwork.hint.openAfterRestart')
- : t('settings.openchamber.desktopNetwork.hint.openNow')}
- {lanUrl}
-
+ {isMacDesktop ? (
+
) : null}
-
-
-
+ {minimizeToTraySupported ? (
+
{
+ if (checked === minimizeToTrayEnabled) return;
+ void handleMinimizeToTrayToggle();
+ }}
+ disabled={isSavingMinimizeToTray}
+ label={t('settings.openchamber.desktopNetwork.field.minimizeToTray')}
+ info={t('settings.openchamber.desktopNetwork.field.minimizeToTrayDescription')}
+ ariaLabel={t('settings.openchamber.desktopNetwork.field.minimizeToTrayAria')}
+ />
+ ) : null}
+
+ {keepAwakeSupported ? (
+ {
+ if (checked === keepAwakeEnabled) return;
+ void handleKeepAwakeToggle();
+ }}
+ disabled={isSavingKeepAwake}
+ label={t('settings.openchamber.desktopNetwork.field.keepAwake')}
+ info={t('settings.openchamber.desktopNetwork.field.keepAwakeDescription')}
+ ariaLabel={t('settings.openchamber.desktopNetwork.field.keepAwakeAria')}
+ />
+ ) : null}
-
- ) : null}
- >
+ ) : null}
+
+
+ {t('settings.openchamber.desktopPassword.field.password')}
+
+ )}
+ info={t('settings.openchamber.desktopPassword.field.passwordDescription')}
+ >
+ handlePasswordChange(event.target.value)}
+ placeholder={t('settings.openchamber.desktopPassword.field.passwordPlaceholder')}
+ disabled={isLoading || isSaving}
+ required={draftValue}
+ aria-invalid={lanRequiresPassword}
+ />
+
+
+
+
+
+
+ {t('settings.openchamber.desktopNetwork.field.warning')}
+
+ {lanRequiresPassword || lanBlockedByMissingPassword ? (
+
+ {t('settings.openchamber.desktopNetwork.field.passwordRequiredWarning')}
+
+ ) : null}
+ >
+ )}
+ ariaLabel={t('settings.openchamber.desktopNetwork.field.allowLanAccessAria')}
+ />
+
+
+ {error ? (
+
{error}
+ ) : null}
+
+ {lanUrl ? (
+
+ {isDirty && !savedValue
+ ? t('settings.openchamber.desktopNetwork.hint.openAfterRestart')
+ : t('settings.openchamber.desktopNetwork.hint.openNow')}
+ {lanUrl}
+
+ ) : null}
+
+
+
+
+
+
);
};
diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx
index 9698a491..ebae3dc5 100644
--- a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx
+++ b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx
@@ -14,7 +14,7 @@ import { DesktopNetworkSettings } from './DesktopNetworkSettings';
import { KeyboardShortcutsSettings } from './KeyboardShortcutsSettings';
import { SettingsPageLayout } from '@/components/sections/shared/SettingsPageLayout';
import { useDeviceInfo } from '@/lib/device';
-import { isDesktopLocalOriginActive, isDesktopShell, isVSCodeRuntime, isWebRuntime, usesFramelessElectronChrome } from '@/lib/desktop';
+import { isDesktopLocalOriginActive, isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
import { isCapacitorApp } from '@/lib/platform';
import { useI18n } from '@/lib/i18n';
import { subscribeRuntimeEndpointChanged } from '@/lib/runtime-switch';
@@ -42,7 +42,7 @@ export const OpenChamberPage: React.FC
= ({ section }) =>
const showAbout = isMobile && isWebRuntime();
const isVSCode = isVSCodeRuntime();
void runtimeEndpointEpoch;
- const showDesktopNetworkSettings = isDesktopShell() && (isDesktopLocalOriginActive() || usesFramelessElectronChrome());
+ const showDesktopNetworkSettings = isDesktopShell() && isDesktopLocalOriginActive();
// If no section specified, show all (mobile/legacy behavior)
if (!section) {
@@ -135,7 +135,7 @@ const GeneralSectionContent: React.FC = () => {
const isVSCode = isVSCodeRuntime();
const runtimeEndpointEpoch = useRuntimeEndpointEpoch();
void runtimeEndpointEpoch;
- const showDesktopNetworkSettings = isDesktopShell() && (isDesktopLocalOriginActive() || usesFramelessElectronChrome());
+ const showDesktopNetworkSettings = isDesktopShell() && isDesktopLocalOriginActive();
// Passkeys only work against the browser's WebAuthn UI on the web surface —
// desktop shell, VS Code, and the Capacitor app never show the login screen.
const showPasskeySettings = isWebRuntime() && !isDesktopShell() && !isVSCode && !isCapacitorApp();
@@ -162,6 +162,7 @@ const VisualSectionContent: React.FC = () => {
const isVSCode = isVSCodeRuntime();
return = [
+ { id: 'auto', labelKey: 'settings.openchamber.desktopNetwork.option.windowControlsAuto' },
+ { id: 'left', labelKey: 'settings.openchamber.desktopNetwork.option.windowControlsLeft' },
+ { id: 'right', labelKey: 'settings.openchamber.desktopNetwork.option.windowControlsRight' },
+];
interface OpenChamberVisualSettingsProps {
/** Which settings to show. If undefined, shows all. */
@@ -408,6 +421,9 @@ export const OpenChamberVisualSettings: React.FC
);
const dockBadgeEnabled = useUIStore(state => state.dockBadgeEnabled);
const setDockBadgeEnabled = useUIStore(state => state.setDockBadgeEnabled);
+ const showWindowControlsPosition = usesFramelessElectronChrome();
+ const desktopWindowControlsPosition = useUIStore((state) => state.desktopWindowControlsPosition);
+ const setDesktopWindowControlsPosition = useUIStore((state) => state.setDesktopWindowControlsPosition);
const [chatRenderPreviewTick, setChatRenderPreviewTick] = React.useState(0);
const reportUsage = useUIStore(state => state.reportUsage);
const setReportUsage = useUIStore(state => state.setReportUsage);
@@ -418,6 +434,11 @@ export const OpenChamberVisualSettings: React.FC
void updateDesktopSettings({ reportUsage: enabled });
}, [setReportUsage]);
+ const handleWindowControlsPositionChange = React.useCallback((value: DesktopWindowControlsPosition) => {
+ setDesktopWindowControlsPosition(value);
+ void updateDesktopSettings({ desktopWindowControlsPosition: value });
+ }, [setDesktopWindowControlsPosition]);
+
const shouldAnimateChatPreview = (isSettingsDialogOpen || isMobile || isVSCodeRuntime())
&& (visibleSettings ? visibleSettings.includes('chatRenderMode') : true);
@@ -596,11 +617,12 @@ export const OpenChamberVisualSettings: React.FC
const isVSCode = isVSCodeRuntime();
const hasThemeSettings = shouldShow('theme') && !isVSCode;
+ const showWindowControlsPositionSetting = shouldShow('windowControlsPosition') && showWindowControlsPosition;
const hasLocalizationSettings = shouldShow('theme') || shouldShow('timeFormat') || shouldShow('weekStart');
const showMobileLayoutSetting = isMobile && isWebRuntime() && !isDesktopShell() && !isVSCode;
const hasAppearanceSettings = isVSCode
? hasLocalizationSettings
- : (shouldShow('theme') || showMobileLayoutSetting || shouldShow('pwaInstallName') || shouldShow('pwaOrientation') || shouldShow('timeFormat') || shouldShow('weekStart'));
+ : (shouldShow('theme') || showWindowControlsPositionSetting || showMobileLayoutSetting || shouldShow('pwaInstallName') || shouldShow('pwaOrientation') || shouldShow('timeFormat') || shouldShow('weekStart'));
const hasLayoutSettings = shouldShow('fontSize') || shouldShow('terminalFontSize') || shouldShow('editorFontSize') || shouldShow('spacing') || (shouldShow('inputBarOffset') && isMobile);
const hasNavigationSettings = (shouldShow('terminalQuickKeys') && !isMobile) || ((shouldShow('terminalShell') || shouldShow('terminalLoginShell')) && !isVSCode) || shouldShow('fileEditorKeymap') || (shouldShow('expandedEditorToolbar') && !isVSCode);
const hasBehaviorSettings = shouldShow('mermaidRendering')
@@ -980,6 +1002,25 @@ export const OpenChamberVisualSettings: React.FC
)}
+ {showWindowControlsPositionSetting && (
+
+ ({
+ value: option.id,
+ label: tUnsafe(option.labelKey),
+ }))}
+ onChange={handleWindowControlsPositionChange}
+ aria-label={t('settings.openchamber.desktopNetwork.field.windowControlsPositionAria')}
+ />
+
+ )}
+
{hasLocalizationSettings && (
diff --git a/packages/ui/src/lib/desktop.ts b/packages/ui/src/lib/desktop.ts
index 1c2ab633..a331c140 100644
--- a/packages/ui/src/lib/desktop.ts
+++ b/packages/ui/src/lib/desktop.ts
@@ -246,8 +246,8 @@ export const getElectronPlatform = (): string | null => {
return typeof platform === 'string' ? platform : null;
};
-/** Width of the three in-app window control buttons (3 × w-11). */
-export const DESKTOP_WINDOW_CONTROLS_WIDTH_PX = 132;
+/** Width of the three in-app window control buttons when placed on the left (3 × w-8). */
+export const DESKTOP_WINDOW_CONTROLS_WIDTH_PX = 96;
/** Windows and Linux use frameless windows with in-app minimize/maximize/close controls. */
export const usesFramelessElectronChrome = (): boolean => {
diff --git a/packages/ui/src/lib/settings/search.ts b/packages/ui/src/lib/settings/search.ts
index 92d932d1..ef54588d 100644
--- a/packages/ui/src/lib/settings/search.ts
+++ b/packages/ui/src/lib/settings/search.ts
@@ -392,7 +392,7 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
},
{
id: 'sessions.desktop-window-controls-position',
- page: 'general',
+ page: 'appearance',
titleKey: 'settings.openchamber.desktopNetwork.field.windowControlsPosition',
descriptionKey: 'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription',
keywords: ['desktop', 'window', 'controls', 'minimize', 'maximize', 'close', 'titlebar', 'linux', 'windows'],