| import type { LoadState } from '../client/useAgent'; |
| import type { PendingSend } from '../client/useAgent'; |
| import { formatBytes } from '../lib/format'; |
|
|
| interface Props { |
| pending: PendingSend | null; |
| load: LoadState | null; |
| loading: boolean; |
| onConfirm: () => void; |
| onCancel: () => void; |
| } |
|
|
| export function ModelDialog({ pending, load, loading, onConfirm, onCancel }: Props) { |
| if (!pending) return null; |
| const progress = load?.phase === 'download' ? load : null; |
| return ( |
| <div className="overlay" onKeyDown={(e) => e.key === 'Escape' && onCancel()}> |
| <div className="dialog" role="dialog" aria-modal="true" aria-labelledby="dlg-title"> |
| <h2 id="dlg-title">Download model weights</h2> |
| <p>Everything happens in your browser. Your prompts and files stay on this device.</p> |
| <div className="facts"> |
| <strong>About 1.84 GB on the first download</strong> |
| <span>Saved in this browser (OPFS) and SHA-256 verified, so next starts are fast.</span> |
| </div> |
| <p>Your message</p> |
| <div className="preview">{pending.text}</div> |
| {loading && ( |
| <> |
| <progress max={progress?.total ?? 1} value={progress?.loaded} /> |
| <p> |
| {load?.phase === 'compile' ? 'Preparing GPU…' : load?.phase === 'warmup' ? 'Warming up…' : progress ? `${formatBytes(progress.loaded)} of ${formatBytes(progress.total)}` : 'Starting…'} |
| </p> |
| </> |
| )} |
| {pending.error && <p className="err" role="alert">{pending.error}</p>} |
| <div className="actions"> |
| <button onClick={onCancel}>Cancel</button> |
| <button className="primary" onClick={onConfirm} disabled={loading} autoFocus> |
| {pending.error ? 'Retry' : 'Load model & send'} |
| </button> |
| </div> |
| </div> |
| </div> |
| ); |
| } |
|
|