From 3d361028b37eff694f58bd125920888e9fcbbecc Mon Sep 17 00:00:00 2001 From: Illia Filippov Date: Thu, 25 Dec 2025 19:38:03 +0100 Subject: [PATCH] feat: add OS detection hook and integrate into FileBrowserDialog for improved keyboard shortcut handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduced useOSDetection hook to determine the user's operating system. - Updated FileBrowserDialog to utilize the OS detection for displaying the correct keyboard shortcut (⌘ or Ctrl) based on the detected OS. --- .../dialogs/file-browser-dialog.tsx | 8 ++-- apps/ui/src/hooks/index.ts | 1 + apps/ui/src/hooks/use-os-detection.ts | 48 +++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 apps/ui/src/hooks/use-os-detection.ts diff --git a/apps/ui/src/components/dialogs/file-browser-dialog.tsx b/apps/ui/src/components/dialogs/file-browser-dialog.tsx index ff41c22b..02552c28 100644 --- a/apps/ui/src/components/dialogs/file-browser-dialog.tsx +++ b/apps/ui/src/components/dialogs/file-browser-dialog.tsx @@ -13,6 +13,7 @@ import { PathInput } from '@/components/ui/path-input'; import { Kbd, KbdGroup } from '@/components/ui/kbd'; import { getJSON, setJSON } from '@/lib/storage'; import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config'; +import { useOSDetection } from '@/hooks'; interface DirectoryEntry { name: string; @@ -68,6 +69,7 @@ export function FileBrowserDialog({ description = 'Navigate to your project folder or paste a path directly', initialPath, }: FileBrowserDialogProps) { + const { isMac } = useOSDetection(); const [currentPath, setCurrentPath] = useState(''); const [parentPath, setParentPath] = useState(null); const [directories, setDirectories] = useState([]); @@ -374,11 +376,7 @@ export function FileBrowserDialog({ Select Current Folder - - {typeof navigator !== 'undefined' && navigator.platform?.includes('Mac') - ? '⌘' - : 'Ctrl'} - + {isMac ? '⌘' : 'Ctrl'} diff --git a/apps/ui/src/hooks/index.ts b/apps/ui/src/hooks/index.ts index b18a85e6..8f2264d6 100644 --- a/apps/ui/src/hooks/index.ts +++ b/apps/ui/src/hooks/index.ts @@ -3,6 +3,7 @@ export { useBoardBackgroundSettings } from './use-board-background-settings'; export { useElectronAgent } from './use-electron-agent'; export { useKeyboardShortcuts } from './use-keyboard-shortcuts'; export { useMessageQueue } from './use-message-queue'; +export { useOSDetection, type OperatingSystem, type OSDetectionResult } from './use-os-detection'; export { useResponsiveKanban } from './use-responsive-kanban'; export { useScrollTracking } from './use-scroll-tracking'; export { useSettingsMigration } from './use-settings-migration'; diff --git a/apps/ui/src/hooks/use-os-detection.ts b/apps/ui/src/hooks/use-os-detection.ts new file mode 100644 index 00000000..a7bcf68b --- /dev/null +++ b/apps/ui/src/hooks/use-os-detection.ts @@ -0,0 +1,48 @@ +import { useMemo } from 'react'; + +export type OperatingSystem = 'mac' | 'windows' | 'linux' | 'unknown'; + +export interface OSDetectionResult { + readonly os: OperatingSystem; + readonly isMac: boolean; + readonly isWindows: boolean; + readonly isLinux: boolean; +} + +function detectOS(): OperatingSystem { + // Check Electron's process.platform first (most reliable in Electron apps) + if (typeof process !== 'undefined' && process.platform) { + if (process.platform === 'darwin') return 'mac'; + if (process.platform === 'win32') return 'windows'; + if (process.platform === 'linux') return 'linux'; + } + + if (typeof navigator === 'undefined') { + return 'unknown'; + } + + // Fallback: use modern userAgentData API with fallback to navigator.platform + const nav = navigator as Navigator & { userAgentData?: { platform: string } }; + const platform = (nav.userAgentData?.platform ?? navigator.platform ?? '').toLowerCase(); + + if (platform.includes('mac')) return 'mac'; + if (platform.includes('win')) return 'windows'; + if (platform.includes('linux') || platform.includes('x11')) return 'linux'; + return 'unknown'; +} + +/** + * Hook to detect the user's operating system. + * Returns OS information and convenience boolean flags. + */ +export function useOSDetection(): OSDetectionResult { + return useMemo(() => { + const os = detectOS(); + return { + os, + isMac: os === 'mac', + isWindows: os === 'windows', + isLinux: os === 'linux', + }; + }, []); +}