import { useState } from 'react' import { Head } from '@inertiajs/react' import SettingsLayout from '~/layouts/SettingsLayout' import { SystemInformationResponse } from '../../../types/system' import { formatBytes } from '~/lib/util' import CircularGauge from '~/components/systeminfo/CircularGauge' import HorizontalBarChart from '~/components/HorizontalBarChart' import InfoCard from '~/components/systeminfo/InfoCard' import Alert from '~/components/Alert' import StyledModal from '~/components/StyledModal' import { useSystemInfo } from '~/hooks/useSystemInfo' import { useNotifications } from '~/context/NotificationContext' import { useModals } from '~/context/ModalContext' import api from '~/lib/api' import StatusCard from '~/components/systeminfo/StatusCard' import { IconCpu, IconDatabase, IconServer, IconDeviceDesktop, IconComponents } from '@tabler/icons-react' export default function SettingsPage(props: { system: { info: SystemInformationResponse | undefined } }) { const { data: info } = useSystemInfo({ initialData: props.system.info, }) const { addNotification } = useNotifications() const { openModal, closeAllModals } = useModals() const [gpuBannerDismissed, setGpuBannerDismissed] = useState(() => { try { return localStorage.getItem('nomad:gpu-banner-dismissed') === 'true' } catch { return false } }) const [reinstalling, setReinstalling] = useState(false) const handleDismissGpuBanner = () => { setGpuBannerDismissed(true) try { localStorage.setItem('nomad:gpu-banner-dismissed', 'true') } catch {} } const handleForceReinstallOllama = () => { openModal( { closeAllModals() setReinstalling(true) try { const response = await api.forceReinstallService('nomad_ollama') if (!response || !response.success) { throw new Error(response?.message || 'Force reinstall failed') } addNotification({ message: 'AI Assistant is being reinstalled with GPU support. This page will reload shortly.', type: 'success', }) try { localStorage.removeItem('nomad:gpu-banner-dismissed') } catch {} setTimeout(() => window.location.reload(), 5000) } catch (error) { addNotification({ message: `Failed to reinstall: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'error', }) setReinstalling(false) } }} onCancel={closeAllModals} open={true} confirmText="Reinstall" cancelText="Cancel" >

This will recreate the AI Assistant container with GPU support enabled. Your downloaded models will be preserved. The service will be briefly unavailable during reinstall.

, 'gpu-health-force-reinstall-modal' ) } // Use (total - available) to reflect actual memory pressure. // mem.used includes reclaimable buff/cache on Linux, which inflates the number. const memoryUsed = info?.mem.total && info?.mem.available != null ? info.mem.total - info.mem.available : info?.mem.used || 0 const memoryUsagePercent = info?.mem.total ? ((memoryUsed / info.mem.total) * 100).toFixed(1) : 0 const swapUsagePercent = info?.mem.swaptotal ? ((info.mem.swapused / info.mem.swaptotal) * 100).toFixed(1) : 0 const uptimeSeconds = info?.uptime.uptime || 0 const uptimeDays = Math.floor(uptimeSeconds / 86400) const uptimeHours = Math.floor((uptimeSeconds % 86400) / 3600) const uptimeMinutes = Math.floor((uptimeSeconds % 3600) / 60) const uptimeDisplay = uptimeDays > 0 ? `${uptimeDays}d ${uptimeHours}h ${uptimeMinutes}m` : uptimeHours > 0 ? `${uptimeHours}h ${uptimeMinutes}m` : `${uptimeMinutes}m` // Build storage display items - fall back to fsSize when disk array is empty // (Same approach as Easy Setup wizard fix from PR #90) const validDisks = info?.disk?.filter((d) => d.totalSize > 0) || [] let storageItems: { label: string value: number total: string used: string subtext: string }[] = [] if (validDisks.length > 0) { storageItems = validDisks.map((disk) => ({ label: disk.name || 'Unknown', value: disk.percentUsed || 0, total: disk.totalSize ? formatBytes(disk.totalSize) : 'N/A', used: disk.totalUsed ? formatBytes(disk.totalUsed) : 'N/A', subtext: `${formatBytes(disk.totalUsed || 0)} / ${formatBytes(disk.totalSize || 0)}`, })) } else if (info?.fsSize && info.fsSize.length > 0) { // Deduplicate by size (same physical disk mounted in multiple places shows identical sizes) const seen = new Set() const uniqueFs = info.fsSize.filter((fs) => { if (fs.size <= 0 || seen.has(fs.size)) return false seen.add(fs.size) return true }) // Prefer real block devices (/dev/), exclude virtual filesystems (efivarfs, tmpfs, etc.) const realDevices = uniqueFs.filter((fs) => fs.fs.startsWith('/dev/')) const displayFs = realDevices.length > 0 ? realDevices : uniqueFs storageItems = displayFs.map((fs) => ({ label: fs.fs || 'Unknown', value: fs.use || 0, total: formatBytes(fs.size), used: formatBytes(fs.used), subtext: `${formatBytes(fs.used)} / ${formatBytes(fs.size)}`, })) } return (

System Information

Real-time monitoring and diagnostics • Last updated: {new Date().toLocaleString()} • Refreshing data every 30 seconds

{Number(memoryUsagePercent) > 90 && (
)}

Resource Usage

} />
} />
} />

System Details

} variant="elevated" data={[ { label: 'Distribution', value: info?.os.distro }, { label: 'Kernel Version', value: info?.os.kernel }, { label: 'Architecture', value: info?.os.arch }, { label: 'Hostname', value: info?.os.hostname }, { label: 'Platform', value: info?.os.platform }, ]} /> } variant="elevated" data={[ { label: 'Manufacturer', value: info?.cpu.manufacturer }, { label: 'Brand', value: info?.cpu.brand }, { label: 'Cores', value: info?.cpu.cores }, { label: 'Physical Cores', value: info?.cpu.physicalCores }, { label: 'Virtualization', value: info?.cpu.virtualization ? 'Enabled' : 'Disabled', }, ]} /> {info?.gpuHealth?.status === 'passthrough_failed' && !gpuBannerDismissed && (
)} {info?.graphics?.controllers && info.graphics.controllers.length > 0 && ( } variant="elevated" data={info.graphics.controllers.map((gpu, i) => { const prefix = info.graphics.controllers.length > 1 ? `GPU ${i + 1} ` : '' return [ { label: `${prefix}Model`, value: gpu.model }, { label: `${prefix}Vendor`, value: gpu.vendor }, { label: `${prefix}VRAM`, value: gpu.vram ? `${gpu.vram} MB` : 'N/A' }, ] }).flat()} /> )}

Memory Allocation

{formatBytes(info?.mem.total || 0)}
Total RAM
{formatBytes(memoryUsed)}
Used RAM
{formatBytes(info?.mem.available || 0)}
Available RAM
{memoryUsagePercent}% Utilized

Storage Devices

{storageItems.length > 0 ? ( ) : (
No storage devices detected
)}

System Status

) }