Add Prettier and ESLint tooling (Refs #77)

This commit is contained in:
2026-10-09 15:56:13 -04:00
parent 8ed059b8d3
commit 16469e2d43
711 changed files with 99737 additions and 28286 deletions
+119 -31
View File
@@ -3,49 +3,137 @@
import type { Host } from '$lib/hosts.js';
import GameFileTransferProgress from './GameFileTransferProgress.svelte';
import Badge from './primitives/Badge.svelte';
let { caches, hosts, latest = false }: { caches: HostLibraryCache[]; hosts: Host[]; latest?: boolean } = $props();
const healthy = $derived(caches.filter(cache => cache.status === 'ready' && hosts.some(host => host.id === cache.hostId && host.connected) && !cache.error));
const attention = $derived(caches.filter(cache => !healthy.includes(cache)));
let {
caches,
hosts,
latest = false
}: { caches: HostLibraryCache[]; hosts: Host[]; latest?: boolean } = $props();
const healthy = $derived(
caches.filter(
(cache) =>
cache.status === 'ready' &&
hosts.some((host) => host.id === cache.hostId && host.connected) &&
!cache.error
)
);
const attention = $derived(caches.filter((cache) => !healthy.includes(cache)));
function automaticDownloadDetail(host: Host | undefined): string {
if (host?.decommission) return 'Automatic setup is paused during host deletion.';
if (!host?.enrolled) return 'Waiting for host enrollment.';
if (host.mode !== 'active') return 'Automatic setup resumes when the host is active.';
if (!host.credentialActive || !host.connected) return 'Automatic setup resumes when the host reconnects.';
if (host.readiness === 'degraded') return host.readinessReasons[0] ?? 'The host needs attention before automatic setup can continue.';
if (!host.credentialActive || !host.connected)
return 'Automatic setup resumes when the host reconnects.';
if (host.readiness === 'degraded')
return (
host.readinessReasons[0] ??
'The host needs attention before automatic setup can continue.'
);
return 'Download starts automatically after runtime preparation and earlier work finish.';
}
</script>
{#if !hosts.length}<p class="muted"><a href="/hosts">Add a host</a> to prepare these files automatically.</p>{/if}
{#if !hosts.length}<p class="muted">
<a href="/hosts">Add a host</a> to prepare these files automatically.
</p>{/if}
{#if attention.length}{@render hostTable(attention)}{/if}
{#if healthy.length}<details><summary>Ready hosts ({healthy.length})</summary>{@render hostTable(healthy)}</details>{/if}
{#if healthy.length}<details>
<summary>Ready hosts ({healthy.length})</summary>{@render hostTable(healthy)}
</details>{/if}
{#snippet hostTable(rows: HostLibraryCache[])}
<div class="table-wrap"><table><thead><tr><th>Host</th><th>File status</th><th>Details</th></tr></thead><tbody>
{#each rows as cache (cache.hostId)}
{@const host = hosts.find(host => host.id === cache.hostId)}
{@const waiting = latest && cache.status === 'missing'}
<tr>
<td><a href={`/hosts/${cache.hostId}`}>{host?.name ?? cache.hostId}</a>{#if !host?.connected}<span class="muted">{' · Offline'}</span>{/if}</td>
<td><Badge variant={cache.status === 'failed' ? 'danger' : cache.status === 'ready' ? 'online' : 'neutral'}>{waiting ? 'Waiting' : cache.status[0].toUpperCase() + cache.status.slice(1)}</Badge></td>
<td>{#if cache.status === 'ready'}<details><summary>Transfer details</summary><GameFileTransferProgress {cache} connected={host?.connected ?? false} /></details>
{:else if cache.status !== 'missing'}<GameFileTransferProgress {cache} connected={host?.connected ?? false} />{/if}
{#if cache.error}<p class="error">{cache.error}</p>{:else if waiting}<p class="muted">{automaticDownloadDetail(host)}</p>{/if}
{#if waiting || cache.status === 'failed'}<a href={`/hosts/${cache.hostId}`}>View host setup</a>{/if}
</td>
</tr>
{/each}
</tbody></table></div>
<div class="table-wrap">
<table>
<thead><tr><th>Host</th><th>File status</th><th>Details</th></tr></thead><tbody>
{#each rows as cache (cache.hostId)}
{@const host = hosts.find((host) => host.id === cache.hostId)}
{@const waiting = latest && cache.status === 'missing'}
<tr>
<td
><a href={`/hosts/${cache.hostId}`}>{host?.name ?? cache.hostId}</a
>{#if !host?.connected}<span class="muted">{' · Offline'}</span
>{/if}</td
>
<td
><Badge
variant={cache.status === 'failed'
? 'danger'
: cache.status === 'ready'
? 'online'
: 'neutral'}
>{waiting
? 'Waiting'
: cache.status[0].toUpperCase() + cache.status.slice(1)}</Badge
></td
>
<td
>{#if cache.status === 'ready'}<details>
<summary>Transfer details</summary><GameFileTransferProgress
{cache}
connected={host?.connected ?? false}
/>
</details>
{:else if cache.status !== 'missing'}<GameFileTransferProgress
{cache}
connected={host?.connected ?? false}
/>{/if}
{#if cache.error}<p class="error">{cache.error}</p>{:else if waiting}<p
class="muted"
>
{automaticDownloadDetail(host)}
</p>{/if}
{#if waiting || cache.status === 'failed'}<a
href={`/hosts/${cache.hostId}`}>View host setup</a
>{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/snippet}
<style>
.table-wrap { overflow-x:auto; margin-top:10px; }
table { width:100%; min-width:520px; border-collapse:collapse; text-align:left; font-size:var(--text-sm); }
th,td { padding:10px; border-bottom:1px solid var(--border-subtle); }
th { color:var(--text-secondary); font-weight:500; font-size:var(--text-xs); }
summary { cursor:pointer; font-size:var(--text-sm); }
a { color:var(--accent-primary-hover); } .muted { color:var(--text-secondary); font-size:var(--text-sm); }
p { margin:6px 0; } .error { color:var(--state-danger); }
summary:focus-visible,a:focus-visible { outline:2px solid var(--accent-primary); outline-offset:3px; }
.table-wrap {
overflow-x: auto;
margin-top: 10px;
}
table {
width: 100%;
min-width: 520px;
border-collapse: collapse;
text-align: left;
font-size: var(--text-sm);
}
th,
td {
padding: 10px;
border-bottom: 1px solid var(--border-subtle);
}
th {
color: var(--text-secondary);
font-weight: 500;
font-size: var(--text-xs);
}
summary {
cursor: pointer;
font-size: var(--text-sm);
}
a {
color: var(--accent-primary-hover);
}
.muted {
color: var(--text-secondary);
font-size: var(--text-sm);
}
p {
margin: 6px 0;
}
.error {
color: var(--state-danger);
}
summary:focus-visible,
a:focus-visible {
outline: 2px solid var(--accent-primary);
outline-offset: 3px;
}
</style>