feat(landing): join-flow rework, player rosters, account page, mod list

Execute plan/18 (WS1-6) + plan/17.

- WS1: homepage join = 3 steps off packwiz (register -> UlicraftLauncher
  -> join); per-OS downloads from a synced launcher-manifest.json (schema +
  example committed); Fjord moved to its own /fjord page. Drop packwizUrl.
- WS2/3: mc-status gains an isolated /players endpoint (admin login ->
  Drasl GET /players, own client+TTL+token cache, never blocks the status
  path); online + registered rosters render shared name+avatar tiles;
  AVATAR_MODE env (default headiso).
- WS4: /account skin CRUD via browser-direct Drasl (login -> upload ->
  reset), in-memory token, players[0]; register relinks "Manage it here".
- WS5: footer link to status.${BASE_DOMAIN} in every footer.
- WS6: tooling/build-modlist.py generates mods.json + extracted logos from
  the distribution forgemods; ModList.astro renders a flat list; stat tile
  fed from the count.

Manifest/mods loaders read at build with a process.cwd() fallback (the
import.meta.url-only path does not resolve in Astro's bundled build).

New env: AVATAR_MODE, DRASL_ADMIN_USERNAME, DRASL_ADMIN_PASSWORD (mc-status
only). Generated mods.json / launcher-manifest.json / logos are gitignored;
.example.json files document the shapes. Build: 12 pages; mc-status builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
oierandClaude Opus 4.8 committed 2026-06-10 18:17:14 +02:00
1 parent 096ef82420
commit 6c7e259fcb
24 files changed
+2319 -117

No files matched your search

+5 -1
View File
@@ -6,6 +6,7 @@ import {
LANG_LABEL,
LANG_PATH,
LANG_REGISTER_PATH,
LANG_ACCOUNT_PATH,
type Lang,
} from "../../i18n/ui";
import Creeper from "../../components/Creeper.astro";
@@ -118,7 +119,7 @@ const clientCfg = {
</form>
<p class="reg-foot">
{r.haveAccount} <a href={site.authUrl}>{r.loginLink}</a>
{r.haveAccount} <a href={LANG_ACCOUNT_PATH[locale]}>{r.loginLink}</a>
</p>
<!-- SUCCESS + SKIN -->
@@ -167,6 +168,9 @@ const clientCfg = {
<Creeper />
<span class="name">ULICRAFT</span>
</a>
<div class="footer-links">
<a class="mc-btn sm" href={site.statusUrl} target="_blank" rel="noopener">{t.footer.status}</a>
</div>
<p class="disc">
{t.footer.disc} {site.name} · {site.baseDomain}
</p>