diff --git a/web-app/src/providers/GlobalEventHandler.tsx b/web-app/src/providers/GlobalEventHandler.tsx new file mode 100644 index 000000000..62bb67185 --- /dev/null +++ b/web-app/src/providers/GlobalEventHandler.tsx @@ -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 +} diff --git a/web-app/src/routes/__root.tsx b/web-app/src/routes/__root.tsx index 4ca2f4623..938c30c84 100644 --- a/web-app/src/routes/__root.tsx +++ b/web-app/src/routes/__root.tsx @@ -29,6 +29,7 @@ import { } from '@/components/ui/resizable' import { useCallback } from 'react' import GlobalError from '@/containers/GlobalError' +import { GlobalEventHandler } from '@/providers/GlobalEventHandler' export const Route = createRootRoute({ component: RootLayout, @@ -162,6 +163,7 @@ function RootLayout() { + {isLocalAPIServerLogsRoute ? : } {/* */} diff --git a/web-app/src/routes/settings/hardware.tsx b/web-app/src/routes/settings/hardware.tsx index a69063809..de2ac2de4 100644 --- a/web-app/src/routes/settings/hardware.tsx +++ b/web-app/src/routes/settings/hardware.tsx @@ -38,6 +38,9 @@ function Hardware() { const llamacpp = providers.find((p) => p.provider === 'llamacpp') // Llamacpp devices hook + const llamacppDevicesResult = useLlamacppDevices() + + // Use default values on macOS since llamacpp devices are not relevant const { devices: llamacppDevices, loading: llamacppDevicesLoading, @@ -45,7 +48,16 @@ function Hardware() { activatedDevices, toggleDevice, fetchDevices, - } = useLlamacppDevices() + } = IS_MACOS + ? { + devices: [], + loading: false, + error: null, + activatedDevices: new Set(), + toggleDevice: () => {}, + fetchDevices: () => {}, + } + : llamacppDevicesResult // Fetch llamacpp devices when component mounts useEffect(() => { @@ -296,8 +308,7 @@ function Hardware() { {( toNumber( - (hardwareData.total_memory - - systemUsage.used_memory) / + systemUsage.used_memory / hardwareData.total_memory ) * 100 ).toFixed(2)} @@ -365,9 +375,9 @@ function Hardware() { title={t('settings:hardware.vram')} actions={ - {formatMegaBytes(device.mem)}{' '} + {formatMegaBytes(device.free)}{' '} {t('settings:hardware.freeOf')}{' '} - {formatMegaBytes(device.free)} + {formatMegaBytes(device.mem)} } /> diff --git a/web-app/src/routes/settings/providers/$providerName.tsx b/web-app/src/routes/settings/providers/$providerName.tsx index 4d63a987f..41d2b0217 100644 --- a/web-app/src/routes/settings/providers/$providerName.tsx +++ b/web-app/src/routes/settings/providers/$providerName.tsx @@ -37,7 +37,6 @@ import { IconFolderPlus, IconLoader, IconRefresh } from '@tabler/icons-react' import { getProviders } from '@/services/providers' import { toast } from 'sonner' import { useEffect, useState } from 'react' -import { events } from '@janhq/core' import { predefinedProviders } from '@/consts/providers' import { useModelLoad } from '@/hooks/useModelLoad' import { useLlamacppDevices } from '@/hooks/useLlamacppDevices' @@ -131,18 +130,8 @@ function ProviderDetail() { } }, [provider, needsBackendConfig]) - // Listen for settingsChanged event and refresh backend list if version_backend changes - useEffect(() => { - const handler = (event: { key: string; value: string }) => { - if (event.key === 'version_backend') { - refreshSettings() - } - } - events.on('settingsChanged', handler) - return () => { - events.off('settingsChanged', handler) - } - }, [provider]) + // Note: settingsChanged event is now handled globally in GlobalEventHandler + // This ensures all screens receive the event intermediately // Auto-refresh models for non-predefined providers useEffect(() => { diff --git a/web-app/src/routes/system-monitor.tsx b/web-app/src/routes/system-monitor.tsx index 6c36c21d7..094ab42b8 100644 --- a/web-app/src/routes/system-monitor.tsx +++ b/web-app/src/routes/system-monitor.tsx @@ -95,10 +95,7 @@ function SystemMonitor() { // Calculate RAM usage percentage const ramUsagePercentage = - toNumber( - (hardwareData.total_memory - systemUsage.used_memory) / - hardwareData.total_memory - ) * 100 + toNumber(systemUsage.used_memory / hardwareData.total_memory) * 100 return (
@@ -197,49 +194,53 @@ function SystemMonitor() {
{/* GPU Usage Card */} -
-

- {t('system-monitor:activeGpus')} -

-
- {llamacppDevices.length > 0 ? ( - llamacppDevices.map((device) => ( -
-
- {device.name} - - {activatedDevices.has(device.id) - ? t('system-monitor:active') - : 'Inactive'} - -
-
- VRAM: - - {formatMegaBytes(device.mem)} - -
-
- Free: - - {formatMegaBytes(device.free)} - + {!IS_MACOS && ( +
+

+ {t('system-monitor:activeGpus')} +

+
+ {llamacppDevices.length > 0 ? ( + llamacppDevices.map((device) => ( +
+
+ + {device.name} + + + {activatedDevices.has(device.id) + ? t('system-monitor:active') + : 'Inactive'} + +
+
+ VRAM: + + {formatMegaBytes(device.mem)} + +
+
+ Free: + + {formatMegaBytes(device.free)} + +
+ )) + ) : ( +
+ {t('system-monitor:noGpus')}
- )) - ) : ( -
- {t('system-monitor:noGpus')} -
- )} + )} +
-
+ )}
)