v0.4.1: OSS skill videos, site version footer, and mail contact.

Host ability demos on Aliyun OSS instead of Pages bundles; add patches-page version label and top-right mailto link; improve spirit bear portrait export.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
voson
2026-07-27 20:26:30 +08:00
co-authored by Cursor
parent adb8f303c2
commit 124bdbb7c7
11 changed files with 289 additions and 22 deletions
+40 -3
View File
@@ -2,6 +2,7 @@
Usage:
python export_relations_site.py [--out dist/relations] [--with-videos]
[--ability-video-base URL]
Copies web/relations/ + a snapshot of the /api/data payload (data.json) +
the referenced image assets into one directory, ready for any static host
@@ -13,13 +14,15 @@ Notes:
- For patch-notes names/icons on the 版本 page, run `python fetch_patches.py`
first (downloads referenced item + ability icons into assets/).
- Ability videos (several GB locally) are skipped unless --with-videos is
given; the UI degrades quietly when a video is missing.
given; production deploys point the UI at OSS via --ability-video-base
(or ABILITY_VIDEO_BASE env) instead of bundling videos into Pages.
"""
from __future__ import annotations
import argparse
import json
import os
import shutil
from pathlib import Path
@@ -36,6 +39,8 @@ from common import (
)
from serve_relations import WEB_DIR, build_payload
SITE_VERSION = "0.4.1"
def copy_glob(src: Path, dst: Path, pattern: str = "*.png") -> int:
if not src.is_dir():
@@ -49,6 +54,21 @@ def copy_glob(src: Path, dst: Path, pattern: str = "*.png") -> int:
return n
def write_config_js(out: Path, ability_video_base: str, site_version: str) -> None:
"""Write config.js consumed by app.js (SITE_VERSION, ABILITY_VIDEO_BASE)."""
base = (ability_video_base or "").strip().rstrip("/")
ver = (site_version or "").strip()
# JSON-encode so values are safe JS string literals.
ver_lit = json.dumps(ver, ensure_ascii=False)
base_lit = json.dumps(base, ensure_ascii=False)
(out / "config.js").write_text(
f"/* generated by export_relations_site.py — do not edit */\n"
f"var SITE_VERSION = {ver_lit};\n"
f"var ABILITY_VIDEO_BASE = {base_lit};\n",
encoding="utf-8",
)
def main() -> None:
ap = argparse.ArgumentParser(
description="Export relations preview as a static site"
@@ -59,8 +79,20 @@ def main() -> None:
action="store_true",
help="also copy assets/ability_videos (several GB)",
)
ap.add_argument(
"--ability-video-base",
default=None,
help="public base URL for ability demos (writes config.js); "
"falls back to ABILITY_VIDEO_BASE env, else empty (same-origin)",
)
args = ap.parse_args()
video_base = (
args.ability_video_base
if args.ability_video_base is not None
else os.environ.get("ABILITY_VIDEO_BASE", "")
)
out = Path(args.out).resolve()
if out == ROOT.resolve() or out.parent == out:
raise SystemExit(f"refusing unsafe --out: {out}")
@@ -69,8 +101,12 @@ def main() -> None:
out.mkdir(parents=True)
# Frontend (uses relative paths — works from any sub-path).
for name in ("index.html", "router.js", "app.js", "style.css"):
shutil.copy2(WEB_DIR / name, out / name)
# `_headers` is a Cloudflare Pages config file (cache TTLs for HTML/JSON/JS).
for name in ("index.html", "router.js", "app.js", "style.css", "_headers"):
src = WEB_DIR / name
if src.is_file():
shutil.copy2(src, out / name)
write_config_js(out, video_base, SITE_VERSION)
# Data snapshot (same payload as serve_relations /api/data).
payload = build_payload()
@@ -124,6 +160,7 @@ def main() -> None:
print(f" {name}/: {n} files")
if args.with_videos:
print(f" ability-video/: {n_videos} files")
print(f" config.js SITE_VERSION={SITE_VERSION!r} ABILITY_VIDEO_BASE={video_base!r}")
print(f" total: {total / 1e6:.1f} MB")
print(f"local check: python -m http.server -d {out} 8080")
print("deploy: upload the directory to any static host (Pages / nginx / ...)")