Files
website/src/lib/game-tracker/PlayerRankBadge.svelte
Stan Runge 54924b39f0
Some checks failed
Test and deploy staging / staging (push) Failing after 43s
feat: upgrade to SvelteKit 3 and adapter-bun
Run the sveltekit-3 migration and finish its manual tasks:

- Move kit config into the sveltekit() Vite plugin; drop svelte.config.js
- Replace $lib with #lib subpath imports
- Declare env vars in src/env.ts and import them from $app/env/{private,public}
- Consolidate param matchers into src/params.ts
- Use error(status, message), Response.json, refreshAll and goto reset/shallow
- Fall back to a full page load when goto rejects a login return path

Switch from adapter-node to adapter-bun: the build script runs Vite under
Bun (the adapter requires it) and the image starts with `bun ./build`.
Runtime env names (PORT, BODY_SIZE_LIMIT, ADDRESS_HEADER, ...) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:17:30 +00:00

111 lines
2.7 KiB
Svelte

<script lang="ts">
import { resolve } from '$app/paths';
import ChevronsUp from 'lucide-svelte/icons/chevrons-up';
import Award from 'lucide-svelte/icons/award';
let {
game,
level,
prestige,
levelCap = null,
prestigeCap = null,
onedit
}: {
game: string;
level: number;
prestige: number;
levelCap?: number | null;
prestigeCap?: number | null;
/** Makes the badge a button for typing an exact rank. */
onedit?: () => void;
} = $props();
const master = $derived(game === 'black-ops-2' && prestige === 10 && level === 55);
</script>
<svelte:element
this={onedit ? 'button' : 'div'}
type={onedit ? 'button' : undefined}
class="badge"
class:editable={!!onedit}
aria-label="Player level and prestige"
title={onedit ? 'Type an exact level and prestige' : undefined}
onclick={onedit}
role={onedit ? undefined : 'group'}
>
<span class="stat">
{#if game === 'black-ops-2' && prestige > 0 && prestige <= 10}
<img
src={resolve(
`game-art/call-of-duty/ranks/black-ops-2-prestige-${master ? 11 : prestige}.webp`
)}
alt={master ? 'Prestige Master emblem' : `Prestige ${prestige} emblem`}
width="48"
height="48"
class="emblem"
/>
{:else}<Award class="icon text-muted-foreground" aria-label="Prestige icon" role="img" />{/if}
<span
><span class="label">PRESTIGE</span><strong
>{master ? 'Master' : prestige}{#if prestigeCap !== null && !master}<small
>/{prestigeCap}</small
>{/if}</strong
></span
>
</span>
<span class="stat">
<ChevronsUp class="icon text-amber-500" aria-label="Level icon" role="img" />
<span
><span class="label">LEVEL</span><strong
>{level}{#if levelCap !== null}<small>/{levelCap}</small>{/if}</strong
></span
>
</span>
</svelte:element>
<style>
.badge {
display: flex;
align-items: center;
gap: calc(18 * var(--u, 1px));
border-radius: calc(10 * var(--u, 1px));
padding: calc(2 * var(--u, 1px)) calc(8 * var(--u, 1px));
text-align: left;
}
.editable {
cursor: pointer;
}
.editable:hover {
background: color-mix(in srgb, #f59e0b 10%, transparent);
}
.editable:focus-visible {
outline: 2px solid #f59e0b;
}
.stat {
display: flex;
align-items: center;
gap: calc(8 * var(--u, 1px));
}
.emblem,
.stat :global(.icon) {
width: calc(44 * var(--u, 1px));
height: calc(44 * var(--u, 1px));
object-fit: contain;
}
.label {
display: block;
font-size: calc(11 * var(--u, 1px));
letter-spacing: 0.14em;
color: var(--site-muted);
}
strong {
display: block;
font-size: calc(24 * var(--u, 1px));
line-height: 1.05;
font-variant-numeric: tabular-nums;
}
small {
font-size: 0.6em;
font-weight: 500;
color: var(--site-muted);
}
</style>