Reorganize UI controls and improve form interactions (#500)

Reorganizes control placement in the playground interfaces and
improves form interactions for better UX, particularly on mobile
devices.

## Key Changes

- **AudioInterface & ImageInterface**: Moved "Clear" buttons from the
top control bar into the action button group below the form inputs for
better visual hierarchy and logical grouping
- **ImageInterface**: 
- Added prompt clearing to the `clearImage()` function so the input
field is reset when clearing generated images
- Updated Clear button disabled state to also check if prompt is empty,
allowing users to clear an empty prompt
- Added responsive flex styling (`flex-1 md:flex-none`) to the Clear
button for better mobile layout
- **ExpandableTextarea**: 
- Imported `untrack` from Svelte to properly handle reactive
dependencies
- Wrapped `expandedValue.length` in `untrack()` to prevent unnecessary
reactivity when setting cursor position
- Improved button visibility on mobile by changing opacity from
`opacity-0` to `opacity-60` with `md:opacity-0` breakpoint, making the
expand button more discoverable on touch devices

## Implementation Details
The `untrack()` usage in ExpandableTextarea ensures that reading the
text length doesn't create a reactive dependency, preventing potential
infinite loops while still allowing the effect to run when `isExpanded`
changes.
This commit is contained in:
Benson Wong
2026-02-01 15:18:22 -08:00
committed by GitHub
parent 7b20fc011b
commit 0462e3dc3f
4 changed files with 19 additions and 15 deletions
@@ -141,9 +141,6 @@
<!-- Model selector -->
<div class="shrink-0 flex flex-wrap gap-2 mb-4">
<ModelSelector bind:value={$selectedModelStore} placeholder="Select an audio model..." disabled={isTranscribing} />
<button class="btn" onclick={clearAll} disabled={!selectedFile && !transcriptionResult && !error}>
Clear
</button>
</div>
<!-- Empty state for no models configured -->
@@ -241,6 +238,13 @@
>
Transcribe
</button>
<button
class="btn"
onclick={clearAll}
disabled={!selectedFile && !transcriptionResult && !error}
>
Clear
</button>
{/if}
</div>
{/if}
@@ -1,4 +1,5 @@
<script lang="ts">
import { untrack } from "svelte";
import { Maximize2, X } from "lucide-svelte";
interface Props {
@@ -45,7 +46,8 @@
$effect(() => {
if (isExpanded && expandedTextarea) {
expandedTextarea.focus();
expandedTextarea.setSelectionRange(expandedValue.length, expandedValue.length);
const len = untrack(() => expandedValue.length);
expandedTextarea.setSelectionRange(len, len);
}
});
</script>
@@ -60,7 +62,7 @@
{disabled}
></textarea>
<button
class="absolute top-2 right-2 p-1.5 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity bg-surface/90 hover:bg-surface border border-gray-200 dark:border-white/10 shadow-sm"
class="absolute top-2 right-2 p-1.5 rounded-lg opacity-60 md:opacity-0 group-hover:opacity-100 transition-opacity bg-surface/90 hover:bg-surface border border-gray-200 dark:border-white/10 shadow-sm"
onclick={openExpanded}
title="Expand to edit"
type="button"
@@ -60,6 +60,7 @@
function clearImage() {
generatedImage = null;
error = null;
prompt = "";
}
function downloadImage() {
@@ -117,9 +118,6 @@
<option value="1024x1792">1024x1792 (SDXL)</option>
</optgroup>
</select>
<button class="btn" onclick={clearImage} disabled={!generatedImage && !error}>
Clear
</button>
</div>
<!-- Empty state for no models configured -->
@@ -192,6 +190,13 @@
>
Generate
</button>
<button
class="btn flex-1 md:flex-none"
onclick={clearImage}
disabled={!generatedImage && !error && !prompt.trim()}
>
Clear
</button>
{/if}
</div>
</div>