feat(landing): rename crew to "El valle" + source downloads from launcher.json
Rename all "crew" mentions (en/es/eu) to the untranslated proper name "El valle" across hero, members, and features copy in i18n/ui.ts. Rewire the homepage launcher download list to the custom-launcher build output launcher.json (productName/version/files[]) instead of the never-produced launcher-manifest.json shape. site.ts now reads launcher.json and normalizes files[] -> internal builds[]; launcher.example.json is the committed fallback. Drop the stale launcher-manifest.example/schema, update .gitignore and docs (CLAUDE.md, plan/18). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -20,9 +20,10 @@ landing/node_modules/
|
|||||||
landing/.astro/
|
landing/.astro/
|
||||||
landing/dist/
|
landing/dist/
|
||||||
|
|
||||||
# launcher download manifest: produced/synced by the custom-launcher release
|
# launcher download list: the custom-launcher build output launcher.json,
|
||||||
# flow (the .example.json is committed as the documented shape)
|
# synced from UlicraftLauncher/dist/launcher.json (launcher.example.json is
|
||||||
landing/src/data/launcher-manifest.json
|
# committed as the documented shape)
|
||||||
|
landing/src/data/launcher.json
|
||||||
|
|
||||||
# mod catalogue + extracted logos: generated by tooling/build-modlist.py from
|
# mod catalogue + extracted logos: generated by tooling/build-modlist.py from
|
||||||
# the distribution forgemods (the .example.json is committed as the empty shape)
|
# the distribution forgemods (the .example.json is committed as the empty shape)
|
||||||
|
|||||||
@@ -137,9 +137,10 @@ mirrored at apex `/launcher/`. Per guest:
|
|||||||
Full design settled across WS1–WS6; all tasks unchecked, ready to execute.
|
Full design settled across WS1–WS6; all tasks unchecked, ready to execute.
|
||||||
- [ ] **WS1 — Join flow off packwiz.** Homepage = UlicraftLauncher, 3 steps
|
- [ ] **WS1 — Join flow off packwiz.** Homepage = UlicraftLauncher, 3 steps
|
||||||
(register → download launcher → join); drop `packwizUrl` + packwiz step.
|
(register → download launcher → join); drop `packwizUrl` + packwiz step.
|
||||||
Per-OS downloads from a synced `launcher-manifest.json` (contract:
|
Per-OS downloads from a synced `launcher.json` (shape
|
||||||
`landing/src/data/launcher-manifest.schema.json`, produced by the
|
`productName/version/files[]`, the build output of the `custom-launcher`
|
||||||
`custom-launcher` repo). Fjord moves to its own `/fjord` page.
|
repo; `launcher.example.json` committed as the documented fallback). Fjord
|
||||||
|
moves to its own `/fjord` page.
|
||||||
- [ ] **WS2/3 — Player rosters.** Online (mc-status, live) + all-registered (new
|
- [ ] **WS2/3 — Player rosters.** Online (mc-status, live) + all-registered (new
|
||||||
`mc-status /api/mcstatus/players`, admin-login → Drasl `GET /players`, ~60s
|
`mc-status /api/mcstatus/players`, admin-login → Drasl `GET /players`, ~60s
|
||||||
cache). Shared avatar tile, `AVATAR_MODE` env (default `headiso`). Admin creds
|
cache). Shared avatar tile, `AVATAR_MODE` env (default `headiso`). Admin creds
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"product": "UlicraftLauncher",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"channel": "stable",
|
|
||||||
"releasedAt": "2026-06-01T12:00:00Z",
|
|
||||||
"minecraft": "1.21.1",
|
|
||||||
"neoforge": "21.1.233",
|
|
||||||
"builds": [
|
|
||||||
{
|
|
||||||
"os": "windows",
|
|
||||||
"arch": "x64",
|
|
||||||
"format": "exe",
|
|
||||||
"filename": "UlicraftLauncher-1.0.0-setup.exe",
|
|
||||||
"url": "https://distribution.ulicraft.net/launcher/UlicraftLauncher-1.0.0-setup.exe",
|
|
||||||
"size": 78643200,
|
|
||||||
"sha256": "0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"os": "macos",
|
|
||||||
"arch": "universal",
|
|
||||||
"format": "dmg",
|
|
||||||
"filename": "UlicraftLauncher-1.0.0-universal.dmg",
|
|
||||||
"url": "https://distribution.ulicraft.net/launcher/UlicraftLauncher-1.0.0-universal.dmg",
|
|
||||||
"size": 92274688
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"os": "linux",
|
|
||||||
"arch": "x64",
|
|
||||||
"format": "appimage",
|
|
||||||
"filename": "UlicraftLauncher-1.0.0-x86_64.AppImage",
|
|
||||||
"url": "https://distribution.ulicraft.net/launcher/UlicraftLauncher-1.0.0-x86_64.AppImage",
|
|
||||||
"size": 85983232
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fjordPack": {
|
|
||||||
"filename": "ulicraft-1.21.1.mrpack",
|
|
||||||
"url": "https://distribution.ulicraft.net/launcher/ulicraft-1.21.1.mrpack",
|
|
||||||
"size": 12582912
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
||||||
"$id": "https://ulicraft.net/schemas/launcher-manifest.schema.json",
|
|
||||||
"title": "Ulicraft launcher download manifest",
|
|
||||||
"description": "Produced by the custom-launcher release flow, consumed by the landing build to render per-OS download buttons. One file describes the current UlicraftLauncher release plus the optional Fjord pack.",
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"required": ["schemaVersion", "product", "version", "builds"],
|
|
||||||
"properties": {
|
|
||||||
"schemaVersion": {
|
|
||||||
"description": "Bump on breaking shape changes. Landing pins the major it understands.",
|
|
||||||
"type": "integer",
|
|
||||||
"const": 1
|
|
||||||
},
|
|
||||||
"product": {
|
|
||||||
"description": "Display name of the launcher.",
|
|
||||||
"type": "string",
|
|
||||||
"examples": ["UlicraftLauncher"]
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"description": "Launcher release version (semver, no leading v).",
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^\\d+\\.\\d+\\.\\d+(?:[-+].+)?$"
|
|
||||||
},
|
|
||||||
"channel": {
|
|
||||||
"description": "Release channel.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["stable", "beta"],
|
|
||||||
"default": "stable"
|
|
||||||
},
|
|
||||||
"releasedAt": {
|
|
||||||
"description": "Release timestamp (ISO 8601 / RFC 3339).",
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time"
|
|
||||||
},
|
|
||||||
"minecraft": {
|
|
||||||
"description": "Target Minecraft version the launcher installs.",
|
|
||||||
"type": "string",
|
|
||||||
"examples": ["1.21.1"]
|
|
||||||
},
|
|
||||||
"neoforge": {
|
|
||||||
"description": "NeoForge version the distribution pins.",
|
|
||||||
"type": "string",
|
|
||||||
"examples": ["21.1.233"]
|
|
||||||
},
|
|
||||||
"builds": {
|
|
||||||
"description": "Downloadable launcher binaries, one entry per OS/arch/format.",
|
|
||||||
"type": "array",
|
|
||||||
"minItems": 1,
|
|
||||||
"items": { "$ref": "#/$defs/asset" }
|
|
||||||
},
|
|
||||||
"fjordPack": {
|
|
||||||
"description": "Optional importable pack for the Fjord (Prism) alternative path. Lives on its own landing page, not the homepage.",
|
|
||||||
"$ref": "#/$defs/file"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"$defs": {
|
|
||||||
"asset": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"required": ["os", "arch", "format", "filename", "url"],
|
|
||||||
"properties": {
|
|
||||||
"os": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["windows", "macos", "linux"]
|
|
||||||
},
|
|
||||||
"arch": {
|
|
||||||
"description": "CPU arch; 'universal' for a fat/any binary.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["x64", "arm64", "universal"]
|
|
||||||
},
|
|
||||||
"format": {
|
|
||||||
"description": "Installer/package kind, drives the button hint.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["exe", "msi", "dmg", "pkg", "appimage", "deb", "rpm", "tar.gz", "zip"]
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"description": "Optional human hint override (else derived from os+arch+format).",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"filename": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 1
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"description": "Absolute download URL.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "uri",
|
|
||||||
"pattern": "^https://"
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"description": "Bytes, for showing a size next to the button.",
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
},
|
|
||||||
"sha256": {
|
|
||||||
"description": "Lowercase hex SHA-256 for integrity display/verify.",
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^[a-f0-9]{64}$"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"file": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"required": ["filename", "url"],
|
|
||||||
"properties": {
|
|
||||||
"filename": { "type": "string", "minLength": 1 },
|
|
||||||
"url": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uri",
|
|
||||||
"pattern": "^https://"
|
|
||||||
},
|
|
||||||
"size": { "type": "integer", "minimum": 0 },
|
|
||||||
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
landing/src/data/launcher.example.json
Normal file
39
landing/src/data/launcher.example.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"productName": "Ulicraft",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"baseUrl": "https://distribution.ulicraft.net/launcher",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"name": "Ulicraft-1.0.0.exe",
|
||||||
|
"os": "windows",
|
||||||
|
"arch": "x64",
|
||||||
|
"ext": "exe",
|
||||||
|
"size": 90893289,
|
||||||
|
"url": "https://distribution.ulicraft.net/launcher/Ulicraft-1.0.0.exe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ulicraft-1.0.0.AppImage",
|
||||||
|
"os": "linux",
|
||||||
|
"arch": "x64",
|
||||||
|
"ext": "AppImage",
|
||||||
|
"size": 95722608,
|
||||||
|
"url": "https://distribution.ulicraft.net/launcher/Ulicraft-1.0.0.AppImage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ulicraft-1.0.0.deb",
|
||||||
|
"os": "linux",
|
||||||
|
"arch": "x64",
|
||||||
|
"ext": "deb",
|
||||||
|
"size": 87751876,
|
||||||
|
"url": "https://distribution.ulicraft.net/launcher/Ulicraft-1.0.0.deb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ulicraft-1.0.0.tar.gz",
|
||||||
|
"os": "linux",
|
||||||
|
"arch": "x64",
|
||||||
|
"ext": "tar.gz",
|
||||||
|
"size": 116347746,
|
||||||
|
"url": "https://distribution.ulicraft.net/launcher/Ulicraft-1.0.0.tar.gz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -23,51 +23,66 @@ const AVATAR_MODE = process.env.AVATAR_MODE ?? "headiso";
|
|||||||
// @ts-ignore — `process` is untyped here (no @types/node).
|
// @ts-ignore — `process` is untyped here (no @types/node).
|
||||||
const ROSTER_AVATAR_MODE = process.env.ROSTER_AVATAR_MODE ?? "fullbodyiso";
|
const ROSTER_AVATAR_MODE = process.env.ROSTER_AVATAR_MODE ?? "fullbodyiso";
|
||||||
|
|
||||||
// Launcher download manifest (per launcher-manifest.schema.json). The
|
// UlicraftLauncher download list. Source of truth is the custom-launcher repo's
|
||||||
// custom-launcher release flow syncs launcher-manifest.json into this dir
|
// build output `launcher.json` (shape: productName/version/baseUrl/files[]),
|
||||||
// (gitignored, generated) — same pattern as sync-server-mods.sh. It MAY NOT
|
// published to https://distribution.${BASE}/launcher/launcher.json. The release
|
||||||
// EXIST at build time, so we read it at runtime with fs (a static `import`
|
// flow syncs that file into this dir as `launcher.json` (gitignored, generated)
|
||||||
// would hard-fail the build when absent). Fall back to the committed
|
// — same pattern as sync-server-mods.sh. It MAY NOT EXIST at build time, so we
|
||||||
// .example.json (the documented shape); if even that is unreadable, expose an
|
// read it at runtime with fs (a static `import` would hard-fail the build when
|
||||||
// empty-builds manifest so the homepage simply renders no download buttons.
|
// absent). Fall back to the committed `launcher.example.json` (the documented
|
||||||
|
// shape); if even that is unreadable, expose an empty file list so the homepage
|
||||||
|
// simply renders no download buttons. The raw launcher.json is normalized into
|
||||||
|
// the internal shape the page renders against (builds[]).
|
||||||
type LauncherBuild = {
|
type LauncherBuild = {
|
||||||
os: "windows" | "macos" | "linux";
|
os: string; // "windows" | "macos" | "linux"
|
||||||
arch: "x64" | "arm64" | "universal";
|
arch: string; // "x64" | "arm64" | "universal"
|
||||||
format: "exe" | "msi" | "dmg" | "pkg" | "appimage" | "deb" | "rpm" | "tar.gz" | "zip";
|
format: string; // file extension: "exe" | "dmg" | "AppImage" | "deb" | "tar.gz" | …
|
||||||
label?: string;
|
|
||||||
filename: string;
|
filename: string;
|
||||||
url: string;
|
url: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
sha256?: string;
|
|
||||||
};
|
};
|
||||||
type LauncherFile = { filename: string; url: string; size?: number; sha256?: string };
|
type LauncherFile = { filename: string; url: string; size?: number };
|
||||||
type LauncherManifest = {
|
type LauncherManifest = {
|
||||||
schemaVersion: number;
|
|
||||||
product: string;
|
product: string;
|
||||||
version: string;
|
version: string;
|
||||||
channel?: string;
|
|
||||||
releasedAt?: string;
|
|
||||||
minecraft?: string;
|
|
||||||
neoforge?: string;
|
|
||||||
builds: LauncherBuild[];
|
builds: LauncherBuild[];
|
||||||
|
// Not present in launcher.json; the Fjord page degrades gracefully when absent.
|
||||||
fjordPack?: LauncherFile;
|
fjordPack?: LauncherFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadLauncherManifest(): LauncherManifest {
|
// Raw shape as produced by the custom-launcher build (UlicraftLauncher/dist/launcher.json).
|
||||||
const empty: LauncherManifest = {
|
type RawLauncherJson = {
|
||||||
schemaVersion: 1,
|
productName?: string;
|
||||||
product: "UlicraftLauncher",
|
version?: string;
|
||||||
version: "0.0.0",
|
baseUrl?: string;
|
||||||
builds: [],
|
files?: Array<{ name: string; os: string; arch: string; ext: string; url: string; size?: number }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeLauncher(raw: RawLauncherJson): LauncherManifest {
|
||||||
|
return {
|
||||||
|
product: raw.productName ?? "UlicraftLauncher",
|
||||||
|
version: raw.version ?? "0.0.0",
|
||||||
|
builds: (raw.files ?? []).map((f) => ({
|
||||||
|
os: f.os,
|
||||||
|
arch: f.arch,
|
||||||
|
format: f.ext,
|
||||||
|
filename: f.name,
|
||||||
|
url: f.url,
|
||||||
|
size: f.size,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLauncherManifest(): LauncherManifest {
|
||||||
|
const empty: LauncherManifest = { product: "UlicraftLauncher", version: "0.0.0", builds: [] };
|
||||||
// Resolve relative to this module first, then fall back to a cwd-relative path:
|
// Resolve relative to this module first, then fall back to a cwd-relative path:
|
||||||
// Astro bundles this module so import.meta.url may not point at src/data/ at
|
// Astro bundles this module so import.meta.url may not point at src/data/ at
|
||||||
// build time, but the build runs from the landing root, so <cwd>/src/data/*
|
// build time, but the build runs from the landing root, so <cwd>/src/data/*
|
||||||
// resolves. Prefer the synced launcher-manifest.json, then the committed example.
|
// resolves. Prefer the synced launcher.json, then the committed example.
|
||||||
// @ts-ignore — `process` is untyped here (no @types/node).
|
// @ts-ignore — `process` is untyped here (no @types/node).
|
||||||
const cwd: string = typeof process !== "undefined" ? process.cwd() : ".";
|
const cwd: string = typeof process !== "undefined" ? process.cwd() : ".";
|
||||||
const candidates: Array<string | URL> = [];
|
const candidates: Array<string | URL> = [];
|
||||||
for (const name of ["launcher-manifest.json", "launcher-manifest.example.json"]) {
|
for (const name of ["launcher.json", "launcher.example.json"]) {
|
||||||
try {
|
try {
|
||||||
candidates.push(new URL(name, import.meta.url));
|
candidates.push(new URL(name, import.meta.url));
|
||||||
} catch {
|
} catch {
|
||||||
@@ -77,8 +92,8 @@ function loadLauncherManifest(): LauncherManifest {
|
|||||||
}
|
}
|
||||||
for (const c of candidates) {
|
for (const c of candidates) {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(readFileSync(c, "utf8")) as LauncherManifest;
|
const raw = JSON.parse(readFileSync(c, "utf8")) as RawLauncherJson;
|
||||||
if (parsed && Array.isArray(parsed.builds)) return parsed;
|
if (raw && Array.isArray(raw.files)) return normalizeLauncher(raw);
|
||||||
} catch {
|
} catch {
|
||||||
// try the next candidate (synced file missing → fall back to example)
|
// try the next candidate (synced file missing → fall back to example)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export const ui: Record<Lang, UI> = {
|
|||||||
eyebrow: "Modded LAN · NeoForge",
|
eyebrow: "Modded LAN · NeoForge",
|
||||||
tagline: "Kitchen-sink survival, built to outlast the weekend.",
|
tagline: "Kitchen-sink survival, built to outlast the weekend.",
|
||||||
sub:
|
sub:
|
||||||
"A self-hosted modded server for the crew — a curated tech + magic + " +
|
"A self-hosted modded server for El valle — a curated tech + magic + " +
|
||||||
"exploration pack, your own skins, one-click setup. Grab the address, " +
|
"exploration pack, your own skins, one-click setup. Grab the address, " +
|
||||||
"import the pack, dig in.",
|
"import the pack, dig in.",
|
||||||
copyIp: "Copy IP",
|
copyIp: "Copy IP",
|
||||||
@@ -206,14 +206,14 @@ export const ui: Record<Lang, UI> = {
|
|||||||
},
|
},
|
||||||
statLabels: ["Mods", "NeoForge", "Player slots", "World backups"],
|
statLabels: ["Mods", "NeoForge", "Player slots", "World backups"],
|
||||||
members: {
|
members: {
|
||||||
eyebrow: "The crew",
|
eyebrow: "El valle",
|
||||||
title: "Who's on the server.",
|
title: "Who's on the server.",
|
||||||
lead: "Everyone registered on Ulicraft, with their own self-hosted skin.",
|
lead: "Everyone registered on Ulicraft, with their own self-hosted skin.",
|
||||||
empty: "No members yet — be the first to register.",
|
empty: "No members yet — be the first to register.",
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
eyebrow: "What makes it Ulicraft",
|
eyebrow: "What makes it Ulicraft",
|
||||||
title: "Built for the crew.",
|
title: "Built for El valle.",
|
||||||
lead:
|
lead:
|
||||||
"Self-hosted, curated, and persistent — a modded server that respects " +
|
"Self-hosted, curated, and persistent — a modded server that respects " +
|
||||||
"your time and outlives the LAN.",
|
"your time and outlives the LAN.",
|
||||||
@@ -370,7 +370,7 @@ export const ui: Record<Lang, UI> = {
|
|||||||
eyebrow: "LAN con mods · NeoForge",
|
eyebrow: "LAN con mods · NeoForge",
|
||||||
tagline: "Supervivencia todo incluido, para durar más que el finde.",
|
tagline: "Supervivencia todo incluido, para durar más que el finde.",
|
||||||
sub:
|
sub:
|
||||||
"Un servidor con mods autoalojado para la cuadrilla: un pack curado de " +
|
"Un servidor con mods autoalojado para El valle: un pack curado de " +
|
||||||
"tecnología, magia y exploración, tus propias skins y configuración en un " +
|
"tecnología, magia y exploración, tus propias skins y configuración en un " +
|
||||||
"clic. Copia la dirección, importa el pack y a cavar.",
|
"clic. Copia la dirección, importa el pack y a cavar.",
|
||||||
copyIp: "Copiar IP",
|
copyIp: "Copiar IP",
|
||||||
@@ -388,14 +388,14 @@ export const ui: Record<Lang, UI> = {
|
|||||||
},
|
},
|
||||||
statLabels: ["Mods", "NeoForge", "Plazas", "Copias del mundo"],
|
statLabels: ["Mods", "NeoForge", "Plazas", "Copias del mundo"],
|
||||||
members: {
|
members: {
|
||||||
eyebrow: "La cuadrilla",
|
eyebrow: "El valle",
|
||||||
title: "Quién está en el servidor.",
|
title: "Quién está en el servidor.",
|
||||||
lead: "Todos los registrados en Ulicraft, cada uno con su skin autoalojada.",
|
lead: "Todos los registrados en Ulicraft, cada uno con su skin autoalojada.",
|
||||||
empty: "Aún no hay miembros — sé el primero en registrarte.",
|
empty: "Aún no hay miembros — sé el primero en registrarte.",
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
eyebrow: "Qué hace especial a Ulicraft",
|
eyebrow: "Qué hace especial a Ulicraft",
|
||||||
title: "Hecho para la cuadrilla.",
|
title: "Hecho para El valle.",
|
||||||
lead:
|
lead:
|
||||||
"Autoalojado, curado y persistente: un servidor con mods que respeta tu " +
|
"Autoalojado, curado y persistente: un servidor con mods que respeta tu " +
|
||||||
"tiempo y sobrevive a la LAN.",
|
"tiempo y sobrevive a la LAN.",
|
||||||
@@ -552,7 +552,7 @@ export const ui: Record<Lang, UI> = {
|
|||||||
eyebrow: "Mod-dun LANa · NeoForge",
|
eyebrow: "Mod-dun LANa · NeoForge",
|
||||||
tagline: "Mod ugariko biziraupena, irauteko egina.",
|
tagline: "Mod ugariko biziraupena, irauteko egina.",
|
||||||
sub:
|
sub:
|
||||||
"Koadrilarentzako mod-dun zerbitzari auto-ostatatua: teknologia, magia eta " +
|
"El valle-rentzako mod-dun zerbitzari auto-ostatatua: teknologia, magia eta " +
|
||||||
"esplorazio pack zaindua, zure azalak eta konfigurazioa klik bakarrean. " +
|
"esplorazio pack zaindua, zure azalak eta konfigurazioa klik bakarrean. " +
|
||||||
"Hartu helbidea, inportatu packa eta hasi zulatzen.",
|
"Hartu helbidea, inportatu packa eta hasi zulatzen.",
|
||||||
copyIp: "Kopiatu IPa",
|
copyIp: "Kopiatu IPa",
|
||||||
@@ -570,14 +570,14 @@ export const ui: Record<Lang, UI> = {
|
|||||||
},
|
},
|
||||||
statLabels: ["Modak", "NeoForge", "Plazak", "Munduaren babeskopiak"],
|
statLabels: ["Modak", "NeoForge", "Plazak", "Munduaren babeskopiak"],
|
||||||
members: {
|
members: {
|
||||||
eyebrow: "Koadrila",
|
eyebrow: "El valle",
|
||||||
title: "Nor dago zerbitzarian.",
|
title: "Nor dago zerbitzarian.",
|
||||||
lead: "Ulicraft-en erregistratutako guztiak, bakoitza bere azal auto-ostatatuarekin.",
|
lead: "Ulicraft-en erregistratutako guztiak, bakoitza bere azal auto-ostatatuarekin.",
|
||||||
empty: "Oraindik ez dago kiderik — izan zaitez lehena erregistratzen.",
|
empty: "Oraindik ez dago kiderik — izan zaitez lehena erregistratzen.",
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
eyebrow: "Zerk egiten du Ulicraft berezi",
|
eyebrow: "Zerk egiten du Ulicraft berezi",
|
||||||
title: "Koadrilarentzat eginda.",
|
title: "El valle-rentzat eginda.",
|
||||||
lead:
|
lead:
|
||||||
"Auto-ostatatua, zaindua eta iraunkorra: zure denbora errespetatzen duen " +
|
"Auto-ostatatua, zaindua eta iraunkorra: zure denbora errespetatzen duen " +
|
||||||
"eta LANa gainditzen duen mod-dun zerbitzaria.",
|
"eta LANa gainditzen duen mod-dun zerbitzaria.",
|
||||||
|
|||||||
@@ -35,14 +35,16 @@ authlib step.
|
|||||||
your credentials. (No authlib URL — launcher is preconfigured.)
|
your credentials. (No authlib URL — launcher is preconfigured.)
|
||||||
3. **Join the server** — launch, connect to `site.serverAddress` (IP copy chip).
|
3. **Join the server** — launch, connect to `site.serverAddress` (IP copy chip).
|
||||||
|
|
||||||
**Launcher manifest (delivery = synced local file, option B):**
|
**Launcher list (delivery = synced local file, option B):**
|
||||||
- Contract: `landing/src/data/launcher-manifest.schema.json` (already written;
|
- Source of truth: the `custom-launcher` build output `launcher.json` (shape
|
||||||
JSON-Schema draft 2020-12).
|
`productName/version/baseUrl/files[]`, each file `{name,os,arch,ext,size,url}`),
|
||||||
- The `custom-launcher` release flow **produces** `launcher-manifest.json` and syncs
|
published to `https://distribution.${BASE}/launcher/launcher.json`.
|
||||||
it into `landing/src/data/launcher-manifest.json` (gitignored, generated), same
|
- The release flow syncs it into `landing/src/data/launcher.json` (gitignored,
|
||||||
pattern as `sync-server-mods.sh`.
|
generated), same pattern as `sync-server-mods.sh`. `launcher.example.json`
|
||||||
- Landing **imports** it at build → renders per-OS buttons from `builds[]`. Missing
|
committed as the documented fallback shape.
|
||||||
file → hide the buttons (build must not fail).
|
- Landing reads it at build (`site.ts` `loadLauncherManifest`), normalizes
|
||||||
|
`files[]` → internal `builds[]`, renders per-OS buttons. Missing file → hide
|
||||||
|
the buttons (build must not fail).
|
||||||
|
|
||||||
**Fjord — alternative path, OWN page, off the homepage:** new `/fjord` (+ `/es`,
|
**Fjord — alternative path, OWN page, off the homepage:** new `/fjord` (+ `/es`,
|
||||||
`/eu`). Download Fjord (existing `/launcher/` mirror), add authlib-injector account
|
`/eu`). Download Fjord (existing `/launcher/` mirror), add authlib-injector account
|
||||||
@@ -174,7 +176,7 @@ Tasks:
|
|||||||
- [ ] `site.ts`: feed `stats[0]` count from `mods.json`.
|
- [ ] `site.ts`: feed `stats[0]` count from `mods.json`.
|
||||||
- [ ] Run the generator pre-`pnpm build`; document in `12-build-order.md` /
|
- [ ] Run the generator pre-`pnpm build`; document in `12-build-order.md` /
|
||||||
`14-deploy.md`. `.gitignore`: `landing/src/data/mods.json`,
|
`14-deploy.md`. `.gitignore`: `landing/src/data/mods.json`,
|
||||||
`landing/src/data/launcher-manifest.json`, `landing/src/data/players.json` (if
|
`landing/src/data/launcher.json`, `landing/src/data/players.json` (if
|
||||||
any baked), `landing/public/mod-logos/`.
|
any baked), `landing/public/mod-logos/`.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -182,7 +184,7 @@ Tasks:
|
|||||||
## Verification (whole rework)
|
## Verification (whole rework)
|
||||||
|
|
||||||
- **Build:** `cd landing && set -a && . ../.env && set +a && pnpm run build` then
|
- **Build:** `cd landing && set -a && . ../.env && set +a && pnpm run build` then
|
||||||
`pnpm check`. Must pass offline even if `launcher-manifest.json` / `mods.json` are
|
`pnpm check`. Must pass offline even if `launcher.json` / `mods.json` are
|
||||||
absent (buttons/list hidden).
|
absent (buttons/list hidden).
|
||||||
- `grep -ri packwiz landing/` → no live join references remain (Fjord page may mention
|
- `grep -ri packwiz landing/` → no live join references remain (Fjord page may mention
|
||||||
its own import).
|
its own import).
|
||||||
@@ -201,4 +203,5 @@ Tasks:
|
|||||||
- `15-mc-status.md` — the mc-status service (now also serves the roster).
|
- `15-mc-status.md` — the mc-status service (now also serves the roster).
|
||||||
- `16-landing-registration.md` — the browser-direct Drasl pattern WS4 reuses.
|
- `16-landing-registration.md` — the browser-direct Drasl pattern WS4 reuses.
|
||||||
- `17-mod-shortlist.md` — the mod gap doc + the `mods.json` dataset.
|
- `17-mod-shortlist.md` — the mod gap doc + the `mods.json` dataset.
|
||||||
- `landing/src/data/launcher-manifest.schema.json` — the launcher manifest contract.
|
- `launcher.json` — the `custom-launcher` build output (download list); landing
|
||||||
|
syncs it to `landing/src/data/launcher.json`, `launcher.example.json` committed.
|
||||||
|
|||||||
Reference in New Issue
Block a user