v0.6.2: hero page site foot, dismiss detail on empty click, drop patches version footer.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
voson
2026-07-30 07:45:03 +08:00
co-authored by Cursor
parent 1aa6710e39
commit aac4593cc5
7 changed files with 110 additions and 38 deletions
+78 -17
View File
@@ -76,6 +76,52 @@ function siteVersionLabel() {
return v ? `v${v}` : "";
}
/** Latest fetched_at among volatile site datasets (for hero detail footer). */
function siteDataFetchedAt() {
if (!state.data) return null;
const candidates = [];
const push = (v) => {
const ms = parseTimeMs(v);
if (Number.isFinite(ms)) candidates.push(ms);
};
push(state.data.hero_stats?.fetched_at);
push(state.data.leaderboards?.fetched_at);
push(state.data.hero_matches?.meta?.fetched_at);
push(state.data.pro_matches?.meta?.fetched_at);
push(state.data.streamers?.fetched_at);
push(state.data.stratz_hero_meta?.fetched_at);
push(state.data.stratz_matchup_tops?.fetched_at);
push(state.data.hero_items?.meta?.fetched_at);
if (!candidates.length) return null;
return new Date(Math.max(...candidates)).toISOString();
}
function renderHeroesSiteFoot() {
const el = $("#heroes-site-foot");
if (!el) return;
// Hide under the detail panel when a hero is selected.
const show = state.page === "heroes" && !state.selectedKey;
el.classList.toggle("hidden", !show);
if (!show) {
el.textContent = "";
el.removeAttribute("title");
el.setAttribute("aria-hidden", "true");
return;
}
const parts = [];
const ver = siteVersionLabel();
if (ver) parts.push(ver);
const fetchedAt = siteDataFetchedAt();
const fetchedInfo = fetchedAt
? formatFriendlyTime(fetchedAt)
: { text: "", title: "" };
if (fetchedInfo.text) parts.push(`更新于 ${fetchedInfo.text}`);
el.textContent = parts.join(" · ");
if (fetchedInfo.title) el.title = fetchedInfo.title;
else el.removeAttribute("title");
el.setAttribute("aria-hidden", parts.length ? "false" : "true");
}
function siteOrigin() {
const configured = typeof SITE_ORIGIN === "string" ? SITE_ORIGIN.trim().replace(/\/+$/, "") : "";
if (configured) return configured;
@@ -208,14 +254,6 @@ function clearSeoPrerender() {
if (el) el.remove();
}
function renderPatchesSiteVersion() {
const el = $("#patches-site-version");
if (!el) return;
const label = siteVersionLabel();
el.textContent = label;
el.setAttribute("aria-hidden", label ? "false" : "true");
}
const state = {
data: null,
page: "heroes", // heroes | rankings | matches | streamers | trends | mechanics | items | patches
@@ -385,6 +423,15 @@ function onHeroClick(key) {
render();
}
function clearHeroDetail() {
if (!state.selectedKey) return false;
state.selectedKey = null;
state.inspect = null;
syncStateToUrl();
render();
return true;
}
function renderTagbar() {
const bar = $("#tagbar");
if (!bar) return;
@@ -3471,7 +3518,6 @@ function renderPatches() {
if (select) select.innerHTML = "";
if (verEl) verEl.textContent = "";
root.innerHTML = '<div class="patches-empty">暂无版本数据</div>';
renderPatchesSiteVersion();
return;
}
@@ -3506,7 +3552,6 @@ function renderPatches() {
const det = details[state.selectedPatch];
if (!det) {
root.innerHTML = '<div class="patches-empty">该版本详情暂未收录</div>';
renderPatchesSiteVersion();
return;
}
@@ -3539,7 +3584,6 @@ function renderPatches() {
.map((h) => renderHeroEntry(h, lookup))
.join("");
renderPatchSection(root, "英雄改动", heroesHtml);
renderPatchesSiteVersion();
}
function escapeHtml(s) {
@@ -5612,6 +5656,7 @@ function render() {
} else {
renderPatches();
}
renderHeroesSiteFoot();
updateDocumentMeta();
}
@@ -5779,6 +5824,26 @@ function bindPageChrome() {
});
}
/** Click empty area above role tags (board remainder / toolbar chrome) to dismiss detail. */
function bindHeroesDismiss() {
const view = $("#heroes-view");
if (view) {
view.addEventListener("click", (e) => {
if (state.page !== "heroes" || !state.selectedKey) return;
if (e.target.closest(".hero")) return;
clearHeroDetail();
});
}
const toolbar = $("#heroes-toolbar");
if (toolbar) {
toolbar.addEventListener("click", (e) => {
if (state.page !== "heroes" || !state.selectedKey) return;
if (e.target.closest("button, a, input, select, textarea, label")) return;
clearHeroDetail();
});
}
}
async function main() {
// mobile-gate.js sets this before paint; skip desktop data boot on phones/tablets.
if (typeof window !== "undefined" && window.__CLIMPEROR_MOBILE__) return;
@@ -5796,12 +5861,7 @@ async function main() {
renderBoard();
return;
}
if (state.selectedKey) {
state.selectedKey = null;
state.inspect = null;
syncStateToUrl();
render();
}
clearHeroDetail();
return;
}
if (state.itemQuery && document.activeElement === $("#q-item")) {
@@ -5839,6 +5899,7 @@ async function main() {
);
bindSearch();
bindPageChrome();
bindHeroesDismiss();
const brandLogo = document.querySelector(".brand-logo");
if (brandLogo) brandLogo.src = assetUrl("/ui-icon/dota2_logo_wordmark.png");
const favicon = document.querySelector('link[rel="icon"]');