New web project with Beat Saber tools

This commit is contained in:
Brian Lee
2025-08-09 00:16:28 -07:00
commit 6c2066d784
41 changed files with 11037 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts">
const links = [
{ href: '/', label: 'Home' },
{ href: '/tools', label: 'Tools' },
{ href: '/guides', label: 'Guides' }
];
let open = false;
const toggle = () => (open = !open);
const close = () => (open = false);
const year = new Date().getFullYear();
</script>
<header class="sticky top-0 z-40 backdrop-blur supports-[backdrop-filter]:bg-surface/50 border-b border-white/10">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-14 items-center justify-between">
<a href="/" class="flex items-center gap-2">
<span class="h-2 w-2 rounded-full bg-neon" style="box-shadow: 0 0 12px rgba(34,211,238,0.60);"></span>
<span class="font-display text-lg tracking-widest">
<span class="neon-text">PLEBSABER</span><span class="text-muted">.stream</span>
</span>
</a>
<nav class="hidden md:flex items-center gap-6">
{#each links as link}
<a href={link.href} class="text-muted hover:text-white transition">{link.label}</a>
{/each}
<a href="/tools" class="btn-neon">Launch Tools</a>
</nav>
<button class="md:hidden btn-neon px-3 py-1.5" on:click={toggle} aria-expanded={open} aria-controls="mobile-nav">
Menu
</button>
</div>
</div>
{#if open}
<div id="mobile-nav" class="md:hidden border-t border-white/10 bg-surface/80">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-3 grid gap-3">
{#each links as link}
<a href={link.href} on:click={close} class="text-muted hover:text-white transition">{link.label}</a>
{/each}
<a href="/tools" on:click={close} class="btn-neon w-max">Launch Tools</a>
</div>
</div>
{/if}
</header>
+51
View File
@@ -0,0 +1,51 @@
<script lang="ts">
import { songPlayerStore, currentTimeStore, togglePlay, setVolume } from '$lib/stores/songPlayer';
export let hash: string;
export let preferBeatLeader = false;
let isCurrent = false;
$: isCurrent = $songPlayerStore?.currentHash === hash;
$: currentTime = isCurrent ? $currentTimeStore : 0;
function onToggle() {
togglePlay(hash, preferBeatLeader);
}
function onVolumeInput(e: Event) {
const v = Number((e.target as HTMLInputElement).value);
setVolume(v);
}
</script>
<div class="player">
<button class="play" on:click={onToggle} aria-label="Play/Pause">
{#if isCurrent && $songPlayerStore?.playing}
❚❚
{:else}
{/if}
</button>
<div class="timeline" title="Progress">
<div class="progress" style="width: {($songPlayerStore?.currentHash ? (currentTime / ($songPlayerStore?.duration || 1)) : 0) * 100}%"></div>
</div>
<div class="time">
{Math.floor(currentTime / 60)}:{String(Math.floor(currentTime % 60)).padStart(2, '0')} /
{Math.floor(($songPlayerStore?.duration || 0) / 60)}:{String(Math.floor(($songPlayerStore?.duration || 0) % 60)).padStart(2, '0')}
</div>
<div class="volume">
<input type="range" min="0" max="1" step="0.01" value={$songPlayerStore?.volume} on:input={onVolumeInput} />
</div>
<slot />
</div>
<style>
.player { display: flex; align-items: center; gap: 0.5rem; }
.play { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,0.15); border-radius: 6px; background: transparent; color: white; cursor: pointer; }
.timeline { flex: 1; height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; }
.progress { height: 100%; background: rgba(255,255,255,0.6); }
.time { font-size: 11px; opacity: 0.8; min-width: 80px; text-align: right; }
.volume input { width: 80px; }
</style>