All checks were successful
Queue Release Build / prepare (push) Successful in 15s
Deploy Web Apps / deploy (push) Successful in 5m35s
Queue Release Build / build-linux (push) Successful in 24m45s
Queue Release Build / build-windows (push) Successful in 13m52s
Queue Release Build / finalize (push) Successful in 23s
130 lines
3.7 KiB
HTML
130 lines
3.7 KiB
HTML
<div class="audio-player-shell">
|
||
<audio
|
||
#audioEl
|
||
[src]="src()"
|
||
preload="metadata"
|
||
(ended)="onPause()"
|
||
(loadedmetadata)="onLoadedMetadata()"
|
||
(pause)="onPause()"
|
||
(play)="onPlay()"
|
||
(timeupdate)="onTimeUpdate()"
|
||
(volumechange)="onVolumeChange()"
|
||
></audio>
|
||
|
||
<div class="audio-top-bar">
|
||
<div class="min-w-0 flex-1">
|
||
<div class="truncate text-sm font-medium text-foreground">{{ filename() }}</div>
|
||
@if (sizeLabel()) {
|
||
<div class="text-xs text-muted-foreground">{{ sizeLabel() }}</div>
|
||
}
|
||
</div>
|
||
<button
|
||
type="button"
|
||
(click)="requestDownload()"
|
||
class="audio-control-btn"
|
||
title="Save to folder"
|
||
aria-label="Save audio to folder"
|
||
>
|
||
<ng-icon
|
||
name="lucideDownload"
|
||
class="w-4 h-4"
|
||
/>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="audio-body">
|
||
<button
|
||
type="button"
|
||
(click)="togglePlayback()"
|
||
class="audio-play-btn"
|
||
[title]="isPlaying() ? 'Pause' : 'Play'"
|
||
[attr.aria-label]="isPlaying() ? 'Pause audio' : 'Play audio'"
|
||
>
|
||
<ng-icon
|
||
[name]="isPlaying() ? 'lucidePause' : 'lucidePlay'"
|
||
class="w-5 h-5"
|
||
/>
|
||
</button>
|
||
|
||
<div class="audio-main">
|
||
<div
|
||
class="audio-waveform-panel"
|
||
[class.expanded]="waveformExpanded()"
|
||
[attr.aria-hidden]="!waveformExpanded()"
|
||
>
|
||
<div class="audio-waveform-shell">
|
||
<div
|
||
#waveformContainer
|
||
class="audio-waveform-container"
|
||
[class.invisible]="waveformLoading() || waveformUnavailable()"
|
||
></div>
|
||
|
||
@if (waveformLoading()) {
|
||
<div class="audio-waveform-overlay text-muted-foreground">Loading waveform...</div>
|
||
} @else if (waveformUnavailable()) {
|
||
<div class="audio-waveform-overlay text-muted-foreground">
|
||
Couldn’t render a waveform preview for this file, but playback still works.
|
||
</div>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<input
|
||
type="range"
|
||
min="0"
|
||
[max]="durationSeconds() || 0"
|
||
[value]="currentTimeSeconds()"
|
||
(input)="onSeek($event)"
|
||
class="seek-slider"
|
||
[style.background]="seekTrackBackground()"
|
||
aria-label="Seek audio"
|
||
/>
|
||
|
||
<div class="audio-controls-row">
|
||
<span class="audio-time-label"> {{ formatTime(currentTimeSeconds()) }} / {{ formatTime(durationSeconds()) }} </span>
|
||
|
||
<div class="audio-actions-group">
|
||
<div class="audio-volume-group">
|
||
<button
|
||
type="button"
|
||
(click)="toggleMute()"
|
||
class="audio-control-btn"
|
||
[title]="isMuted() ? 'Unmute' : 'Mute'"
|
||
[attr.aria-label]="isMuted() ? 'Unmute audio' : 'Mute audio'"
|
||
>
|
||
<ng-icon
|
||
[name]="isMuted() ? 'lucideVolumeX' : 'lucideVolume2'"
|
||
class="w-4 h-4"
|
||
/>
|
||
</button>
|
||
|
||
<input
|
||
type="range"
|
||
min="0"
|
||
max="100"
|
||
[value]="displayVolumePercent()"
|
||
(input)="onVolumeInput($event)"
|
||
class="volume-slider"
|
||
[style.background]="volumeTrackBackground()"
|
||
aria-label="Audio volume"
|
||
/>
|
||
</div>
|
||
|
||
<button
|
||
type="button"
|
||
(click)="toggleWaveform()"
|
||
class="audio-control-btn"
|
||
[title]="waveformExpanded() ? 'Hide waveform' : 'Show waveform'"
|
||
[attr.aria-label]="waveformExpanded() ? 'Hide waveform' : 'Show waveform'"
|
||
>
|
||
<ng-icon
|
||
[name]="waveformToggleIcon()"
|
||
class="w-4 h-4"
|
||
/>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|