diff --git a/desktop/src/pages/Settings.tsx b/desktop/src/pages/Settings.tsx index 4ea0329d..404f7a82 100644 --- a/desktop/src/pages/Settings.tsx +++ b/desktop/src/pages/Settings.tsx @@ -9,7 +9,7 @@ import { ConfirmDialog } from '../components/shared/ConfirmDialog' import { Input } from '../components/shared/Input' import { Button } from '../components/shared/Button' import { Dropdown } from '../components/shared/Dropdown' -import type { PermissionMode, EffortLevel, ThemeMode, WebSearchMode } from '../types/settings' +import type { PermissionMode, EffortLevel, ThemeMode, WebSearchMode, AppMode } from '../types/settings' import type { Locale } from '../i18n' import type { SavedProvider, UpdateProviderInput, ProviderTestResult, ModelMapping, ApiFormat, ProviderAuthStrategy } from '../types/provider' import type { ProviderPreset } from '../types/providerPreset' @@ -1382,11 +1382,17 @@ function GeneralSettings() { setWebSearch, responseLanguage, setResponseLanguage, + appMode, + appModeRequiresRestart, + fetchAppMode, + setAppMode: setAppModeAction, } = useSettingsStore() const t = useTranslation() const [webSearchDraft, setWebSearchDraft] = useState(webSearch) const [notificationPermission, setNotificationPermission] = useState('default') const [notificationActionRunning, setNotificationActionRunning] = useState(false) + const [modeSwitchConfirmOpen, setModeSwitchConfirmOpen] = useState(false) + const [pendingMode, setPendingMode] = useState(null) const webSearchDirty = JSON.stringify(webSearchDraft) !== JSON.stringify(webSearch) useEffect(() => { @@ -1403,6 +1409,11 @@ function GeneralSettings() { } }, []) + useEffect(() => { + if (!isTauriRuntime()) return + void fetchAppMode() + }, [fetchAppMode]) + const EFFORT_LABELS: Record = { low: t('settings.general.effort.low'), medium: t('settings.general.effort.medium'), @@ -1510,6 +1521,79 @@ function GeneralSettings() { return (
+ {/* Mode Section */} + {isTauriRuntime() && ( +
+

{t('settings.general.modeTitle')}

+

{t('settings.general.modeDescription')}

+
+ + +
+ {appMode.mode === 'portable' && appMode.portableDir && ( +
+ {t('settings.general.modePortableDir')}: {appMode.portableDir} +
+ )} + {appMode.mode === 'default' && ( +
+ {t('settings.general.modeDefaultHint')} +
+ )} +
+ )} + + {/* Restart Required Banner */} + {appModeRequiresRestart && ( +
+ + warning + +
+
+ {t('settings.general.modeRestartTitle')} +
+
+ {t('settings.general.modeRestartHint')} +
+
+
+ )} + {/* Appearance selector */}

{t('settings.general.appearanceTitle')}

{t('settings.general.appearanceDescription')}

@@ -1776,6 +1860,22 @@ function GeneralSettings() {
+ {/* Confirm dialog for mode switch */} + { setModeSwitchConfirmOpen(false); setPendingMode(null); }} + onConfirm={() => { + if (pendingMode) { void setAppModeAction(pendingMode); } + setModeSwitchConfirmOpen(false); + setPendingMode(null); + }} + title={t('settings.general.modeSwitchTitle')} + body={t('settings.general.modeSwitchBody', { + mode: pendingMode === 'portable' ? t('settings.general.modePortable') : t('settings.general.modeDefault'), + })} + confirmLabel={t('settings.general.modeSwitchConfirm')} + cancelLabel={t('common.cancel')} + /> ) }