ui: migrate Rerank and normalize remaining views to shadcn tokens

- RerankInterface uses Button/Input/Textarea/ToggleGroup
- normalize legacy color utilities and lucide imports across the
  remaining playground interfaces, Performance and CaptureDialog

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmuGqwNBJNEAMaWsdCDqUC
This commit is contained in:
Claude
2026-06-27 12:01:19 +00:00
parent 2b087dffb1
commit fc24722258
8 changed files with 139 additions and 155 deletions
+13 -13
View File
@@ -193,17 +193,17 @@
<dialog <dialog
bind:this={dialogEl} bind:this={dialogEl}
onclose={handleDialogClose} onclose={handleDialogClose}
class="bg-surface text-txtmain rounded-lg shadow-xl max-w-[80%] w-full max-h-[90vh] p-0 backdrop:bg-black/50 m-auto" class="bg-background text-foreground rounded-lg shadow-xl max-w-[80%] w-full max-h-[90vh] p-0 backdrop:bg-black/50 m-auto"
> >
{#if capture} {#if capture}
<div class="flex flex-col max-h-[90vh]"> <div class="flex flex-col max-h-[90vh]">
<div <div
class="flex justify-between items-center p-4 border-b border-card-border" class="flex justify-between items-center p-4 border-b border-card-border"
> >
<h2 class="text-xl font-bold pb-0">Capture #{capture.id + 1}{#if capture.req_path} <span class="text-base font-mono font-normal text-txtsecondary">{capture.req_path}</span>{/if}</h2> <h2 class="text-xl font-bold pb-0">Capture #{capture.id + 1}{#if capture.req_path} <span class="text-base font-mono font-normal text-muted-foreground">{capture.req_path}</span>{/if}</h2>
<button <button
onclick={() => dialogEl?.close()} onclick={() => dialogEl?.close()}
class="text-txtsecondary hover:text-txtmain text-2xl leading-none" class="text-muted-foreground hover:text-foreground text-2xl leading-none"
> >
&times; &times;
</button> </button>
@@ -213,7 +213,7 @@
<!-- Request Headers --> <!-- Request Headers -->
<details class="group" open> <details class="group" open>
<summary <summary
class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-txtsecondary hover:text-txtmain" class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-muted-foreground hover:text-foreground"
> >
Request Headers Request Headers
</summary> </summary>
@@ -238,7 +238,7 @@
<!-- Request Body --> <!-- Request Body -->
<details class="group" open> <details class="group" open>
<summary <summary
class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-txtsecondary hover:text-txtmain" class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-muted-foreground hover:text-foreground"
> >
Request Body Request Body
</summary> </summary>
@@ -290,7 +290,7 @@
<!-- Response Headers --> <!-- Response Headers -->
<details class="group" open> <details class="group" open>
<summary <summary
class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-txtsecondary hover:text-txtmain" class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-muted-foreground hover:text-foreground"
> >
Response Headers Response Headers
</summary> </summary>
@@ -315,7 +315,7 @@
<!-- Response Body --> <!-- Response Body -->
<details class="group" open> <details class="group" open>
<summary <summary
class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-txtsecondary hover:text-txtmain" class="cursor-pointer font-semibold text-sm uppercase tracking-wider text-muted-foreground hover:text-foreground"
> >
Response Body Response Body
</summary> </summary>
@@ -375,19 +375,19 @@
{#if sseChat.reasoning} {#if sseChat.reasoning}
<div> <div>
<div <div
class="text-xs font-semibold uppercase tracking-wider text-txtsecondary mb-1" class="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1"
> >
Reasoning Reasoning
</div> </div>
<pre <pre
class="font-mono whitespace-pre-wrap break-all text-txtsecondary">{sseChat.reasoning}</pre> class="font-mono whitespace-pre-wrap break-all text-muted-foreground">{sseChat.reasoning}</pre>
</div> </div>
{/if} {/if}
{#if sseChat.content} {#if sseChat.content}
<div> <div>
{#if sseChat.reasoning} {#if sseChat.reasoning}
<div <div
class="text-xs font-semibold uppercase tracking-wider text-txtsecondary mb-1" class="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1"
> >
Response Response
</div> </div>
@@ -409,7 +409,7 @@
<div <div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-96" class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-96"
> >
<div class="p-3 text-sm text-txtsecondary italic"> <div class="p-3 text-sm text-muted-foreground italic">
(binary data - {responseContentType || "unknown content type"}) (binary data - {responseContentType || "unknown content type"})
</div> </div>
</div> </div>
@@ -429,8 +429,8 @@
</div> </div>
{:else} {:else}
<div class="flex flex-col items-center justify-center p-12"> <div class="flex flex-col items-center justify-center p-12">
<p class="text-lg text-txtsecondary">Capture not found</p> <p class="text-lg text-muted-foreground">Capture not found</p>
<p class="text-sm text-txtsecondary mt-1">The capture may have expired or was never recorded.</p> <p class="text-sm text-muted-foreground mt-1">The capture may have expired or was never recorded.</p>
<div class="mt-4"> <div class="mt-4">
<button onclick={() => dialogEl?.close()} class="btn">Close</button> <button onclick={() => dialogEl?.close()} class="btn">Close</button>
</div> </div>
@@ -150,14 +150,14 @@
<!-- Empty state for no models configured --> <!-- Empty state for no models configured -->
{#if !hasModels} {#if !hasModels}
<div class="flex-1 flex items-center justify-center text-txtsecondary"> <div class="flex-1 flex items-center justify-center text-muted-foreground">
<p>No models configured. Add models to your configuration to transcribe audio.</p> <p>No models configured. Add models to your configuration to transcribe audio.</p>
</div> </div>
{:else} {:else}
<!-- File upload / Result display area --> <!-- File upload / Result display area -->
<div class="flex-1 overflow-auto mb-4 flex items-center justify-center bg-surface border border-gray-200 dark:border-white/10 rounded"> <div class="flex-1 overflow-auto mb-4 flex items-center justify-center bg-background border border-border rounded">
{#if isTranscribing} {#if isTranscribing}
<div class="text-center text-txtsecondary"> <div class="text-center text-muted-foreground">
<div class="inline-block w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin mb-2"></div> <div class="inline-block w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin mb-2"></div>
<p>Transcribing audio...</p> <p>Transcribing audio...</p>
</div> </div>
@@ -186,12 +186,12 @@
{/if} {/if}
</button> </button>
</div> </div>
<div class="flex-1 overflow-auto p-3 rounded border border-gray-200 dark:border-white/10 bg-background whitespace-pre-wrap"> <div class="flex-1 overflow-auto p-3 rounded border border-border bg-background whitespace-pre-wrap">
{transcriptionResult} {transcriptionResult}
</div> </div>
</div> </div>
{:else if selectedFile} {:else if selectedFile}
<div class="text-center text-txtsecondary p-4"> <div class="text-center text-muted-foreground p-4">
<p class="font-medium mb-2">File Selected</p> <p class="font-medium mb-2">File Selected</p>
<p class="text-sm">{selectedFile.name}</p> <p class="text-sm">{selectedFile.name}</p>
<p class="text-xs mt-1">{formatFileSize(selectedFile.size)}</p> <p class="text-xs mt-1">{formatFileSize(selectedFile.size)}</p>
@@ -200,7 +200,7 @@
<div <div
role="region" role="region"
aria-label="Audio file drop zone" aria-label="Audio file drop zone"
class="w-full h-full flex items-center justify-center text-center text-txtsecondary p-8 {isDragging ? 'bg-primary/10' : ''}" class="w-full h-full flex items-center justify-center text-center text-muted-foreground p-8 {isDragging ? 'bg-primary/10' : ''}"
ondragover={handleDragOver} ondragover={handleDragOver}
ondragleave={handleDragLeave} ondragleave={handleDragLeave}
ondrop={handleDrop} ondrop={handleDrop}
@@ -237,7 +237,7 @@
</button> </button>
{:else} {:else}
<button <button
class="btn bg-primary text-btn-primary-text hover:opacity-90" class="btn bg-primary text-primary-foreground hover:opacity-90"
onclick={transcribe} onclick={transcribe}
disabled={!canTranscribe} disabled={!canTranscribe}
> >
@@ -371,7 +371,7 @@
</button> </button>
{:else} {:else}
<button <button
class="btn bg-primary text-btn-primary-text hover:opacity-90" class="btn bg-primary text-primary-foreground hover:opacity-90"
onclick={run} onclick={run}
disabled={!canRun} disabled={!canRun}
title={$testListStore.length === 0 ? "Add models from the list below" : "Run concurrent requests"} title={$testListStore.length === 0 ? "Add models from the list below" : "Run concurrent requests"}
@@ -386,18 +386,18 @@
<!-- Available models --> <!-- Available models -->
<div class="flex flex-col min-h-0 flex-1"> <div class="flex flex-col min-h-0 flex-1">
<div class="text-xs font-medium text-txtsecondary mb-1"> <div class="text-xs font-medium text-muted-foreground mb-1">
Models <span class="text-[10px] font-normal">— click to queue (add the same model more than once to test parallel requests)</span> Models <span class="text-[10px] font-normal">— click to queue (add the same model more than once to test parallel requests)</span>
</div> </div>
<div class="flex-1 border border-gray-200 dark:border-white/10 rounded overflow-y-auto min-h-0"> <div class="flex-1 border border-border rounded overflow-y-auto min-h-0">
{#if !hasModels} {#if !hasModels}
<div class="p-3 text-sm text-txtsecondary text-center">No models configured.</div> <div class="p-3 text-sm text-muted-foreground text-center">No models configured.</div>
{:else} {:else}
<ul class="divide-y divide-gray-100 dark:divide-white/5"> <ul class="divide-y divide-gray-100 dark:divide-white/5">
{#each availableModels as m (m.id)} {#each availableModels as m (m.id)}
<li> <li>
<button <button
class="w-full text-left px-2 py-1.5 text-sm hover:bg-secondary-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2" class="w-full text-left px-2 py-1.5 text-sm hover:bg-accent transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
onclick={() => addModel(m.id)} onclick={() => addModel(m.id)}
disabled={isRunning} disabled={isRunning}
title="Add {m.id}" title="Add {m.id}"
@@ -413,11 +413,11 @@
</div> </div>
<!-- Settings --> <!-- Settings -->
<div class="flex flex-col gap-2 border-t border-gray-200 dark:border-white/10 pt-3"> <div class="flex flex-col gap-2 border-t border-border pt-3">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<label for="concurrency-prompt" class="text-xs font-medium text-txtsecondary">Prompt</label> <label for="concurrency-prompt" class="text-xs font-medium text-muted-foreground">Prompt</label>
<button <button
class="text-[10px] text-txtsecondary hover:text-txtmain underline" class="text-[10px] text-muted-foreground hover:text-foreground underline"
onclick={resetDefaults} onclick={resetDefaults}
disabled={isRunning} disabled={isRunning}
> >
@@ -426,17 +426,17 @@
</div> </div>
<textarea <textarea
id="concurrency-prompt" id="concurrency-prompt"
class="w-full px-2 py-1.5 text-sm rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary resize-none" class="w-full px-2 py-1.5 text-sm rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary resize-none"
rows="3" rows="3"
bind:value={$promptStore} bind:value={$promptStore}
disabled={isRunning} disabled={isRunning}
></textarea> ></textarea>
<label for="concurrency-max-tokens" class="text-xs font-medium text-txtsecondary">max_tokens</label> <label for="concurrency-max-tokens" class="text-xs font-medium text-muted-foreground">max_tokens</label>
<input <input
id="concurrency-max-tokens" id="concurrency-max-tokens"
type="number" type="number"
min="1" min="1"
class="w-full px-2 py-1.5 text-sm rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="w-full px-2 py-1.5 text-sm rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$maxTokensStore} bind:value={$maxTokensStore}
disabled={isRunning} disabled={isRunning}
/> />
@@ -447,8 +447,8 @@
<div class="flex-1 min-w-0 min-h-0 overflow-y-auto"> <div class="flex-1 min-w-0 min-h-0 overflow-y-auto">
{#if $testListStore.length === 0} {#if $testListStore.length === 0}
<div class="h-full flex items-center justify-center px-6"> <div class="h-full flex items-center justify-center px-6">
<div class="max-w-md text-sm text-txtsecondary space-y-4"> <div class="max-w-md text-sm text-muted-foreground space-y-4">
<h4 class="text-base font-semibold text-txtmain pb-0">Load Test</h4> <h4 class="text-base font-semibold text-foreground pb-0">Load Test</h4>
<p> <p>
Fire several streaming chat completions at llama-swap at the same time to see how it handles parallel Fire several streaming chat completions at llama-swap at the same time to see how it handles parallel
loading and concurrent inference. Each request streams into its own panel with a live timer and status. loading and concurrent inference. Each request streams into its own panel with a live timer and status.
@@ -456,16 +456,16 @@
<ol class="list-decimal list-inside space-y-1"> <ol class="list-decimal list-inside space-y-1">
<li>Click models on the left to queue them — repeat a model to hit it with parallel requests.</li> <li>Click models on the left to queue them — repeat a model to hit it with parallel requests.</li>
<li>Tweak the prompt and <code>max_tokens</code> if you want.</li> <li>Tweak the prompt and <code>max_tokens</code> if you want.</li>
<li>Press <span class="font-semibold text-txtmain">Go</span> to launch them concurrently.</li> <li>Press <span class="font-semibold text-foreground">Go</span> to launch them concurrently.</li>
</ol> </ol>
<p class="text-xs">Tip: drag a result card's header to reorder, or hit × to drop it.</p> <p class="text-xs">Tip: drag a result card's header to reorder, or hit × to drop it.</p>
</div> </div>
</div> </div>
{:else} {:else}
<!-- Gantt-style timeline --> <!-- Gantt-style timeline -->
<div class="mb-3 border border-gray-200 dark:border-white/10 rounded"> <div class="mb-3 border border-border rounded">
<button <button
class="w-full flex items-center gap-2 px-2 py-1.5 text-xs font-medium text-txtsecondary hover:bg-secondary-hover transition-colors {$timelineCollapsedStore ? 'rounded' : 'rounded-t border-b border-gray-200 dark:border-white/10'}" class="w-full flex items-center gap-2 px-2 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent transition-colors {$timelineCollapsedStore ? 'rounded' : 'rounded-t border-b border-border'}"
onclick={() => timelineCollapsedStore.update((v) => !v)} onclick={() => timelineCollapsedStore.update((v) => !v)}
aria-expanded={!$timelineCollapsedStore} aria-expanded={!$timelineCollapsedStore}
> >
@@ -480,7 +480,7 @@
</svg> </svg>
<span>Timeline</span> <span>Timeline</span>
{#if !$timelineCollapsedStore} {#if !$timelineCollapsedStore}
<span class="flex items-center gap-3 text-[10px] text-txtsecondary font-normal ml-3" aria-hidden="true"> <span class="flex items-center gap-3 text-[10px] text-muted-foreground font-normal ml-3" aria-hidden="true">
<span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-slate-200 dark:bg-white/10 border border-gray-300 dark:border-white/10"></span>waiting</span> <span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-slate-200 dark:bg-white/10 border border-gray-300 dark:border-white/10"></span>waiting</span>
<span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-slate-400 dark:bg-slate-500"></span>loading</span> <span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-slate-400 dark:bg-slate-500"></span>loading</span>
<span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-purple-500"></span>reasoning</span> <span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-purple-500"></span>reasoning</span>
@@ -489,7 +489,7 @@
<span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-red-500"></span>error</span> <span class="flex items-center gap-1"><span class="inline-block w-2.5 h-2.5 rounded-sm bg-red-500"></span>error</span>
</span> </span>
{/if} {/if}
<span class="ml-auto tabular-nums text-txtsecondary"> <span class="ml-auto tabular-nums text-muted-foreground">
max {formatElapsed(timelineMaxMs)} · {$testListStore.length} request{$testListStore.length === 1 ? "" : "s"} max {formatElapsed(timelineMaxMs)} · {$testListStore.length} request{$testListStore.length === 1 ? "" : "s"}
</span> </span>
</button> </button>
@@ -498,13 +498,13 @@
<!-- X axis ticks --> <!-- X axis ticks -->
<div class="flex" aria-hidden="true"> <div class="flex" aria-hidden="true">
<div class="w-40 shrink-0"></div> <div class="w-40 shrink-0"></div>
<div class="relative flex-1 h-4 border-b border-gray-200 dark:border-white/10"> <div class="relative flex-1 h-4 border-b border-border">
{#each timelineTicks as t (t)} {#each timelineTicks as t (t)}
<div <div
class="absolute top-0 bottom-0 border-l border-gray-200 dark:border-white/10" class="absolute top-0 bottom-0 border-l border-border"
style="left: {(t / timelineMaxMs) * 100}%;" style="left: {(t / timelineMaxMs) * 100}%;"
> >
<span class="absolute -top-0.5 left-1 text-[10px] text-txtsecondary tabular-nums">{formatTickMs(t)}</span> <span class="absolute -top-0.5 left-1 text-[10px] text-muted-foreground tabular-nums">{formatTickMs(t)}</span>
</div> </div>
{/each} {/each}
</div> </div>
@@ -519,14 +519,14 @@
{@const reasoningPct = run ? (run.reasoningMs / timelineMaxMs) * 100 : 0} {@const reasoningPct = run ? (run.reasoningMs / timelineMaxMs) * 100 : 0}
{@const contentPct = run ? (run.contentMs / timelineMaxMs) * 100 : 0} {@const contentPct = run ? (run.contentMs / timelineMaxMs) * 100 : 0}
<div class="flex items-center text-xs"> <div class="flex items-center text-xs">
<div class="w-40 shrink-0 flex items-center gap-1 pr-2 text-txtsecondary"> <div class="w-40 shrink-0 flex items-center gap-1 pr-2 text-muted-foreground">
<span class="tabular-nums w-5 text-right">{i + 1}.</span> <span class="tabular-nums w-5 text-right">{i + 1}.</span>
<span class="truncate" title={entry.model}>{entry.model}</span> <span class="truncate" title={entry.model}>{entry.model}</span>
</div> </div>
<div class="relative flex-1 h-4"> <div class="relative flex-1 h-4">
{#each timelineTicks as t (t)} {#each timelineTicks as t (t)}
<div <div
class="absolute top-0 bottom-0 border-l border-gray-100 dark:border-white/5" class="absolute top-0 bottom-0 border-l border-border"
style="left: {(t / timelineMaxMs) * 100}%;" style="left: {(t / timelineMaxMs) * 100}%;"
aria-hidden="true" aria-hidden="true"
></div> ></div>
@@ -560,7 +560,7 @@
></div> ></div>
{/if} {/if}
</div> </div>
<div class="w-16 shrink-0 pl-2 tabular-nums text-txtsecondary text-right"> <div class="w-16 shrink-0 pl-2 tabular-nums text-muted-foreground text-right">
{run ? formatElapsed(run.elapsedMs) : "—"} {run ? formatElapsed(run.elapsedMs) : "—"}
</div> </div>
</div> </div>
@@ -576,14 +576,14 @@
<div <div
class="border rounded flex flex-col min-h-0 transition-colors {dragOverIndex === i && dragIndex !== i class="border rounded flex flex-col min-h-0 transition-colors {dragOverIndex === i && dragIndex !== i
? 'border-primary ring-2 ring-primary/40' ? 'border-primary ring-2 ring-primary/40'
: 'border-gray-200 dark:border-white/10'} {dragIndex === i ? 'opacity-40' : ''}" : 'border-border'} {dragIndex === i ? 'opacity-40' : ''}"
style="height: 280px;" style="height: 280px;"
role="listitem" role="listitem"
ondragover={(e) => onDragOver(i, e)} ondragover={(e) => onDragOver(i, e)}
ondrop={(e) => onDrop(i, e)} ondrop={(e) => onDrop(i, e)}
> >
<div <div
class="shrink-0 flex items-center gap-2 px-2 py-1.5 border-b border-gray-200 dark:border-white/10 bg-secondary/40 rounded-t" class="shrink-0 flex items-center gap-2 px-2 py-1.5 border-b border-border bg-secondary/40 rounded-t"
draggable={!isRunning} draggable={!isRunning}
role="button" role="button"
tabindex="-1" tabindex="-1"
@@ -593,15 +593,15 @@
class:cursor-grab={!isRunning} class:cursor-grab={!isRunning}
title={isRunning ? "" : "Drag to reorder"} title={isRunning ? "" : "Drag to reorder"}
> >
<span class="text-txtsecondary select-none" aria-hidden="true">⋮⋮</span> <span class="text-muted-foreground select-none" aria-hidden="true">⋮⋮</span>
<span class="text-txtsecondary tabular-nums text-xs w-5 text-right">{i + 1}.</span> <span class="text-muted-foreground tabular-nums text-xs w-5 text-right">{i + 1}.</span>
<span class="flex-1 truncate text-sm font-medium" title={entry.model}>{entry.model}</span> <span class="flex-1 truncate text-sm font-medium" title={entry.model}>{entry.model}</span>
<span class="text-xs tabular-nums text-txtsecondary"> <span class="text-xs tabular-nums text-muted-foreground">
{run ? formatElapsed(run.elapsedMs) : "—"} {run ? formatElapsed(run.elapsedMs) : "—"}
</span> </span>
<span class="status text-[10px] {statusBadgeClass(status)}">{status}</span> <span class="status text-[10px] {statusBadgeClass(status)}">{status}</span>
<button <button
class="w-5 h-5 flex items-center justify-center text-txtsecondary hover:text-red-500 transition-colors rounded disabled:opacity-30 disabled:cursor-not-allowed" class="w-5 h-5 flex items-center justify-center text-muted-foreground hover:text-red-500 transition-colors rounded disabled:opacity-30 disabled:cursor-not-allowed"
onclick={() => removeEntry(entry.id)} onclick={() => removeEntry(entry.id)}
disabled={isRunning} disabled={isRunning}
aria-label="Remove" aria-label="Remove"
@@ -612,7 +612,7 @@
</div> </div>
<div class="flex-1 min-h-0 overflow-y-auto font-mono text-xs px-2 py-1.5"> <div class="flex-1 min-h-0 overflow-y-auto font-mono text-xs px-2 py-1.5">
{#if run?.loadingText} {#if run?.loadingText}
<div class="bg-secondary/40 dark:bg-white/5 text-txtsecondary rounded px-2 py-1 mb-2 whitespace-pre-wrap">{run.loadingText.trim()}</div> <div class="bg-secondary/40 dark:bg-white/5 text-muted-foreground rounded px-2 py-1 mb-2 whitespace-pre-wrap">{run.loadingText.trim()}</div>
{/if} {/if}
{#if run?.reasoningContent} {#if run?.reasoningContent}
<div class="text-purple-700 dark:text-purple-300 whitespace-pre-wrap">{run.reasoningContent}</div> <div class="text-purple-700 dark:text-purple-300 whitespace-pre-wrap">{run.reasoningContent}</div>
@@ -196,7 +196,7 @@
<ModelSelector bind:value={$selectedModelStore} placeholder="Select an image model..." disabled={isGenerating} capabilities={["image_generation", "image_to_image"]} matchAny={true} /> <ModelSelector bind:value={$selectedModelStore} placeholder="Select an image model..." disabled={isGenerating} capabilities={["image_generation", "image_to_image"]} matchAny={true} />
<select <select
class="px-3 py-2 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-3 py-2 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$apiModeStore} bind:value={$apiModeStore}
disabled={isGenerating} disabled={isGenerating}
> >
@@ -205,7 +205,7 @@
</select> </select>
<select <select
class="px-3 py-2 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-3 py-2 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$selectedSizeStore} bind:value={$selectedSizeStore}
disabled={isGenerating} disabled={isGenerating}
> >
@@ -227,7 +227,7 @@
{#if isSdapi} {#if isSdapi}
<button <button
class="px-3 py-2 rounded border border-gray-200 dark:border-white/10 bg-surface hover:bg-secondary-hover transition-colors" class="px-3 py-2 rounded border border-border bg-background hover:bg-accent transition-colors"
onclick={() => showSettings = !showSettings} onclick={() => showSettings = !showSettings}
> >
{showSettings ? "Hide Settings" : "Settings"} {showSettings ? "Hide Settings" : "Settings"}
@@ -237,23 +237,23 @@
<!-- SDAPI Settings Panel --> <!-- SDAPI Settings Panel -->
{#if isSdapi && showSettings} {#if isSdapi && showSettings}
<div class="shrink-0 mb-4 p-4 rounded border border-gray-200 dark:border-white/10 bg-surface"> <div class="shrink-0 mb-4 p-4 rounded border border-border bg-background">
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-3"> <div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-3">
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
<span class="text-xs text-txtsecondary">Steps</span> <span class="text-xs text-muted-foreground">Steps</span>
<input <input
type="number" type="number"
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$sdStepsStore} bind:value={$sdStepsStore}
min="1" min="1"
max="150" max="150"
/> />
</label> </label>
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
<span class="text-xs text-txtsecondary">CFG Scale</span> <span class="text-xs text-muted-foreground">CFG Scale</span>
<input <input
type="number" type="number"
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$sdCfgScaleStore} bind:value={$sdCfgScaleStore}
min="1" min="1"
max="30" max="30"
@@ -261,28 +261,28 @@
/> />
</label> </label>
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
<span class="text-xs text-txtsecondary">Seed (-1 = random)</span> <span class="text-xs text-muted-foreground">Seed (-1 = random)</span>
<input <input
type="number" type="number"
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$sdSeedStore} bind:value={$sdSeedStore}
min="-1" min="-1"
/> />
</label> </label>
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
<span class="text-xs text-txtsecondary">Batch Size</span> <span class="text-xs text-muted-foreground">Batch Size</span>
<input <input
type="number" type="number"
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$sdBatchSizeStore} bind:value={$sdBatchSizeStore}
min="1" min="1"
max="8" max="8"
/> />
</label> </label>
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
<span class="text-xs text-txtsecondary">Sampler</span> <span class="text-xs text-muted-foreground">Sampler</span>
<select <select
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$sdSamplerStore} bind:value={$sdSamplerStore}
> >
<option value="">Default</option> <option value="">Default</option>
@@ -301,9 +301,9 @@
</select> </select>
</label> </label>
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
<span class="text-xs text-txtsecondary">Scheduler</span> <span class="text-xs text-muted-foreground">Scheduler</span>
<select <select
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
bind:value={$sdSchedulerStore} bind:value={$sdSchedulerStore}
> >
<option value="">Auto for model</option> <option value="">Auto for model</option>
@@ -317,9 +317,9 @@
</div> </div>
<label class="flex flex-col gap-1 mb-3"> <label class="flex flex-col gap-1 mb-3">
<span class="text-xs text-txtsecondary">Negative Prompt</span> <span class="text-xs text-muted-foreground">Negative Prompt</span>
<textarea <textarea
class="px-2 py-1 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary resize-y text-sm" class="px-2 py-1 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary resize-y text-sm"
bind:value={$sdNegativePromptStore} bind:value={$sdNegativePromptStore}
rows="2" rows="2"
placeholder="Elements to avoid..." placeholder="Elements to avoid..."
@@ -328,10 +328,10 @@
<!-- LoRA Selection --> <!-- LoRA Selection -->
<div> <div>
<span class="text-xs text-txtsecondary block mb-1">LoRAs</span> <span class="text-xs text-muted-foreground block mb-1">LoRAs</span>
<div class="flex items-center gap-2 mb-2"> <div class="flex items-center gap-2 mb-2">
<button <button
class="px-3 py-1.5 text-sm rounded border border-gray-200 dark:border-white/10 bg-surface hover:bg-secondary-hover transition-colors disabled:opacity-50" class="px-3 py-1.5 text-sm rounded border border-border bg-background hover:bg-accent transition-colors disabled:opacity-50"
onclick={loadLoras} onclick={loadLoras}
disabled={!$selectedModelStore || isLoadingLoras} disabled={!$selectedModelStore || isLoadingLoras}
> >
@@ -339,7 +339,7 @@
</button> </button>
{#if lorasLoaded && availableLoras.length > 0} {#if lorasLoaded && availableLoras.length > 0}
<select <select
class="flex-1 px-2 py-1.5 text-sm rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="flex-1 px-2 py-1.5 text-sm rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
onchange={addLora} onchange={addLora}
> >
<option value="">Add a LoRA...</option> <option value="">Add a LoRA...</option>
@@ -353,7 +353,7 @@
<p class="text-xs text-red-500 mb-1">{loraError}</p> <p class="text-xs text-red-500 mb-1">{loraError}</p>
{/if} {/if}
{#if lorasLoaded && availableLoras.length === 0} {#if lorasLoaded && availableLoras.length === 0}
<p class="text-xs text-txtsecondary">No LoRAs available</p> <p class="text-xs text-muted-foreground">No LoRAs available</p>
{/if} {/if}
{#if selectedLoras.length > 0} {#if selectedLoras.length > 0}
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
@@ -362,7 +362,7 @@
<span class="flex-1 truncate">{getLoraName(lora.path)}</span> <span class="flex-1 truncate">{getLoraName(lora.path)}</span>
<input <input
type="number" type="number"
class="w-20 px-1.5 py-1 text-xs rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-1 focus:ring-primary" class="w-20 px-1.5 py-1 text-xs rounded border border-border bg-background focus:outline-none focus:ring-1 focus:ring-primary"
value={lora.multiplier} value={lora.multiplier}
oninput={(e) => updateLoraMultiplier(lora.path, parseFloat((e.target as HTMLInputElement).value) || 1)} oninput={(e) => updateLoraMultiplier(lora.path, parseFloat((e.target as HTMLInputElement).value) || 1)}
min="0" min="0"
@@ -370,7 +370,7 @@
step="0.1" step="0.1"
/> />
<button <button
class="px-1.5 py-0.5 text-xs rounded border border-gray-200 dark:border-white/10 hover:bg-red-500 hover:text-white hover:border-red-500 transition-colors" class="px-1.5 py-0.5 text-xs rounded border border-border hover:bg-red-500 hover:text-white hover:border-red-500 transition-colors"
onclick={() => removeLora(lora.path)} onclick={() => removeLora(lora.path)}
aria-label="Remove LoRA" aria-label="Remove LoRA"
> >
@@ -386,14 +386,14 @@
<!-- Empty state for no models configured --> <!-- Empty state for no models configured -->
{#if !hasModels} {#if !hasModels}
<div class="flex-1 flex items-center justify-center text-txtsecondary"> <div class="flex-1 flex items-center justify-center text-muted-foreground">
<p>No models configured. Add models to your configuration to generate images.</p> <p>No models configured. Add models to your configuration to generate images.</p>
</div> </div>
{:else} {:else}
<!-- Image display area --> <!-- Image display area -->
<div class="flex-1 overflow-auto mb-4 flex items-center justify-center bg-surface border border-gray-200 dark:border-white/10 rounded"> <div class="flex-1 overflow-auto mb-4 flex items-center justify-center bg-background border border-border rounded">
{#if isGenerating} {#if isGenerating}
<div class="text-center text-txtsecondary"> <div class="text-center text-muted-foreground">
<div class="inline-block w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin mb-2"></div> <div class="inline-block w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin mb-2"></div>
<p>Generating image...</p> <p>Generating image...</p>
</div> </div>
@@ -454,7 +454,7 @@
</button> </button>
</div> </div>
{:else} {:else}
<div class="text-center text-txtsecondary"> <div class="text-center text-muted-foreground">
<p>Enter a prompt below to generate an image</p> <p>Enter a prompt below to generate an image</p>
</div> </div>
{/if} {/if}
@@ -476,7 +476,7 @@
</button> </button>
{:else} {:else}
<button <button
class="btn bg-primary text-btn-primary-text hover:opacity-90 flex-1 md:flex-none" class="btn bg-primary text-primary-foreground hover:opacity-90 flex-1 md:flex-none"
onclick={generate} onclick={generate}
disabled={!prompt.trim() || !$selectedModelStore} disabled={!prompt.trim() || !$selectedModelStore}
> >
@@ -7,7 +7,7 @@
</script> </script>
<div class="flex items-center justify-center h-full"> <div class="flex items-center justify-center h-full">
<div class="text-center text-txtsecondary"> <div class="text-muted-foreground text-center">
<p class="text-lg">{featureName}</p> <p class="text-lg">{featureName}</p>
<p class="text-sm mt-2">To be implemented</p> <p class="text-sm mt-2">To be implemented</p>
</div> </div>
@@ -4,6 +4,10 @@
import { rerank } from "../../lib/rerankApi"; import { rerank } from "../../lib/rerankApi";
import { playgroundStores } from "../../stores/playgroundActivity"; import { playgroundStores } from "../../stores/playgroundActivity";
import ModelSelector from "./ModelSelector.svelte"; import ModelSelector from "./ModelSelector.svelte";
import { Button } from "$lib/components/ui/button/index.js";
import { Input } from "$lib/components/ui/input/index.js";
import { Textarea } from "$lib/components/ui/textarea/index.js";
import * as ToggleGroup from "$lib/components/ui/toggle-group/index.js";
type RerankRow = { doc: string; score: number | null }; type RerankRow = { doc: string; score: number | null };
type SortOrder = "none" | "asc" | "desc"; type SortOrder = "none" | "asc" | "desc";
@@ -234,9 +238,9 @@
} }
function scoreColor(score: number | null): string { function scoreColor(score: number | null): string {
if (score === null) return "text-txtsecondary"; if (score === null) return "text-muted-foreground";
if (score > 0) return "text-green-600 dark:text-green-400"; if (score > 0) return "text-green-600 dark:text-green-400";
return "text-red-500 dark:text-red-400"; return "text-destructive";
} }
function formatScore(score: number | null): string { function formatScore(score: number | null): string {
@@ -266,9 +270,9 @@
<div class="shrink-0 flex flex-wrap gap-2 mb-4"> <div class="shrink-0 flex flex-wrap gap-2 mb-4">
<ModelSelector bind:value={$selectedModelStore} placeholder="Select a rerank model..." disabled={isLoading} capabilities={["reranker"]} /> <ModelSelector bind:value={$selectedModelStore} placeholder="Select a rerank model..." disabled={isLoading} capabilities={["reranker"]} />
{#if editorMode === "table"} {#if editorMode === "table"}
<input <Input
type="text" type="text"
class="min-w-0 flex-1 basis-48 px-3 py-2 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="min-w-0 flex-1 basis-48"
placeholder="Query..." placeholder="Query..."
bind:value={query} bind:value={query}
disabled={isLoading} disabled={isLoading}
@@ -276,60 +280,50 @@
/> />
{/if} {/if}
<!-- Table / JSON toggle --> <!-- Table / JSON toggle -->
<div class="flex rounded border border-gray-200 dark:border-white/10 overflow-hidden shrink-0"> <ToggleGroup.Root
<button type="single"
class="px-3 py-1.5 text-sm transition-colors {editorMode === 'table' variant="outline"
? 'bg-primary text-btn-primary-text' value={editorMode}
: 'bg-surface hover:bg-secondary-hover'}" onValueChange={(v) => v && (v === "table" ? switchToTable() : switchToJson())}
onclick={switchToTable} class="shrink-0"
disabled={isLoading} >
> <ToggleGroup.Item value="table" disabled={isLoading}>Table</ToggleGroup.Item>
Table <ToggleGroup.Item value="json" disabled={isLoading}>JSON</ToggleGroup.Item>
</button> </ToggleGroup.Root>
<button
class="px-3 py-1.5 text-sm border-l border-gray-200 dark:border-white/10 transition-colors {editorMode === 'json'
? 'bg-primary text-btn-primary-text'
: 'bg-surface hover:bg-secondary-hover'}"
onclick={switchToJson}
disabled={isLoading}
>
JSON
</button>
</div>
</div> </div>
{#if !hasModels} {#if !hasModels}
<div class="flex-1 flex items-center justify-center text-txtsecondary"> <div class="text-muted-foreground flex flex-1 items-center justify-center">
<p>No models configured. Add models to your configuration to use reranking.</p> <p>No models configured. Add models to your configuration to use reranking.</p>
</div> </div>
{:else if editorMode === "json"} {:else if editorMode === "json"}
<!-- JSON editor --> <!-- JSON editor -->
<div class="flex-1 flex flex-col min-h-0 mb-4"> <div class="mb-4 flex min-h-0 flex-1 flex-col">
<textarea <Textarea
class="flex-1 w-full font-mono text-sm px-3 py-2 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary resize-none" class="w-full flex-1 resize-none font-mono text-sm"
bind:value={jsonText} bind:value={jsonText}
disabled={isLoading} disabled={isLoading}
placeholder={'{\n "query": "your search query",\n "documents": [\n "document one",\n "document two"\n ]\n}'} placeholder={'{\n "query": "your search query",\n "documents": [\n "document one",\n "document two"\n ]\n}'}
spellcheck={false} spellcheck={false}
></textarea> />
{#if jsonError} {#if jsonError}
<p class="mt-1 text-sm text-red-500">{jsonError}</p> <p class="text-destructive mt-1 text-sm">{jsonError}</p>
{/if} {/if}
</div> </div>
{:else} {:else}
<!-- Document table --> <!-- Document table -->
<div class="flex-1 overflow-y-auto mb-4 border border-gray-200 dark:border-white/10 rounded"> <div class="mb-4 flex-1 overflow-y-auto rounded-lg border">
<table class="w-full border-collapse table-fixed"> <table class="w-full table-fixed border-collapse">
<colgroup> <colgroup>
<col class="w-auto" /> <col class="w-auto" />
<col style="width: 120px" /> <col style="width: 120px" />
<col style="width: 40px" /> <col style="width: 40px" />
</colgroup> </colgroup>
<thead class="sticky top-0 bg-surface border-b border-gray-200 dark:border-white/10"> <thead class="bg-card sticky top-0 border-b">
<tr> <tr>
<th class="px-3 py-2 text-left text-sm font-medium text-txtsecondary">Document</th> <th class="text-muted-foreground px-3 py-2 text-left text-sm font-medium">Document</th>
<th <th
class="px-3 py-2 text-right text-sm font-medium text-txtsecondary cursor-pointer select-none hover:text-txtprimary transition-colors" class="text-muted-foreground hover:text-foreground cursor-pointer select-none px-3 py-2 text-right text-sm font-medium transition-colors"
onclick={cycleSortOrder} onclick={cycleSortOrder}
> >
Score{sortIndicator()} Score{sortIndicator()}
@@ -339,11 +333,11 @@
</thead> </thead>
<tbody> <tbody>
{#each displayRows as { row, i } (i)} {#each displayRows as { row, i } (i)}
<tr class="border-b border-gray-100 dark:border-white/5 last:border-0"> <tr class="border-b last:border-0">
<td class="px-3 py-1.5"> <td class="px-3 py-1.5">
<input <input
type="text" type="text"
class="w-full bg-transparent focus:outline-none focus:ring-1 focus:ring-primary rounded px-1 py-0.5" class="focus:ring-ring w-full rounded bg-transparent px-1 py-0.5 outline-none focus:ring-1"
placeholder={i === rows.length - 1 ? "Add document..." : "Document text..."} placeholder={i === rows.length - 1 ? "Add document..." : "Document text..."}
value={row.doc} value={row.doc}
oninput={(e) => updateDoc(i, (e.target as HTMLInputElement).value)} oninput={(e) => updateDoc(i, (e.target as HTMLInputElement).value)}
@@ -353,14 +347,14 @@
</td> </td>
<td class="px-3 py-1.5 text-right font-mono text-sm {scoreColor(row.score)}"> <td class="px-3 py-1.5 text-right font-mono text-sm {scoreColor(row.score)}">
{#if isLoading && row.score === null && row.doc.trim() !== ""} {#if isLoading && row.score === null && row.doc.trim() !== ""}
<span class="inline-block w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin align-middle"></span> <span class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent align-middle"></span>
{:else} {:else}
{formatScore(row.score)} {formatScore(row.score)}
{/if} {/if}
</td> </td>
<td class="px-2 py-1.5 text-center"> <td class="px-2 py-1.5 text-center">
<button <button
class="w-7 h-7 flex items-center justify-center text-txtsecondary hover:text-red-500 transition-colors rounded disabled:opacity-30 disabled:cursor-not-allowed" class="text-muted-foreground hover:text-destructive flex h-7 w-7 items-center justify-center rounded transition-colors disabled:cursor-not-allowed disabled:opacity-30"
onclick={() => deleteRow(i)} onclick={() => deleteRow(i)}
disabled={rows.length <= 1} disabled={rows.length <= 1}
tabindex="-1" tabindex="-1"
@@ -378,28 +372,18 @@
<!-- Bottom toolbar --> <!-- Bottom toolbar -->
{#if hasModels} {#if hasModels}
<div class="shrink-0 flex flex-wrap items-center gap-2"> <div class="flex shrink-0 flex-wrap items-center gap-2">
{#if isLoading} {#if isLoading}
<button class="btn bg-red-500 hover:bg-red-600 text-white" onclick={cancel}> <Button variant="destructive" onclick={cancel}>Cancel</Button>
Cancel
</button>
{:else} {:else}
<button <Button onclick={submit} disabled={!canSubmit}>Rerank</Button>
class="btn bg-primary text-btn-primary-text hover:opacity-90" <Button variant="outline" onclick={clear} disabled={isCleared}>Clear</Button>
onclick={submit}
disabled={!canSubmit}
>
Rerank
</button>
<button class="btn" onclick={clear} disabled={isCleared}>
Clear
</button>
{/if} {/if}
{#if error} {#if error}
<span class="text-sm text-red-500 ml-2">{error}</span> <span class="text-destructive ml-2 text-sm">{error}</span>
{:else if usage} {:else if usage}
<span class="text-sm text-txtsecondary ml-2">{usage.total_tokens} tokens</span> <span class="text-muted-foreground ml-2 text-sm">{usage.total_tokens} tokens</span>
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -209,7 +209,7 @@
<ModelSelector bind:value={$selectedModelStore} placeholder="Select a speech model..." disabled={isGenerating} capabilities={["audio_speech"]} /> <ModelSelector bind:value={$selectedModelStore} placeholder="Select a speech model..." disabled={isGenerating} capabilities={["audio_speech"]} />
<div class="flex gap-2"> <div class="flex gap-2">
<select <select
class="shrink-0 px-3 py-2 rounded border border-gray-200 dark:border-white/10 bg-surface focus:outline-none focus:ring-2 focus:ring-primary" class="shrink-0 px-3 py-2 rounded border border-border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
value={$selectedVoiceStore} value={$selectedVoiceStore}
onchange={handleVoiceChange} onchange={handleVoiceChange}
disabled={isGenerating || isLoadingVoices || !$selectedModelStore} disabled={isGenerating || isLoadingVoices || !$selectedModelStore}
@@ -243,14 +243,14 @@
<!-- Empty state for no models configured --> <!-- Empty state for no models configured -->
{#if !hasModels} {#if !hasModels}
<div class="flex-1 flex items-center justify-center text-txtsecondary"> <div class="flex-1 flex items-center justify-center text-muted-foreground">
<p>No models configured. Add models to your configuration to generate speech.</p> <p>No models configured. Add models to your configuration to generate speech.</p>
</div> </div>
{:else} {:else}
<!-- Audio display area --> <!-- Audio display area -->
<div class="shrink-0 mb-4 bg-surface border border-gray-200 dark:border-white/10 rounded p-4 md:p-6"> <div class="shrink-0 mb-4 bg-background border border-border rounded p-4 md:p-6">
{#if isGenerating} {#if isGenerating}
<div class="flex items-center justify-center text-txtsecondary py-8"> <div class="flex items-center justify-center text-muted-foreground py-8">
<div class="text-center"> <div class="text-center">
<div class="inline-block w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin mb-2"></div> <div class="inline-block w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin mb-2"></div>
<p>Generating speech...</p> <p>Generating speech...</p>
@@ -267,7 +267,7 @@
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<!-- Header with metadata and download --> <!-- Header with metadata and download -->
<div class="flex items-center justify-between gap-4"> <div class="flex items-center justify-between gap-4">
<div class="flex flex-wrap gap-3 text-sm text-txtsecondary"> <div class="flex flex-wrap gap-3 text-sm text-muted-foreground">
{#if generatedVoice} {#if generatedVoice}
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -305,7 +305,7 @@
</div> </div>
</div> </div>
{:else} {:else}
<div class="flex items-center justify-center text-txtsecondary py-8"> <div class="flex items-center justify-center text-muted-foreground py-8">
<div class="text-center"> <div class="text-center">
<svg class="w-12 h-12 md:w-16 md:h-16 mx-auto mb-2 opacity-40" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-12 h-12 md:w-16 md:h-16 mx-auto mb-2 opacity-40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"></path>
@@ -332,7 +332,7 @@
</button> </button>
{:else} {:else}
<button <button
class="btn bg-primary text-btn-primary-text hover:opacity-90 flex-1 md:flex-none" class="btn bg-primary text-primary-foreground hover:opacity-90 flex-1 md:flex-none"
onclick={generate} onclick={generate}
disabled={!inputText.trim() || !$selectedModelStore} disabled={!inputText.trim() || !$selectedModelStore}
> >
+12 -12
View File
@@ -352,14 +352,14 @@
<div class="space-y-6"> <div class="space-y-6">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h2 class="text-xl font-semibold text-txtmain">Performance (Experimental)</h2> <h2 class="text-xl font-semibold text-foreground">Performance (Experimental)</h2>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
{#each WINDOWS as win, i} {#each WINDOWS as win, i}
<button <button
class="btn btn--sm" class="btn btn--sm"
class:bg-primary={$selectedWindow === i} class:bg-primary={$selectedWindow === i}
class:text-btn-primary-text={$selectedWindow === i} class:text-primary-foreground={$selectedWindow === i}
onclick={() => ($selectedWindow = i)} onclick={() => ($selectedWindow = i)}
> >
{win.label} {win.label}
@@ -367,12 +367,12 @@
{/each} {/each}
</div> </div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<span class="text-xs text-txtsecondary mr-1">Refresh:</span> <span class="text-xs text-muted-foreground mr-1">Refresh:</span>
{#each INTERVALS as intv, i} {#each INTERVALS as intv, i}
<button <button
class="btn btn--sm" class="btn btn--sm"
class:bg-primary={$selectedInterval === i} class:bg-primary={$selectedInterval === i}
class:text-btn-primary-text={$selectedInterval === i} class:text-primary-foreground={$selectedInterval === i}
onclick={() => handleIntervalChange(i)} onclick={() => handleIntervalChange(i)}
> >
{intv.label} {intv.label}
@@ -399,18 +399,18 @@
</button> </button>
</div> </div>
</div> </div>
<p class="text-sm text-txtsecondary"> <p class="text-sm text-muted-foreground">
This is an experimental feature. Please use <a This is an experimental feature. Please use <a
class="underline hover:text-txtmain" class="underline hover:text-foreground"
href="https://github.com/mostlygeek/llama-swap/discussions/771">discussion #771</a href="https://github.com/mostlygeek/llama-swap/discussions/771">discussion #771</a
> for instructions and to share feedback. > for instructions and to share feedback.
</p> </p>
<!-- GPU Section --> <!-- GPU Section -->
<section class="space-y-4"> <section class="space-y-4">
<h3 class="text-lg font-medium text-txtmain">GPU</h3> <h3 class="text-lg font-medium text-foreground">GPU</h3>
{#if !hasGpuData} {#if !hasGpuData}
<p class="text-txtsecondary card p-4">No GPU data available</p> <p class="text-muted-foreground card p-4">No GPU data available</p>
{:else} {:else}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<PerformanceChart <PerformanceChart
@@ -458,7 +458,7 @@
<!-- System Section --> <!-- System Section -->
<section class="space-y-4"> <section class="space-y-4">
<h3 class="text-lg font-medium text-txtmain">System</h3> <h3 class="text-lg font-medium text-foreground">System</h3>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<PerformanceChart <PerformanceChart
title="CPU Utilization (%)" title="CPU Utilization (%)"
@@ -479,15 +479,15 @@
yLabel="%" yLabel="%"
/> />
{#if latestMemSwap} {#if latestMemSwap}
<div class="flex items-center justify-center gap-4 text-xs text-txtsecondary mt-1 px-4"> <div class="flex items-center justify-center gap-4 text-xs text-muted-foreground mt-1 px-4">
<span <span
>Mem: <span class="text-txtmain font-medium" >Mem: <span class="text-foreground font-medium"
>{latestMemSwap.mem_used_mb.toLocaleString()} / {latestMemSwap.mem_total_mb.toLocaleString()} MB ({latestMemSwap.mem_used_pct}%)</span >{latestMemSwap.mem_used_mb.toLocaleString()} / {latestMemSwap.mem_total_mb.toLocaleString()} MB ({latestMemSwap.mem_used_pct}%)</span
></span ></span
> >
{#if latestMemSwap.swap_used_pct !== null} {#if latestMemSwap.swap_used_pct !== null}
<span <span
>Swap: <span class="text-txtmain font-medium" >Swap: <span class="text-foreground font-medium"
>{latestMemSwap.swap_used_mb.toLocaleString()} / {latestMemSwap.swap_total_mb.toLocaleString()} MB ({latestMemSwap.swap_used_pct}%)</span >{latestMemSwap.swap_used_mb.toLocaleString()} / {latestMemSwap.swap_total_mb.toLocaleString()} MB ({latestMemSwap.swap_used_pct}%)</span
></span ></span
> >