- Servers: live status, players and TPS; add a server and get its token with copy-paste setup for the plugin/mod and authlib-injector; detail page with leaderboard (sortable), online players and activity feed - Capes: upload, preview, and choose who can wear each one - Players: click a player for their skin (upload, arm style, reset), cape, playtime and per-server stats; disable with a reason - Settings: Microsoft section replaced by the auth server (public address, Yggdrasil URL, start flag, signing key) - Dashboard: players online now and connected servers - Avatars come from the panel's own skins Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ARcGWxLx21FwXJ3yfGriS
57 lines
2.8 KiB
Svelte
57 lines
2.8 KiB
Svelte
<script lang="ts">
|
|
import { Globe, UserRound, Tag } from '@lucide/svelte';
|
|
import Toggle from './Toggle.svelte';
|
|
import type { Group, ServerDraft } from '../lib/types';
|
|
|
|
let { draft = $bindable(), groups }: { draft: ServerDraft; groups: Group[] } = $props();
|
|
|
|
const accessOptions = [
|
|
{ id: 'all', label: 'Anyone', help: 'Anyone who can connect. Panel accounts are still checked for bans.', icon: Globe },
|
|
{ id: 'members', label: 'Panel accounts', help: 'Only players with an active account on this panel.', icon: UserRound },
|
|
{ id: 'groups', label: 'Groups', help: 'Only accounts in the groups you pick (admins always get in).', icon: Tag },
|
|
] as const;
|
|
</script>
|
|
|
|
<label class="field">Name<input bind:value={draft.name} maxlength="48" placeholder="Survival SMP" /></label>
|
|
<div class="field">
|
|
<span class="lbl">Who can join</span>
|
|
<div class="opts">
|
|
{#each accessOptions as o}
|
|
<button type="button" class="opt" class:on={draft.access === o.id} onclick={() => (draft.access = o.id)}>
|
|
<o.icon size={16} /><span><strong>{o.label}</strong><span class="tiny muted">{o.help}</span></span>
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
{#if draft.access === 'groups'}
|
|
<div class="row wrap">
|
|
{#each groups as g}
|
|
{@const on = draft.allowed_groups.includes(g.name)}
|
|
<button type="button" class="gpick" class:on style:--c={g.color} onclick={() => (draft.allowed_groups = on ? draft.allowed_groups.filter((x) => x !== g.name) : [...draft.allowed_groups, g.name])}>
|
|
<span class="dot" style:background={g.color}></span>{g.name}
|
|
</button>
|
|
{:else}
|
|
<span class="muted small">Create groups on the Players page first.</span>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
<Toggle
|
|
bind:checked={draft.require_launcher}
|
|
label="Must join through the launcher"
|
|
help="Accounts are only let in if they launched the game from the SCOPENET launcher on the same network in the last 12 hours. Useful for offline-mode servers."
|
|
/>
|
|
|
|
<style>
|
|
.field { display: flex; flex-direction: column; gap: 7px; }
|
|
.lbl { font-size: 0.85rem; color: var(--text-2); font-weight: 500; }
|
|
.opts { display: flex; flex-direction: column; gap: 6px; }
|
|
.opt { justify-content: flex-start; gap: 12px; padding: 10px 12px; text-align: left; white-space: normal; background: var(--bg-2); }
|
|
.opt > span { display: flex; flex-direction: column; gap: 2px; }
|
|
.opt :global(svg) { color: var(--muted); flex-shrink: 0; }
|
|
.opt.on { border-color: color-mix(in srgb, var(--accent) 60%, transparent); background: var(--accent-soft); }
|
|
.opt.on :global(svg) { color: var(--accent-2); }
|
|
.gpick { padding: 6px 12px; border-radius: 99px; font-size: 0.85rem; background: var(--bg-2); }
|
|
.gpick.on { border-color: var(--c); background: color-mix(in srgb, var(--c) 18%, transparent); }
|
|
.dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
</style>
|