Spaces:
Running
Running
File size: 4,037 Bytes
e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 e355be5 6d55c38 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | <script lang="ts">
import { Button } from '$lib/components/ui/button';
import * as Tooltip from '$lib/components/ui/tooltip';
import FlagIcon from 'phosphor-svelte/lib/FlagIcon';
import ArrowRightIcon from 'phosphor-svelte/lib/ArrowRightIcon';
import ArrowLeftIcon from 'phosphor-svelte/lib/ArrowLeftIcon';
import XIcon from 'phosphor-svelte/lib/XIcon';
import { goto } from '$app/navigation';
import { page } from '$app/state';
import {
addValidation,
evalUrl,
nextUnreviewed,
reviewedKeySet,
type EvalCandidate
} from '$lib/eval';
import { toast } from 'svelte-sonner';
import EvalFlagDialog from './eval-flag-dialog.svelte';
interface Props {
queue: EvalCandidate[];
index: number;
matchId: number;
mapName: string;
round: number;
}
let { queue, index, matchId, mapName, round }: Props = $props();
let flagOpen = $state(false);
// Bumped after each review action so the dialog count and the Next-skip
// logic re-read localStorage without needing a reactive store.
let reviewBump = $state(0);
const reviewedCount = $derived.by(() => {
void reviewBump;
const reviewed = reviewedKeySet();
let n = 0;
for (const c of queue) if (reviewed.has(`${c.matchId}|${c.mapName}|${c.round}`)) n++;
return n;
});
function next() {
// Hitting Next without flagging implicitly validates the current candidate.
if (round && index >= 0) {
addValidation({ matchId, mapName, round });
reviewBump++;
}
const ni = nextUnreviewed(queue, index < 0 ? -1 : index, reviewedKeySet());
if (ni < 0) {
toast.success('Evaluation complete', {
description: `Reviewed ${reviewedCount + 1} / ${queue.length} candidates.`
});
return;
}
goto(evalUrl(queue[ni], ni));
}
function previous() {
if (index <= 0) return;
const pi = index - 1;
goto(evalUrl(queue[pi], pi));
}
function exitEval() {
const url = new URL(page.url);
url.searchParams.delete('eval');
url.searchParams.delete('i');
goto(url.pathname + (url.searchParams.size ? '?' + url.searchParams.toString() : ''));
}
const total = $derived(queue.length);
const positionLabel = $derived(index < 0 ? `— / ${total}` : `${index + 1} / ${total}`);
</script>
<div
class="border-b border-amber-500/30 bg-amber-500/10 dark:border-amber-500/25 dark:bg-amber-500/[0.07]"
>
<div class="mx-auto flex h-10 w-full max-w-[1600px] items-center gap-2 px-4 text-xs">
<span class="font-mono font-semibold tracking-wide text-amber-700 uppercase dark:text-amber-300"
>Eval</span
>
<span class="text-muted-foreground tabular-nums">{positionLabel}</span>
<span class="text-muted-foreground/60 tabular-nums">·</span>
<span class="text-muted-foreground tabular-nums">{reviewedCount} reviewed</span>
<div class="ml-auto flex items-center gap-1">
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<Button
{...props}
variant="ghost"
size="icon-sm"
onclick={previous}
disabled={index <= 0}
aria-label="Previous candidate"
>
<ArrowLeftIcon size={14} weight="bold" />
</Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content side="bottom">Previous candidate</Tooltip.Content>
</Tooltip.Root>
<Button variant="outline" size="sm" onclick={() => (flagOpen = true)}>
<FlagIcon size={14} weight="fill" /> Flag
</Button>
<Button onclick={next} size="sm" disabled={total === 0}>
Next <ArrowRightIcon size={14} weight="bold" />
</Button>
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<Button
{...props}
variant="ghost"
size="icon-sm"
onclick={exitEval}
aria-label="Exit evaluation"
>
<XIcon size={14} weight="bold" />
</Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content side="bottom">Exit evaluation</Tooltip.Content>
</Tooltip.Root>
</div>
</div>
</div>
<EvalFlagDialog
bind:open={flagOpen}
{matchId}
{mapName}
{round}
queueLength={total}
onChange={() => reviewBump++}
/>
|