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:
@@ -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"
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
</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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user