forked from Keelrleek/SoulStream
mini player + dark glass: eight shapes run by the hidden main as the one brain, dissolving covers and accents, drag from anywhere, and the space between the panes turned to real acrylic - with the webview flags that keep rest frames honest
This commit is contained in:
1 parent
9b84b30aef
commit
dc7ea16b22
33 files changed
+2432
-5
No files matched your search
@@ -0,0 +1,229 @@
|
||||
<script>
|
||||
// V8 · The Queue card — the Rdio shape in our clothes: a header player
|
||||
// over the live queue, the current row wearing the app's stepped
|
||||
// equalizer (height keyframes, never transform — the corner saga).
|
||||
import Icon from "../../components/Icon.svelte";
|
||||
import Wash from "../bits/Wash.svelte";
|
||||
import Art from "../bits/Art.svelte";
|
||||
import Lane from "../bits/Lane.svelte";
|
||||
import VolumeFly from "../bits/VolumeFly.svelte";
|
||||
import Chrome from "../bits/Chrome.svelte";
|
||||
import { mini, playing, cmd, fmt } from "../mini.svelte.js";
|
||||
|
||||
let artEl = $state(null);
|
||||
</script>
|
||||
|
||||
<div class="shape">
|
||||
<Wash artUrl={mini.artUrl} fade="head" />
|
||||
<Chrome anchor={artEl} />
|
||||
<div class="head">
|
||||
<div class="art" bind:this={artEl}>
|
||||
<Art url={mini.artUrl} />
|
||||
</div>
|
||||
<div class="hr">
|
||||
<div class="toprow">
|
||||
<div class="name">
|
||||
<b>{mini.title ?? "Nothing playing"}</b>{mini.artist ? ` — ${mini.artist}` : ""}
|
||||
</div>
|
||||
<button class="m-mode" class:on={mini.shuffle} title="Shuffle" onclick={() => cmd("shuffle")}><Icon name="shuffle" size={15} /></button>
|
||||
<button class="m-mode" class:on={mini.repeat !== "off"} title="Repeat" onclick={() => cmd("repeat")}><Icon name={mini.repeat === "one" ? "repeat-one" : "repeat"} size={15} /></button>
|
||||
<VolumeFly />
|
||||
</div>
|
||||
<div class="ctlrow">
|
||||
<button class="m-skip" title="Previous" onclick={() => cmd("prev")}><Icon name="skip-back" size={16} /></button>
|
||||
<button class="m-disc small" title="Play/Pause" onclick={() => cmd("toggle")}><Icon name={playing() ? "pause" : "play"} size={13} /></button>
|
||||
<button class="m-skip" title="Next" onclick={() => cmd("next")}><Icon name="skip-forward" size={16} /></button>
|
||||
<Lane slim />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="queue">
|
||||
<div class="qhead"><span>Queue</span><span>{mini.rows.length} tracks</span></div>
|
||||
<div class="qlist">
|
||||
{#each mini.rows as row, i (i)}
|
||||
<button class="qrow" class:now={i === mini.index} onclick={() => cmd("jump", i)}>
|
||||
{#if i === mini.index}
|
||||
<span class="eq" class:active={playing()}><b></b><b></b><b></b></span>
|
||||
{:else}
|
||||
<span class="n">{i + 1}</span>
|
||||
{/if}
|
||||
<span class="qt">{row.title}</span>
|
||||
<span class="qd">{row.dur != null ? fmt(row.dur) : ""}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.shape {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: var(--bg-player);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.head {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
gap: 11px;
|
||||
padding: 11px 12px 9px;
|
||||
flex: none;
|
||||
}
|
||||
.art {
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
flex: none;
|
||||
background: var(--bg-highlight);
|
||||
}
|
||||
.hr {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
.toprow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
min-width: 0;
|
||||
}
|
||||
.name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.name b {
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.ctlrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.ctlrow :global(.lane) {
|
||||
flex: 1;
|
||||
}
|
||||
.queue {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.07);
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.qhead {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 12px 4px;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
flex: none;
|
||||
}
|
||||
.qlist {
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
|
||||
}
|
||||
.qrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 0 12px;
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-align: left;
|
||||
}
|
||||
.qrow:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
.n {
|
||||
width: 16px;
|
||||
flex: none;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.qt {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.qd {
|
||||
flex: none;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.qrow.now {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
.qrow.now .qd {
|
||||
color: var(--accent);
|
||||
}
|
||||
/* the stepped equalizer — HEIGHT keyframes, never transform */
|
||||
.eq {
|
||||
display: inline-flex;
|
||||
align-items: flex-end;
|
||||
gap: 1.5px;
|
||||
width: 16px;
|
||||
height: 11px;
|
||||
flex: none;
|
||||
justify-content: center;
|
||||
}
|
||||
.eq b {
|
||||
width: 2.5px;
|
||||
background: var(--accent);
|
||||
border-radius: 1px;
|
||||
}
|
||||
.eq.active b {
|
||||
animation: eq 1.2s steps(1, end) infinite;
|
||||
}
|
||||
.eq b:nth-child(1) {
|
||||
height: 40%;
|
||||
--eq-hi: 40%;
|
||||
--eq-lo: 16%;
|
||||
animation-delay: -0.2s;
|
||||
}
|
||||
.eq b:nth-child(2) {
|
||||
height: 90%;
|
||||
--eq-hi: 90%;
|
||||
--eq-lo: 36%;
|
||||
}
|
||||
.eq b:nth-child(3) {
|
||||
height: 60%;
|
||||
--eq-hi: 60%;
|
||||
--eq-lo: 24%;
|
||||
animation-delay: -0.4s;
|
||||
}
|
||||
@keyframes eq {
|
||||
0%,
|
||||
100% {
|
||||
height: var(--eq-lo);
|
||||
}
|
||||
50% {
|
||||
height: var(--eq-hi);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user