Merge pull request #5049 from menloresearch/chore/system-monitor
chore: intial new window system monitor
This commit is contained in:
commit
a7e4037449
14
src-tauri/capabilities/system-monitor-window.json
Normal file
14
src-tauri/capabilities/system-monitor-window.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "system-monitor-window",
|
||||||
|
"description": "enables permissions for the system monitor window",
|
||||||
|
"windows": ["system-monitor-window"],
|
||||||
|
"permissions": [
|
||||||
|
"core:default",
|
||||||
|
"core:window:allow-start-dragging",
|
||||||
|
"core:window:allow-set-theme",
|
||||||
|
"log:default",
|
||||||
|
"core:webview:allow-create-webview-window",
|
||||||
|
"core:window:allow-set-focus"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -17,5 +17,6 @@ export const route = {
|
|||||||
},
|
},
|
||||||
hub: '/hub',
|
hub: '/hub',
|
||||||
localApiServerlogs: '/local-api-server/logs',
|
localApiServerlogs: '/local-api-server/logs',
|
||||||
|
systemMonitor: '/system-monitor',
|
||||||
threadsDetail: '/threads/$threadId',
|
threadsDetail: '/threads/$threadId',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { clsx, type ClassValue } from 'clsx'
|
import { type ClassValue, clsx } from 'clsx'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
@ -132,3 +132,13 @@ export const toGigabytes = (
|
|||||||
return input + (options?.hideUnit ? '' : 'B')
|
return input + (options?.hideUnit ? '' : 'B')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatMegaBytes(mb: number) {
|
||||||
|
const tb = mb / (1024 * 1024)
|
||||||
|
if (tb >= 1) {
|
||||||
|
return `${tb.toFixed(2)} TB`
|
||||||
|
} else {
|
||||||
|
const gb = mb / 1024
|
||||||
|
return `${gb.toFixed(2)} GB`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
// Import Routes
|
// Import Routes
|
||||||
|
|
||||||
import { Route as rootRoute } from './routes/__root'
|
import { Route as rootRoute } from './routes/__root'
|
||||||
|
import { Route as SystemMonitorImport } from './routes/system-monitor'
|
||||||
import { Route as HubImport } from './routes/hub'
|
import { Route as HubImport } from './routes/hub'
|
||||||
import { Route as AssistantImport } from './routes/assistant'
|
import { Route as AssistantImport } from './routes/assistant'
|
||||||
import { Route as IndexImport } from './routes/index'
|
import { Route as IndexImport } from './routes/index'
|
||||||
@ -29,6 +30,12 @@ import { Route as SettingsProvidersProviderNameImport } from './routes/settings/
|
|||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
|
|
||||||
|
const SystemMonitorRoute = SystemMonitorImport.update({
|
||||||
|
id: '/system-monitor',
|
||||||
|
path: '/system-monitor',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const HubRoute = HubImport.update({
|
const HubRoute = HubImport.update({
|
||||||
id: '/hub',
|
id: '/hub',
|
||||||
path: '/hub',
|
path: '/hub',
|
||||||
@ -145,6 +152,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof HubImport
|
preLoaderRoute: typeof HubImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/system-monitor': {
|
||||||
|
id: '/system-monitor'
|
||||||
|
path: '/system-monitor'
|
||||||
|
fullPath: '/system-monitor'
|
||||||
|
preLoaderRoute: typeof SystemMonitorImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/local-api-server/logs': {
|
'/local-api-server/logs': {
|
||||||
id: '/local-api-server/logs'
|
id: '/local-api-server/logs'
|
||||||
path: '/local-api-server/logs'
|
path: '/local-api-server/logs'
|
||||||
@ -238,6 +252,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/assistant': typeof AssistantRoute
|
'/assistant': typeof AssistantRoute
|
||||||
'/hub': typeof HubRoute
|
'/hub': typeof HubRoute
|
||||||
|
'/system-monitor': typeof SystemMonitorRoute
|
||||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||||
@ -256,6 +271,7 @@ export interface FileRoutesByTo {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/assistant': typeof AssistantRoute
|
'/assistant': typeof AssistantRoute
|
||||||
'/hub': typeof HubRoute
|
'/hub': typeof HubRoute
|
||||||
|
'/system-monitor': typeof SystemMonitorRoute
|
||||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||||
@ -275,6 +291,7 @@ export interface FileRoutesById {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/assistant': typeof AssistantRoute
|
'/assistant': typeof AssistantRoute
|
||||||
'/hub': typeof HubRoute
|
'/hub': typeof HubRoute
|
||||||
|
'/system-monitor': typeof SystemMonitorRoute
|
||||||
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
'/local-api-server/logs': typeof LocalApiServerLogsRoute
|
||||||
'/settings/appearance': typeof SettingsAppearanceRoute
|
'/settings/appearance': typeof SettingsAppearanceRoute
|
||||||
'/settings/extensions': typeof SettingsExtensionsRoute
|
'/settings/extensions': typeof SettingsExtensionsRoute
|
||||||
@ -295,6 +312,7 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/assistant'
|
| '/assistant'
|
||||||
| '/hub'
|
| '/hub'
|
||||||
|
| '/system-monitor'
|
||||||
| '/local-api-server/logs'
|
| '/local-api-server/logs'
|
||||||
| '/settings/appearance'
|
| '/settings/appearance'
|
||||||
| '/settings/extensions'
|
| '/settings/extensions'
|
||||||
@ -312,6 +330,7 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/assistant'
|
| '/assistant'
|
||||||
| '/hub'
|
| '/hub'
|
||||||
|
| '/system-monitor'
|
||||||
| '/local-api-server/logs'
|
| '/local-api-server/logs'
|
||||||
| '/settings/appearance'
|
| '/settings/appearance'
|
||||||
| '/settings/extensions'
|
| '/settings/extensions'
|
||||||
@ -329,6 +348,7 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/assistant'
|
| '/assistant'
|
||||||
| '/hub'
|
| '/hub'
|
||||||
|
| '/system-monitor'
|
||||||
| '/local-api-server/logs'
|
| '/local-api-server/logs'
|
||||||
| '/settings/appearance'
|
| '/settings/appearance'
|
||||||
| '/settings/extensions'
|
| '/settings/extensions'
|
||||||
@ -348,6 +368,7 @@ export interface RootRouteChildren {
|
|||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
AssistantRoute: typeof AssistantRoute
|
AssistantRoute: typeof AssistantRoute
|
||||||
HubRoute: typeof HubRoute
|
HubRoute: typeof HubRoute
|
||||||
|
SystemMonitorRoute: typeof SystemMonitorRoute
|
||||||
LocalApiServerLogsRoute: typeof LocalApiServerLogsRoute
|
LocalApiServerLogsRoute: typeof LocalApiServerLogsRoute
|
||||||
SettingsAppearanceRoute: typeof SettingsAppearanceRoute
|
SettingsAppearanceRoute: typeof SettingsAppearanceRoute
|
||||||
SettingsExtensionsRoute: typeof SettingsExtensionsRoute
|
SettingsExtensionsRoute: typeof SettingsExtensionsRoute
|
||||||
@ -366,6 +387,7 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
AssistantRoute: AssistantRoute,
|
AssistantRoute: AssistantRoute,
|
||||||
HubRoute: HubRoute,
|
HubRoute: HubRoute,
|
||||||
|
SystemMonitorRoute: SystemMonitorRoute,
|
||||||
LocalApiServerLogsRoute: LocalApiServerLogsRoute,
|
LocalApiServerLogsRoute: LocalApiServerLogsRoute,
|
||||||
SettingsAppearanceRoute: SettingsAppearanceRoute,
|
SettingsAppearanceRoute: SettingsAppearanceRoute,
|
||||||
SettingsExtensionsRoute: SettingsExtensionsRoute,
|
SettingsExtensionsRoute: SettingsExtensionsRoute,
|
||||||
@ -393,6 +415,7 @@ export const routeTree = rootRoute
|
|||||||
"/",
|
"/",
|
||||||
"/assistant",
|
"/assistant",
|
||||||
"/hub",
|
"/hub",
|
||||||
|
"/system-monitor",
|
||||||
"/local-api-server/logs",
|
"/local-api-server/logs",
|
||||||
"/settings/appearance",
|
"/settings/appearance",
|
||||||
"/settings/extensions",
|
"/settings/extensions",
|
||||||
@ -416,6 +439,9 @@ export const routeTree = rootRoute
|
|||||||
"/hub": {
|
"/hub": {
|
||||||
"filePath": "hub.tsx"
|
"filePath": "hub.tsx"
|
||||||
},
|
},
|
||||||
|
"/system-monitor": {
|
||||||
|
"filePath": "system-monitor.tsx"
|
||||||
|
},
|
||||||
"/local-api-server/logs": {
|
"/local-api-server/logs": {
|
||||||
"filePath": "local-api-server/logs.tsx"
|
"filePath": "local-api-server/logs.tsx"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -18,9 +18,6 @@ export const Route = createRootRoute({
|
|||||||
const AppLayout = () => {
|
const AppLayout = () => {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ExtensionProvider>
|
|
||||||
<DataProvider />
|
|
||||||
</ExtensionProvider>
|
|
||||||
<KeyboardShortcutsProvider />
|
<KeyboardShortcutsProvider />
|
||||||
<main className="relative h-svh text-sm antialiased select-none bg-app">
|
<main className="relative h-svh text-sm antialiased select-none bg-app">
|
||||||
{/* Fake absolute panel top to enable window drag */}
|
{/* Fake absolute panel top to enable window drag */}
|
||||||
@ -62,13 +59,17 @@ const LogsLayout = () => {
|
|||||||
function RootLayout() {
|
function RootLayout() {
|
||||||
const router = useRouterState()
|
const router = useRouterState()
|
||||||
const isLocalAPIServerLogsRoute =
|
const isLocalAPIServerLogsRoute =
|
||||||
router.location.pathname === route.localApiServerlogs
|
router.location.pathname === route.localApiServerlogs ||
|
||||||
|
router.location.pathname === route.systemMonitor
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ThemeProvider />
|
<ThemeProvider />
|
||||||
<AppearanceProvider />
|
<AppearanceProvider />
|
||||||
<ToasterProvider />
|
<ToasterProvider />
|
||||||
|
<ExtensionProvider>
|
||||||
|
<DataProvider />
|
||||||
|
</ExtensionProvider>
|
||||||
{isLocalAPIServerLogsRoute ? <LogsLayout /> : <AppLayout />}
|
{isLocalAPIServerLogsRoute ? <LogsLayout /> : <AppLayout />}
|
||||||
{/* <TanStackRouterDevtools position="bottom-right" /> */}
|
{/* <TanStackRouterDevtools position="bottom-right" /> */}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@ -24,23 +24,18 @@ import {
|
|||||||
useSortable,
|
useSortable,
|
||||||
} from '@dnd-kit/sortable'
|
} from '@dnd-kit/sortable'
|
||||||
import { CSS } from '@dnd-kit/utilities'
|
import { CSS } from '@dnd-kit/utilities'
|
||||||
import { IconGripVertical } from '@tabler/icons-react'
|
import {
|
||||||
|
IconGripVertical,
|
||||||
|
IconDeviceDesktopAnalytics,
|
||||||
|
} from '@tabler/icons-react'
|
||||||
import { getHardwareInfo } from '@/services/hardware'
|
import { getHardwareInfo } from '@/services/hardware'
|
||||||
|
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
|
||||||
|
import { formatMegaBytes } from '@/lib/utils'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const Route = createFileRoute(route.settings.hardware as any)({
|
export const Route = createFileRoute(route.settings.hardware as any)({
|
||||||
component: Hardware,
|
component: Hardware,
|
||||||
})
|
})
|
||||||
// Format bytes to a human-readable format
|
|
||||||
function formatMegaBytes(mb: number) {
|
|
||||||
const tb = mb / (1024 * 1024)
|
|
||||||
if (tb >= 1) {
|
|
||||||
return `${tb.toFixed(2)} TB`
|
|
||||||
} else {
|
|
||||||
const gb = mb / 1024
|
|
||||||
return `${gb.toFixed(2)} GB`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
function SortableGPUItem({ gpu, index }: { gpu: GPU; index: number }) {
|
||||||
const {
|
const {
|
||||||
@ -168,10 +163,56 @@ function Hardware() {
|
|||||||
return () => clearInterval(intervalId)
|
return () => clearInterval(intervalId)
|
||||||
}, [setHardwareData, updateCPUUsage, updateRAMAvailable])
|
}, [setHardwareData, updateCPUUsage, updateRAMAvailable])
|
||||||
|
|
||||||
|
const handleClickSystemMonitor = async () => {
|
||||||
|
try {
|
||||||
|
// Check if system monitor window already exists
|
||||||
|
const existingWindow = await WebviewWindow.getByLabel(
|
||||||
|
'system-monitor-window'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingWindow) {
|
||||||
|
// If window exists, focus it
|
||||||
|
await existingWindow.setFocus()
|
||||||
|
console.log('Focused existing system monitor window')
|
||||||
|
} else {
|
||||||
|
// Create a new system monitor window
|
||||||
|
const monitorWindow = new WebviewWindow('system-monitor-window', {
|
||||||
|
url: route.systemMonitor,
|
||||||
|
title: 'System Monitor - Jan',
|
||||||
|
width: 900,
|
||||||
|
height: 600,
|
||||||
|
resizable: true,
|
||||||
|
center: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for window creation
|
||||||
|
monitorWindow.once('tauri://created', () => {
|
||||||
|
console.log('System monitor window created')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for window errors
|
||||||
|
monitorWindow.once('tauri://error', (e) => {
|
||||||
|
console.error('Error creating system monitor window:', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to open system monitor window:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<HeaderPage>
|
<HeaderPage>
|
||||||
<h1 className="font-medium">{t('common.settings')}</h1>
|
<div className="flex items-center gap-2 justify-between w-full pr-3">
|
||||||
|
<h1 className="font-medium">{t('common.settings')}</h1>
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-1 hover:bg-main-view-fg/8 px-1.5 py-0.5 rounded relative z-10 cursor-pointer"
|
||||||
|
onClick={handleClickSystemMonitor}
|
||||||
|
>
|
||||||
|
<IconDeviceDesktopAnalytics className="text-main-view-fg/50 size-5" />
|
||||||
|
<p>System monitor</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</HeaderPage>
|
</HeaderPage>
|
||||||
<div className="flex h-full w-full">
|
<div className="flex h-full w-full">
|
||||||
<SettingsMenu />
|
<SettingsMenu />
|
||||||
|
|||||||
@ -115,7 +115,13 @@ function LocalAPIServer() {
|
|||||||
Start an OpenAI-compatible local HTTP server.
|
Start an OpenAI-compatible local HTTP server.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={toggleAPIServer}>
|
<Button
|
||||||
|
onClick={toggleAPIServer}
|
||||||
|
variant={
|
||||||
|
serverStatus === 'running' ? 'destructive' : 'default'
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
{`${serverStatus === 'running' ? 'Stop' : 'Start'}`} Server
|
{`${serverStatus === 'running' ? 'Stop' : 'Start'}`} Server
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
237
web-app/src/routes/system-monitor.tsx
Normal file
237
web-app/src/routes/system-monitor.tsx
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { useHardware } from '@/hooks/useHardware'
|
||||||
|
import { getHardwareInfo } from '@/services/hardware'
|
||||||
|
import { Progress } from '@/components/ui/progress'
|
||||||
|
import type { HardwareData } from '@/hooks/useHardware'
|
||||||
|
import { route } from '@/constants/routes'
|
||||||
|
import { formatMegaBytes } from '@/lib/utils'
|
||||||
|
import { IconDeviceDesktopAnalytics } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const Route = createFileRoute(route.systemMonitor as any)({
|
||||||
|
component: SystemMonitor,
|
||||||
|
})
|
||||||
|
|
||||||
|
function SystemMonitor() {
|
||||||
|
const { hardwareData, setHardwareData, updateCPUUsage, updateRAMAvailable } =
|
||||||
|
useHardware()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Initial data fetch
|
||||||
|
getHardwareInfo().then((data) => {
|
||||||
|
setHardwareData(data as unknown as HardwareData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set up interval for real-time updates
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
getHardwareInfo().then((data) => {
|
||||||
|
setHardwareData(data as unknown as HardwareData)
|
||||||
|
updateCPUUsage(data.cpu.usage)
|
||||||
|
updateRAMAvailable(data.ram.available)
|
||||||
|
})
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
return () => clearInterval(intervalId)
|
||||||
|
}, [setHardwareData, updateCPUUsage, updateRAMAvailable])
|
||||||
|
|
||||||
|
// Calculate RAM usage percentage
|
||||||
|
const ramUsagePercentage =
|
||||||
|
((hardwareData.ram.total - hardwareData.ram.available) /
|
||||||
|
hardwareData.ram.total) *
|
||||||
|
100
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full bg-main-view overflow-y-auto p-6">
|
||||||
|
<div className="flex items-center mb-4 gap-2">
|
||||||
|
<IconDeviceDesktopAnalytics className="text-main-view-fg/80 size-6" />
|
||||||
|
<h1 className="text-xl font-bold text-main-view-fg">System Monitor</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
{/* CPU Usage Card */}
|
||||||
|
<div className="bg-main-view-fg/2 rounded-lg p-6 shadow-sm">
|
||||||
|
<h2 className="text-base font-semibold text-main-view-fg mb-4">
|
||||||
|
CPU Usage
|
||||||
|
</h2>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Model</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{hardwareData.cpu.model}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Cores</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{hardwareData.cpu.cores}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Architecture</span>
|
||||||
|
<span className="text-main-view-fg">{hardwareData.cpu.arch}</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="text-main-view-fg/70">Current Usage</span>
|
||||||
|
<span className="text-main-view-fg font-bold">
|
||||||
|
{hardwareData.cpu.usage.toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Progress value={hardwareData.cpu.usage} className="h-3 w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* RAM Usage Card */}
|
||||||
|
<div className="bg-main-view-fg/2 rounded-lg p-6 shadow-sm">
|
||||||
|
<h2 className="text-base font-semibold text-main-view-fg mb-4">
|
||||||
|
Memory Usage
|
||||||
|
</h2>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Total RAM</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{formatMegaBytes(hardwareData.ram.total)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Available RAM</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{formatMegaBytes(hardwareData.ram.available)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Used RAM</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{formatMegaBytes(
|
||||||
|
hardwareData.ram.total - hardwareData.ram.available
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="text-main-view-fg/70">Current Usage</span>
|
||||||
|
<span className="text-main-view-fg font-bold">
|
||||||
|
{ramUsagePercentage.toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Progress value={ramUsagePercentage} className="h-3 w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Current Active Model Section */}
|
||||||
|
<div className="mt-6 bg-main-view-fg/2 rounded-lg p-6 shadow-sm">
|
||||||
|
<h2 className="text-base font-semibold text-main-view-fg mb-4">
|
||||||
|
Current Active Model
|
||||||
|
</h2>
|
||||||
|
<div className="bg-main-view-fg/3 rounded-lg p-4">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="font-semibold text-main-view-fg">GPT-4o</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2 mt-3">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Provider</span>
|
||||||
|
<span className="text-main-view-fg">OpenAI</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Context Length</span>
|
||||||
|
<span className="text-main-view-fg">128K tokens</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">Status</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
<div className="bg-green-500/20 px-1 font-bold py-0.5 rounded text-green-700 text-xs">
|
||||||
|
Running
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Active GPUs Section */}
|
||||||
|
<div className="mt-6 bg-main-view-fg/2 rounded-lg p-6 shadow-sm">
|
||||||
|
<h2 className="text-base font-semibold text-main-view-fg mb-4">
|
||||||
|
Active GPUs
|
||||||
|
</h2>
|
||||||
|
{hardwareData.gpus.length > 0 ? (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{hardwareData.gpus
|
||||||
|
.filter((gpu) => gpu.activated)
|
||||||
|
.map((gpu, index) => (
|
||||||
|
<div
|
||||||
|
key={gpu.id || index}
|
||||||
|
className="bg-main-view-fg/3 rounded-lg p-4"
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="font-semibold text-main-view-fg">
|
||||||
|
{gpu.name}
|
||||||
|
</span>
|
||||||
|
<div className="bg-green-500/20">Active</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2 mt-3">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">VRAM Usage</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{formatMegaBytes(gpu.total_vram - gpu.free_vram)} /{' '}
|
||||||
|
{formatMegaBytes(gpu.total_vram)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">
|
||||||
|
Driver Version:
|
||||||
|
</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{gpu.additional_information.driver_version}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-main-view-fg/70">
|
||||||
|
Compute Capability:
|
||||||
|
</span>
|
||||||
|
<span className="text-main-view-fg">
|
||||||
|
{gpu.additional_information.compute_cap}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<Progress
|
||||||
|
value={
|
||||||
|
((gpu.total_vram - gpu.free_vram) / gpu.total_vram) *
|
||||||
|
100
|
||||||
|
}
|
||||||
|
className={`h-2 w-full ${
|
||||||
|
((gpu.total_vram - gpu.free_vram) / gpu.total_vram) *
|
||||||
|
100 >
|
||||||
|
80
|
||||||
|
? 'bg-red-500/30'
|
||||||
|
: ((gpu.total_vram - gpu.free_vram) /
|
||||||
|
gpu.total_vram) *
|
||||||
|
100 >
|
||||||
|
50
|
||||||
|
? 'bg-yellow-500/30'
|
||||||
|
: 'bg-green-500/30'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-center text-main-view-fg/50 py-4">
|
||||||
|
No GPUs detected
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.gpus.length > 0 &&
|
||||||
|
!hardwareData.gpus.some((gpu) => gpu.activated) && (
|
||||||
|
<div className="text-center text-main-view-fg/50 py-4">
|
||||||
|
No active GPUs. All GPUs are currently disabled.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user