fix: update ui version_backend, mem usage hardware (#5932)

* fix: update ui version_backend, mem usage hardware

* chore: hidden gpu from system monitor on mac

* chore: fix gpus vram
This commit is contained in:
Faisal Amir 2025-07-26 18:36:18 +07:00 committed by GitHub
parent 8ec4a36826
commit b89d9d090f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 120 additions and 64 deletions

View File

@ -0,0 +1,54 @@
import { useEffect } from 'react'
import { events } from '@janhq/core'
import { useModelProvider } from '@/hooks/useModelProvider'
import { getProviders } from '@/services/providers'
/**
* GlobalEventHandler handles global events that should be processed across all screens
* This provider should be mounted at the root level to ensure all screens can benefit from global event handling
*/
export function GlobalEventHandler() {
const { setProviders } = useModelProvider()
// Handle settingsChanged event globally
useEffect(() => {
const handleSettingsChanged = async (event: {
key: string
value: string
}) => {
console.log('Global settingsChanged event:', event)
// Handle version_backend changes specifically
if (event.key === 'version_backend') {
try {
// Refresh providers to get updated settings from the extension
const updatedProviders = await getProviders()
setProviders(updatedProviders)
console.log('Providers refreshed after version_backend change')
} catch (error) {
console.error(
'Failed to refresh providers after settingsChanged:',
error
)
}
}
// Add more global event handlers here as needed
// For example:
// if (event.key === 'some_other_setting') {
// // Handle other setting changes
// }
}
// Subscribe to the settingsChanged event
events.on('settingsChanged', handleSettingsChanged)
// Cleanup subscription on unmount
return () => {
events.off('settingsChanged', handleSettingsChanged)
}
}, [setProviders])
// This component doesn't render anything
return null
}

View File

@ -29,6 +29,7 @@ import {
} from '@/components/ui/resizable' } from '@/components/ui/resizable'
import { useCallback } from 'react' import { useCallback } from 'react'
import GlobalError from '@/containers/GlobalError' import GlobalError from '@/containers/GlobalError'
import { GlobalEventHandler } from '@/providers/GlobalEventHandler'
export const Route = createRootRoute({ export const Route = createRootRoute({
component: RootLayout, component: RootLayout,
@ -162,6 +163,7 @@ function RootLayout() {
<TranslationProvider> <TranslationProvider>
<ExtensionProvider> <ExtensionProvider>
<DataProvider /> <DataProvider />
<GlobalEventHandler />
</ExtensionProvider> </ExtensionProvider>
{isLocalAPIServerLogsRoute ? <LogsLayout /> : <AppLayout />} {isLocalAPIServerLogsRoute ? <LogsLayout /> : <AppLayout />}
{/* <TanStackRouterDevtools position="bottom-right" /> */} {/* <TanStackRouterDevtools position="bottom-right" /> */}

View File

@ -38,6 +38,9 @@ function Hardware() {
const llamacpp = providers.find((p) => p.provider === 'llamacpp') const llamacpp = providers.find((p) => p.provider === 'llamacpp')
// Llamacpp devices hook // Llamacpp devices hook
const llamacppDevicesResult = useLlamacppDevices()
// Use default values on macOS since llamacpp devices are not relevant
const { const {
devices: llamacppDevices, devices: llamacppDevices,
loading: llamacppDevicesLoading, loading: llamacppDevicesLoading,
@ -45,7 +48,16 @@ function Hardware() {
activatedDevices, activatedDevices,
toggleDevice, toggleDevice,
fetchDevices, fetchDevices,
} = useLlamacppDevices() } = IS_MACOS
? {
devices: [],
loading: false,
error: null,
activatedDevices: new Set(),
toggleDevice: () => {},
fetchDevices: () => {},
}
: llamacppDevicesResult
// Fetch llamacpp devices when component mounts // Fetch llamacpp devices when component mounts
useEffect(() => { useEffect(() => {
@ -296,8 +308,7 @@ function Hardware() {
<Progress <Progress
value={ value={
toNumber( toNumber(
(hardwareData.total_memory - systemUsage.used_memory /
systemUsage.used_memory) /
hardwareData.total_memory hardwareData.total_memory
) * 100 ) * 100
} }
@ -306,8 +317,7 @@ function Hardware() {
<span className="text-main-view-fg/80"> <span className="text-main-view-fg/80">
{( {(
toNumber( toNumber(
(hardwareData.total_memory - systemUsage.used_memory /
systemUsage.used_memory) /
hardwareData.total_memory hardwareData.total_memory
) * 100 ) * 100
).toFixed(2)} ).toFixed(2)}
@ -365,9 +375,9 @@ function Hardware() {
title={t('settings:hardware.vram')} title={t('settings:hardware.vram')}
actions={ actions={
<span className="text-main-view-fg/80"> <span className="text-main-view-fg/80">
{formatMegaBytes(device.mem)}{' '} {formatMegaBytes(device.free)}{' '}
{t('settings:hardware.freeOf')}{' '} {t('settings:hardware.freeOf')}{' '}
{formatMegaBytes(device.free)} {formatMegaBytes(device.mem)}
</span> </span>
} }
/> />

View File

@ -37,7 +37,6 @@ import { IconFolderPlus, IconLoader, IconRefresh } from '@tabler/icons-react'
import { getProviders } from '@/services/providers' import { getProviders } from '@/services/providers'
import { toast } from 'sonner' import { toast } from 'sonner'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { events } from '@janhq/core'
import { predefinedProviders } from '@/consts/providers' import { predefinedProviders } from '@/consts/providers'
import { useModelLoad } from '@/hooks/useModelLoad' import { useModelLoad } from '@/hooks/useModelLoad'
import { useLlamacppDevices } from '@/hooks/useLlamacppDevices' import { useLlamacppDevices } from '@/hooks/useLlamacppDevices'
@ -131,18 +130,8 @@ function ProviderDetail() {
} }
}, [provider, needsBackendConfig]) }, [provider, needsBackendConfig])
// Listen for settingsChanged event and refresh backend list if version_backend changes // Note: settingsChanged event is now handled globally in GlobalEventHandler
useEffect(() => { // This ensures all screens receive the event intermediately
const handler = (event: { key: string; value: string }) => {
if (event.key === 'version_backend') {
refreshSettings()
}
}
events.on('settingsChanged', handler)
return () => {
events.off('settingsChanged', handler)
}
}, [provider])
// Auto-refresh models for non-predefined providers // Auto-refresh models for non-predefined providers
useEffect(() => { useEffect(() => {

View File

@ -95,10 +95,7 @@ function SystemMonitor() {
// Calculate RAM usage percentage // Calculate RAM usage percentage
const ramUsagePercentage = const ramUsagePercentage =
toNumber( toNumber(systemUsage.used_memory / hardwareData.total_memory) * 100
(hardwareData.total_memory - systemUsage.used_memory) /
hardwareData.total_memory
) * 100
return ( return (
<div className="flex flex-col h-full bg-main-view overflow-y-auto p-6"> <div className="flex flex-col h-full bg-main-view overflow-y-auto p-6">
@ -197,49 +194,53 @@ function SystemMonitor() {
</div> </div>
{/* GPU Usage Card */} {/* GPU Usage Card */}
<div className="bg-main-view-fg/2 rounded-lg p-6 shadow-sm"> {!IS_MACOS && (
<h2 className="text-base font-semibold text-main-view-fg mb-4"> <div className="bg-main-view-fg/2 rounded-lg p-6 shadow-sm">
{t('system-monitor:activeGpus')} <h2 className="text-base font-semibold text-main-view-fg mb-4">
</h2> {t('system-monitor:activeGpus')}
<div className="flex flex-col gap-2"> </h2>
{llamacppDevices.length > 0 ? ( <div className="flex flex-col gap-2">
llamacppDevices.map((device) => ( {llamacppDevices.length > 0 ? (
<div key={device.id} className="flex flex-col gap-1"> llamacppDevices.map((device) => (
<div className="flex justify-between items-center"> <div key={device.id} className="flex flex-col gap-1">
<span className="text-main-view-fg/70">{device.name}</span> <div className="flex justify-between items-center">
<span <span className="text-main-view-fg/70">
className={`text-sm px-2 py-1 rounded-md ${ {device.name}
activatedDevices.has(device.id) </span>
? 'bg-green-500/20 text-green-600 dark:text-green-400' <span
: 'hidden' className={`text-sm px-2 py-1 rounded-md ${
}`} activatedDevices.has(device.id)
> ? 'bg-green-500/20 text-green-600 dark:text-green-400'
{activatedDevices.has(device.id) : 'hidden'
? t('system-monitor:active') }`}
: 'Inactive'} >
</span> {activatedDevices.has(device.id)
</div> ? t('system-monitor:active')
<div className="flex justify-between items-center text-sm"> : 'Inactive'}
<span className="text-main-view-fg/70">VRAM:</span> </span>
<span className="text-main-view-fg"> </div>
{formatMegaBytes(device.mem)} <div className="flex justify-between items-center text-sm">
</span> <span className="text-main-view-fg/70">VRAM:</span>
</div> <span className="text-main-view-fg">
<div className="flex justify-between items-center text-sm"> {formatMegaBytes(device.mem)}
<span className="text-main-view-fg/70">Free:</span> </span>
<span className="text-main-view-fg"> </div>
{formatMegaBytes(device.free)} <div className="flex justify-between items-center text-sm">
</span> <span className="text-main-view-fg/70">Free:</span>
<span className="text-main-view-fg">
{formatMegaBytes(device.free)}
</span>
</div>
</div> </div>
))
) : (
<div className="text-main-view-fg/70 text-center py-4">
{t('system-monitor:noGpus')}
</div> </div>
)) )}
) : ( </div>
<div className="text-main-view-fg/70 text-center py-4">
{t('system-monitor:noGpus')}
</div>
)}
</div> </div>
</div> )}
</div> </div>
</div> </div>
) )