Add Tauri launcher with Svelte UI

- Accounts: panel login/sign-up, Microsoft device-code sign-in, offline
  play; tokens encrypted with a key kept in the OS credential store
- Home: instance rail, live server status, news feed, social links,
  Play button with download progress, speed and ETA
- Full settings: accounts, memory/resolution/after-launch behaviour,
  Java and GC presets, Minecraft keybind editor applied to options.txt,
  theme overrides, storage manager, self-update via GitHub releases
- Game console, crash dialog with hints, per-instance overrides
- Live admin branding (colours, fonts, backgrounds, custom CSS)
- Online install tests for vanilla/Fabric/Quilt/Forge/NeoForge (CI)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011ARcGWxLx21FwXJ3yfGriS
This commit is contained in:
Claude committed 2026-09-28 05:00:30 +00:00
1 parent af9147ec75
commit 50eb1a0cac
60 files changed
+9424 -47

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
<script lang="ts">
// Full-window backdrop: instance banner → admin background → animated gradient.
import { abs, app, selectedInstance } from '../lib/store.svelte';
const b = $derived(app.manifest?.branding);
const inst = $derived(selectedInstance());
const image = $derived(abs(inst?.banner_url) ?? (b?.background.kind === 'image' ? abs(b.background.url) : null));
const video = $derived(!inst?.banner_url && b?.background.kind === 'video' && app.settings?.background_video !== false && !app.running ? abs(b.background.url) : null);
const blur = $derived(b?.background.blur ?? 0);
const dim = $derived((b?.background.dim ?? 45) / 100);
let hidden = $state(false);
</script>
<svelte:document onvisibilitychange={() => (hidden = document.hidden)} />
<div class="bg" aria-hidden="true">
{#key image ?? video ?? 'gradient'}
<div class="layer">
{#if video && !hidden}
<video src={video} autoplay muted loop playsinline style:filter="blur({blur}px)"></video>
{:else if image}
<div class="img" style:background-image="url(&quot;{image}&quot;)" style:filter="blur({blur}px)"></div>
{:else}
<div class="gradient"><span class="o1"></span><span class="o2"></span><span class="o3"></span></div>
{/if}
</div>
{/key}
<div class="shade" style:--dim={dim}></div>
<div class="grain"></div>
</div>
<style>
.bg { position: fixed; inset: 0; z-index: 0; overflow: hidden; background: var(--bg); }
.layer { position: absolute; inset: 0; animation: fade 0.6s ease; }
video, .img { position: absolute; inset: -2rem; width: calc(100% + 4rem); height: calc(100% + 4rem); object-fit: cover; background-size: cover; background-position: center; }
.gradient { position: absolute; inset: 0; }
.gradient span { position: absolute; border-radius: 50%; filter: blur(5rem); opacity: 0.55; will-change: transform; }
.o1 { width: 55vw; height: 55vw; right: -12vw; top: -22vw; background: var(--accent); animation: drift 22s ease-in-out infinite alternate; }
.o2 { width: 45vw; height: 45vw; left: -10vw; bottom: -25vw; background: var(--accent-2); opacity: 0.3 !important; animation: drift 28s ease-in-out infinite alternate-reverse; }
.o3 { width: 30vw; height: 30vw; left: 35vw; top: 30vh; background: color-mix(in srgb, var(--accent) 50%, var(--accent-2)); opacity: 0.18 !important; animation: drift 34s ease-in-out infinite alternate; }
@keyframes drift { to { transform: translate(-6vw, 5vh) scale(1.15); } }
.shade {
position: absolute; inset: 0;
background:
linear-gradient(90deg, color-mix(in srgb, var(--bg) calc(var(--dim) * 100% + 30%), transparent) 0%, color-mix(in srgb, var(--bg) calc(var(--dim) * 100%), transparent) 45%, transparent 80%),
linear-gradient(0deg, color-mix(in srgb, var(--bg) 85%, transparent) 0%, transparent 45%),
color-mix(in srgb, var(--bg) calc(var(--dim) * 60%), transparent);
}
.grain { position: absolute; inset: 0; opacity: 0.035; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
</style>