A zero-download device capability report rendered entirely from browser APIs (useCapabilities / useStorageQuota / useAdaptiveBatchSize): hardware cores/memory/GPU name, six feature flags (WebGPU/WASM/SIMD/Threads/IndexedDB/Workers), storage used/quota/available, browser + OS line, a reference 1B-Q4_K_M readiness verdict, and adaptive batch size + reasoning. Composes device-badge, device-capability-grid, browser-compat-card, storage-meter, and adaptive-batch-card. Zero network and zero model bytes on mount. Split from ui/blocks/device-model-lab (device report section).
src/app/blocks/device/device-report/device-report.tsx
'use client';
/**
* @file device-report.tsx
* @description Self-sufficient zero-download device capability report — hardware, six feature flags, storage, browser/OS, a reference-model readiness verdict, and adaptive batch sizing, all from browser APIs.
* @constraint Renders entirely from browser APIs — zero network, zero model bytes on mount.
*/
import { useSyncExternalStore } from 'react';
import { AlertTriangle, RefreshCw } from 'lucide-react';
import { useAdaptiveBatchSize, useCapabilities, useStorageQuota } from '@localmode/react';
import { formatBytes } from '@localmode/core';
import { DeviceBadge } from '@/components/device-badge';
import { DeviceCapabilityGrid } from '@/components/device-capability-grid';
import { BrowserCompatCard } from '@/components/browser-compat-card';
import { StorageMeter } from '@/components/storage-meter';
import { AdaptiveBatchCard } from '@/components/adaptive-batch-card';
const REFERENCE_MODEL_LABEL = 'Readiness check: 1B-parameter Q4_K_M GGUF LLM';
const REFERENCE_MODEL_SHORT = '1B Q4_K_M GGUF LLM';
const REFERENCE_REQUIRED_GB = 1.2;
const ASSUMED_MEMORY_GB = 4;
function sanitizeGpuLabel(raw: string): string {
let out = raw.trim();
while ((out.match(/\(/g) ?? []).length > (out.match(/\)/g) ?? []).length) {
const cut = out.replace(/\s*\([^()]*$/, '').trim();
if (cut === out) break;
out = cut;
}
return out;
}
function simplifyGpuName(raw: string): string {
const renderer = raw.match(/Renderer:\s*([^,()]+)/i);
if (renderer?.[1]) return renderer[1].trim();
if (/SwiftShader/i.test(raw)) return 'SwiftShader';
if (/llvmpipe/i.test(raw)) return 'llvmpipe';
const angle = raw.match(/^ANGLE\s*\((.*)\)\s*$/i);
if (angle?.[1]) {
const parts = angle[1]
.split(',')
.map((p) => p.trim())
.filter(Boolean);
const label = (parts[1] ?? parts[0] ?? '').replace(/\bDirect3D.*$/i, '').trim();
if (label) return sanitizeGpuLabel(label);
}
return sanitizeGpuLabel(raw);
}
const subscribeNever = () => () => {};
const getTrue = () => true;
const getFalse = () => false;
export function DeviceReportBlock() {
const { capabilities, isDetecting, error: capabilitiesError, refresh } = useCapabilities();
const { quota, error: quotaError, refresh: refreshQuota } = useStorageQuota();
const batch = useAdaptiveBatchSize({ taskType: 'embedding', modelDimensions: 384 });
const mounted = useSyncExternalStore(subscribeNever, getTrue, getFalse);
const error = capabilitiesError ?? quotaError;
const status = error ? 'error' : isDetecting || !capabilities ? 'detecting' : 'ready';
const memoryGB = capabilities?.hardware.memory ?? null;
const gpuName = capabilities?.hardware.gpu ?? null;
const memoryUnknown = capabilities != null && memoryGB == null;
const browserLine = capabilities
? [
`${capabilities.browser.name} v${capabilities.browser.version}`,
`${capabilities.device.os}${capabilities.device.osVersion ? ` ${capabilities.device.osVersion}` : ''}`,
capabilities.device.type,
].join(' · ')
: null;
const estimatedSpeed = capabilities
? capabilities.features.webgpu
? 'Fast (WebGPU available)'
: capabilities.features.simd
? 'Moderate (WASM SIMD)'
: 'Slow (plain WASM)'
: undefined;
const readinessWarnings = memoryUnknown
? [
`Device memory is unknown (navigator.deviceMemory unavailable in this browser) - assuming ${ASSUMED_MEMORY_GB} GB, so this verdict may be conservative or optimistic.`,
]
: [];
return (
<div className="flex flex-col gap-4 p-4">
{}
<span data-state={status} className="sr-only">
{status}
</span>
{}
<span aria-label="Device report status" className="sr-only">
{status}
</span>
{capabilities && (
<span aria-label="Detected capabilities" className="sr-only">
{JSON.stringify({
cores: capabilities.hardware.cores,
memoryGB,
gpu: gpuName,
webgpu: capabilities.features.webgpu,
wasm: capabilities.features.wasm,
simd: capabilities.features.simd,
threads: capabilities.features.threads,
indexeddb: capabilities.features.indexeddb,
webworkers: capabilities.features.webworkers,
})}
</span>
)}
{}
<div className="flex flex-wrap items-center justify-between gap-2">
<div className="flex flex-wrap items-center gap-2">
<DeviceBadge capability="webgpu" />
<DeviceBadge capability="wasm" />
<DeviceBadge capability="storage" />
</div>
{browserLine && (
<p className="text-xs text-muted-foreground">
{browserLine}
</p>
)}
</div>
{error && (
<div
className="flex items-center gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive"
>
<AlertTriangle className="size-4 shrink-0" aria-hidden="true" />
<span className="min-w-0 flex-1">{error.message}</span>
<button
type="button"
onClick={() => {
void refresh();
void refreshQuota();
}}
className="inline-flex shrink-0 items-center gap-1 rounded-md border border-border bg-background px-2 py-1 font-medium text-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
<RefreshCw className="size-3" aria-hidden="true" />
Retry
</button>
</div>
)}
<div className="grid items-start gap-4 md:grid-cols-2">
{}
<div className="flex flex-col gap-2">
<div>
{}
<DeviceCapabilityGrid className="max-w-none [&_p]:hidden" />
</div>
{capabilities && (
<p className="px-1 text-xs text-muted-foreground">
GPU:{' '}
<span className="font-medium text-foreground">
{gpuName ? simplifyGpuName(gpuName) : 'None'}
</span>
</p>
)}
{memoryUnknown && (
<p
className="flex items-start gap-1.5 px-1 text-xs text-amber-600 dark:text-amber-400"
>
<AlertTriangle className="mt-0.5 size-3.5 shrink-0" aria-hidden="true" />
<span>
navigator.deviceMemory is unavailable in this browser - device memory is unknown;
the readiness check assumes {ASSUMED_MEMORY_GB} GB.
</span>
</p>
)}
</div>
{}
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div>
<StorageMeter
className="max-w-none"
quota={
quota ? { usedBytes: quota.usedBytes, quotaBytes: quota.quotaBytes } : undefined
}
/>
</div>
{quota && (
<p
aria-label="Storage availability"
data-available-bytes={quota.availableBytes}
data-quota-bytes={quota.quotaBytes}
className="px-1 text-xs text-muted-foreground"
>
<span className="font-medium text-foreground">
{formatBytes(quota.availableBytes)}
</span>{' '}
available of {formatBytes(quota.quotaBytes)} quota
{quota.isPersisted ? ' · persisted' : ''}
</p>
)}
</div>
<div className="flex flex-col gap-2">
{}
<p className="px-1 text-sm font-medium text-foreground">{REFERENCE_MODEL_LABEL}</p>
{capabilities ? (
<BrowserCompatCard
className="max-w-none"
modelName={REFERENCE_MODEL_SHORT}
requiredGB={REFERENCE_REQUIRED_GB}
deviceGB={memoryGB ?? ASSUMED_MEMORY_GB}
availableStorageGB={
quota ? Math.round((quota.availableBytes / 1024 ** 3) * 10) / 10 : undefined
}
crossOriginIsolated={capabilities.features.crossOriginisolated}
estimatedSpeed={estimatedSpeed}
warnings={readinessWarnings}
/>
) : (
<div
role="status"
className="rounded-xl border border-border bg-card p-5 text-sm text-muted-foreground"
>
Checking device readiness…
</div>
)}
</div>
<div>
{mounted ? (
<>
{}
<AdaptiveBatchCard
className="max-w-none [&_.grid-cols-3]:hidden"
result={batch}
/>
<span
aria-label="Adaptive batch size"
data-batch-size={batch.batchSize}
className="sr-only"
>
{batch.batchSize}
</span>
<span aria-label="Batch sizing reasoning" className="sr-only">
{batch.reasoning}
</span>
</>
) : (
<div
role="status"
className="rounded-xl border border-border bg-card p-5 text-sm text-muted-foreground"
>
Computing optimal batch size…
</div>
)}
</div>
</div>
</div>
</div>
);
}