121 lines
3.6 KiB
Svelte
121 lines
3.6 KiB
Svelte
<script lang="ts">
|
||
import type { Snippet } from 'svelte';
|
||
|
||
import { Server } from '@lucide/svelte';
|
||
import * as Card from '$lib/components/ui/card';
|
||
import { m } from '$lib/paraglide/messages';
|
||
|
||
import type { SystemDetails } from './types';
|
||
|
||
import { Badge } from '../ui/badge';
|
||
import { ghz } from './format';
|
||
|
||
let {
|
||
cpu,
|
||
details,
|
||
os
|
||
}: {
|
||
cpu: { logical_cpus: number; max_mhz: number; min_mhz: number; model: string };
|
||
details: SystemDetails;
|
||
os: { architecture: string; kernel: string };
|
||
} = $props();
|
||
</script>
|
||
|
||
{#snippet field(
|
||
label: string,
|
||
value: string,
|
||
opts: { class: string; mono: boolean } = { class: '', mono: false }
|
||
)}
|
||
<div class="flex items-baseline justify-between gap-3 text-sm">
|
||
<span class="text-muted-foreground text-xs">{label}</span>
|
||
<span
|
||
class="truncate text-right {opts.mono ? 'font-mono text-xs' : 'font-medium'} {opts.class ??
|
||
''}">{value}</span
|
||
>
|
||
</div>
|
||
{/snippet}
|
||
|
||
{#snippet section(heading: string, body: Snippet)}
|
||
<div class="flex flex-col gap-2">
|
||
<Badge variant="outline" class="rounded-sm">{heading}</Badge>
|
||
<div class="px-1">
|
||
{@render body()}
|
||
</div>
|
||
</div>
|
||
{/snippet}
|
||
|
||
{#snippet cpuBody()}
|
||
{@render field(m.dashboard_cpu(), cpu.model)}
|
||
{@render field(
|
||
m.dashboard_logical_cores({ cores: cpu.logical_cpus }),
|
||
`${ghz(cpu.min_mhz)}–${ghz(cpu.max_mhz)}`,
|
||
{ class: '', mono: true }
|
||
)}
|
||
{/snippet}
|
||
|
||
{#snippet osBody()}
|
||
{@render field(m.dashboard_kernel(), os.kernel, { class: '', mono: true })}
|
||
{@render field(m.dashboard_architecture(), os.architecture, { class: '', mono: true })}
|
||
{/snippet}
|
||
|
||
{#snippet timeBody()}
|
||
{@render field(m.dashboard_timezone(), details.time!.timezone)}
|
||
{@render field(
|
||
m.dashboard_clock(),
|
||
details.time!.ntp_synchronized
|
||
? m.dashboard_synchronized()
|
||
: details.time!.ntp
|
||
? m.dashboard_syncing()
|
||
: m.dashboard_not_synced(),
|
||
{ class: details.time!.ntp_synchronized ? 'text-emerald-500' : 'text-amber-500', mono: false }
|
||
)}
|
||
{@render field(
|
||
m.dashboard_hardware_clock(),
|
||
details.time!.local_rtc ? m.dashboard_local_time() : m.dashboard_utc()
|
||
)}
|
||
{/snippet}
|
||
|
||
{#snippet localeBody()}
|
||
{@render field(m.dashboard_language(), details.locale!.lang, { class: '', mono: true })}
|
||
{@render field(m.dashboard_keymap(), details.locale!.vc_keymap || '—', { class: '', mono: true })}
|
||
{/snippet}
|
||
|
||
{#snippet dnsBody()}
|
||
{#each details.dns ?? [] as server (server)}
|
||
{@render field(m.dashboard_nameserver(), server, { class: '', mono: true })}
|
||
{:else}
|
||
{@render field(m.dashboard_nameserver(), m.dashboard_none())}
|
||
{/each}
|
||
{/snippet}
|
||
|
||
{#snippet updatesBody()}
|
||
{@render field(
|
||
details.updates!.manager,
|
||
details.updates!.count > 0
|
||
? m.dashboard_updates({ count: details.updates!.count })
|
||
: m.dashboard_up_to_date(),
|
||
{ class: details.updates!.count > 0 ? 'text-amber-500' : 'text-emerald-500', mono: false }
|
||
)}
|
||
{/snippet}
|
||
|
||
<Card.Root>
|
||
<Card.Header>
|
||
<div class="flex items-center gap-2">
|
||
<span
|
||
class="bg-muted text-muted-foreground flex size-7 items-center justify-center rounded-md"
|
||
>
|
||
<Server class="size-4" />
|
||
</span>
|
||
<Card.Title class="leading-1">{m.dashboard_system()}</Card.Title>
|
||
</div>
|
||
</Card.Header>
|
||
<Card.Content class="flex flex-col grow gap-4">
|
||
{@render section(m.dashboard_cpu(), cpuBody)}
|
||
{@render section(m.dashboard_os(), osBody)}
|
||
{#if details.time}{@render section(m.dashboard_time(), timeBody)}{/if}
|
||
{#if details.locale}{@render section(m.dashboard_locale(), localeBody)}{/if}
|
||
{#if details.dns}{@render section(m.dashboard_dns(), dnsBody)}{/if}
|
||
{#if details.updates}{@render section(m.dashboard_packages(), updatesBody)}{/if}
|
||
</Card.Content>
|
||
</Card.Root>
|