ui: convert Image, Speech, Audio interfaces to shadcn buttons

Replace .btn elements and inline SVG icons with shadcn Button and
@lucide/svelte icons in the image, speech and audio playground tabs.

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:05:19 +00:00
parent fc24722258
commit b20be6dcd1
3 changed files with 52 additions and 75 deletions
@@ -4,6 +4,8 @@
import { transcribeAudio } from "../../lib/audioApi";
import { playgroundStores } from "../../stores/playgroundActivity";
import ModelSelector from "./ModelSelector.svelte";
import { Button } from "$lib/components/ui/button/index.js";
import { Copy, Check } from "@lucide/svelte";
const selectedModelStore = persistentStore<string>("playground-audio-model", "");
@@ -169,22 +171,19 @@
{:else if transcriptionResult}
<div class="w-full h-full flex flex-col p-4">
<div class="flex justify-between items-center mb-2">
<h3 class="font-medium">Transcription Result</h3>
<button
class="btn btn-sm"
<h3 class="pb-0 font-medium">Transcription Result</h3>
<Button
variant="outline"
size="icon-sm"
onclick={copyToClipboard}
title={copied ? 'Copied!' : 'Copy to clipboard'}
>
{#if copied}
<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<Check class="text-success" />
{:else}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
<Copy />
{/if}
</button>
</Button>
</div>
<div class="flex-1 overflow-auto p-3 rounded border border-border bg-background whitespace-pre-wrap">
{transcriptionResult}
@@ -223,33 +222,21 @@
onchange={handleFileSelect}
bind:this={fileInput}
/>
<button
class="btn"
onclick={() => fileInput?.click()}
disabled={isTranscribing}
>
<Button variant="outline" onclick={() => fileInput?.click()} disabled={isTranscribing}>
Browse Files
</button>
</Button>
<div class="flex-1"></div>
{#if isTranscribing}
<button class="btn bg-red-500 hover:bg-red-600 text-white" onclick={cancelTranscription}>
Cancel
</button>
<Button variant="destructive" onclick={cancelTranscription}>Cancel</Button>
{:else}
<button
class="btn bg-primary text-primary-foreground hover:opacity-90"
onclick={transcribe}
disabled={!canTranscribe}
>
Transcribe
</button>
<button
class="btn"
<Button onclick={transcribe} disabled={!canTranscribe}>Transcribe</Button>
<Button
variant="outline"
onclick={clearAll}
disabled={!selectedFile && !transcriptionResult && !error}
>
Clear
</button>
</Button>
{/if}
</div>
{/if}
@@ -7,6 +7,7 @@
import ModelSelector from "./ModelSelector.svelte";
import ExpandableTextarea from "./ExpandableTextarea.svelte";
import type { ImageApiMode, SdApiLora, SdApiLoraRef } from "../../lib/types";
import { Button } from "$lib/components/ui/button/index.js";
const selectedModelStore = persistentStore<string>("playground-image-model", "");
const selectedSizeStore = persistentStore<string>("playground-image-size", "1024x1024");
@@ -226,12 +227,9 @@
</select>
{#if isSdapi}
<button
class="px-3 py-2 rounded border border-border bg-background hover:bg-accent transition-colors"
onclick={() => showSettings = !showSettings}
>
<Button variant="outline" onclick={() => showSettings = !showSettings}>
{showSettings ? "Hide Settings" : "Settings"}
</button>
</Button>
{/if}
</div>
@@ -330,13 +328,14 @@
<div>
<span class="text-xs text-muted-foreground block mb-1">LoRAs</span>
<div class="flex items-center gap-2 mb-2">
<button
class="px-3 py-1.5 text-sm rounded border border-border bg-background hover:bg-accent transition-colors disabled:opacity-50"
<Button
variant="outline"
size="sm"
onclick={loadLoras}
disabled={!$selectedModelStore || isLoadingLoras}
>
{isLoadingLoras ? "Loading..." : lorasLoaded ? "Reload LoRAs" : "Load LoRAs"}
</button>
</Button>
{#if lorasLoaded && availableLoras.length > 0}
<select
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"
@@ -471,24 +470,25 @@
/>
<div class="flex flex-row md:flex-col gap-2">
{#if isGenerating}
<button class="btn bg-red-500 hover:bg-red-600 text-white flex-1 md:flex-none" onclick={cancelGeneration}>
<Button variant="destructive" class="flex-1 md:flex-none" onclick={cancelGeneration}>
Cancel
</button>
</Button>
{:else}
<button
class="btn bg-primary text-primary-foreground hover:opacity-90 flex-1 md:flex-none"
<Button
class="flex-1 md:flex-none"
onclick={generate}
disabled={!prompt.trim() || !$selectedModelStore}
>
Generate
</button>
<button
class="btn flex-1 md:flex-none"
</Button>
<Button
variant="outline"
class="flex-1 md:flex-none"
onclick={clearImage}
disabled={generatedImages.length === 0 && !error && !prompt.trim()}
>
Clear
</button>
</Button>
{/if}
</div>
</div>
@@ -5,6 +5,8 @@
import { playgroundStores } from "../../stores/playgroundActivity";
import ModelSelector from "./ModelSelector.svelte";
import ExpandableTextarea from "./ExpandableTextarea.svelte";
import { Button } from "$lib/components/ui/button/index.js";
import { RefreshCw, Download } from "@lucide/svelte";
const selectedModelStore = persistentStore<string>("playground-speech-model", "");
const selectedVoiceStore = persistentStore<string>("playground-speech-voice", "coral");
@@ -220,23 +222,16 @@
<option value="(refresh)">(refresh)</option>
</select>
{#if $selectedModelStore && !getVoicesCache()[$selectedModelStore]}
<button
class="btn shrink-0"
<Button
variant="outline"
size="icon"
class="shrink-0"
onclick={refreshVoices}
disabled={isLoadingVoices}
title={isLoadingVoices ? "Loading voices..." : "Load voices for this model"}
>
{#if isLoadingVoices}
<svg class="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
{:else}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
{/if}
</button>
<RefreshCw class={isLoadingVoices ? "animate-spin" : ""} />
</Button>
{/if}
</div>
</div>
@@ -285,15 +280,9 @@
</span>
{/if}
</div>
<button
class="btn shrink-0"
onclick={downloadAudio}
title="Download audio file"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
</button>
<Button variant="outline" size="icon" class="shrink-0" onclick={downloadAudio} title="Download audio file">
<Download />
</Button>
</div>
<!-- Audio player with larger controls -->
@@ -327,24 +316,25 @@
/>
<div class="shrink-0 flex md:flex-col gap-2">
{#if isGenerating}
<button class="btn bg-red-500 hover:bg-red-600 text-white flex-1 md:flex-none" onclick={cancelGeneration}>
<Button variant="destructive" class="flex-1 md:flex-none" onclick={cancelGeneration}>
Cancel
</button>
</Button>
{:else}
<button
class="btn bg-primary text-primary-foreground hover:opacity-90 flex-1 md:flex-none"
<Button
class="flex-1 md:flex-none"
onclick={generate}
disabled={!inputText.trim() || !$selectedModelStore}
>
Generate
</button>
<button
class="btn flex-1 md:flex-none"
</Button>
<Button
variant="outline"
class="flex-1 md:flex-none"
onclick={clearInput}
disabled={!inputText.trim()}
>
Clear
</button>
</Button>
<label class="flex items-center justify-center gap-2 text-sm cursor-pointer">
<input
type="checkbox"