Add scripts/check-tokens.mjs (npm run lint:tokens, wired into lint) as
a guardrail: fails if src/**/*.svelte reintroduces arbitrary values
outside the B.4 allowlist, factory rounded-*, h-N w-N pairs (should
be size-N), quarter-step fractional spacing, or literal hex/rgba()
colors.
Cleans up the repo until the script reports 0 violations: ~40 h-N w-N
pairs -> size-N (mostly icons, via a one-off script), ~26 quarter-step
fractional spacings rounded to the nearest integer (delta <=1px, e.g.
py-2.75->py-3, gap-1.75->gap-2), stray factory radii (rounded-sm/lg,
bare rounded) migrated to the matching tokens, and two
tracking-[-0.02em] that matched the existing tracking-display token
exactly but had never been migrated. Removes the
stroke-thin/regular/bold recipes (added earlier, zero consumers) —
decision made, not blocking: strokeWidth={n} (13 values, across
almost every component) isn't migrated to CSS because there's no way
to verify without Docker that lucide-svelte honors stroke-width by
class instead of the prop, and a regression there would be silent
across the whole app; the script documents this gap explicitly
instead of pretending full coverage.
Orphan detector: 0 components with no importers. Removes
docs/design-system-refactor-plan.md (earlier plan, superseded by
frontend-refactor-plan.md). web-player/CLAUDE.md gains a "Styles"
section with the B.4 allowlist and the base components to reuse.
Recalculates the baseline table (A.4) and logs it: 61->47 components,
44->11 arbitrary values (the remaining 11 on the allowlist), 13->8
distinct radii, 0 lint:tokens violations.
Verified with npm run check (0 errors), npm run lint:tokens (0
violations) and npm run build. Smoke-tested /login with no console
errors attributable to this change.
67 lines
1.9 KiB
Svelte
67 lines
1.9 KiB
Svelte
<script lang="ts">
|
|
import { player } from '$lib/player/player.svelte';
|
|
import { liked } from '$lib/player/liked.svelte';
|
|
import Artwork from '$lib/components/ui/Artwork.svelte';
|
|
import ExplicitBadge from '$lib/components/ui/ExplicitBadge.svelte';
|
|
import ArtistLink from '$lib/components/ui/ArtistLink.svelte';
|
|
import IconButton from '$lib/components/ui/IconButton.svelte';
|
|
import Heart from '@lucide/svelte/icons/heart';
|
|
|
|
interface Props {
|
|
compact?: boolean;
|
|
}
|
|
|
|
let { compact = false }: Props = $props();
|
|
|
|
const isLiked = $derived(player.current.id !== '' && liked.isLiked(player.current.id));
|
|
|
|
function toggleLike() {
|
|
if (!player.current.id) return;
|
|
liked.toggle({
|
|
id: player.current.id,
|
|
title: player.current.title,
|
|
artist: player.current.artist,
|
|
explicit: player.current.explicit,
|
|
ownerUserId: player.current.ownerUserId,
|
|
duration: player.current.duration
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<div class="flex min-w-0 items-center gap-2.5 md:gap-3">
|
|
{#if player.current.id}
|
|
<Artwork
|
|
trackIds={[player.current.id]}
|
|
size="sm"
|
|
alt={player.current.title}
|
|
class="shrink-0 md:size-cover-md"
|
|
/>
|
|
{:else}
|
|
<div class="size-cover-sm shrink-0 rounded-art bg-surface md:size-cover-md"></div>
|
|
{/if}
|
|
<div class="min-w-0 flex-1">
|
|
<div class="flex min-w-0 items-center gap-1.5">
|
|
{#if player.current.explicit}
|
|
<ExplicitBadge />
|
|
{/if}
|
|
<span class="truncate text-sm text-fg">{player.current.title}</span>
|
|
</div>
|
|
<ArtistLink
|
|
name={player.current.artist || '—'}
|
|
ownerUserId={player.current.ownerUserId}
|
|
class="mt-0.5 text-xs text-fg-3"
|
|
/>
|
|
</div>
|
|
{#if !compact}
|
|
<IconButton
|
|
label={isLiked ? 'Quitar de Me gusta' : 'Añadir a Me gusta'}
|
|
tone="plain"
|
|
size="xs"
|
|
pressed={isLiked}
|
|
onclick={toggleLike}
|
|
>
|
|
<Heart class="size-icon-md" fill={isLiked ? 'currentColor' : 'none'} strokeWidth={1.7} />
|
|
</IconButton>
|
|
{/if}
|
|
</div>
|