Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/main/presenter/devicePresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,37 @@ export class DevicePresenter implements IDevicePresenter {
}

async getDeviceInfo(): Promise<DeviceInfo> {
const platform = process.platform
const osVersion = os.release()

// Build version metadata based on current platform
let osVersionMetadata: Array<{ name: string; build: number }> = []

if (platform === 'win32') {
osVersionMetadata = [
{ name: 'Windows 11', build: 22000 },
{ name: 'Windows 10', build: 10240 },
{ name: 'Windows 8.1', build: 9600 },
{ name: 'Windows 8', build: 9200 }
]
} else if (platform === 'darwin') {
osVersionMetadata = [
{ name: 'macOS Tahoe', build: 25 },
{ name: 'macOS Sequoia', build: 24 },
{ name: 'macOS Sonoma', build: 23 },
{ name: 'macOS Ventura', build: 22 },
{ name: 'macOS Monterey', build: 21 },
{ name: 'macOS Big Sur', build: 20 }
]
}

return {
platform: process.platform,
platform,
arch: process.arch,
cpuModel: os.cpus()[0].model,
totalMemory: os.totalmem(),
osVersion: os.release()
osVersion,
osVersionMetadata
}
}

Expand Down
8 changes: 3 additions & 5 deletions src/renderer/settings/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import { useLanguageStore } from '../src/stores/language'
import { useModelCheckStore } from '../src/stores/modelCheck'
import { Button } from '@shadcn/components/ui/button'
import ModelCheckDialog from '@/components/settings/ModelCheckDialog.vue'
import { useDeviceVersion } from '../src/composables/useDeviceVersion'

const devicePresenter = usePresenter('devicePresenter')
const windowPresenter = usePresenter('windowPresenter')
Expand All @@ -71,8 +72,8 @@ const settingsStore = useSettingsStore()
const languageStore = useLanguageStore()
const modelCheckStore = useModelCheckStore()

const isMacOS = ref(false)
const isWinMacOS = ref(false)
// Detect platform to apply proper styling
const { isMacOS, isWinMacOS } = useDeviceVersion()
const { t, locale } = useI18n()
const router = useRouter()
const route = useRoute()
Expand All @@ -89,9 +90,6 @@ const settings: Ref<
// Get all routes and build settings navigation
const routes = router.getRoutes()
onMounted(() => {
devicePresenter.getDeviceInfo().then((deviceInfo) => {
isWinMacOS.value = deviceInfo.platform === 'darwin' || deviceInfo.platform === 'win32'
})
const tempArray: {
title: string
name: string
Expand Down
14 changes: 4 additions & 10 deletions src/renderer/shell/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,10 @@

<script setup lang="ts">
import AppBar from './components/AppBar.vue'
import { ref, onMounted } from 'vue'
import { usePresenter } from '@/composables/usePresenter'
const isWinMacOS = ref(false)
const devicePresenter = usePresenter('devicePresenter')
// Shell component setup
onMounted(() => {
devicePresenter.getDeviceInfo().then((deviceInfo) => {
isWinMacOS.value = deviceInfo.platform === 'darwin' || deviceInfo.platform === 'win32'
})
})
import { useDeviceVersion } from '@/composables/useDeviceVersion'

// Detect platform to apply proper styling
const { isWinMacOS } = useDeviceVersion()
</script>

<style>
Expand Down
63 changes: 63 additions & 0 deletions src/renderer/src/composables/useDeviceVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// === Vue Core ===
import { ref, onMounted } from 'vue'

// === Composables ===
import { usePresenter } from '@/composables/usePresenter'

/**
* Composable for detecting device platform and OS version
*
* Features:
* - Detects if the current OS is macOS or Windows 11+
* - Automatically loads device info on component mount
* - Centralizes platform-specific UI logic
*
* Platform Detection Rules:
* - macOS: All versions return true
* - Windows: Only Windows 11+ returns true
* - Linux: Returns false
*
* Technical Notes:
* - Windows osVersion format: "10.0.22621" (Major.Minor.BuildNumber)
* - Windows 11 starts at build 22000
* - We extract the 3rd part (index [2]) for build number comparison
* - macOS osVersion format: "25.1.0" (Darwin kernel version)
* - macOS Tahoe (26) = Darwin 25.x
* - macOS Sequoia (15) = Darwin 24.x
* - macOS Sonoma (14) = Darwin 23.x
* - No version check needed, all macOS versions are treated equally
*/
export function useDeviceVersion() {
// === Local State ===
const isWinMacOS = ref(false)
const isMacOS = ref(false)
const devicePresenter = usePresenter('devicePresenter')

// === Lifecycle Hooks ===
onMounted(() => {
devicePresenter.getDeviceInfo().then((deviceInfo) => {
// Detect macOS (all versions)
const isMacOSPlatform = deviceInfo.platform === 'darwin'
isMacOS.value = isMacOSPlatform

// Check if it's Windows 11+
// Note: Windows osVersion format is "10.0.22621", we need the 3rd part (build number)
let isWin11Plus = false
if (deviceInfo.platform === 'win32') {
const buildNumber = parseInt(deviceInfo.osVersion.split('.')[2] || '0', 10)
const win11Metadata = deviceInfo.osVersionMetadata.find((v) => v.name === 'Windows 11')
isWin11Plus = win11Metadata ? buildNumber >= win11Metadata.build : false
Comment on lines +37 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard against missing OS metadata in tests

The new useDeviceVersion composable assumes that devicePresenter.getDeviceInfo() always returns osVersion and osVersionMetadata. In the repo’s renderer test setup the mock still returns { platform, arch, version } only, so deviceInfo.osVersion.split('.') will throw as soon as any test mounts a component that uses this composable. Either provide defensive defaults here or update the shared test mock to include the new fields, otherwise the test suite will crash rather than exercising the component logic.

Useful? React with 👍 / 👎.

}

// isWinMacOS is true for: macOS (all versions) OR Windows 11+
isWinMacOS.value = isMacOSPlatform || isWin11Plus
})
})
Comment on lines +37 to +55
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for device info retrieval.

The getDeviceInfo() promise has no error handling. If the call fails, the platform flags will remain false, potentially causing incorrect UI rendering.

Apply this diff to add error handling:

   onMounted(() => {
-    devicePresenter.getDeviceInfo().then((deviceInfo) => {
+    devicePresenter.getDeviceInfo().then((deviceInfo) => {
       // Detect macOS (all versions)
       const isMacOSPlatform = deviceInfo.platform === 'darwin'
       isMacOS.value = isMacOSPlatform
 
       // Check if it's Windows 11+
       // Note: Windows osVersion format is "10.0.22621", we need the 3rd part (build number)
       let isWin11Plus = false
       if (deviceInfo.platform === 'win32') {
         const buildNumber = parseInt(deviceInfo.osVersion.split('.')[2] || '0', 10)
         const win11Metadata = deviceInfo.osVersionMetadata.find((v) => v.name === 'Windows 11')
         isWin11Plus = win11Metadata ? buildNumber >= win11Metadata.build : false
       }
 
       // isWinMacOS is true for: macOS (all versions) OR Windows 11+
       isWinMacOS.value = isMacOSPlatform || isWin11Plus
+    }).catch((error) => {
+      console.error('Failed to load device info:', error)
+      // Fallback to safe defaults (already set to false)
     })
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onMounted(() => {
devicePresenter.getDeviceInfo().then((deviceInfo) => {
// Detect macOS (all versions)
const isMacOSPlatform = deviceInfo.platform === 'darwin'
isMacOS.value = isMacOSPlatform
// Check if it's Windows 11+
// Note: Windows osVersion format is "10.0.22621", we need the 3rd part (build number)
let isWin11Plus = false
if (deviceInfo.platform === 'win32') {
const buildNumber = parseInt(deviceInfo.osVersion.split('.')[2] || '0', 10)
const win11Metadata = deviceInfo.osVersionMetadata.find((v) => v.name === 'Windows 11')
isWin11Plus = win11Metadata ? buildNumber >= win11Metadata.build : false
}
// isWinMacOS is true for: macOS (all versions) OR Windows 11+
isWinMacOS.value = isMacOSPlatform || isWin11Plus
})
})
onMounted(() => {
devicePresenter.getDeviceInfo().then((deviceInfo) => {
// Detect macOS (all versions)
const isMacOSPlatform = deviceInfo.platform === 'darwin'
isMacOS.value = isMacOSPlatform
// Check if it's Windows 11+
// Note: Windows osVersion format is "10.0.22621", we need the 3rd part (build number)
let isWin11Plus = false
if (deviceInfo.platform === 'win32') {
const buildNumber = parseInt(deviceInfo.osVersion.split('.')[2] || '0', 10)
const win11Metadata = deviceInfo.osVersionMetadata.find((v) => v.name === 'Windows 11')
isWin11Plus = win11Metadata ? buildNumber >= win11Metadata.build : false
}
// isWinMacOS is true for: macOS (all versions) OR Windows 11+
isWinMacOS.value = isMacOSPlatform || isWin11Plus
}).catch((error) => {
console.error('Failed to load device info:', error)
// Fallback to safe defaults (already set to false)
})
})
🤖 Prompt for AI Agents
In src/renderer/src/composables/useDeviceVersion.ts around lines 37 to 55, the
call to devicePresenter.getDeviceInfo() lacks error handling; add a .catch
handler (or convert to async/await with try/catch) to handle rejected promises,
log the error (console.error or a logger), and explicitly set isMacOS.value and
isWinMacOS.value to safe defaults (false) so UI rendering won't rely on
stale/undefined values; ensure the error path still leaves the component in a
predictable state (optionally set an error flag or emit an event) and keep the
existing success logic unchanged.


// === Return API ===
return {
// State
isWinMacOS,
isMacOS
}
}
4 changes: 4 additions & 0 deletions src/shared/types/presenters/legacy.presenters.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,10 @@ export type DeviceInfo = {
cpuModel: string
totalMemory: number
osVersion: string
osVersionMetadata: Array<{
name: string
build: number
}>
}

export type MemoryInfo = {
Expand Down