Update packages
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
</script>
|
||||
|
||||
<aside
|
||||
class="hidden shrink-0 flex-col border-r border-hairline bg-[image:var(--mf-sidebar-bg)] p-5 transition-[background] duration-500 ease-out lg:flex"
|
||||
class="hidden shrink-0 flex-col border-r border-hairline bg-bg p-5 transition-[background] duration-500 ease-out lg:flex"
|
||||
style="width:var(--mf-sidebar-w)"
|
||||
>
|
||||
<a href="/" class="px-3 pb-3 font-display text-lg font-semibold tracking-tight text-fg">Musify</a>
|
||||
@@ -60,7 +60,7 @@
|
||||
href={link.href}
|
||||
aria-current={active ? 'page' : undefined}
|
||||
class="flex items-center gap-3 rounded-control px-3 py-2 text-sm transition-colors duration-150 {active
|
||||
? 'bg-surface-hover text-fg'
|
||||
? 'bg-surface-2 text-fg'
|
||||
: 'text-fg-2 hover:bg-hover hover:text-fg'}"
|
||||
>
|
||||
<link.icon class="h-4.5 w-4.5 shrink-0" strokeWidth={1.5} />
|
||||
@@ -105,7 +105,7 @@
|
||||
class="h-8.5 w-8.5 shrink-0 overflow-hidden rounded-lg opacity-90"
|
||||
/>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-sm font-medium {active ? 'text-fg' : 'text-fg-2'}">
|
||||
<div class="truncate text-sm {active ? 'text-fg' : 'text-fg-2'}">
|
||||
{playlist.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class="pointer-events-auto flex items-center justify-around rounded-panel border border-hairline bg-[image:var(--mf-bar-bg)] p-1.75 backdrop-blur-xl transition-[background] duration-500 lg:hidden {klass}"
|
||||
class="pointer-events-auto flex items-center justify-around border-t border-hairline bg-[image:var(--mf-bar-bg)] px-4 py-1.75 backdrop-blur-xl transition-[background] duration-500 lg:hidden {klass}"
|
||||
style="padding-bottom:calc(0.4375rem + var(--mf-safe-b))"
|
||||
>
|
||||
{#each links as link (link.href)}
|
||||
|
||||
@@ -25,7 +25,5 @@
|
||||
{#if hasTrack}
|
||||
<PlayerBar />
|
||||
{/if}
|
||||
<div class="px-4 pt-2.5 pb-4 lg:hidden">
|
||||
<TabsBar {user} />
|
||||
</div>
|
||||
<TabsBar {user} />
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{#if queuePanel.open}
|
||||
<aside
|
||||
transition:fly={{ x: 24, duration: 220, easing: cubicOut }}
|
||||
class="hidden shrink-0 flex-col border-l border-hairline bg-[image:var(--mf-panel-bg)] px-4 pt-5 transition-[background] duration-500 sm:flex"
|
||||
class="hidden shrink-0 flex-col border-l border-hairline bg-bg px-4 pt-5 transition-[background] duration-500 sm:flex"
|
||||
style="width:var(--mf-queue-w); padding-bottom:calc(var(--mf-player-h) + 2rem)"
|
||||
>
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
|
||||
{#if player.current.id}
|
||||
<p class="mb-2.75 text-xs font-medium tracking-widest text-muted uppercase">Reproduciendo</p>
|
||||
<p class="mb-2.75 text-xs font-medium tracking-widest text-fg-3 uppercase">Reproduciendo</p>
|
||||
<div class="mb-6 flex items-center gap-3 rounded-xl bg-accent-tint p-2.5">
|
||||
<Cover
|
||||
trackId={player.current.id}
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
<div class="mb-2.75 flex items-center justify-between gap-3">
|
||||
<span
|
||||
class="min-w-0 flex-1 truncate text-xs font-medium tracking-widest text-muted uppercase"
|
||||
class="min-w-0 flex-1 truncate text-xs font-medium tracking-widest text-fg-3 uppercase"
|
||||
>
|
||||
A continuación
|
||||
</span>
|
||||
@@ -64,7 +64,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => player.clearUpcoming()}
|
||||
class="shrink-0 text-xs text-muted transition-colors hover:text-fg"
|
||||
class="shrink-0 text-xs text-fg-3 transition-colors hover:text-fg"
|
||||
>
|
||||
Vaciar
|
||||
</button>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
aria-label="Barra de progreso"
|
||||
class="group relative h-3.5 flex-1 cursor-pointer"
|
||||
>
|
||||
<div class="h-0.875 w-full overflow-hidden rounded-full bg-track">
|
||||
<div class="h-[3.5px] w-full overflow-hidden rounded-full bg-track">
|
||||
<div class="h-full rounded-full bg-accent" style="width:{player.progressPercent}%"></div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
title: player.current.title,
|
||||
artist: player.current.artist,
|
||||
explicit: player.current.explicit,
|
||||
ownerUserId: player.current.ownerUserId
|
||||
ownerUserId: player.current.ownerUserId,
|
||||
duration: player.current.duration
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -42,9 +43,7 @@
|
||||
{#if player.current.explicit}
|
||||
<ExplicitBadge />
|
||||
{/if}
|
||||
<span class="truncate text-sm font-semibold tracking-[-0.01em] text-fg"
|
||||
>{player.current.title}</span
|
||||
>
|
||||
<span class="truncate text-sm text-fg">{player.current.title}</span>
|
||||
</div>
|
||||
<ArtistLink
|
||||
name={player.current.artist || '—'}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
style="width:{size}px;height:{size}px"
|
||||
>
|
||||
{#if imageUrl}
|
||||
<img src={imageUrl} alt="" class="h-full w-full object-cover" />
|
||||
<img src={imageUrl} alt="" class="absolute inset-0 h-full w-full object-cover" />
|
||||
{:else}
|
||||
<div
|
||||
class="pointer-events-none absolute inset-0"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<a
|
||||
href="/u/{ownerUserId}"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
class="inline-block max-w-full truncate align-top hover:text-fg-2 hover:underline {klass}"
|
||||
class="block max-w-full truncate hover:text-fg-2 hover:underline {klass}"
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import LoaderCircle from '@lucide/svelte/icons/loader-circle';
|
||||
|
||||
interface Props {
|
||||
variant?: 'primary' | 'secondary' | 'accent' | 'danger';
|
||||
variant?: 'primary' | 'secondary' | 'secondaryOnGlow' | 'accent' | 'danger';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
type?: 'button' | 'submit';
|
||||
href?: string;
|
||||
@@ -34,11 +34,15 @@
|
||||
lg: 'px-6 py-3.5 text-sm'
|
||||
};
|
||||
|
||||
const subtleButton = 'bg-surface-2 text-fg-2 hover:bg-surface-hover hover:text-fg';
|
||||
const subtleButton =
|
||||
'bg-[var(--mf-btn-bg)] text-fg-2 hover:bg-[var(--mf-btn-bg-hover)] hover:text-fg';
|
||||
const subtleButtonOnGlow =
|
||||
'bg-[var(--mf-btn-secondary-bg)] text-fg-2 hover:bg-[var(--mf-btn-secondary-bg-hover)] hover:text-fg';
|
||||
|
||||
const variants = {
|
||||
primary: subtleButton,
|
||||
secondary: subtleButton,
|
||||
secondaryOnGlow: subtleButtonOnGlow,
|
||||
accent: 'bg-accent-btn text-accent-soft hover:bg-accent-btn-hover',
|
||||
danger: 'bg-danger text-on-accent hover:brightness-110'
|
||||
};
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
>
|
||||
{@render cover()}
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-xs font-semibold tracking-[0.16em] text-fg-2 uppercase">{eyebrow}</p>
|
||||
<p class="text-xs font-medium tracking-widest text-fg-2 uppercase">{eyebrow}</p>
|
||||
<h1
|
||||
class="{align === 'end'
|
||||
? 'mt-1.5'
|
||||
: 'mt-3'} font-display text-3xl font-semibold tracking-[-0.035em] break-words text-fg sm:text-4xl"
|
||||
: 'mt-3'} font-display text-3xl font-medium tracking-tight wrap-break-word text-fg sm:text-4xl"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
>
|
||||
<div
|
||||
bind:this={panel}
|
||||
class="animate-pop max-h-[85dvh] w-full overflow-y-auto {maxWidth} rounded-panel border border-hairline p-5 backdrop-blur-xl transition-[background] duration-500 outline-none sm:p-6 {panelClass}"
|
||||
class="animate-pop max-h-[85dvh] w-full overflow-y-auto {maxWidth} rounded-panel p-5 backdrop-blur-xl transition-[background] duration-500 outline-none sm:p-6 {panelClass}"
|
||||
style="background-color:var(--mf-panel-bg); background-image:var(--mf-modal-glow)"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
@@ -87,11 +87,11 @@
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
{#if eyebrow}
|
||||
<p class="mb-2.5 text-xs font-semibold tracking-widest text-muted uppercase">
|
||||
<p class="mb-2.5 text-xs font-medium tracking-widest text-muted uppercase">
|
||||
{eyebrow}
|
||||
</p>
|
||||
{/if}
|
||||
<h2 class="font-display text-xl font-semibold tracking-[-0.02em] text-fg">
|
||||
<h2 class="font-display text-xl font-medium tracking-[-0.02em] text-fg">
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between {klass}">
|
||||
<div class="min-w-0">
|
||||
<h1 class="font-display text-3xl font-semibold tracking-[-0.03em] text-fg sm:text-4xl">
|
||||
<h1 class="font-display text-3xl font-medium tracking-tight text-fg sm:text-4xl">
|
||||
{title}
|
||||
</h1>
|
||||
{#if subtitle}
|
||||
|
||||
@@ -51,5 +51,11 @@
|
||||
{:else if single !== null}
|
||||
<Cover trackId={single} size="medium" class={klass} />
|
||||
{:else}
|
||||
<div class="bg-surface {klass}"></div>
|
||||
<div class="flex items-center justify-center bg-surface {klass}">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" class="h-2/5 w-2/5 text-fg-3">
|
||||
<path
|
||||
d="M19.952 1.651a.75.75 0 0 1 .298.599V16.303a3 3 0 0 1-2.176 2.884l-1.32.377a2.553 2.553 0 1 1-1.401-4.909l2.311-.66a1.5 1.5 0 0 0 1.088-1.442V6.994l-9 2.572v9.737a3 3 0 0 1-2.176 2.884l-1.32.377a2.553 2.553 0 1 1-1.402-4.909l2.312-.66a1.5 1.5 0 0 0 1.088-1.442V5.25a.75.75 0 0 1 .544-.721l10.5-3a.75.75 0 0 1 .754.122Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4">
|
||||
<div class="flex flex-1 flex-col gap-2">
|
||||
<Field label="Nombre" for="playlist-name">
|
||||
{#snippet hint()}
|
||||
<span class="ml-auto tabular-nums">{name.length}/{NAME_MAX_LENGTH}</span>
|
||||
@@ -121,7 +121,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (visibility = 'private')}
|
||||
class="relative z-10 w-20 rounded-full py-1.5 text-center text-xs font-semibold transition-colors {visibility ===
|
||||
class="relative z-10 w-20 rounded-full py-1.5 text-center text-xs font-medium transition-colors {visibility ===
|
||||
'private'
|
||||
? 'text-ink'
|
||||
: 'text-muted hover:text-fg-2'}"
|
||||
@@ -131,7 +131,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (visibility = 'public')}
|
||||
class="relative z-10 w-20 rounded-full py-1.5 text-center text-xs font-semibold transition-colors {visibility ===
|
||||
class="relative z-10 w-20 rounded-full py-1.5 text-center text-xs font-medium transition-colors {visibility ===
|
||||
'public'
|
||||
? 'text-ink'
|
||||
: 'text-muted hover:text-fg-2'}"
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
>
|
||||
{@render overlay?.()}
|
||||
</Cover>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="min-w-0">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
{#if explicit}
|
||||
<ExplicitBadge />
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface LikedTrack {
|
||||
explicit?: boolean;
|
||||
ownerUserId?: string | number | null;
|
||||
listensCount?: number | string;
|
||||
duration?: number | string;
|
||||
likedAt: number;
|
||||
}
|
||||
|
||||
@@ -16,6 +17,7 @@ type LikeToggleInput = {
|
||||
artist?: string;
|
||||
explicit?: boolean;
|
||||
ownerUserId?: string | number | null;
|
||||
duration?: number | string;
|
||||
};
|
||||
|
||||
class LikedStore {
|
||||
@@ -25,7 +27,7 @@ class LikedStore {
|
||||
count = $derived(Object.keys(this.entries).length);
|
||||
list = $derived(Object.values(this.entries).sort((a, b) => b.likedAt - a.likedAt));
|
||||
|
||||
hydrate(tracks: (ApiTrackLike & { createdAt: string })[]) {
|
||||
hydrate(tracks: (ApiTrackLike & { createdAt: string; duration: number | string })[]) {
|
||||
if (this.hydrated) return;
|
||||
this.hydrated = true;
|
||||
|
||||
@@ -39,6 +41,7 @@ class LikedStore {
|
||||
explicit: item.explicit,
|
||||
ownerUserId: item.ownerUserId,
|
||||
listensCount: item.listensCount,
|
||||
duration: tracks[i].duration,
|
||||
likedAt: new Date(tracks[i].createdAt).getTime()
|
||||
};
|
||||
});
|
||||
@@ -62,6 +65,7 @@ class LikedStore {
|
||||
artist: track.artist,
|
||||
explicit: track.explicit,
|
||||
ownerUserId: track.ownerUserId,
|
||||
duration: track.duration,
|
||||
likedAt: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
--mf-cover-grad: linear-gradient(150deg, oklch(0.5 0.13 145), oklch(0.16 0.035 145));
|
||||
--mf-liked-grad: linear-gradient(150deg, oklch(0.4 0.09 330), oklch(0.18 0.04 10));
|
||||
--mf-tile-l: 0.34;
|
||||
--mf-tile-c: 0.13;
|
||||
--mf-tile-c: 0.1;
|
||||
--mf-tile-shade-l: 0.14;
|
||||
--mf-tile-shade-c: 0.05;
|
||||
--mf-tile-shade-c: 0.035;
|
||||
|
||||
--mf-accent: oklch(0.74 0.145 145);
|
||||
--mf-accent-title: oklch(0.87 0.07 145);
|
||||
@@ -60,6 +60,10 @@
|
||||
--mf-surface: rgba(255, 255, 255, 0.022);
|
||||
--mf-surface-2: rgba(255, 255, 255, 0.045);
|
||||
--mf-surface-hover: rgba(255, 255, 255, 0.06);
|
||||
--mf-btn-bg: var(--mf-surface-2);
|
||||
--mf-btn-bg-hover: var(--mf-surface-hover);
|
||||
--mf-btn-secondary-bg: rgba(255, 255, 255, 0.045);
|
||||
--mf-btn-secondary-bg-hover: rgba(255, 255, 255, 0.07);
|
||||
|
||||
--mf-border: rgba(255, 255, 255, 0.04);
|
||||
--mf-border-strong: rgba(255, 255, 255, 0.09);
|
||||
@@ -106,7 +110,7 @@
|
||||
:root[data-theme='light'] {
|
||||
--mf-bg: oklch(0.97 0.006 90);
|
||||
--mf-elevated: oklch(0.99 0.003 90);
|
||||
--mf-sidebar-bg: linear-gradient(180deg, oklch(0.985 0.004 90), oklch(0.965 0.006 90));
|
||||
--mf-sidebar-bg: oklch(0.97 0.006 90); /*linear-gradient(180deg, oklch(0.985 0.004 90), oklch(0.965 0.006 90));*/
|
||||
--mf-panel-bg: oklch(0.97 0.006 90 / 0.7);
|
||||
--mf-bar-bg: oklch(0.97 0.006 90 / 0.9);
|
||||
--mf-hairline: rgba(0, 0, 0, 0.08);
|
||||
@@ -118,7 +122,11 @@
|
||||
|
||||
--mf-surface: rgba(0, 0, 0, 0.035);
|
||||
--mf-surface-2: rgba(0, 0, 0, 0.06);
|
||||
--mf-surface-hover: rgba(0, 0, 0, 0.08);
|
||||
--mf-surface-hover: #e8eceb;
|
||||
--mf-btn-bg: var(--mf-surface-2);
|
||||
--mf-btn-bg-hover: var(--mf-surface-hover);
|
||||
--mf-btn-secondary-bg: rgba(0, 0, 0, 0.09);
|
||||
--mf-btn-secondary-bg-hover: rgba(0, 0, 0, 0.14);
|
||||
|
||||
--mf-border: rgba(0, 0, 0, 0.08);
|
||||
--mf-border-strong: rgba(0, 0, 0, 0.14);
|
||||
@@ -129,31 +137,23 @@
|
||||
--mf-cta-strong-bg: #17171a;
|
||||
|
||||
--mf-tile-l: 0.58;
|
||||
--mf-tile-c: 0.15;
|
||||
--mf-tile-c: 0.085;
|
||||
--mf-tile-shade-l: 0.42;
|
||||
--mf-tile-shade-c: 0.12;
|
||||
--mf-tile-shade-c: 0.05;
|
||||
--mf-liked-grad: linear-gradient(150deg, oklch(0.62 0.12 330), oklch(0.46 0.08 10));
|
||||
|
||||
--mf-ambient: radial-gradient(46% 34% at 78% 0%, oklch(0.7476 0.121 145 / 0.2), transparent 56%);
|
||||
--mf-hero-bg: linear-gradient(
|
||||
105deg,
|
||||
oklch(0.7531 0.1172 145 / 0.34) 0%,
|
||||
oklch(0.8303 0.0641 145 / 0.11) 37%,
|
||||
oklch(0.88 0.03 145 / 0.03) 65%,
|
||||
rgba(255, 255, 255, 0) 92%
|
||||
);
|
||||
--mf-ambient: none;
|
||||
--mf-hero-bg: linear-gradient(oklch(0.789 0.0926 145 / 0.16), oklch(0.789 0.0926 145 / 0.16));
|
||||
--mf-spotlight-bg: linear-gradient(
|
||||
100deg,
|
||||
oklch(0.7641 0.1097 145 / 0.28) 0%,
|
||||
oklch(0.8414 0.0566 145 / 0.1) 40%,
|
||||
oklch(0.88 0.03 145 / 0.03) 69%,
|
||||
rgba(255, 255, 255, 0) 96%
|
||||
oklch(0.789 0.0926 145 / 0.16),
|
||||
oklch(0.789 0.0926 145 / 0.16)
|
||||
);
|
||||
--mf-modal-glow: linear-gradient(
|
||||
135deg,
|
||||
oklch(0.7531 0.1172 145 / 0.34) 0%,
|
||||
oklch(0.8303 0.0641 145 / 0.11) 28%,
|
||||
oklch(0.88 0.03 145 / 0.03) 50%,
|
||||
rgba(255, 255, 255, 0) 70%
|
||||
oklch(0.789 0.0926 145 / 0.2) 30%,
|
||||
oklch(0.8303 0.0641 145 / 0.1) 55%,
|
||||
oklch(0.8717 0.0357 145 / 0.04) 80%,
|
||||
oklch(0.88 0.03 145 / 0.02) 100%
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,16 +74,16 @@ export function buildThemeTokens(hue: number, mode: 'dark' | 'light' = 'dark'):
|
||||
'--mf-bar-bg': light ? `oklch(0.97 0.006 ${H} / 0.9)` : `oklch(0.105 0.012 ${H} / 0.9)`,
|
||||
'--mf-hairline': light ? `oklch(0.3 0.02 ${H} / 0.1)` : `oklch(0.62 0.03 ${H} / 0.08)`,
|
||||
'--mf-ambient': light
|
||||
? `radial-gradient(46% 34% at 78% 0%, ${tcLight(0.2, 40, h)}, transparent 56%)`
|
||||
? 'none'
|
||||
: `radial-gradient(52% 38% at 14% 0%, ${tc(0.26, 30, h)}, transparent 54%), radial-gradient(46% 34% at 78% 0%, ${tc(0.2, 40, h)}, transparent 56%)`,
|
||||
'--mf-hero-bg': light
|
||||
? `linear-gradient(105deg, ${tcLight(0.34, 42, h)} 0%, ${tcLight(0.11, 70, h)} 37%, ${tcLight(0.03, 88, h)} 65%, rgba(255,255,255,0) 92%)`
|
||||
? `linear-gradient(${tcLight(0.16, 55, h)}, ${tcLight(0.16, 55, h)})`
|
||||
: `linear-gradient(105deg, ${tc(0.34, 42, h)} 0%, ${tc(0.11, 70, h)} 37%, ${tc(0.03, 88, h)} 65%, rgba(6,6,9,0) 92%)`,
|
||||
'--mf-modal-glow': light
|
||||
? `linear-gradient(135deg, ${tcLight(0.34, 42, h)} 0%, ${tcLight(0.11, 70, h)} 28%, ${tcLight(0.03, 88, h)} 50%, rgba(255,255,255,0) 70%)`
|
||||
? `linear-gradient(135deg, ${tcLight(0.34, 42, h)} 0%, ${tcLight(0.2, 55, h)} 30%, ${tcLight(0.1, 70, h)} 55%, ${tcLight(0.04, 85, h)} 80%, ${tcLight(0.02, 88, h)} 100%)`
|
||||
: `linear-gradient(135deg, ${tc(0.34, 42, h)} 0%, ${tc(0.11, 70, h)} 28%, ${tc(0.03, 88, h)} 50%, rgba(6,6,9,0) 70%)`,
|
||||
'--mf-spotlight-bg': light
|
||||
? `linear-gradient(100deg, ${tcLight(0.28, 46, h)} 0%, ${tcLight(0.1, 74, h)} 40%, ${tcLight(0.03, 88, h)} 69%, rgba(255,255,255,0) 96%)`
|
||||
? `linear-gradient(${tcLight(0.16, 55, h)}, ${tcLight(0.16, 55, h)})`
|
||||
: `linear-gradient(100deg, ${tc(0.28, 46, h)} 0%, ${tc(0.1, 74, h)} 40%, ${tc(0.03, 88, h)} 69%, rgba(6,6,9,0) 96%)`,
|
||||
'--mf-logo-grad': light
|
||||
? `linear-gradient(140deg, oklch(0.68 0.16 ${H}), oklch(0.5 0.14 ${H2}) 92%)`
|
||||
|
||||
@@ -412,11 +412,11 @@
|
||||
{#each genreTiles as genre, i (genre.query)}
|
||||
<a
|
||||
href={buildHref(genre.query)}
|
||||
class="animate-enter group relative flex h-28 flex-col gap-1 overflow-hidden rounded-2xl p-4.25 transition-[filter] hover:brightness-125"
|
||||
class="animate-enter group relative flex h-28 flex-col gap-1 overflow-hidden rounded-2xl p-4.25 transition-[filter] hover:brightness-110"
|
||||
style="animation-delay:{Math.min(i, 10) *
|
||||
40}ms; background:linear-gradient(140deg, oklch(var(--mf-tile-l) var(--mf-tile-c) {genre.hue}), oklch(var(--mf-tile-shade-l) var(--mf-tile-shade-c) {genre.hue}))"
|
||||
>
|
||||
<div class="font-display text-base font-medium tracking-tight text-on-art">
|
||||
<div class="text-on-art">
|
||||
{genre.label}
|
||||
</div>
|
||||
<div class="text-xs text-on-art-2">{genre.tagline}</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import X from '@lucide/svelte/icons/x';
|
||||
import { player, type QueueItem } from '$lib/player/player.svelte';
|
||||
import { liked } from '$lib/player/liked.svelte';
|
||||
import { fmtDate } from '$lib/format';
|
||||
import { fmtDate, fmtTime } from '$lib/format';
|
||||
import Page from '$lib/components/ui/Page.svelte';
|
||||
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
@@ -48,7 +48,7 @@
|
||||
<CollectionHeader
|
||||
eyebrow="Colección"
|
||||
title="Me gusta"
|
||||
meta="{tracks.length} {tracks.length === 1 ? 'canción' : 'canciones'}"
|
||||
meta="{tracks.length} {tracks.length === 1 ? 'Canción' : 'Canciones'}"
|
||||
align="center"
|
||||
>
|
||||
{#snippet cover()}
|
||||
@@ -60,13 +60,7 @@
|
||||
{/snippet}
|
||||
{#snippet actions()}
|
||||
<Button size="sm" onclick={playAll} disabled={tracks.length === 0}>
|
||||
{#if isCurrentQueue && player.playing}
|
||||
<Pause class="h-4 w-4" strokeWidth={1.5} />
|
||||
Pausar
|
||||
{:else}
|
||||
<Play class="h-4 w-4" strokeWidth={1.5} />
|
||||
Reproducir
|
||||
{/if}
|
||||
{isCurrentQueue && player.playing ? 'Pausar' : 'Reproducir'}
|
||||
</Button>
|
||||
{/snippet}
|
||||
</CollectionHeader>
|
||||
@@ -103,7 +97,7 @@
|
||||
<TrackMeta class="hidden sm:block">—</TrackMeta>
|
||||
<TrackMeta class="hidden sm:block">{fmtDate(track.likedAt)}</TrackMeta>
|
||||
<TrackMeta class="hidden sm:block">{Number(track.listensCount ?? 0)}</TrackMeta>
|
||||
<TrackMeta>—</TrackMeta>
|
||||
<TrackMeta>{fmtTime(Number(track.duration))}</TrackMeta>
|
||||
<IconButton
|
||||
label="Quitar de Me gusta"
|
||||
size="sm"
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
>
|
||||
{#snippet actions()}
|
||||
{#if items.length > 0}
|
||||
<Button onclick={() => createPlaylistModal.show()}>Crear lista</Button>
|
||||
<Button onclick={() => createPlaylistModal.show()}>Crear nueva lista</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</PageHeader>
|
||||
@@ -55,7 +55,7 @@
|
||||
description="Crea la primera y añádele canciones de tu biblioteca."
|
||||
>
|
||||
{#snippet actions()}
|
||||
<Button onclick={() => createPlaylistModal.show()}>Crear lista</Button>
|
||||
<Button onclick={() => createPlaylistModal.show()}>Crear nueva lista</Button>
|
||||
{/snippet}
|
||||
</EmptyState>
|
||||
{/if}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import Button from '$lib/components/ui/Button.svelte';
|
||||
import ArtistAvatar from '$lib/components/ui/ArtistAvatar.svelte';
|
||||
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
|
||||
import SectionHeading from '$lib/components/ui/SectionHeading.svelte';
|
||||
|
||||
let { data, form } = $props();
|
||||
|
||||
@@ -69,9 +70,7 @@
|
||||
</CollectionHeader>
|
||||
|
||||
<div class="mt-9">
|
||||
<h2 class="font-display text-lg font-semibold tracking-[-0.02em] text-fg">
|
||||
Playlists públicas
|
||||
</h2>
|
||||
<SectionHeading title="Playlists públicas" subtitle="Creadas y compartidas por este usuario" />
|
||||
|
||||
{#if playlists.length > 0}
|
||||
<MediaGrid min="210px" minMobile="180px" class="mt-6">
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
<Surface padding="sm" class={stepClass(stepStates[i])}>
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
class="grid h-7 w-7 shrink-0 place-items-center rounded-full text-xs font-bold {stepStates[
|
||||
class="grid h-7 w-7 shrink-0 place-items-center rounded-full text-sm {stepStates[
|
||||
i
|
||||
] === 'idle'
|
||||
? 'bg-surface-2 text-fg-2'
|
||||
@@ -228,7 +228,7 @@
|
||||
{#if coverPreview !== ''}
|
||||
<img src={coverPreview} alt="Portada" class="h-full w-full object-cover" />
|
||||
{:else}
|
||||
<ImageIcon class="h-11 w-11 text-on-art/70" strokeWidth={1.3} />
|
||||
<ImageIcon class="h-16 w-16 text-on-art/60" strokeWidth={1} />
|
||||
{/if}
|
||||
</label>
|
||||
</Field>
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
<Modal
|
||||
open={createPlaylistModal.open}
|
||||
onClose={() => createPlaylistModal.close()}
|
||||
title="Crear playlist"
|
||||
title="Crear nueva playlist"
|
||||
>
|
||||
<PlaylistForm
|
||||
action="/playlists?/create"
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
spotlightTrackCount === 0
|
||||
? spotlightVisibilityLabel
|
||||
: [
|
||||
`${spotlightTrackCount} ${spotlightTrackCount === 1 ? 'canción' : 'canciones'}`,
|
||||
`${spotlightTrackCount} ${spotlightTrackCount === 1 ? 'Canción' : 'Canciones'}`,
|
||||
fmtDurationLong(spotlightDurationSeconds),
|
||||
spotlightVisibilityLabel
|
||||
].join(' · ')
|
||||
@@ -159,7 +159,9 @@
|
||||
{spotlight.name}
|
||||
</Button>
|
||||
{/if}
|
||||
<Button href="/explore" variant="secondary" class="!font-normal">Explorar música</Button>
|
||||
<Button href="/explore" variant="secondaryOnGlow" class="!font-normal"
|
||||
>Explorar música</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -244,7 +246,7 @@
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class="flex w-fit items-center gap-6 self-start"
|
||||
class="flex w-fit items-center gap-5 self-start sm:gap-6"
|
||||
onclick={() => goto(`/playlists/${spotlight.id}`)}
|
||||
onkeydown={(e) => onRowKeydown(e, () => goto(`/playlists/${spotlight.id}`))}
|
||||
>
|
||||
@@ -255,7 +257,7 @@
|
||||
size="large"
|
||||
class="h-34 w-34 shrink-0 rounded-2xl"
|
||||
/>
|
||||
<div class="flex min-w-0 flex-col gap-4">
|
||||
<div class="flex min-w-0 flex-col gap-3">
|
||||
<p class="text-xs font-medium tracking-widest text-fg-2 uppercase">
|
||||
Playlist destacada
|
||||
</p>
|
||||
@@ -270,7 +272,7 @@
|
||||
</p>
|
||||
<div class="mt-5 flex gap-2.5">
|
||||
<Button variant="accent" onclick={playSpotlight}>Reproducir</Button>
|
||||
<Button variant="secondary" onclick={shuffleSpotlight}>Aleatorio</Button>
|
||||
<Button variant="secondaryOnGlow" onclick={shuffleSpotlight}>Aleatorio</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-0.5 p-5.5 sm:p-6">
|
||||
@@ -284,13 +286,15 @@
|
||||
e.preventDefault();
|
||||
playSpotlightTrack(i);
|
||||
}}
|
||||
class="flex items-center gap-3 rounded-control p-2 text-left transition-colors hover:bg-hover {isTargetCurrent(
|
||||
class="flex items-center gap-4 rounded-control p-2 text-left transition-colors hover:bg-hover {isTargetCurrent(
|
||||
target
|
||||
)
|
||||
? 'bg-accent-tint'
|
||||
: ''}"
|
||||
>
|
||||
<span class="w-5 shrink-0 text-center text-xs text-muted tabular-nums">{i + 1}</span>
|
||||
<span class="w-5 shrink-0 text-center text-xs text-muted tabular-nums"
|
||||
>{i + 1}</span
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div
|
||||
class="truncate text-sm {isTargetCurrent(target)
|
||||
@@ -305,7 +309,9 @@
|
||||
class="mt-0.5 text-xs text-fg-3"
|
||||
/>
|
||||
</div>
|
||||
<span class="shrink-0 text-xs text-muted tabular-nums">{fmtTime(seconds)}</span>
|
||||
<span class="shrink-0 text-xs text-muted tabular-nums"
|
||||
>{fmtTime(seconds)}</span
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="p-2 text-sm text-fg-3">Esta playlist todavía no tiene canciones.</p>
|
||||
@@ -421,7 +427,7 @@
|
||||
class="h-15.5 w-15.5 shrink-0 rounded-control"
|
||||
/>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-sm font-medium tracking-tight text-fg">
|
||||
<div class="truncate text-sm tracking-tight text-fg">
|
||||
{playlist.name}
|
||||
</div>
|
||||
{#if playlist.description}
|
||||
|
||||
Reference in New Issue
Block a user