mirror of
https://github.com/SoPat712/maisie-heardle.git
synced 2025-08-21 18:28:45 -04:00
win screen, fixed progress bar, pinned play and progress to bottom, version bump
This commit is contained in:
@@ -129,7 +129,7 @@
|
|||||||
url: `https://soundcloud.com/${SC_USER}/${slug}`
|
url: `https://soundcloud.com/${SC_USER}/${slug}`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// ─── DAILY‐SEEDED RANDOM TRACK ───────────────────────────────────────────────
|
// ─── SEED & PICK TRACK ────────────────────────────────────────────────────────
|
||||||
let seed = parseInt(moment().format('YYYYMMDD'), 10);
|
let seed = parseInt(moment().format('YYYYMMDD'), 10);
|
||||||
function seededRandom() {
|
function seededRandom() {
|
||||||
seed = (seed * 9301 + 49297) % 233280;
|
seed = (seed * 9301 + 49297) % 233280;
|
||||||
@@ -139,16 +139,16 @@
|
|||||||
|
|
||||||
// ─── SEGMENTS ───────────────────────────────────────────────────────────────
|
// ─── SEGMENTS ───────────────────────────────────────────────────────────────
|
||||||
const SEGMENT_INCREMENTS = [2, 1, 2, 3, 4, 5];
|
const SEGMENT_INCREMENTS = [2, 1, 2, 3, 4, 5];
|
||||||
const segmentDurations = SEGMENT_INCREMENTS.reduce<number[]>((acc, inc) => {
|
const segmentDurations = SEGMENT_INCREMENTS.reduce<number[]>((a, inc) => {
|
||||||
acc.push((acc.at(-1) ?? 0) + inc * 1000);
|
a.push((a.at(-1) ?? 0) + inc * 1000);
|
||||||
return acc;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
const TOTAL_MS = segmentDurations.at(-1)!;
|
const TOTAL_MS = segmentDurations.at(-1)!;
|
||||||
const TOTAL_SECONDS = TOTAL_MS / 1000;
|
const TOTAL_SECONDS = TOTAL_MS / 1000;
|
||||||
const maxAttempts = SEGMENT_INCREMENTS.length;
|
const maxAttempts = SEGMENT_INCREMENTS.length;
|
||||||
$: boundaries = segmentDurations.map((ms) => ms / 1000).slice(0, -1);
|
$: boundaries = segmentDurations.map((ms) => ms / 1000).slice(0, -1);
|
||||||
|
|
||||||
// ─── GAME STATE & PLAYER TIMERS ──────────────────────────────────────────────
|
// ─── STATE & TIMERS ─────────────────────────────────────────────────────────
|
||||||
type Info = { status: 'skip' | 'wrong' | 'correct'; title?: string };
|
type Info = { status: 'skip' | 'wrong' | 'correct'; title?: string };
|
||||||
let attemptInfos: Info[] = [];
|
let attemptInfos: Info[] = [];
|
||||||
let attemptCount = 0;
|
let attemptCount = 0;
|
||||||
@@ -158,10 +158,12 @@
|
|||||||
let iframeElement: HTMLIFrameElement;
|
let iframeElement: HTMLIFrameElement;
|
||||||
let widget: any;
|
let widget: any;
|
||||||
let widgetReady = false;
|
let widgetReady = false;
|
||||||
|
let artworkUrl = '';
|
||||||
let isPlaying = false;
|
let isPlaying = false;
|
||||||
let currentPosition = 0;
|
let currentPosition = 0;
|
||||||
let snippetTimeout: ReturnType<typeof setTimeout>;
|
let snippetTimeout: ReturnType<typeof setTimeout>;
|
||||||
let progressInterval: ReturnType<typeof setInterval>;
|
let progressInterval: ReturnType<typeof setInterval>;
|
||||||
|
let fullDuration = 0;
|
||||||
|
|
||||||
let showHowTo = false;
|
let showHowTo = false;
|
||||||
let showInfo = false;
|
let showInfo = false;
|
||||||
@@ -174,10 +176,27 @@
|
|||||||
let selectedTrack: Track | null = null;
|
let selectedTrack: Track | null = null;
|
||||||
let inputEl: HTMLInputElement;
|
let inputEl: HTMLInputElement;
|
||||||
|
|
||||||
|
// ─── COUNTDOWN ───────────────────────────────────────────────────────────────
|
||||||
|
let timeLeft = '';
|
||||||
|
let countdownInterval: ReturnType<typeof setInterval>;
|
||||||
|
function updateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const midnight = new Date(now);
|
||||||
|
midnight.setHours(24, 0, 0, 0);
|
||||||
|
const diff = midnight.getTime() - now.getTime();
|
||||||
|
const h = String(Math.floor(diff / 3600000)).padStart(2, '0');
|
||||||
|
const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2, '0');
|
||||||
|
const s = String(Math.floor((diff % 60000) / 1000)).padStart(2, '0');
|
||||||
|
timeLeft = `${h}:${m}:${s}`;
|
||||||
|
}
|
||||||
|
|
||||||
$: suggestions = userInput
|
$: suggestions = userInput
|
||||||
? tracks.filter((t) => t.title.toLowerCase().includes(userInput.toLowerCase())).slice(0, 5)
|
? tracks.filter((t) => t.title.toLowerCase().includes(userInput.toLowerCase())).slice(0, 5)
|
||||||
: [];
|
: [];
|
||||||
$: fillPercent = (currentPosition / TOTAL_MS) * 100;
|
// switch fill% between snippet & full track
|
||||||
|
$: fillPercent = gameOver
|
||||||
|
? (currentPosition / fullDuration) * 100
|
||||||
|
: (currentPosition / TOTAL_MS) * 100;
|
||||||
$: nextIncrementSec =
|
$: nextIncrementSec =
|
||||||
attemptCount < SEGMENT_INCREMENTS.length - 1 ? SEGMENT_INCREMENTS[attemptCount + 1] : 0;
|
attemptCount < SEGMENT_INCREMENTS.length - 1 ? SEGMENT_INCREMENTS[attemptCount + 1] : 0;
|
||||||
|
|
||||||
@@ -191,7 +210,7 @@
|
|||||||
clearInterval(progressInterval);
|
clearInterval(progressInterval);
|
||||||
progressInterval = setInterval(() => {
|
progressInterval = setInterval(() => {
|
||||||
widget.getPosition((pos: number) => {
|
widget.getPosition((pos: number) => {
|
||||||
const limit = segmentDurations[attemptCount];
|
const limit = gameOver ? fullDuration : segmentDurations[attemptCount];
|
||||||
currentPosition = Math.min(pos, limit);
|
currentPosition = Math.min(pos, limit);
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -204,20 +223,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
// dark‑mode listener
|
||||||
mq.addEventListener('change', (e) => (darkMode = e.matches));
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', (e) => (darkMode = e.matches));
|
||||||
|
|
||||||
|
// countdown
|
||||||
|
updateTime();
|
||||||
|
countdownInterval = setInterval(updateTime, 1000);
|
||||||
|
|
||||||
|
// SoundCloud widget
|
||||||
widget = SC.Widget(iframeElement);
|
widget = SC.Widget(iframeElement);
|
||||||
widget.bind(SC.Widget.Events.READY, () => (widgetReady = true));
|
widget.bind(SC.Widget.Events.READY, () => {
|
||||||
|
widgetReady = true;
|
||||||
|
widget.getDuration((d: number) => (fullDuration = d));
|
||||||
|
widget.getCurrentSound((sound: any) => {
|
||||||
|
artworkUrl = sound.artwork_url || '';
|
||||||
|
});
|
||||||
|
});
|
||||||
widget.bind(SC.Widget.Events.PLAY, () => {
|
widget.bind(SC.Widget.Events.PLAY, () => {
|
||||||
startPolling();
|
startPolling();
|
||||||
|
if (!gameOver) {
|
||||||
snippetTimeout = setTimeout(() => widget.pause(), segmentDurations[attemptCount]);
|
snippetTimeout = setTimeout(() => widget.pause(), segmentDurations[attemptCount]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
widget.bind(SC.Widget.Events.PAUSE, stopAllTimers);
|
widget.bind(SC.Widget.Events.PAUSE, stopAllTimers);
|
||||||
widget.bind(SC.Widget.Events.FINISH, stopAllTimers);
|
widget.bind(SC.Widget.Events.FINISH, stopAllTimers);
|
||||||
widget.bind(SC.Widget.Events.PLAY_PROGRESS, (e: { currentPosition: number }) => {
|
widget.bind(SC.Widget.Events.PLAY_PROGRESS, (e: { currentPosition: number }) => {
|
||||||
const limit = segmentDurations[attemptCount];
|
const limit = gameOver ? fullDuration : segmentDurations[attemptCount];
|
||||||
if (e.currentPosition >= limit) {
|
if (e.currentPosition >= limit) {
|
||||||
currentPosition = limit;
|
currentPosition = limit;
|
||||||
widget.pause();
|
widget.pause();
|
||||||
@@ -229,18 +262,19 @@
|
|||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
stopAllTimers();
|
stopAllTimers();
|
||||||
|
clearInterval(countdownInterval);
|
||||||
widget?.unbind && Object.values(SC.Widget.Events).forEach((ev) => widget.unbind(ev));
|
widget?.unbind && Object.values(SC.Widget.Events).forEach((ev) => widget.unbind(ev));
|
||||||
});
|
});
|
||||||
|
|
||||||
function playSegment() {
|
function playSegment() {
|
||||||
if (!widgetReady || gameOver) return;
|
if (!widgetReady) return;
|
||||||
currentPosition = 0;
|
currentPosition = 0;
|
||||||
widget.seekTo(0);
|
widget.seekTo(0);
|
||||||
widget.play();
|
widget.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePlayPause() {
|
function togglePlayPause() {
|
||||||
if (!widgetReady || gameOver) return;
|
if (!widgetReady) return;
|
||||||
isPlaying ? widget.pause() : playSegment();
|
isPlaying ? widget.pause() : playSegment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,25 +339,19 @@
|
|||||||
<!-- How to Play Modal -->
|
<!-- How to Play Modal -->
|
||||||
{#if showHowTo}
|
{#if showHowTo}
|
||||||
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
<div class="absolute inset-0" style="background: rgba(0,0,0,0.4)"></div>
|
<div class="absolute inset-0 bg-black/40"></div>
|
||||||
<div
|
<div
|
||||||
class="relative w-4/5 max-w-md rounded-lg p-8 text-center"
|
class="relative w-4/5 max-w-md rounded-lg p-8 text-center"
|
||||||
style="background: {COLORS.background}"
|
style="background: {COLORS.background}"
|
||||||
>
|
>
|
||||||
<h2
|
<h2 class="mb-4 text-2xl font-bold uppercase" style="color: {COLORS.primary}">How to Play</h2>
|
||||||
class="mb-4 text-2xl font-bold uppercase"
|
|
||||||
style="color: {COLORS.primary}; font-family: 'Inter', sans-serif"
|
|
||||||
>
|
|
||||||
How to Play
|
|
||||||
</h2>
|
|
||||||
<ul class="space-y-4 text-base" style="color: {COLORS.text}">
|
<ul class="space-y-4 text-base" style="color: {COLORS.text}">
|
||||||
<li>🎵 Play the snippet.</li>
|
<li>🎵 Play the snippet.</li>
|
||||||
<li>🔊 Skips & wrongs unlock more.</li>
|
<li>🔊 Skips & wrongs unlock more.</li>
|
||||||
<li>👍 Guess in as few tries as possible!</li>
|
<li>👍 Guess in as few tries as possible!</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="mt-6 flex justify-center">
|
|
||||||
<button
|
<button
|
||||||
class="rounded px-6 py-2 font-semibold"
|
class="mt-6 rounded px-6 py-2 font-semibold"
|
||||||
style="background: {COLORS.primary}; color: {COLORS.background}"
|
style="background: {COLORS.primary}; color: {COLORS.background}"
|
||||||
on:click={() => (showHowTo = false)}
|
on:click={() => (showHowTo = false)}
|
||||||
>
|
>
|
||||||
@@ -331,32 +359,28 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Info Modal -->
|
<!-- Info Modal -->
|
||||||
{#if showInfo}
|
{#if showInfo}
|
||||||
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
<div class="absolute inset-0" style="background: rgba(0,0,0,0.4)"></div>
|
<div class="absolute inset-0 bg-black/40"></div>
|
||||||
<div
|
<div
|
||||||
class="relative w-4/5 max-w-md space-y-4 rounded-lg p-8"
|
class="relative w-4/5 max-w-md space-y-4 rounded-lg p-8"
|
||||||
style="background:{COLORS.background};color:{COLORS.text}"
|
style="background:{COLORS.background};color:{COLORS.text}"
|
||||||
>
|
>
|
||||||
<p class="text-base font-semibold">
|
<p class="font-semibold">{ARTIST_NAME} – Test your knowledge!</p>
|
||||||
{ARTIST_NAME} – Test your {ARTIST_NAME} knowledge!
|
<p>All songs used are copyrighted and belong to {ARTIST_NAME}.</p>
|
||||||
</p>
|
<hr class="my-4" style="border-color:{COLORS.text}" />
|
||||||
<p class="text-base">
|
|
||||||
All songs used are copyrighted and belong to {ARTIST_NAME}.
|
|
||||||
</p>
|
|
||||||
<hr style="border-color: {COLORS.text}" class="my-4" />
|
|
||||||
<p class="text-xs" style="color:{COLORS.accent}">
|
<p class="text-xs" style="color:{COLORS.accent}">
|
||||||
Prepared with SoundCloud, Svelte, Tailwind, Inter font,<br />
|
Prepared with SoundCloud, Svelte, Tailwind CSS, Inter font, svelte-hero-icons
|
||||||
svelte‑hero‑icons
|
<br />
|
||||||
|
<br />
|
||||||
|
Game version: 1.2.0
|
||||||
</p>
|
</p>
|
||||||
<p class="text-xs" style="color: {COLORS.accent}">Game version: 1.0.0</p>
|
<p class="text-sm italic">New track in <strong>{timeLeft}</strong></p>
|
||||||
<div class="mt-4 flex justify-center">
|
|
||||||
<button
|
<button
|
||||||
class="rounded px-6 py-2 font-semibold"
|
class="mt-4 rounded px-6 py-2 font-semibold"
|
||||||
style="background:{COLORS.primary};color:{COLORS.background}"
|
style="background:{COLORS.primary};color:{COLORS.background}"
|
||||||
on:click={() => (showInfo = false)}
|
on:click={() => (showInfo = false)}
|
||||||
>
|
>
|
||||||
@@ -364,58 +388,49 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Main UI anchored to viewport -->
|
<!-- Main UI -->
|
||||||
<div
|
<div
|
||||||
class="fixed inset-0 flex flex-col overflow-hidden"
|
class="fixed inset-0 flex flex-col overflow-hidden"
|
||||||
style="
|
style="background:{darkMode ? COLORS.text : COLORS.background};color:{darkMode
|
||||||
background: {darkMode ? COLORS.text : COLORS.background};
|
? COLORS.background
|
||||||
color: {darkMode ? COLORS.background : COLORS.text};
|
: COLORS.text}"
|
||||||
font-family:'Inter', sans-serif
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<!-- header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between px-4 pt-4">
|
<div class="flex items-center justify-between px-4 pt-4">
|
||||||
|
<div class="flex space-x-2">
|
||||||
<button on:click={() => (showInfo = true)}>
|
<button on:click={() => (showInfo = true)}>
|
||||||
<Icon src={InformationCircle} class="h-6 w-6" style="color:{COLORS.primary}" />
|
<Icon src={InformationCircle} class="h-6 w-6" style="color:{COLORS.primary}" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<h1 class="flex-1 text-center font-serif text-lg font-bold whitespace-nowrap sm:text-3xl">
|
|
||||||
Heardle – {ARTIST_NAME}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<button on:click={toggleDark}>
|
<button on:click={toggleDark}>
|
||||||
<Icon src={darkMode ? Sun : Moon} class="h-6 w-6" style="color:{COLORS.primary}" />
|
<Icon src={darkMode ? Sun : Moon} class="h-6 w-6" style="color:{COLORS.primary}" />
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
<h1 class="flex-1 text-center font-serif text-lg font-bold whitespace-nowrap sm:text-3xl">
|
||||||
|
Heardle – {ARTIST_NAME}
|
||||||
|
</h1>
|
||||||
<button on:click={() => (showHowTo = true)}>
|
<button on:click={() => (showHowTo = true)}>
|
||||||
<Icon src={QuestionMarkCircle} class="h-6 w-6" style="color:{COLORS.accent}" />
|
<Icon src={QuestionMarkCircle} class="h-6 w-6" style="color:{COLORS.accent}" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<hr class="mx-4 my-3" style="border-color:{darkMode ? COLORS.background : COLORS.text}" />
|
<hr class="mx-4 my-3" style="border-color:{darkMode ? COLORS.background : COLORS.text}" />
|
||||||
|
|
||||||
<!-- attempts -->
|
<!-- Attempts -->
|
||||||
<div class="mb-6 flex-shrink-0 space-y-2 px-4">
|
{#if !gameOver}
|
||||||
|
<div class="mb-6 space-y-2 px-4">
|
||||||
{#each attemptInfos as info}
|
{#each attemptInfos as info}
|
||||||
<div
|
<div
|
||||||
class="flex h-12 items-center rounded border px-3 font-semibold"
|
class="flex h-12 items-center rounded border px-3 font-semibold"
|
||||||
style="
|
style="border-color:{info.status === 'skip'
|
||||||
border-color:
|
|
||||||
{info.status === 'skip'
|
|
||||||
? COLORS.primary
|
? COLORS.primary
|
||||||
: info.status === 'wrong'
|
: info.status === 'wrong'
|
||||||
? COLORS.accent
|
? COLORS.accent
|
||||||
: COLORS.secondary};
|
: COLORS.secondary};color:{info.status === 'skip'
|
||||||
color:
|
|
||||||
{info.status === 'skip'
|
|
||||||
? COLORS.primary
|
? COLORS.primary
|
||||||
: info.status === 'wrong'
|
: info.status === 'wrong'
|
||||||
? COLORS.accent
|
? COLORS.accent
|
||||||
: COLORS.secondary};
|
: COLORS.secondary}"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{#if info.status === 'skip'}▢ Skipped
|
{#if info.status === 'skip'}▢ Skipped
|
||||||
{:else if info.status === 'wrong'}☒ {info.title}
|
{:else if info.status === 'wrong'}☒ {info.title}
|
||||||
@@ -429,6 +444,32 @@
|
|||||||
></div>
|
></div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Win/Lose Card -->
|
||||||
|
{#if gameOver}
|
||||||
|
<div class="mb-6 px-4">
|
||||||
|
<a
|
||||||
|
href={currentTrack.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
class="flex items-center overflow-hidden rounded-lg border-2 hover:bg-gray-100"
|
||||||
|
style="border-color:{COLORS.primary}"
|
||||||
|
>
|
||||||
|
{#if artworkUrl}
|
||||||
|
<img
|
||||||
|
src={artworkUrl.replace('-large', '-t500x500')}
|
||||||
|
alt="{currentTrack.title} cover"
|
||||||
|
class="h-16 w-16 flex-shrink-0 object-cover"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<div class="px-4 py-2">
|
||||||
|
<div class="font-semibold" style="color:{COLORS.primary}">{currentTrack.title}</div>
|
||||||
|
<div class="text-sm" style="color:{COLORS.accent}">{ARTIST_NAME}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<iframe
|
<iframe
|
||||||
bind:this={iframeElement}
|
bind:this={iframeElement}
|
||||||
@@ -438,53 +479,47 @@
|
|||||||
title="preview player"
|
title="preview player"
|
||||||
></iframe>
|
></iframe>
|
||||||
|
|
||||||
<!-- hollow progress bar -->
|
<!-- Bottom‐pinned controls -->
|
||||||
<div class="mb-4 flex-shrink-0 px-4">
|
<div class="mt-auto px-4 pb-4">
|
||||||
|
<!-- Progress bar -->
|
||||||
<div
|
<div
|
||||||
class="relative w-full overflow-hidden rounded border"
|
class="relative mb-2 w-full overflow-hidden rounded border"
|
||||||
style="
|
style="height:1.25rem;border-color:{darkMode ? COLORS.background : COLORS.text}"
|
||||||
height: 1.25rem;
|
|
||||||
border-color: {darkMode ? COLORS.background : COLORS.text}
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="absolute top-0 left-0 h-full"
|
class="absolute top-0 left-0 h-full transition-[width] duration-100"
|
||||||
style="
|
style="width:{fillPercent}%;background:{COLORS.primary}"
|
||||||
width: {fillPercent}%;
|
|
||||||
background: {COLORS.primary};
|
|
||||||
transition: width 0.1s;
|
|
||||||
"
|
|
||||||
></div>
|
></div>
|
||||||
|
{#if !gameOver}
|
||||||
{#each boundaries as b}
|
{#each boundaries as b}
|
||||||
<div
|
<div
|
||||||
class="absolute top-0 bottom-0"
|
class="absolute top-0 bottom-0"
|
||||||
style="
|
style="left:{(b / TOTAL_SECONDS) * 100}%;border-left:1px solid {darkMode
|
||||||
left: {(b / TOTAL_SECONDS) * 100}%;
|
? COLORS.background
|
||||||
border-left: 1px solid {darkMode ? COLORS.background : COLORS.text};
|
: COLORS.text}"
|
||||||
"
|
|
||||||
></div>
|
></div>
|
||||||
{/each}
|
{/each}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 flex justify-between text-xs">
|
<div class="mb-4 flex justify-between text-xs">
|
||||||
<span>{formatTime(currentPosition)}</span>
|
<span>{formatTime(currentPosition)}</span>
|
||||||
<span>{formatTime(TOTAL_MS)}</span>
|
<span>{formatTime(gameOver ? fullDuration : TOTAL_MS)}</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- play/pause -->
|
<!-- Play/Pause -->
|
||||||
<div class="mb-4 flex justify-center">
|
<div class="mb-4 flex justify-center">
|
||||||
<button
|
<button
|
||||||
on:click={togglePlayPause}
|
on:click={togglePlayPause}
|
||||||
class="flex h-16 w-16 items-center justify-center rounded-full border-2 disabled:opacity-50"
|
class="flex h-16 w-16 items-center justify-center rounded-full border-2 disabled:opacity-50"
|
||||||
style="border-color:{COLORS.primary}"
|
style="border-color:{COLORS.primary}"
|
||||||
>
|
>
|
||||||
<Icon src={isPlaying ? Pause : Play} class="h-8 w-8" style="color: {COLORS.primary}" solid />
|
<Icon src={isPlaying ? Pause : Play} class="h-8 w-8" style="color:{COLORS.primary}" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- guess & skip/submit or final -->
|
<!-- Guess & Skip/Submit -->
|
||||||
{#if !gameOver}
|
{#if !gameOver}
|
||||||
<div class="mb-4 flex-shrink-0 px-4">
|
<div class="mb-4">
|
||||||
<input
|
<input
|
||||||
bind:this={inputEl}
|
bind:this={inputEl}
|
||||||
type="text"
|
type="text"
|
||||||
@@ -492,19 +527,16 @@
|
|||||||
bind:value={userInput}
|
bind:value={userInput}
|
||||||
on:keydown={onInputKeydown}
|
on:keydown={onInputKeydown}
|
||||||
class="w-full rounded border px-3 py-2"
|
class="w-full rounded border px-3 py-2"
|
||||||
style="
|
style="border-color:{COLORS.primary};background:{darkMode
|
||||||
border-color: {COLORS.primary};
|
? COLORS.text
|
||||||
background: {darkMode ? COLORS.text : COLORS.background};
|
: COLORS.background};color:{darkMode ? COLORS.background : COLORS.text}"
|
||||||
color: {darkMode ? COLORS.background : COLORS.text};
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
{#if suggestions.length}
|
{#if suggestions.length}
|
||||||
<ul
|
<ul
|
||||||
class="mt-1 max-h-36 overflow-y-auto rounded border"
|
class="mt-1 max-h-36 overflow-y-auto rounded border"
|
||||||
style="
|
style="border-color:{darkMode ? COLORS.background : COLORS.text};background:{darkMode
|
||||||
border-color: {darkMode ? COLORS.background : COLORS.text};
|
? COLORS.text
|
||||||
background: {darkMode ? COLORS.text : COLORS.background};
|
: COLORS.background}"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{#each suggestions as s}
|
{#each suggestions as s}
|
||||||
<li>
|
<li>
|
||||||
@@ -521,17 +553,13 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-6 flex justify-between px-4">
|
<div class="flex justify-between">
|
||||||
<button
|
<button
|
||||||
on:click={skipIntro}
|
on:click={skipIntro}
|
||||||
class="rounded px-4 py-2 font-semibold"
|
class="rounded px-4 py-2 font-semibold"
|
||||||
style="background:{COLORS.primary};color:{COLORS.background}"
|
style="background:{COLORS.primary};color:{COLORS.background}"
|
||||||
>
|
>
|
||||||
{#if nextIncrementSec > 0}
|
{#if nextIncrementSec > 0}Skip (+{nextIncrementSec}s){:else}I don't know it{/if}
|
||||||
Skip (+{nextIncrementSec}s)
|
|
||||||
{:else}
|
|
||||||
I don't know it
|
|
||||||
{/if}
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
on:click={submitGuess}
|
on:click={submitGuess}
|
||||||
@@ -542,15 +570,9 @@
|
|||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="mt-6 text-center text-lg"
|
|
||||||
style="color: {darkMode ? COLORS.background : COLORS.text}"
|
|
||||||
>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Tailwind in app.css handles all spacing/layout */
|
/* Tailwind in app.css handles all spacing/layout */
|
||||||
|
Reference in New Issue
Block a user