| <script lang="ts"> |
| import { |
| modelPhase, |
| deviceInfo, |
| loadProgress, |
| initMs, |
| modelId, |
| modelError, |
| loadModel, |
| checkDevice, |
| } from '../lib/stores/app-state'; |
| import { MODELS } from '../lib/model/model-adapter'; |
| import { onMount } from 'svelte'; |
| |
| onMount(() => checkDevice()); |
| </script> |
| |
| <div class="panel status"> |
| <div class="row"> |
| <div> |
| <strong>Local inference with WebGPU</strong> |
| <span class="dim">— your prompt and text never leave this browser.</span> |
| </div> |
| {#if $deviceInfo} |
| {#if $deviceInfo.webgpuSupported} |
| <span class="badge ok"> |
| WebGPU available{$deviceInfo.shaderF16 ? ' + shader-f16' : ''} |
| {#if $deviceInfo.adapterInfo?.vendor} |
| · {$deviceInfo.adapterInfo.vendor}{$deviceInfo.adapterInfo.architecture |
| ? ` / ${$deviceInfo.adapterInfo.architecture}` |
| : ''} |
| {/if} |
| </span> |
| {:else} |
| <span class="badge warn">WebGPU not available — falling back to WASM (CPU, slow)</span> |
| {/if} |
| {/if} |
| </div> |
| |
| <div class="row"> |
| <label> |
| Model |
| <select bind:value={$modelId} disabled={$modelPhase === 'loading' || $modelPhase === 'ready'}> |
| {#each MODELS as m} |
| <option value={m.id}>{m.label}</option> |
| {/each} |
| </select> |
| </label> |
| |
| {#if $modelPhase === 'idle' || $modelPhase === 'checking' || $modelPhase === 'error'} |
| <button class="primary" onclick={loadModel} disabled={$modelPhase === 'checking'}> |
| Download & load model |
| </button> |
| {:else if $modelPhase === 'loading'} |
| <span class="dim"> |
| Loading… |
| {#if $loadProgress} |
| <span class="mono"> |
| {$loadProgress.file.split('/').pop()} {Math.round($loadProgress.progress)}% |
| ({$loadProgress.loadedMB.toFixed(0)}/{$loadProgress.totalMB.toFixed(0)} MB) |
| </span> |
| {/if} |
| </span> |
| {:else if $modelPhase === 'ready'} |
| <span class="badge ok"> |
| Ready · device {$deviceInfo?.device} · dtype {$deviceInfo?.dtype} |
| {#if $initMs}· init {(($initMs ?? 0) / 1000).toFixed(1)}s{/if} |
| </span> |
| {/if} |
| </div> |
| |
| {#if $modelPhase === 'loading' && $loadProgress} |
| <progress max="100" value={$loadProgress.progress}></progress> |
| {/if} |
| {#if $modelError} |
| <p class="dim" style="color: var(--red)">Model load failed: {$modelError}</p> |
| {/if} |
| </div> |
| |
| <style> |
| .status { |
| display: flex; |
| flex-direction: column; |
| gap: 10px; |
| } |
| .row { |
| display: flex; |
| flex-wrap: wrap; |
| align-items: center; |
| gap: 12px; |
| justify-content: space-between; |
| } |
| label { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| progress { |
| width: 100%; |
| } |
| </style> |
| |