t0ng7u b08febaa3c refactor: system settings UI for consistent, compact layouts
Redesign the system settings interface to align with the rest of the console experience by using fixed header actions, removing redundant subtitles, respecting global content width, and standardizing responsive form layouts.

Introduce reusable settings layout primitives for forms, switch rows, grouped controls, nested control sections, title status indicators, and page action portals. Replace duplicated card-style switch markup with explicit compact components, improve nested switch readability, and reduce visual noise across authentication, billing, content, integrations, maintenance, models, and request-limit settings.

Also complete missing i18n translations, remove obsolete subtitle translation keys, refine i18n sync reporting, fix sidebar truncation for long labels, and verify the frontend with type checking and lint diagnostics.
2026-05-25 00:34:26 +08:00

919 lines
32 KiB
TypeScript
Vendored

/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useCallback, useEffect, useState } from 'react'
import * as z from 'zod'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { api } from '@/lib/api'
import dayjs from '@/lib/dayjs'
import { Alert, AlertDescription } from '@/components/ui/alert'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog'
import { Button } from '@/components/ui/button'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Progress } from '@/components/ui/progress'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Separator } from '@/components/ui/separator'
import { Switch } from '@/components/ui/switch'
import { StatusBadge } from '@/components/status-badge'
import {
SettingsForm,
SettingsSwitchContent,
SettingsSwitchItem,
} from '../components/settings-form-layout'
import { SettingsPageFormActions } from '../components/settings-page-context'
import { SettingsSection } from '../components/settings-section'
import { useResetForm } from '../hooks/use-reset-form'
import { useUpdateOption } from '../hooks/use-update-option'
const perfSchema = z.object({
'performance_setting.disk_cache_enabled': z.boolean(),
'performance_setting.disk_cache_threshold_mb': z.coerce.number().min(1),
'performance_setting.disk_cache_max_size_mb': z.coerce.number().min(100),
'performance_setting.disk_cache_path': z.string().optional(),
'performance_setting.monitor_enabled': z.boolean(),
'performance_setting.monitor_cpu_threshold': z.coerce.number().min(0),
'performance_setting.monitor_memory_threshold': z.coerce
.number()
.min(0)
.max(100),
'performance_setting.monitor_disk_threshold': z.coerce
.number()
.min(0)
.max(100),
'perf_metrics_setting.enabled': z.boolean(),
'perf_metrics_setting.flush_interval': z.coerce.number().min(1),
'perf_metrics_setting.bucket_time': z.enum(['minute', '5min', 'hour']),
'perf_metrics_setting.retention_days': z.coerce.number().min(0),
})
type PerfFormValues = z.infer<typeof perfSchema>
function formatBytes(bytes: number, decimals = 2): string {
if (!bytes || isNaN(bytes)) return '0 Bytes'
if (bytes === 0) return '0 Bytes'
if (bytes < 0) return '-' + formatBytes(-bytes, decimals)
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k))
if (i < 0 || i >= sizes.length) return bytes + ' Bytes'
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i]
}
interface Props {
defaultValues: PerfFormValues
}
type LogInfo = {
enabled: boolean
log_dir: string
file_count: number
total_size: number
oldest_time?: string
newest_time?: string
}
type PerformanceStats = {
cache_stats?: {
current_disk_usage_bytes: number
disk_cache_max_bytes: number
active_disk_files: number
disk_cache_hits: number
current_memory_usage_bytes: number
active_memory_buffers: number
memory_cache_hits: number
}
disk_space_info?: {
total: number
free: number
used: number
used_percent: number
}
memory_stats?: {
alloc: number
total_alloc: number
sys: number
num_gc: number
num_goroutine: number
}
disk_cache_info?: {
path: string
file_count: number
total_size: number
}
config?: {
is_running_in_container: boolean
}
}
export function PerformanceSection(props: Props) {
const { t } = useTranslation()
const updateOption = useUpdateOption()
const [stats, setStats] = useState<PerformanceStats | null>(null)
const [logInfo, setLogInfo] = useState<LogInfo | null>(null)
const [logCleanupMode, setLogCleanupMode] = useState('by_count')
const [logCleanupValue, setLogCleanupValue] = useState(10)
const [logCleanupLoading, setLogCleanupLoading] = useState(false)
const form = useForm<PerfFormValues>({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resolver: zodResolver(perfSchema) as any,
defaultValues: props.defaultValues,
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
useResetForm(form as any, props.defaultValues)
const fetchStats = useCallback(async () => {
try {
const res = await api.get('/api/performance/stats')
if (res.data.success) setStats(res.data.data)
} catch {
/* ignore */
}
}, [])
const fetchLogInfo = useCallback(async () => {
try {
const res = await api.get('/api/performance/logs')
if (res.data.success) setLogInfo(res.data.data)
} catch {
/* ignore */
}
}, [])
useEffect(() => {
fetchStats()
fetchLogInfo()
}, [fetchStats, fetchLogInfo])
const onSubmit = async (data: PerfFormValues) => {
const entries = Object.entries(data) as [string, unknown][]
const updates = entries.filter(
([key, value]) =>
value !== (props.defaultValues[key as keyof PerfFormValues] as unknown)
)
if (updates.length === 0) {
toast.info(t('No changes to save'))
return
}
for (const [key, value] of updates) {
await updateOption.mutateAsync({
key,
value: value as string | number | boolean,
})
}
toast.success(t('Saved successfully'))
fetchStats()
}
const clearDiskCache = async () => {
try {
const res = await api.delete('/api/performance/disk_cache')
if (res.data.success) {
toast.success(t('Disk cache cleared'))
fetchStats()
}
} catch {
toast.error(t('Cleanup failed'))
}
}
const resetStats = async () => {
try {
const res = await api.post('/api/performance/reset_stats')
if (res.data.success) {
toast.success(t('Statistics reset'))
fetchStats()
}
} catch {
toast.error(t('Reset failed'))
}
}
const forceGC = async () => {
try {
const res = await api.post('/api/performance/gc')
if (res.data.success) {
toast.success(t('GC executed'))
fetchStats()
}
} catch {
toast.error(t('GC execution failed'))
}
}
const cleanupLogFiles = async () => {
if (!logCleanupValue || isNaN(logCleanupValue) || logCleanupValue < 1) {
toast.error(t('Please enter a valid number'))
return
}
setLogCleanupLoading(true)
try {
const res = await api.delete(
`/api/performance/logs?mode=${logCleanupMode}&value=${logCleanupValue}`
)
if (res.data.success) {
const { deleted_count, freed_bytes } = res.data.data
toast.success(
t('Cleaned up {{count}} log files, freed {{size}}', {
count: deleted_count,
size: formatBytes(freed_bytes),
})
)
} else {
toast.error(res.data.message || t('Cleanup failed'))
}
fetchLogInfo()
} catch {
toast.error(t('Cleanup failed'))
} finally {
setLogCleanupLoading(false)
}
}
const diskEnabled = form.watch('performance_setting.disk_cache_enabled')
const monitorEnabled = form.watch('performance_setting.monitor_enabled')
const perfMetricsEnabled = form.watch('perf_metrics_setting.enabled')
const maxCacheSizeMb = form.watch(
'performance_setting.disk_cache_max_size_mb'
)
const lowDiskSpace =
diskEnabled &&
stats?.disk_space_info &&
stats.disk_space_info.free > 0 &&
maxCacheSizeMb > 0 &&
stats.disk_space_info.free < maxCacheSizeMb * 1024 * 1024
const diskCachePercent =
stats?.cache_stats?.disk_cache_max_bytes &&
stats.cache_stats.disk_cache_max_bytes > 0
? Math.round(
(stats.cache_stats.current_disk_usage_bytes /
stats.cache_stats.disk_cache_max_bytes) *
100
)
: 0
return (
<SettingsSection title={t('Performance Settings')}>
<Form {...form}>
<SettingsForm onSubmit={form.handleSubmit(onSubmit)}>
<SettingsPageFormActions
onSave={form.handleSubmit(onSubmit)}
isSaving={updateOption.isPending}
/>
{/* Disk Cache Settings */}
<div>
<h4 className='font-medium'>{t('Disk Cache Settings')}</h4>
<p className='text-muted-foreground mt-1 text-xs'>
{t(
'When enabled, large request bodies are temporarily stored on disk instead of memory, significantly reducing memory usage. SSD recommended.'
)}
</p>
</div>
<div className='grid grid-cols-1 gap-4 md:grid-cols-3'>
<FormField
control={form.control}
name='performance_setting.disk_cache_enabled'
render={({ field }) => (
<SettingsSwitchItem>
<SettingsSwitchContent>
<FormLabel>{t('Enable Disk Cache')}</FormLabel>
</SettingsSwitchContent>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</SettingsSwitchItem>
)}
/>
<FormField
control={form.control}
name='performance_setting.disk_cache_threshold_mb'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Disk Cache Threshold (MB)')}</FormLabel>
<FormControl>
<Input type='number' {...field} disabled={!diskEnabled} />
</FormControl>
<FormDescription>
{t('Use disk cache when request body exceeds this size')}
</FormDescription>
</FormItem>
)}
/>
<FormField
control={form.control}
name='performance_setting.disk_cache_max_size_mb'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Max Disk Cache Size (MB)')}</FormLabel>
<FormControl>
<Input type='number' {...field} disabled={!diskEnabled} />
</FormControl>
{stats?.disk_space_info &&
stats.disk_space_info.total > 0 && (
<FormDescription>
{t('Free: {{free}} / Total: {{total}}', {
free: formatBytes(stats.disk_space_info.free),
total: formatBytes(stats.disk_space_info.total),
})}
</FormDescription>
)}
</FormItem>
)}
/>
</div>
{lowDiskSpace && (
<Alert variant='destructive'>
<AlertDescription>
{`${t('Warning')}: ${t('Available disk space')} (${formatBytes(stats?.disk_space_info?.free ?? 0)}) ${t('is less than the configured maximum cache size')} (${maxCacheSizeMb} MB). ${t('This may cause cache failures.')}`}
</AlertDescription>
</Alert>
)}
{!stats?.config?.is_running_in_container && (
<FormField
control={form.control}
name='performance_setting.disk_cache_path'
render={({ field }) => (
<FormItem className='max-w-md'>
<FormLabel>{t('Cache Directory')}</FormLabel>
<FormControl>
<Input
placeholder={t(
'Leave empty to use system temp directory'
)}
{...field}
value={field.value ?? ''}
disabled={!diskEnabled}
/>
</FormControl>
</FormItem>
)}
/>
)}
<Separator />
{/* System Performance Monitor */}
<div>
<h4 className='font-medium'>
{t('System Performance Monitoring')}
</h4>
<p className='text-muted-foreground mt-1 text-xs'>
{t(
'When performance monitoring is enabled and system resource usage exceeds the set threshold, new Relay requests will be rejected.'
)}
</p>
</div>
<div className='grid grid-cols-1 gap-4 md:grid-cols-4'>
<FormField
control={form.control}
name='performance_setting.monitor_enabled'
render={({ field }) => (
<SettingsSwitchItem>
<SettingsSwitchContent>
<FormLabel>{t('Enable Performance Monitoring')}</FormLabel>
</SettingsSwitchContent>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</SettingsSwitchItem>
)}
/>
<FormField
control={form.control}
name='performance_setting.monitor_cpu_threshold'
render={({ field }) => (
<FormItem>
<FormLabel>{t('CPU Threshold (%)')}</FormLabel>
<FormControl>
<Input
type='number'
{...field}
disabled={!monitorEnabled}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name='performance_setting.monitor_memory_threshold'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Memory Threshold (%)')}</FormLabel>
<FormControl>
<Input
type='number'
{...field}
disabled={!monitorEnabled}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name='performance_setting.monitor_disk_threshold'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Disk Threshold (%)')}</FormLabel>
<FormControl>
<Input
type='number'
{...field}
disabled={!monitorEnabled}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<Separator />
<div>
<h4 className='font-medium'>{t('Model performance metrics')}</h4>
<p className='text-muted-foreground mt-1 text-xs'>
{t(
'Collect relay latency and success-rate metrics for the model square.'
)}
</p>
</div>
<div className='grid grid-cols-1 gap-4 md:grid-cols-4'>
<FormField
control={form.control}
name='perf_metrics_setting.enabled'
render={({ field }) => (
<SettingsSwitchItem>
<SettingsSwitchContent>
<FormLabel>
{t('Enable model performance metrics')}
</FormLabel>
</SettingsSwitchContent>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</SettingsSwitchItem>
)}
/>
<FormField
control={form.control}
name='perf_metrics_setting.flush_interval'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Flush interval (minutes)')}</FormLabel>
<FormControl>
<Input
type='number'
min={1}
{...field}
disabled={!perfMetricsEnabled}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name='perf_metrics_setting.bucket_time'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Aggregation bucket')}</FormLabel>
<Select
items={[
{ value: 'minute', label: t('1 minute') },
{ value: '5min', label: t('5 minutes') },
{ value: 'hour', label: t('1 hour') },
]}
value={field.value}
onValueChange={field.onChange}
disabled={!perfMetricsEnabled}
>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
<SelectItem value='minute'>{t('1 minute')}</SelectItem>
<SelectItem value='5min'>{t('5 minutes')}</SelectItem>
<SelectItem value='hour'>{t('1 hour')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</FormItem>
)}
/>
<FormField
control={form.control}
name='perf_metrics_setting.retention_days'
render={({ field }) => (
<FormItem>
<FormLabel>{t('Retention days')}</FormLabel>
<FormControl>
<Input
type='number'
min={0}
{...field}
disabled={!perfMetricsEnabled}
/>
</FormControl>
<FormDescription>
{t('0 means data is kept permanently')}
</FormDescription>
</FormItem>
)}
/>
</div>
</SettingsForm>
</Form>
<Separator />
{/* Server Log Management */}
<div className='space-y-4'>
<div>
<h4 className='font-medium'>{t('Server Log Management')}</h4>
<p className='text-muted-foreground mt-1 text-xs'>
{t(
'Manage server log files. Log files accumulate over time; regular cleanup is recommended to free disk space.'
)}
</p>
</div>
{logInfo === null ? null : logInfo.enabled ? (
<div className='space-y-4'>
<div className='rounded-lg border p-4'>
<div className='grid grid-cols-2 gap-2 text-sm md:grid-cols-4'>
<div>
<span className='text-muted-foreground'>
{t('Log Directory')}:
</span>{' '}
<span className='font-mono text-xs'>{logInfo.log_dir}</span>
</div>
<div>
<span className='text-muted-foreground'>
{t('Log File Count')}:
</span>{' '}
{logInfo.file_count}
</div>
<div>
<span className='text-muted-foreground'>
{t('Total Log Size')}:
</span>{' '}
{formatBytes(logInfo.total_size)}
</div>
{logInfo.oldest_time && logInfo.newest_time && (
<div>
<span className='text-muted-foreground'>
{t('Date Range')}:
</span>{' '}
{dayjs(logInfo.oldest_time).format('YYYY-MM-DD')} ~{' '}
{dayjs(logInfo.newest_time).format('YYYY-MM-DD')}
</div>
)}
</div>
</div>
<div className='flex flex-wrap items-end gap-3'>
<div className='grid gap-1.5'>
<Label className='text-xs'>{t('Cleanup Mode')}</Label>
<Select
items={[
{ value: 'by_count', label: t('Retain last N files') },
{ value: 'by_days', label: t('Retain last N days') },
]}
value={logCleanupMode}
onValueChange={(v) => v !== null && setLogCleanupMode(v)}
>
<SelectTrigger className='w-[160px]'>
<SelectValue />
</SelectTrigger>
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
<SelectItem value='by_count'>
{t('Retain last N files')}
</SelectItem>
<SelectItem value='by_days'>
{t('Retain last N days')}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className='grid gap-1.5'>
<Label className='text-xs'>
{logCleanupMode === 'by_count'
? t('Files to Retain')
: t('Days to Retain')}
</Label>
<Input
type='number'
min={1}
max={logCleanupMode === 'by_count' ? 1000 : 3650}
value={logCleanupValue}
onChange={(e) => setLogCleanupValue(Number(e.target.value))}
className='w-[120px]'
/>
</div>
<AlertDialog>
<AlertDialogTrigger
render={
<Button
variant='destructive'
size='sm'
disabled={logCleanupLoading}
/>
}
>
{logCleanupLoading
? t('Cleaning...')
: t('Clean Up Log Files')}
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t('Confirm log file cleanup?')}
</AlertDialogTitle>
<AlertDialogDescription>
{logCleanupMode === 'by_count'
? t(
'Only the last {{value}} log files will be retained; the rest will be deleted.',
{
value: logCleanupValue,
}
)
: t(
'Log files older than {{value}} days will be deleted.',
{
value: logCleanupValue,
}
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t('Cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={cleanupLogFiles}>
{t('Confirm Cleanup')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
) : (
<Alert>
<AlertDescription>
{t(
'Server logging is not enabled (log directory not configured)'
)}
</AlertDescription>
</Alert>
)}
</div>
<Separator />
{/* Performance Stats Dashboard */}
<div className='space-y-4'>
<div className='flex items-center gap-2'>
<h4 className='font-medium'>{t('Performance Monitor')}</h4>
<Button variant='outline' size='sm' onClick={fetchStats}>
{t('Refresh Stats')}
</Button>
<AlertDialog>
<AlertDialogTrigger render={<Button variant='outline' size='sm' />}>
{t('Clean up inactive cache')}
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t('Confirm cleanup of inactive disk cache?')}
</AlertDialogTitle>
<AlertDialogDescription>
{t(
'This will delete temporary cache files that have not been used for more than 10 minutes'
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t('Cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={clearDiskCache}>
{t('Confirm')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<Button variant='outline' size='sm' onClick={resetStats}>
{t('Reset Stats')}
</Button>
<Button variant='outline' size='sm' onClick={forceGC}>
{t('Run GC')}
</Button>
</div>
{stats && (
<>
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
<div className='space-y-2 rounded-lg border p-4'>
<p className='text-sm font-medium'>
{t('Request Body Disk Cache')}
</p>
<Progress value={diskCachePercent} />
<div className='text-muted-foreground flex justify-between text-xs'>
<span>
{formatBytes(
stats.cache_stats?.current_disk_usage_bytes ?? 0
)}{' '}
/{' '}
{formatBytes(stats.cache_stats?.disk_cache_max_bytes ?? 0)}
</span>
<span>
{t('Active Files')}:{' '}
{stats.cache_stats?.active_disk_files ?? 0}
</span>
</div>
<StatusBadge variant='neutral' copyable={false}>
{t('Disk Hits')}: {stats.cache_stats?.disk_cache_hits ?? 0}
</StatusBadge>
</div>
<div className='space-y-2 rounded-lg border p-4'>
<p className='text-sm font-medium'>
{t('Request Body Memory Cache')}
</p>
<div className='text-muted-foreground flex justify-between text-xs'>
<span>
{t('Current Cache Size')}:{' '}
{formatBytes(
stats.cache_stats?.current_memory_usage_bytes ?? 0
)}
</span>
<span>
{t('Active Cache Count')}:{' '}
{stats.cache_stats?.active_memory_buffers ?? 0}
</span>
</div>
<StatusBadge variant='neutral' copyable={false}>
{t('Memory Hits')}:{' '}
{stats.cache_stats?.memory_cache_hits ?? 0}
</StatusBadge>
</div>
</div>
{stats.disk_space_info && stats.disk_space_info.total > 0 && (
<div className='rounded-lg border p-4'>
<p className='mb-2 text-sm font-medium'>
{t('Cache Directory Disk Space')}
</p>
<Progress
value={Math.round(stats.disk_space_info.used_percent)}
/>
<div className='text-muted-foreground mt-2 flex justify-between text-xs'>
<span>
{t('Used')}: {formatBytes(stats.disk_space_info.used)}
</span>
<span>
{t('Available')}: {formatBytes(stats.disk_space_info.free)}
</span>
<span>
{t('Total')}: {formatBytes(stats.disk_space_info.total)}
</span>
</div>
</div>
)}
{stats.memory_stats && (
<div className='rounded-lg border p-4'>
<p className='mb-2 text-sm font-medium'>
{t('System Memory Stats')}
</p>
<div className='grid grid-cols-2 gap-2 text-xs md:grid-cols-5'>
<div>
<span className='text-muted-foreground'>
{t('Allocated Memory')}:
</span>{' '}
{formatBytes(stats.memory_stats.alloc)}
</div>
<div>
<span className='text-muted-foreground'>
{t('Total Allocated')}:
</span>{' '}
{formatBytes(stats.memory_stats.total_alloc)}
</div>
<div>
<span className='text-muted-foreground'>
{t('System Memory')}:
</span>{' '}
{formatBytes(stats.memory_stats.sys)}
</div>
<div>
<span className='text-muted-foreground'>
{t('GC Count')}:
</span>{' '}
{stats.memory_stats.num_gc}
</div>
<div>
<span className='text-muted-foreground'>Goroutines:</span>{' '}
{stats.memory_stats.num_goroutine}
</div>
</div>
</div>
)}
{stats.disk_cache_info && (
<div className='rounded-lg border p-4'>
<p className='mb-2 text-sm font-medium'>
{t('Cache Directory Info')}
</p>
<div className='grid grid-cols-3 gap-2 text-xs'>
<div>
<span className='text-muted-foreground'>
{t('Cache Directory')}:
</span>{' '}
<span className='font-mono'>
{stats.disk_cache_info.path}
</span>
</div>
<div>
<span className='text-muted-foreground'>
{t('Directory File Count')}:
</span>{' '}
{stats.disk_cache_info.file_count}
</div>
<div>
<span className='text-muted-foreground'>
{t('Directory Total Size')}:
</span>{' '}
{formatBytes(stats.disk_cache_info.total_size)}
</div>
</div>
</div>
)}
</>
)}
</div>
</SettingsSection>
)
}