File size: 2,770 Bytes
c126239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<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 &amp; 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>