chore(format): run prettier over src/ and scripts/

Formatting was configured but never enforced: `bun run format:check`
reported 199 unformatted files and ran in no workflow and in no git hook,
so .prettierrc (printWidth 100, trailing commas) described an intention
rather than the tree.

This is the one-time sweep that makes the check gateable. Whitespace and
token-reflow only -- no behavioural change: `bun run check` reports 0
errors and all 1053 frontend tests pass before and after.

Kept out of every other commit on purpose. A 199-file diff mixed with
real changes is unreviewable, and the next commit turns format:check
into a hard CI gate so this cannot silently accumulate again.
This commit is contained in:
2026-08-21 17:41:44 +02:00
parent d095e1f410
commit ad48d89dfe
199 changed files with 4698 additions and 3453 deletions
@@ -19,13 +19,15 @@
let { isOpen = false, onClose, onSelectSession }: Props = $props();
// MACs currently part of any sync group, for rendering toggle state.
const groupedMacs = $derived(new Set(($lmsSync.groups ?? []).flatMap(
(g) => [g.masterMac, ...(g.slaveMacs ?? [])]
)));
const groupedMacs = $derived(
new Set(($lmsSync.groups ?? []).flatMap((g) => [g.masterMac, ...(g.slaveMacs ?? [])])),
);
// The sync master is the LMS zone we're currently controlling. Zones can only
// be fused once we have a master to fuse them into.
const masterMac = $derived(isLmsSession($selectedSession) ? macForSession($selectedSession) : null);
const masterMac = $derived(
isLmsSession($selectedSession) ? macForSession($selectedSession) : null,
);
function isZoneFused(session: Session): boolean {
const mac = macForSession(session);
@@ -111,11 +113,23 @@
function getSessionIcon(client: string | null | undefined): string {
const clientLower = (client ?? "").toLowerCase();
if (clientLower.includes("tv") || clientLower.includes("roku") || clientLower.includes("android tv")) {
if (
clientLower.includes("tv") ||
clientLower.includes("roku") ||
clientLower.includes("android tv")
) {
return "tv";
} else if (clientLower.includes("web") || clientLower.includes("chrome") || clientLower.includes("firefox")) {
} else if (
clientLower.includes("web") ||
clientLower.includes("chrome") ||
clientLower.includes("firefox")
) {
return "web";
} else if (clientLower.includes("mobile") || clientLower.includes("ios") || clientLower.includes("android")) {
} else if (
clientLower.includes("mobile") ||
clientLower.includes("ios") ||
clientLower.includes("android")
) {
return "phone";
}
return "device";
@@ -134,7 +148,9 @@
<div
class="fixed inset-0 bg-black/60 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4"
onclick={handleBackdropClick}
onkeydown={(e) => { if (e.key === 'Escape' && onClose) onClose(); }}
onkeydown={(e) => {
if (e.key === "Escape" && onClose) onClose();
}}
role="dialog"
aria-modal="true"
aria-labelledby="session-picker-title"
@@ -148,16 +164,19 @@
>
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-800 flex items-center justify-between">
<h2 id="session-picker-title" class="text-lg font-semibold text-white">
Cast to Device
</h2>
<h2 id="session-picker-title" class="text-lg font-semibold text-white">Cast to Device</h2>
<button
onclick={onClose}
class="p-2 -m-2 text-gray-400 hover:text-white transition-colors"
aria-label="Close"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
@@ -168,7 +187,9 @@
<!-- Loading -->
<div class="flex items-center justify-center py-12">
<div class="flex flex-col items-center gap-3">
<div class="w-8 h-8 border-4 border-[var(--color-jellyfin)] border-t-transparent rounded-full animate-spin"></div>
<div
class="w-8 h-8 border-4 border-[var(--color-jellyfin)] border-t-transparent rounded-full animate-spin"
></div>
<p class="text-sm text-gray-400">Searching for devices...</p>
</div>
</div>
@@ -177,9 +198,13 @@
<div class="p-4 space-y-2">
{#if $selectedSession}
<!-- Currently connected session -->
<div class="mb-4 p-4 rounded-lg bg-[var(--color-jellyfin)]/10 border border-[var(--color-jellyfin)]/30">
<div
class="mb-4 p-4 rounded-lg bg-[var(--color-jellyfin)]/10 border border-[var(--color-jellyfin)]/30"
>
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-medium text-[var(--color-jellyfin)] uppercase">Connected</span>
<span class="text-xs font-medium text-[var(--color-jellyfin)] uppercase"
>Connected</span
>
<button
onclick={handleDisconnect}
class="text-xs text-gray-400 hover:text-white transition-colors"
@@ -188,16 +213,30 @@
</button>
</div>
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-[var(--color-jellyfin)]/20 flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-[var(--color-jellyfin)]" fill="currentColor" viewBox="0 0 24 24">
<div
class="w-10 h-10 rounded-lg bg-[var(--color-jellyfin)]/20 flex items-center justify-center flex-shrink-0"
>
<svg
class="w-5 h-5 text-[var(--color-jellyfin)]"
fill="currentColor"
viewBox="0 0 24 24"
>
{#if getSessionIcon($selectedSession.client) === "tv"}
<path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z" />
<path
d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"
/>
{:else if getSessionIcon($selectedSession.client) === "web"}
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z" />
<path
d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"
/>
{:else if getSessionIcon($selectedSession.client) === "phone"}
<path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z" />
<path
d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"
/>
{:else}
<path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z" />
<path
d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"
/>
{/if}
</svg>
</div>
@@ -215,28 +254,41 @@
{@const lmsZone = isLmsSession(session)}
{@const fused = lmsZone && isZoneFused(session)}
<button
onclick={() => (lmsZone ? handleLmsZoneToggle(session) : handleSessionSelect(session))}
onclick={() =>
lmsZone ? handleLmsZoneToggle(session) : handleSessionSelect(session)}
disabled={$lmsSync.isBusy}
aria-pressed={lmsZone ? fused : undefined}
class="w-full p-4 rounded-lg border transition-all text-left disabled:opacity-60
{fused
? 'border-[var(--color-jellyfin)] bg-[var(--color-jellyfin)]/10'
: 'border-gray-800 hover:border-[var(--color-jellyfin)]/50 hover:bg-[var(--color-jellyfin)]/5'}"
? 'border-[var(--color-jellyfin)] bg-[var(--color-jellyfin)]/10'
: 'border-gray-800 hover:border-[var(--color-jellyfin)]/50 hover:bg-[var(--color-jellyfin)]/5'}"
>
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-gray-800 flex items-center justify-center flex-shrink-0">
<div
class="w-10 h-10 rounded-lg bg-gray-800 flex items-center justify-center flex-shrink-0"
>
<svg class="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
{#if lmsZone}
<!-- speaker icon for LMS zones -->
<path d="M12 2a1 1 0 011 1v18a1 1 0 01-1 1H6a2 2 0 01-2-2V4a2 2 0 012-2h6zm0 2H6v16h6V4zm-3 9a2 2 0 110 4 2 2 0 010-4zm0-2a4 4 0 100 8 4 4 0 000-8zm0-4a1 1 0 110 2 1 1 0 010-2zm9 0h2v2h-2V7zm0 4h2v2h-2v-2z" />
<path
d="M12 2a1 1 0 011 1v18a1 1 0 01-1 1H6a2 2 0 01-2-2V4a2 2 0 012-2h6zm0 2H6v16h6V4zm-3 9a2 2 0 110 4 2 2 0 010-4zm0-2a4 4 0 100 8 4 4 0 000-8zm0-4a1 1 0 110 2 1 1 0 010-2zm9 0h2v2h-2V7zm0 4h2v2h-2v-2z"
/>
{:else if getSessionIcon(session.client) === "tv"}
<path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z" />
<path
d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"
/>
{:else if getSessionIcon(session.client) === "web"}
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z" />
<path
d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"
/>
{:else if getSessionIcon(session.client) === "phone"}
<path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z" />
<path
d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"
/>
{:else}
<path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z" />
<path
d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"
/>
{/if}
</svg>
</div>
@@ -244,7 +296,11 @@
<h3 class="font-medium text-white truncate">{session.deviceName}</h3>
<p class="text-sm text-gray-400 truncate">
{#if lmsZone}
{fused ? "In sync group — tap to remove" : masterMac ? "Tap to add to group" : "Speaker zone"}
{fused
? "In sync group — tap to remove"
: masterMac
? "Tap to add to group"
: "Speaker zone"}
{:else}
{session.client}
{/if}
@@ -259,11 +315,24 @@
<!-- Fuse toggle indicator -->
<div class="flex-shrink-0">
{#if fused}
<svg class="w-5 h-5 text-[var(--color-jellyfin)]" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
<svg
class="w-5 h-5 text-[var(--color-jellyfin)]"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
/>
</svg>
{:else}
<svg class="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<svg
class="w-5 h-5 text-gray-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<circle cx="12" cy="12" r="9" stroke-width="2" />
</svg>
{/if}
@@ -271,11 +340,19 @@
{:else if session.playState}
<div class="flex-shrink-0">
{#if session.playState.isPaused}
<svg class="w-4 h-4 text-gray-500" fill="currentColor" viewBox="0 0 24 24">
<svg
class="w-4 h-4 text-gray-500"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
</svg>
{:else}
<svg class="w-4 h-4 text-[var(--color-jellyfin)]" fill="currentColor" viewBox="0 0 24 24">
<svg
class="w-4 h-4 text-[var(--color-jellyfin)]"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M8 5v14l11-7z" />
</svg>
{/if}
@@ -289,7 +366,12 @@
{:else}
<!-- Empty state -->
<div class="flex flex-col items-center justify-center py-12 px-6 text-center">
<svg class="w-16 h-16 text-gray-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg
class="w-16 h-16 text-gray-600 mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
@@ -322,7 +404,9 @@
disabled={$isTransferring}
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z" />
<path
d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"
/>
</svg>
Play Locally
</button>
@@ -354,7 +438,9 @@
{#if $isTransferring}
<div class="absolute inset-0 bg-black/70 flex items-center justify-center z-10 rounded-2xl">
<div class="bg-[var(--color-surface)] p-6 rounded-lg flex flex-col items-center gap-4">
<div class="w-10 h-10 border-4 border-[var(--color-jellyfin)] border-t-transparent rounded-full animate-spin"></div>
<div
class="w-10 h-10 border-4 border-[var(--color-jellyfin)] border-t-transparent rounded-full animate-spin"
></div>
<p class="text-white font-medium">Transferring playback...</p>
<button
onclick={() => {
@@ -371,10 +457,14 @@
<!-- LMS sync error -->
{#if $lmsSync.error}
<div class="absolute bottom-0 left-0 right-0 bg-red-500/90 text-white px-6 py-3 flex items-center justify-between z-10 rounded-b-2xl">
<div
class="absolute bottom-0 left-0 right-0 bg-red-500/90 text-white px-6 py-3 flex items-center justify-between z-10 rounded-b-2xl"
>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"
/>
</svg>
<span class="text-sm">{$lmsSync.error}</span>
</div>
@@ -384,7 +474,12 @@
aria-label="Dismiss error"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
@@ -392,10 +487,14 @@
<!-- Error Display -->
{#if $transferError}
<div class="absolute bottom-0 left-0 right-0 bg-red-500/90 text-white px-6 py-3 flex items-center justify-between z-10 rounded-b-2xl">
<div
class="absolute bottom-0 left-0 right-0 bg-red-500/90 text-white px-6 py-3 flex items-center justify-between z-10 rounded-b-2xl"
>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 flex-shrink-0" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"
/>
</svg>
<span class="text-sm">{$transferError}</span>
</div>
@@ -405,7 +504,12 @@
aria-label="Dismiss error"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>