Fullfører oppgave 17.1: Responsivt studio-layout
Lydstudioet var kun desktop-optimalisert med fast sidebar (w-72). Nå responsivt med to moduser: - Desktop (lg+): Sidebar med verktøypanel til høyre, som før - Mobil/tablet (< lg): Waveform fyller full bredde, verktøypanel tilgjengelig via flytende knapp som åpner bottom sheet (modal). Operation-badge på knappen viser antall aktive operasjoner. Header: Kompaktere padding på mobil, tittel truncates, audio-info skjules på små skjermer. min-w-0 på hovedområdet hindrer overflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4edad399cd
commit
9f66f114ff
2 changed files with 98 additions and 11 deletions
|
|
@ -46,6 +46,9 @@
|
|||
let renderJobId: string | null = $state(null);
|
||||
let resultNodeId: string | null = $state(null);
|
||||
|
||||
// Mobile tool panel sheet
|
||||
let showToolSheet = $state(false);
|
||||
|
||||
// Session persistence
|
||||
let sessionNodeId: string | null = $state(null);
|
||||
let saving = $state(false);
|
||||
|
|
@ -287,18 +290,18 @@
|
|||
|
||||
<div class="flex min-h-screen flex-col bg-gray-50">
|
||||
<!-- Header -->
|
||||
<header class="flex items-center justify-between border-b border-gray-200 bg-white px-4 py-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<a href="/" class="text-gray-400 hover:text-gray-600">
|
||||
<header class="flex items-center justify-between border-b border-gray-200 bg-white px-3 py-2 sm:px-4 sm:py-3">
|
||||
<div class="flex min-w-0 items-center gap-2 sm:gap-3">
|
||||
<a href="/" class="shrink-0 text-gray-400 hover:text-gray-600">
|
||||
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
</a>
|
||||
<h1 class="text-lg font-semibold text-gray-800">
|
||||
<h1 class="truncate text-base font-semibold text-gray-800 sm:text-lg">
|
||||
{mediaNode?.title ?? 'Lydstudio'}
|
||||
</h1>
|
||||
{#if audioInfo}
|
||||
<span class="rounded bg-gray-100 px-2 py-0.5 text-xs text-gray-500">
|
||||
<span class="hidden rounded bg-gray-100 px-2 py-0.5 text-xs text-gray-500 sm:inline">
|
||||
{audioInfo.codec} / {audioInfo.sample_rate}Hz / {audioInfo.channels}ch
|
||||
</span>
|
||||
{/if}
|
||||
|
|
@ -329,9 +332,9 @@
|
|||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-1 gap-4 p-4">
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 lg:flex-row">
|
||||
<!-- Main area: waveform + transcription -->
|
||||
<div class="flex flex-1 flex-col gap-4">
|
||||
<div class="flex flex-1 flex-col gap-4 min-w-0">
|
||||
<StudioWaveform
|
||||
bind:this={waveformRef}
|
||||
src={audioSrc}
|
||||
|
|
@ -362,8 +365,8 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Sidebar: operation panel -->
|
||||
<div class="flex w-72 shrink-0 flex-col gap-4 overflow-y-auto lg:w-80">
|
||||
<!-- Sidebar: operation panel (desktop) -->
|
||||
<div class="hidden w-72 shrink-0 flex-col gap-4 overflow-y-auto lg:flex lg:w-80">
|
||||
<OperationPanel
|
||||
{loudness}
|
||||
{silenceRegions}
|
||||
|
|
@ -401,6 +404,91 @@
|
|||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile: floating tool button + bottom sheet -->
|
||||
<div class="lg:hidden">
|
||||
<!-- Floating action button -->
|
||||
<button
|
||||
onclick={() => { showToolSheet = !showToolSheet; }}
|
||||
class="fixed bottom-4 right-4 z-40 flex h-14 w-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-lg transition-transform hover:bg-blue-700 active:scale-95"
|
||||
aria-label="Verktoy"
|
||||
>
|
||||
{#if showToolSheet}
|
||||
<!-- Close icon -->
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
{:else}
|
||||
<!-- Settings/tool icon -->
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
{/if}
|
||||
{#if operations.length > 0 && !showToolSheet}
|
||||
<span class="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-[10px] font-bold">
|
||||
{operations.length}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<!-- Bottom sheet backdrop -->
|
||||
{#if showToolSheet}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="fixed inset-0 z-40 bg-black/30"
|
||||
onclick={() => { showToolSheet = false; }}
|
||||
onkeydown={(e) => { if (e.key === 'Escape') showToolSheet = false; }}
|
||||
></div>
|
||||
|
||||
<!-- Bottom sheet -->
|
||||
<div class="fixed inset-x-0 bottom-0 z-50 flex max-h-[85vh] flex-col rounded-t-2xl bg-gray-50 shadow-2xl">
|
||||
<!-- Handle -->
|
||||
<div class="flex justify-center py-2">
|
||||
<div class="h-1 w-10 rounded-full bg-gray-300"></div>
|
||||
</div>
|
||||
|
||||
<!-- Sheet content -->
|
||||
<div class="flex-1 overflow-y-auto px-4 pb-8">
|
||||
<OperationPanel
|
||||
{loudness}
|
||||
{silenceRegions}
|
||||
{audioInfo}
|
||||
{analyzing}
|
||||
{operations}
|
||||
activeRegion={waveformRef?.getActiveRegion() ?? null}
|
||||
onanalyze={handleAnalyze}
|
||||
oncut={handleCut}
|
||||
onaddop={handleAddOp}
|
||||
onremoveop={handleRemoveOp}
|
||||
onrender={handleRenderStart}
|
||||
onundo={handleUndo}
|
||||
ontrimsilence={handleTrimSilence}
|
||||
/>
|
||||
|
||||
<!-- Version history -->
|
||||
{#if versions.length > 0}
|
||||
<div class="mt-4 rounded-lg border border-gray-200 bg-white p-4">
|
||||
<h3 class="mb-2 text-sm font-semibold text-gray-700">Versjoner</h3>
|
||||
<ul class="space-y-1">
|
||||
<li class="flex items-center justify-between rounded bg-blue-50 px-2 py-1">
|
||||
<span class="text-xs font-medium text-blue-700">Original</span>
|
||||
<span class="text-[10px] text-blue-500">Navarende</span>
|
||||
</li>
|
||||
{#each versions as ver, i}
|
||||
<li class="flex items-center justify-between rounded px-2 py-1 hover:bg-gray-50">
|
||||
<a href="/studio/{ver.id}" class="text-xs text-gray-600 hover:text-blue-600">
|
||||
v{i + 1}: {ver.title}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
3
tasks.md
3
tasks.md
|
|
@ -189,8 +189,7 @@ Ref: `docs/features/lydmixer.md`
|
|||
|
||||
Ref: Kodegjennomgang av `b4c4bb8` (Lydstudio: lydredigering via FFmpeg).
|
||||
|
||||
- [~] 17.1 Responsivt studio-layout: `/studio/[id]` sidebar stacker under waveform på mobil. Verktøypanel som modal/sheet på små skjermer. Ref: feedback om at alt UI skal være responsivt uten unntak.
|
||||
> Påbegynt: 2026-03-18T05:36
|
||||
- [x] 17.1 Responsivt studio-layout: `/studio/[id]` sidebar stacker under waveform på mobil. Verktøypanel som modal/sheet på små skjermer. Ref: feedback om at alt UI skal være responsivt uten unntak.
|
||||
- [ ] 17.2 FFmpeg-parametervalidering: valider at alle numeriske verdier (threshold, gain, ratio, frekvenser) er innenfor sikre grenser i `audio.rs` før de interpoleres i filterstrenger. Avvis ugyldige verdier med feilmelding.
|
||||
- [ ] 17.3 Fade/silence-logikk: fiks negativ fade-out start (clamp til 0), og adaptiv silence-margin (margin skal ikke overstige halve regionens varighet). Gi feilmelding ved ugyldige fade-varigheter.
|
||||
- [ ] 17.4 Frontend input-begrensninger: legg til `min`/`max` på alle tallfelter i OperationPanel (silenceThreshold, fadeMs, normTarget, compRatio). Hindre ugyldig input.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue