Files
SCOPENET-MC/launcher/src/pages/Commands.svelte
T

154 lines
12 KiB
Svelte

<script lang="ts">
import { Terminal, Search, Copy, Check } from '@lucide/svelte';
type Cmd = { usage: string; does: string; perm: string; aliases?: string[] };
type Group = { title: string; blurb: string; cmds: Cmd[] };
const groups: Group[] = [
{ title: 'Getting around', blurb: 'Teleports, homes and warps.', cmds: [
{ usage: '/spawn', does: 'Teleport to the server spawn.', perm: 'scopenet.command.spawn', aliases: ['hub', 'lobby'] },
{ usage: '/home [name]', does: 'Teleport to a home, or open the homes menu.', perm: 'scopenet.command.home', aliases: ['homes'] },
{ usage: '/sethome [name]', does: 'Save your current spot as a home.', perm: 'scopenet.command.sethome' },
{ usage: '/delhome <name>', does: 'Delete one of your homes.', perm: 'scopenet.command.delhome', aliases: ['rmhome'] },
{ usage: '/back', does: 'Return to where you were, or where you died.', perm: 'scopenet.command.back', aliases: ['return'] },
{ usage: '/warp [name]', does: 'Go to a server warp, or open the warps menu.', perm: 'scopenet.command.warp', aliases: ['warps'] },
{ usage: '/reqwarp <name>', does: 'Request a public warp at your current location.', perm: 'scopenet.command.reqwarp' },
{ usage: '/warprequests', does: 'Staff: list pending warp requests.', perm: 'scopenet.command.warp.manage' },
{ usage: '/warpapprove <name> · /warpdeny <name>', does: 'Staff: approve or deny a warp request.', perm: 'scopenet.command.warp.manage' },
{ usage: '/setwarp <name> · /delwarp <name>', does: 'Staff: create or delete public warps.', perm: 'scopenet.command.warp.manage' },
{ usage: '/rtp', does: 'Jump to a random safe spot in the wild.', perm: 'scopenet.command.rtp', aliases: ['wild', 'randomtp'] },
{ usage: '/tpa <player>', does: 'Ask to teleport to another player.', perm: 'scopenet.command.tpa' },
{ usage: '/tpaccept', does: 'Accept a teleport request.', perm: 'scopenet.command.tpaccept', aliases: ['tpyes'] },
{ usage: '/tpdeny', does: 'Decline a teleport request.', perm: 'scopenet.command.tpdeny', aliases: ['tpno'] },
] },
{ title: 'Money & trading', blurb: 'Your balance, the shop, the market and player trades.', cmds: [
{ usage: '/balance', does: 'Check your balance.', perm: 'scopenet.command.balance', aliases: ['bal', 'money'] },
{ usage: '/pay <player> <amount>', does: 'Send money to a player.', perm: 'scopenet.command.pay' },
{ usage: '/baltop', does: 'See the richest players.', perm: 'scopenet.command.baltop', aliases: ['richest'] },
{ usage: '/shop', does: 'Open the server shop.', perm: 'scopenet.command.shop' },
{ usage: '/sell hand', does: 'Sell the item in your hand to the shop.', perm: 'scopenet.command.sell.hand' },
{ usage: '/sell', does: 'Open the selling menu.', perm: 'scopenet.command.sell' },
{ usage: '/market', does: 'Browse and buy from the player market.', perm: 'scopenet.command.market', aliases: ['ah', 'auction'] },
{ usage: '/market sell <price>', does: 'List the item in your hand on the market.', perm: 'scopenet.command.market.sell' },
{ usage: '/market point add <name> [market|shop]', does: 'Staff: turn the block you look at into a shop that opens the market or shop when right-clicked. Also shown on the map.', perm: 'scopenet.command.market.point' },
{ usage: '/orders', does: 'View your active market orders.', perm: 'scopenet.command.orders' },
{ usage: '/trade <player>', does: 'Start a secure trade with another player.', perm: 'scopenet.command.trade' },
{ usage: '/transactions', does: 'See your recent money movements.', perm: 'scopenet.command.transactions' },
] },
{ title: 'Guilds & land', blurb: 'Team up, claim land and protect it.', cmds: [
{ usage: '/guild', does: 'Open your guild dashboard.', perm: 'scopenet.command.guild', aliases: ['g', 'clan'] },
{ usage: '/guild create <name> <tag>', does: 'Found a new guild.', perm: 'scopenet.command.guild.create' },
{ usage: '/guild leave', does: 'Leave your guild.', perm: 'scopenet.command.guild.leave' },
{ usage: '/guild members', does: 'List your guild members.', perm: 'scopenet.command.guild.members' },
{ usage: '/guild chat <message>', does: 'Talk to your guild only.', perm: 'scopenet.command.guild.chat', aliases: ['/guild c'] },
{ usage: '/guild sethome · /guild home', does: 'Set or visit the guild home.', perm: 'scopenet.command.guild.sethome' },
{ usage: '/guild map', does: 'Show nearby claims on a map.', perm: 'scopenet.command.guild.map' },
{ usage: '/guild invite <player>', does: 'Invite a player to your guild (leaders and officers).', perm: 'scopenet.command.guild.invite' },
{ usage: '/guild accept [tag] · /guild decline [tag]', does: 'Answer a guild invitation. You can also answer in the launcher.', perm: 'scopenet.command.guild.accept' },
{ usage: '/claim', does: 'Claim the chunk you stand in for your guild.', perm: 'scopenet.command.claim' },
{ usage: '/unclaim', does: 'Give up the chunk you stand in.', perm: 'scopenet.command.unclaim' },
{ usage: '/autoclaim on|off', does: 'Automatically claim new chunks as you walk.', perm: 'scopenet.command.claim' },
] },
{ title: 'Guild bank', blurb: 'Shared guild money. Officers and leaders can spend it.', cmds: [
{ usage: '/guild bank', does: 'Show the bank balance and recent activity.', perm: 'scopenet.command.guild.bank' },
{ usage: '/guild bank deposit <amount>', does: 'Put your own money into the bank.', perm: 'scopenet.command.guild.bank.deposit' },
{ usage: '/guild bank withdraw <amount>', does: 'Take money out (officers and leaders).', perm: 'scopenet.command.guild.bank.withdraw' },
{ usage: '/guild sell hand', does: 'Sell your held item and pay the bank.', perm: 'scopenet.command.guild.sell' },
{ usage: '/guild market', does: 'Buy and sell on the market as your guild.', perm: 'scopenet.command.guild.market' },
{ usage: '/guild pay <tag> <amount>', does: 'Pay another guild from your bank.', perm: 'scopenet.command.guild.pay' },
] },
{ title: 'Info & admin', blurb: 'Status and help.', cmds: [
{ usage: '/playtime', does: 'Check your total and session playtime.', perm: 'scopenet.command.playtime', aliases: ['ontime'] },
{ usage: '/hand', does: 'Share your held item as an inspectable chat link.', perm: 'scopenet.command.hand' },
{ usage: '/nick <name|off>', does: 'Set or remove your chat nickname.', perm: 'scopenet.command.nick' },
{ usage: '/scopenet help', does: 'List SCOPENET commands.', perm: 'scopenet.command.scopenet.help', aliases: ['sn'] },
{ usage: '/scopenet status', does: 'Panel connection, features and plugin integrations (admins).', perm: 'scopenet.command.scopenet.status' },
{ usage: '/scopenet reload', does: 'Reload the plugin config (admins).', perm: 'scopenet.command.scopenet.reload' },
] },
];
const tips = [
'Placeholders: use %scopenet_level%, %scopenet_guild%, %scopenet_balance% and more in scoreboards, tab and chat.',
'Right-click a shop point (a marked block) to open the market or shop. On a server with the SCOPENET client mod it opens the shop window.',
'Some commands may be switched off or limited by the server. If one says you lack permission, ask an admin.',
'Everything here also works through your rank: servers using LuckPerms can grant each permission node to any group.',
];
let query = $state('');
let copied = $state('');
const q = $derived(query.trim().toLowerCase());
const shown = $derived(
groups
.map((g) => ({ ...g, cmds: g.cmds.filter((c) => !q || `${c.usage} ${c.does} ${c.perm} ${(c.aliases ?? []).join(' ')}`.toLowerCase().includes(q)) }))
.filter((g) => g.cmds.length)
);
async function copy(c: Cmd) {
const text = c.usage.split(' · ')[0];
try { await navigator.clipboard.writeText(text); } catch { /* clipboard may be unavailable */ }
copied = c.usage;
setTimeout(() => (copied = ''), 1200);
}
</script>
<div class="page">
<div class="head">
<div>
<h1><Terminal size={26} class="ic" /> Command guide</h1>
<p class="lead">Everything you can type in-game on SCOPENET servers. Click a command to copy it.</p>
</div>
<label class="search"><Search size={15} /><input bind:value={query} placeholder="Search commands" aria-label="Search commands" /></label>
</div>
{#each shown as g (g.title)}
<section class="group">
<div class="gh"><h2>{g.title}</h2><span class="muted tiny">{g.blurb}</span></div>
<div class="list">
{#each g.cmds as c (c.usage)}
<button class="cmd glass" onclick={() => copy(c)} title="Copy command">
<code class="usage">{c.usage}</code>
<span class="does">{c.does}</span>
<span class="meta">
{#each c.aliases ?? [] as a}<span class="alias">/{a.replace(/^\//, '')}</span>{/each}
<span class="perm" title="LuckPerms permission node">{c.perm}</span>
</span>
<span class="copy">{#if copied === c.usage}<Check size={14} />{:else}<Copy size={14} />{/if}</span>
</button>
{/each}
</div>
</section>
{:else}
<p class="muted">No commands match “{query}”.</p>
{/each}
<section class="tips glass">
<h2>Good to know</h2>
<ul>{#each tips as t}<li>{t}</li>{/each}</ul>
</section>
</div>
<style>
.page { height: 100%; display: flex; flex-direction: column; overflow-y: auto; min-height: 0; padding: 1.6rem 2.2rem 2.5rem; gap: 1.2rem; }
.head { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; flex-wrap: wrap; border-bottom: 1px solid var(--line); padding-bottom: 1rem; }
h1 { font-size: 1.5rem; font-weight: 700; display: flex; align-items: center; gap: 0.6rem; margin: 0; }
:global(.ic) { color: var(--accent); }
.lead { color: var(--muted); font-size: 0.88rem; margin: 0.2rem 0 0; }
.search { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.75rem; border-radius: var(--radius-sm); border: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 80%, transparent); min-width: 14rem; }
.search input { background: transparent; border: none; outline: none; color: var(--text); flex: 1; font-size: 0.85rem; }
.group { display: flex; flex-direction: column; gap: 0.6rem; }
.gh { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; }
.gh h2 { font-size: 1.05rem; margin: 0; }
.list { display: grid; grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); gap: 0.6rem; }
.cmd { position: relative; display: flex; flex-direction: column; gap: 0.3rem; text-align: left; padding: 0.75rem 2.2rem 0.75rem 0.9rem; border-radius: var(--radius-sm); border: 1px solid var(--line); transition: border-color 0.15s, transform 0.15s; }
.cmd:hover { border-color: var(--accent); transform: translateY(-1px); }
.usage { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.88rem; font-weight: 650; color: var(--accent); }
.does { font-size: 0.83rem; color: var(--text); line-height: 1.35; }
.meta { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }
.alias { font-size: 0.68rem; padding: 0.05rem 0.45rem; border-radius: 99rem; background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--muted); }
.perm { font-family: ui-monospace, monospace; font-size: 0.66rem; color: var(--muted); opacity: 0.85; }
.copy { position: absolute; top: 0.7rem; right: 0.75rem; color: var(--muted); }
.tips { padding: 1rem 1.2rem; border-radius: var(--radius-sm); border: 1px solid var(--line); }
.tips h2 { font-size: 0.95rem; margin: 0 0 0.4rem; }
.tips ul { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.83rem; color: var(--muted); }
</style>