diff --git a/desktop/src/components/layout/AppShell.tsx b/desktop/src/components/layout/AppShell.tsx index c9e578ab..af09547b 100644 --- a/desktop/src/components/layout/AppShell.tsx +++ b/desktop/src/components/layout/AppShell.tsx @@ -28,6 +28,7 @@ function isChatTab(tab: Tab | undefined) { export function AppShell() { const fetchSettings = useSettingsStore((s) => s.fetchAll) + const uiZoom = useSettingsStore((s) => s.uiZoom) const sidebarOpen = useUIStore((s) => s.sidebarOpen) const toggleSidebar = useUIStore((s) => s.toggleSidebar) const setSidebarOpen = useUIStore((s) => s.setSidebarOpen) @@ -194,7 +195,7 @@ export function AppShell() { } return ( -
+
{isMobileShell && effectiveSidebarOpen ? (
+ {/* UI Zoom */} +
+

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

+

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

+
+
+ + {Math.round(uiZoom * 100)}% + +
+
+ {Math.round(UI_ZOOM_MIN * 100)}% + setUiZoom(parseFloat(e.target.value))} + onMouseUp={(e) => e.currentTarget.blur()} + className="flex-1" + /> + {Math.round(UI_ZOOM_MAX * 100)}% +
+
+
{/* Confirm dialog for mode switch */} = Promise.resolve() function getStoredLocale(): Locale { @@ -19,6 +23,17 @@ function getStoredLocale(): Locale { return 'zh' } +function getStoredUiZoom(): number { + try { + const stored = localStorage.getItem(UI_ZOOM_STORAGE_KEY) + if (stored === null) return 1.0 + const parsed = parseFloat(stored) + if (isNaN(parsed)) return 1.0 + return Math.min(UI_ZOOM_MAX, Math.max(UI_ZOOM_MIN, parsed)) + } catch { /* localStorage unavailable */ } + return 1.0 +} + type SettingsStore = { permissionMode: PermissionMode currentModel: ModelInfo | null @@ -34,6 +49,7 @@ type SettingsStore = { h5Access: H5AccessSettings h5AccessError: string | null responseLanguage: string + uiZoom: number isLoading: boolean error: string | null @@ -61,6 +77,7 @@ type SettingsStore = { setResponseLanguage: (language: string) => Promise fetchAppMode: () => Promise setAppMode: (mode: AppMode, portableDir?: string | null) => Promise + setUiZoom: (zoom: number) => void } const DEFAULT_H5_ACCESS_SETTINGS: H5AccessSettings = { @@ -85,11 +102,17 @@ export const useSettingsStore = create((set, get) => ({ h5Access: DEFAULT_H5_ACCESS_SETTINGS, h5AccessError: null, responseLanguage: '', + uiZoom: getStoredUiZoom(), isLoading: false, error: null, appMode: { mode: 'default', portableDir: null, defaultPortableDir: null }, appModeRequiresRestart: false, + setUiZoom: (zoom: number) => { + const clamped = Math.min(UI_ZOOM_MAX, Math.max(UI_ZOOM_MIN, zoom)) + set({ uiZoom: clamped }) + try { localStorage.setItem(UI_ZOOM_STORAGE_KEY, String(clamped)) } catch { /* noop */ } + }, fetchAll: async () => { set({ isLoading: true, error: null }) diff --git a/desktop/src/types/settings.ts b/desktop/src/types/settings.ts index 0aaf62f5..101de007 100644 --- a/desktop/src/types/settings.ts +++ b/desktop/src/types/settings.ts @@ -43,6 +43,7 @@ export type UserSettings = { desktopNotificationsEnabled?: boolean webSearch?: WebSearchSettings language?: string + uiZoom?: number [key: string]: unknown }