580 lines
23 KiB
JavaScript
580 lines
23 KiB
JavaScript
(function () {
|
|
var catalog = window.GOD_CATALOG || {};
|
|
var root = document.body.dataset.siteRoot || "";
|
|
var assetPrefix = document.body.dataset.assetPrefix || root;
|
|
var detailBase = document.body.dataset.detailBase || "experiments/";
|
|
var pageDefaultLocale = (document.documentElement.lang || "en").toLowerCase().indexOf("zh") === 0 ? "zh" : "en";
|
|
var locale = resolveLocale();
|
|
document.documentElement.lang = locale === "zh" ? "zh-CN" : "en";
|
|
|
|
var STATIC_TEXT = {
|
|
en: {
|
|
"nav.replays": "Replays",
|
|
"nav.maps": "Map Packs",
|
|
"nav.agents": "Agent Packs",
|
|
"nav.experiments": "Experiments",
|
|
"nav.github": "GitHub",
|
|
"experiments.metaTitle": "Experiments - GOD",
|
|
"experiments.eyebrow": "Experiment library",
|
|
"experiments.title": "Create your own world.",
|
|
"experiments.summary": "Experiments are playable setup seeds: map, cast, scenario, steps, and operator notes. Example replays stay separate as watchable results before you run your own version locally.",
|
|
"experiments.footer": "GOD Experiments",
|
|
"experiments.footerReplays": "Replays",
|
|
"mapPacks.metaTitle": "Map Packs - GOD",
|
|
"mapPacks.eyebrow": "Map Pack library",
|
|
"mapPacks.title": "Places agents can inhabit.",
|
|
"mapPacks.summary": "Map packs publish the location graph, Tiled map, pixel assets, interactions, anchors, and sprites needed to run or replay a world.",
|
|
"mapPacks.footer": "GOD Map Packs",
|
|
"agentPacks.metaTitle": "Agent Packs - GOD",
|
|
"agentPacks.eyebrow": "Agent Pack library",
|
|
"agentPacks.title": "Casts with memory, routines, and motives.",
|
|
"agentPacks.summary": "Agent packs expose reusable populations for maps and experiments: personas, daily rhythms, relationships, roles, needs, and replay-visible state.",
|
|
"agentPacks.footer": "GOD Agent Packs",
|
|
"replays.metaTitle": "Replays - GOD",
|
|
"replays.eyebrow": "Example replay",
|
|
"replays.title": "Open a finished world.",
|
|
"replays.summary": "These replays are static example results: timeline frames, map assets, agent profiles, and the recorded operator flow. Use ExperimentPacks when you want a playable setup.",
|
|
"replays.footer": "GOD Replays",
|
|
"replays.home": "Home",
|
|
"replays.developerDocs": "Developer docs",
|
|
"replay.example": "Example replay",
|
|
"replay.open": "Open replay",
|
|
"replay.manifest": "Manifest",
|
|
"replay.operatorRecords": "operator records",
|
|
"home.metaTitle": "GOD - Govern, Observe, Direct",
|
|
"home.badge": "Govern - Observe - Direct",
|
|
"home.subtitle": "An operator console for inspectable agent-society replays.",
|
|
"home.line": "Browse public replays and portable packs.",
|
|
"home.primaryCta": "Browse experiment packs",
|
|
"home.githubCta": "View on GitHub",
|
|
"home.scrollCue": "Scroll down to review the artifacts",
|
|
"home.productsEyebrow": "Public review artifacts",
|
|
"home.productsTitle": "Four artifact hubs.",
|
|
"home.productsSummary": "ExperimentPacks are playable setup seeds. Replays are inspectable results. MapPacks and AgentPacks remain portable dependencies.",
|
|
"home.replaysEyebrow": "Featured replays",
|
|
"home.replaysTitle": "Open an existing replay.",
|
|
"home.replaysSummary": "Timeline, map assets, agent profiles, and operator traces are browser-readable; download the ExperimentPack to reproduce from the same setup.",
|
|
"home.footer": "GOD - Govern, Observe, Direct",
|
|
"home.contact": "Contact:",
|
|
"common.github": "GitHub",
|
|
"common.library": "Library",
|
|
"common.mapPack": "Map Pack",
|
|
"common.agentPack": "Agent Pack",
|
|
"common.locations": "Locations",
|
|
"common.downloadMapPack": "Download Map Pack",
|
|
"common.downloadAgentPack": "Download Agent Pack",
|
|
"common.tiles": "tiles",
|
|
"common.locationsCount": "locations",
|
|
"common.interactions": "interactions",
|
|
"common.agents": "agents",
|
|
"common.profiles": "profiles",
|
|
"common.sprites": "sprites"
|
|
},
|
|
zh: {
|
|
"nav.replays": "回放",
|
|
"nav.maps": "地图包",
|
|
"nav.agents": "角色包",
|
|
"nav.experiments": "实验",
|
|
"nav.github": "GitHub",
|
|
"experiments.metaTitle": "实验 - GOD",
|
|
"experiments.eyebrow": "实验库",
|
|
"experiments.title": "创建你自己的世界。",
|
|
"experiments.summary": "实验是可运行的 setup seed:地图、角色、场景、步骤和操作说明。示例回放作为可观看结果单独发布,方便你本地运行自己的版本。",
|
|
"experiments.footer": "GOD 实验",
|
|
"experiments.footerReplays": "回放",
|
|
"mapPacks.metaTitle": "地图包 - GOD",
|
|
"mapPacks.eyebrow": "地图包库",
|
|
"mapPacks.title": "Agent 可以居住和行动的地方。",
|
|
"mapPacks.summary": "地图包发布运行或回放一个世界所需的地点图、Tiled 地图、像素资产、交互、锚点和角色 sprites。",
|
|
"mapPacks.footer": "GOD 地图包",
|
|
"agentPacks.metaTitle": "角色包 - GOD",
|
|
"agentPacks.eyebrow": "角色包库",
|
|
"agentPacks.title": "带记忆、日程和动机的角色群。",
|
|
"agentPacks.summary": "角色包提供可复用的人群:persona、日常节奏、关系、角色、需求以及回放可见状态。",
|
|
"agentPacks.footer": "GOD 角色包",
|
|
"replays.metaTitle": "回放 - GOD",
|
|
"replays.eyebrow": "示例回放",
|
|
"replays.title": "打开一个已经发生过的世界。",
|
|
"replays.summary": "这些回放是静态示例结果:时间线帧、地图资产、角色档案和已记录的操作流程。想自己运行同一套设定时,请下载 ExperimentPack。",
|
|
"replays.footer": "GOD 回放",
|
|
"replays.home": "首页",
|
|
"replays.developerDocs": "开发者文档",
|
|
"replay.example": "示例回放",
|
|
"replay.open": "打开回放",
|
|
"replay.manifest": "清单",
|
|
"replay.operatorRecords": "条操作记录",
|
|
"home.metaTitle": "GOD - 治理、观察、引导",
|
|
"home.badge": "Govern - Observe - Direct",
|
|
"home.subtitle": "用于检查 agent society 回放的 operator console。",
|
|
"home.line": "浏览公开回放和可移植 artifact packs。",
|
|
"home.primaryCta": "浏览实验包",
|
|
"home.githubCta": "在 GitHub 查看",
|
|
"home.scrollCue": "向下滚动查看 artifacts",
|
|
"home.productsEyebrow": "可审查的公开 artifacts",
|
|
"home.productsTitle": "四个 artifact 枢纽。",
|
|
"home.productsSummary": "ExperimentPack 是可运行的 setup seed。回放是可检查的结果。MapPack 和 AgentPack 是可移植依赖。",
|
|
"home.replaysEyebrow": "精选回放",
|
|
"home.replaysTitle": "打开一个已有回放。",
|
|
"home.replaysSummary": "时间线、地图资产、角色档案和操作者记录都可在浏览器中读取;想从同一套设定复现时,可以下载 ExperimentPack。",
|
|
"home.footer": "GOD - 治理、观察、引导",
|
|
"home.contact": "联系:",
|
|
"common.github": "GitHub",
|
|
"common.library": "库",
|
|
"common.mapPack": "地图包",
|
|
"common.agentPack": "角色包",
|
|
"common.locations": "地点",
|
|
"common.downloadMapPack": "下载地图包",
|
|
"common.downloadAgentPack": "下载角色包",
|
|
"common.tiles": "格",
|
|
"common.locationsCount": "个地点",
|
|
"common.interactions": "个交互",
|
|
"common.agents": "个角色",
|
|
"common.profiles": "档案",
|
|
"common.sprites": "sprites"
|
|
}
|
|
};
|
|
|
|
function url(path) {
|
|
if (!path) {
|
|
return "#";
|
|
}
|
|
if (/^https?:\/\//.test(path)) {
|
|
return path;
|
|
}
|
|
return root + path;
|
|
}
|
|
|
|
function localizedUrl(path) {
|
|
var href = url(path);
|
|
if (!path || href === "#" || isAbsoluteUrl(path)) {
|
|
return href;
|
|
}
|
|
var split = href.split("#");
|
|
var base = split[0];
|
|
var hash = split.length > 1 ? "#" + split.slice(1).join("#") : "";
|
|
var queryIndex = base.indexOf("?");
|
|
var pathname = queryIndex === -1 ? base : base.slice(0, queryIndex);
|
|
var params = new URLSearchParams(queryIndex === -1 ? "" : base.slice(queryIndex + 1));
|
|
params.set("lang", locale);
|
|
params.set("v", "20260708-pack-card-tight");
|
|
return pathname + "?" + params.toString() + hash;
|
|
}
|
|
|
|
function isAbsoluteUrl(path) {
|
|
return /^https?:\/\//.test(path || "");
|
|
}
|
|
|
|
function resolveLocale() {
|
|
var param = new URLSearchParams(window.location.search).get("lang");
|
|
if (param === "en" || param === "zh") {
|
|
try {
|
|
window.localStorage.setItem("godSiteLanguage", param);
|
|
} catch (error) {}
|
|
return param;
|
|
}
|
|
try {
|
|
var stored = window.localStorage.getItem("godSiteLanguage");
|
|
if (stored === "en" || stored === "zh") {
|
|
return stored;
|
|
}
|
|
} catch (error) {}
|
|
return pageDefaultLocale;
|
|
}
|
|
|
|
function assetUrl(path) {
|
|
if (!path) {
|
|
return "";
|
|
}
|
|
if (/^https?:\/\//.test(path)) {
|
|
return path;
|
|
}
|
|
return assetPrefix + path;
|
|
}
|
|
|
|
function escapeHtml(value) {
|
|
return String(value == null ? "" : value)
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """);
|
|
}
|
|
|
|
function productIcon(key) {
|
|
var icons = {
|
|
replay: "▶",
|
|
map: "⌖",
|
|
agent: "◉",
|
|
experiment: "□"
|
|
};
|
|
return icons[key] || "·";
|
|
}
|
|
|
|
function metricLine(item) {
|
|
if (!item.stats || !item.stats.length) {
|
|
return "";
|
|
}
|
|
return '<div class="stat-row">' + item.stats.map(function (stat) {
|
|
return "<span>" + escapeHtml(stat) + "</span>";
|
|
}).join("") + "</div>";
|
|
}
|
|
|
|
function kindLabel(kind) {
|
|
if (locale === "zh") {
|
|
return kind === "map" ? "地图包" : kind === "agent" ? "角色包" : "实验";
|
|
}
|
|
return kind === "map" ? "Map Pack" : kind === "agent" ? "Agent Pack" : "Experiment";
|
|
}
|
|
|
|
function text(key) {
|
|
return (STATIC_TEXT[locale] && STATIC_TEXT[locale][key]) || STATIC_TEXT.en[key] || "";
|
|
}
|
|
|
|
function applyStaticText() {
|
|
document.querySelectorAll("[data-i18n]").forEach(function (node) {
|
|
var value = text(node.getAttribute("data-i18n"));
|
|
if (value) {
|
|
node.textContent = value;
|
|
}
|
|
});
|
|
document.querySelectorAll(".nav-links [data-nav]").forEach(function (link) {
|
|
var href = link.getAttribute("href") || "";
|
|
var key = "";
|
|
if (href.indexOf("replays") !== -1) key = "nav.replays";
|
|
if (href.indexOf("map-packs") !== -1) key = "nav.maps";
|
|
if (href.indexOf("agent-packs") !== -1) key = "nav.agents";
|
|
if (href.indexOf("experiments") !== -1) key = "nav.experiments";
|
|
if (key) {
|
|
link.textContent = text(key);
|
|
}
|
|
});
|
|
var titleKey = document.body.dataset.titleI18n;
|
|
if (titleKey) {
|
|
document.title = text(titleKey) || document.title;
|
|
}
|
|
}
|
|
|
|
function renderLanguageToggle() {
|
|
var nav = document.querySelector(".nav-links");
|
|
if (!nav || nav.querySelector("[data-language-toggle]")) {
|
|
return;
|
|
}
|
|
var nextLocale = locale === "zh" ? "en" : "zh";
|
|
var label = locale === "zh" ? "English" : "Chinese";
|
|
var params = new URLSearchParams(window.location.search);
|
|
params.set("lang", nextLocale);
|
|
params.set("v", "20260708-pack-card-tight");
|
|
var href = window.location.pathname + "?" + params.toString() + window.location.hash;
|
|
var link = document.createElement("a");
|
|
link.className = "language-toggle";
|
|
link.href = href;
|
|
link.setAttribute("data-language-toggle", "");
|
|
link.setAttribute("aria-label", locale === "zh" ? "Switch to English" : "Switch to Chinese");
|
|
link.textContent = label;
|
|
nav.appendChild(link);
|
|
}
|
|
|
|
function renderProducts() {
|
|
var grid = document.querySelector("[data-product-grid]");
|
|
if (!grid || !catalog.products) {
|
|
return;
|
|
}
|
|
grid.innerHTML = catalog.products.map(function (item) {
|
|
var display = localizedCatalogItem(item);
|
|
var label = display.label || item.label;
|
|
var title = display.title || item.title;
|
|
var summary = display.summary || item.summary;
|
|
return [
|
|
'<a class="product-card product-card--' + escapeHtml(item.key) + '" href="' + localizedUrl(item.href) + '">',
|
|
' <span class="product-card__icon" aria-hidden="true">' + productIcon(item.key) + '</span>',
|
|
' <span class="product-card__label">' + escapeHtml(label) + '</span>',
|
|
' <strong>' + escapeHtml(title) + '</strong>',
|
|
' <span>' + escapeHtml(summary) + '</span>',
|
|
'</a>'
|
|
].join("");
|
|
}).join("");
|
|
}
|
|
|
|
function replayCard(item) {
|
|
var display = localizedCatalogItem(item);
|
|
var title = display.title || item.title;
|
|
var summary = display.summary || item.summary;
|
|
var eyebrow = display.eyebrow || item.eyebrow || text("replay.example");
|
|
var mapPack = display.mapPack || item.mapPack;
|
|
var agentPack = display.agentPack || item.agentPack;
|
|
return [
|
|
'<article class="feature-card">',
|
|
' <a class="feature-card__media" href="' + url(item.href) + '">',
|
|
' <img src="' + url(item.image) + '" alt="' + escapeHtml(title) + ' map preview" loading="lazy">',
|
|
' </a>',
|
|
' <div class="feature-card__body">',
|
|
' <p class="mini-label">' + escapeHtml(eyebrow) + '</p>',
|
|
' <h3>' + escapeHtml(title) + '</h3>',
|
|
' <p>' + escapeHtml(summary) + '</p>',
|
|
' <div class="feature-card__meta" data-replay-stats="' + escapeHtml(item.slug) + '">',
|
|
' <span>' + escapeHtml(text("replay.example")) + '</span><span>' + escapeHtml(mapPack) + '</span><span>' + escapeHtml(agentPack) + '</span>',
|
|
' </div>',
|
|
' <div class="button-row">',
|
|
' <a class="button button--small" href="' + url(item.href) + '">' + escapeHtml(text("replay.open")) + '</a>',
|
|
' <a class="button button--small button--ghost" href="' + url(item.manifest) + '">' + escapeHtml(text("replay.manifest")) + '</a>',
|
|
' </div>',
|
|
' </div>',
|
|
'</article>'
|
|
].join("");
|
|
}
|
|
|
|
function renderReplays() {
|
|
var grid = document.querySelector("[data-replay-grid]");
|
|
if (!grid || !catalog.replays) {
|
|
return;
|
|
}
|
|
grid.innerHTML = catalog.replays.map(replayCard).join("");
|
|
hydrateReplayStats();
|
|
}
|
|
|
|
function libraryCard(item, kind) {
|
|
var display = localizedCatalogItem(item);
|
|
var title = display.title || item.title;
|
|
var summary = display.summary || item.summary;
|
|
var stats = display.stats || item.stats;
|
|
var previewHref = item.previewHref || item.replayHref || item.href || item.download;
|
|
var previewLabel = kind === "experiment"
|
|
? (locale === "zh" ? "查看示例回放" : "View example replay")
|
|
: (locale === "zh" ? "预览" : "Preview");
|
|
var defaultDownloadLabel = (
|
|
kind === "experiment"
|
|
? (locale === "zh" ? "下载 ExperimentPack" : "Download ExperimentPack")
|
|
: kind === "map"
|
|
? (locale === "zh" ? text("common.downloadMapPack") : text("common.downloadMapPack"))
|
|
: (locale === "zh" ? text("common.downloadAgentPack") : text("common.downloadAgentPack"))
|
|
);
|
|
var downloadLabel = locale === "zh" ? defaultDownloadLabel : (item.downloadLabel || defaultDownloadLabel);
|
|
var showPreview = Boolean(previewHref);
|
|
if (kind === "experiment" && !item.previewHref && !item.replayHref && !item.href) {
|
|
showPreview = false;
|
|
}
|
|
var image = item.image || (kind === "experiment" ? experimentImage(item) : "");
|
|
var media = image
|
|
? '<a class="library-card__media library-card__media--' + escapeHtml(kind) + '" href="' + (showPreview ? localizedUrl(previewHref) : url(item.download)) + '"><img src="' + url(image) + '" alt="' + escapeHtml(title) + ' preview" loading="lazy"></a>'
|
|
: '<div class="library-card__glyph" aria-hidden="true">' + productIcon(kind) + '</div>';
|
|
var buttons = [];
|
|
if (showPreview) {
|
|
buttons.push('<a class="button button--small" href="' + localizedUrl(previewHref) + '">' + escapeHtml(previewLabel) + '</a>');
|
|
}
|
|
if (item.download) {
|
|
buttons.push('<a class="button button--small button--ghost" href="' + url(item.download) + '">' + escapeHtml(downloadLabel) + '</a>');
|
|
}
|
|
return [
|
|
'<article class="library-card">',
|
|
media,
|
|
' <div class="library-card__body">',
|
|
' <p class="mini-label">' + escapeHtml(kindLabel(kind)) + '</p>',
|
|
' <h3>' + escapeHtml(title) + '</h3>',
|
|
' <p>' + escapeHtml(summary) + '</p>',
|
|
metricLine({ stats: stats }),
|
|
' <div class="button-row">',
|
|
buttons.join(""),
|
|
' </div>',
|
|
' </div>',
|
|
'</article>'
|
|
].join("");
|
|
}
|
|
|
|
function renderLibrary(selector, items, kind) {
|
|
var grid = document.querySelector(selector);
|
|
if (!grid || !items) {
|
|
return;
|
|
}
|
|
grid.innerHTML = items.map(function (item) {
|
|
return libraryCard(item, kind);
|
|
}).join("");
|
|
}
|
|
|
|
function experimentManifestToCard(item) {
|
|
var packId = item.pack_id || item.slug;
|
|
var download = "";
|
|
(item.downloads || []).forEach(function (entry) {
|
|
if (!download && entry.type === "experiment") {
|
|
download = isAbsoluteUrl(entry.href) ? entry.href : "public-data/experiments/" + packId + "/" + entry.href;
|
|
}
|
|
});
|
|
var replayHref = item.replay_slug ? "replays/" + item.replay_slug + "/" : "";
|
|
var display = localizedCatalogItem(item);
|
|
return {
|
|
title: display.title || item.display_name || packId,
|
|
slug: packId,
|
|
summary: display.summary || item.summary || "",
|
|
image: experimentImage(item),
|
|
map_pack: item.map_pack || "",
|
|
replayHref: replayHref,
|
|
download: download,
|
|
downloadLabel: "Download ExperimentPack",
|
|
stats: [
|
|
display.mapLabel || (item.map_pack ? (locale === "zh" ? item.map_pack + " 地图" : item.map_pack + " map") : ""),
|
|
item.agent_count ? (locale === "zh" ? item.agent_count + " 个角色" : item.agent_count + " agents") : "",
|
|
item.total_steps ? (locale === "zh" ? item.total_steps + " 步" : item.total_steps + " steps") : ""
|
|
].filter(Boolean)
|
|
};
|
|
}
|
|
|
|
function renderExperimentLibrary() {
|
|
var grid = document.querySelector("[data-experiment-pack-grid]");
|
|
if (!grid) {
|
|
return;
|
|
}
|
|
var fallback = function () {
|
|
renderLibrary("[data-experiment-pack-grid]", catalog.experiments, "experiment");
|
|
};
|
|
if (!window.fetch) {
|
|
fallback();
|
|
return;
|
|
}
|
|
fetch(url("public-data/experiments/index.json?v=20260603-lang-toggle"))
|
|
.then(function (response) {
|
|
return response.ok ? response.json() : null;
|
|
})
|
|
.then(function (items) {
|
|
if (!Array.isArray(items) || !items.length) {
|
|
fallback();
|
|
return;
|
|
}
|
|
renderLibrary("[data-experiment-pack-grid]", items.map(experimentManifestToCard), "experiment");
|
|
})
|
|
.catch(fallback);
|
|
}
|
|
|
|
function experimentImage(item) {
|
|
if (item.image) {
|
|
return item.image;
|
|
}
|
|
var mapId = item.map_pack || item.mapPack || "";
|
|
if (mapId === "the_ville" || item.slug === "god-town-daily-life") {
|
|
return "assets/screenshots/map-the-ville.png";
|
|
}
|
|
if (mapId === "pku" || item.slug === "pku-public-situation") {
|
|
return "assets/screenshots/map-pku.png";
|
|
}
|
|
if (mapId) {
|
|
return "public-data/map-packs/" + mapId + "/preview.png";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function localizedCatalogItem(item) {
|
|
var localized = item.localized || {};
|
|
var entry = localized[locale] || localized.en || localized.zh || {};
|
|
return {
|
|
label: entry.label || "",
|
|
title: entry.display_name || entry.title || "",
|
|
eyebrow: entry.eyebrow || "",
|
|
summary: entry.summary || "",
|
|
mapLabel: entry.map_label || "",
|
|
mapPack: entry.mapPack || entry.map_pack || "",
|
|
agentPack: entry.agentPack || entry.agent_pack || "",
|
|
stats: entry.stats || null
|
|
};
|
|
}
|
|
|
|
function localized(value) {
|
|
if (!value || typeof value !== "object") {
|
|
return value || "";
|
|
}
|
|
return value[locale] || value.en || value.zh || "";
|
|
}
|
|
|
|
function renderLegacyExperiments() {
|
|
var grid = document.querySelector("[data-experiment-grid]");
|
|
var experiments = window.GOD_EXPERIMENTS || [];
|
|
if (!grid || !experiments.length) {
|
|
return;
|
|
}
|
|
var labels = locale === "zh" ? {
|
|
open: "打开实验",
|
|
folder: "仓库目录",
|
|
repo: "仓库路径"
|
|
} : {
|
|
open: "Open experiment",
|
|
folder: "Repository folder",
|
|
repo: "Repository path"
|
|
};
|
|
grid.innerHTML = experiments.map(function (item) {
|
|
var title = localized(item.title);
|
|
var kicker = localized(item.kicker);
|
|
var summary = localized(item.summary);
|
|
var tryItems = localized(item.try) || [];
|
|
var detailHref = detailBase + item.slug + ".html";
|
|
var repoHref = "https://github.com/XiaoLuoLYG/GOD/tree/main/" + item.repoPath;
|
|
return [
|
|
'<article class="experiment-card">',
|
|
' <a class="experiment-card__media" href="' + escapeHtml(detailHref) + '">',
|
|
' <img src="' + escapeHtml(assetUrl(item.image)) + '" alt="' + escapeHtml(title) + ' map preview" loading="lazy">',
|
|
' </a>',
|
|
' <div class="experiment-card__body">',
|
|
' <p class="eyebrow">' + escapeHtml(kicker) + '</p>',
|
|
' <h3>' + escapeHtml(title) + '</h3>',
|
|
' <p>' + escapeHtml(summary) + '</p>',
|
|
' <ul class="experiment-card__list">',
|
|
tryItems.map(function (entry) {
|
|
return '<li>' + escapeHtml(entry) + '</li>';
|
|
}).join(""),
|
|
' </ul>',
|
|
' <code class="repo-path" aria-label="' + escapeHtml(labels.repo) + '">' + escapeHtml(item.repoPath) + '</code>',
|
|
' <div class="button-row">',
|
|
' <a class="button button--small" href="' + escapeHtml(detailHref) + '">' + labels.open + '</a>',
|
|
' <a class="button button--small button--ghost" href="' + escapeHtml(repoHref) + '">' + labels.folder + '</a>',
|
|
' </div>',
|
|
' </div>',
|
|
'</article>'
|
|
].join("");
|
|
}).join("");
|
|
}
|
|
|
|
function hydrateReplayStats() {
|
|
if (!catalog.replays || !window.fetch) {
|
|
return;
|
|
}
|
|
catalog.replays.forEach(function (item) {
|
|
fetch(url(item.manifest))
|
|
.then(function (response) {
|
|
return response.ok ? response.json() : null;
|
|
})
|
|
.then(function (manifest) {
|
|
if (!manifest) {
|
|
return;
|
|
}
|
|
document.querySelectorAll('[data-replay-stats="' + item.slug + '"]').forEach(function (node) {
|
|
node.innerHTML = [
|
|
"<span>" + manifest.total_steps + (locale === "zh" ? " 步" : " steps") + "</span>",
|
|
"<span>" + manifest.agent_count + (locale === "zh" ? " 个角色" : " agents") + "</span>",
|
|
"<span>" + manifest.command_count + " " + text("replay.operatorRecords") + "</span>"
|
|
].join("");
|
|
});
|
|
})
|
|
.catch(function () {});
|
|
});
|
|
}
|
|
|
|
function markActiveNav() {
|
|
var path = window.location.pathname.replace(/\/index\.html$/, "/");
|
|
document.querySelectorAll("[data-nav]").forEach(function (link) {
|
|
var href = link.getAttribute("href") || "";
|
|
var absolute = new URL(href, window.location.href).pathname.replace(/\/index\.html$/, "/");
|
|
if (path === absolute) {
|
|
link.setAttribute("aria-current", "page");
|
|
}
|
|
});
|
|
}
|
|
|
|
renderProducts();
|
|
renderReplays();
|
|
renderLibrary("[data-map-pack-grid]", catalog.mapPacks, "map");
|
|
renderLibrary("[data-agent-pack-grid]", catalog.agentPacks, "agent");
|
|
renderExperimentLibrary();
|
|
renderLegacyExperiments();
|
|
applyStaticText();
|
|
renderLanguageToggle();
|
|
markActiveNav();
|
|
window.GOD_LOCALE = locale;
|
|
window.GOD_TEXT = text;
|
|
window.GOD_LOCALIZED_CATALOG_ITEM = localizedCatalogItem;
|
|
})();
|