diff --git a/08-threat-intel/generated/dashboard/assets/app.js b/08-threat-intel/generated/dashboard/assets/app.js
index 2a6f92e3..c32876f7 100644
--- a/08-threat-intel/generated/dashboard/assets/app.js
+++ b/08-threat-intel/generated/dashboard/assets/app.js
@@ -59,6 +59,23 @@ const DATA_HUB_ITEMS = [
{ title: "最新同步摘要", href: "/docs/coverage-matrix.html", description: "覆盖矩阵与本地生成态入口。", badge: "generated" }
];
+const HERO_FOLD_STORAGE_KEY = "websafe.dashboard.heroFolded";
+
+function readHeroFolded() {
+ try {
+ return window.localStorage.getItem(HERO_FOLD_STORAGE_KEY) === "1";
+ } catch (_error) {
+ return false;
+ }
+}
+
+function writeHeroFolded(value) {
+ try {
+ window.localStorage.setItem(HERO_FOLD_STORAGE_KEY, value ? "1" : "0");
+ } catch (_error) {
+ }
+}
+
const state = {
routeSection: resolveRouteSection(),
summary: null,
@@ -72,6 +89,12 @@ const state = {
refreshHandle: null,
refreshMs: 5000,
autoRefresh: true,
+ heroFolded: readHeroFolded(),
+ syncStatus: {
+ kind: "boot",
+ title: "启动中",
+ detail: "正在载入本地生成数据"
+ },
filters: {
search: "",
status: "",
@@ -303,6 +326,7 @@ function renderMetrics() {
}
function renderSyncState(kind, title, detail) {
+ state.syncStatus = { kind, title, detail };
$("syncState").innerHTML = `
${icon("sync", "icon icon-sync")}
@@ -311,6 +335,7 @@ function renderSyncState(kind, title, detail) {
`;
$("syncState").dataset.kind = kind;
+ renderHeroChrome();
}
function renderSectionNav() {
@@ -1244,14 +1269,34 @@ function renderWorkspace() {
workspace.innerHTML = html;
}
+function renderHeroChrome() {
+ const meta = sectionMeta();
+ const chip = $("heroSectionChip");
+ const title = $("heroSummaryTitle");
+ const detail = $("heroSummaryDetail");
+ const toggle = $("toggleHeroCollapse");
+ const toggleLabel = $("heroToggleLabel");
+ if (!chip || !title || !detail || !toggle || !toggleLabel) return;
+
+ chip.innerHTML = `${icon(meta.icon)}${escapeHtml(meta.label)}`;
+ title.textContent = state.heroFolded ? `${meta.label} · 顶部区域已折叠` : `${meta.label} · 顶部操作区`;
+ detail.textContent = state.heroFolded
+ ? `${state.syncStatus.title} · ${state.syncStatus.detail}`
+ : `${meta.description} · ${state.syncStatus.detail}`;
+ toggleLabel.textContent = state.heroFolded ? "展开顶部" : "折叠顶部";
+ toggle.setAttribute("aria-expanded", String(!state.heroFolded));
+}
+
function syncRouteChrome() {
const compactHero = state.routeSection !== "overview";
document.body.dataset.routeSection = state.routeSection;
document.body.classList.toggle("hero-compact", compactHero);
+ document.body.classList.toggle("hero-folded", state.heroFolded);
}
function renderAll() {
syncRouteChrome();
+ renderHeroChrome();
renderMetrics();
renderSectionNav();
renderTopMenus();
@@ -1285,6 +1330,14 @@ function clearFilters() {
function attachGlobalEvents() {
document.addEventListener("click", (event) => {
+ const heroToggle = event.target.closest("[data-hero-toggle]");
+ if (heroToggle) {
+ state.heroFolded = !state.heroFolded;
+ writeHeroFolded(state.heroFolded);
+ renderAll();
+ return;
+ }
+
const toggle = event.target.closest("[data-panel-toggle]");
if (toggle) {
const key = toggle.dataset.panelToggle;
diff --git a/08-threat-intel/generated/dashboard/assets/styles.css b/08-threat-intel/generated/dashboard/assets/styles.css
index a90616ba..89d804ef 100644
--- a/08-threat-intel/generated/dashboard/assets/styles.css
+++ b/08-threat-intel/generated/dashboard/assets/styles.css
@@ -148,6 +148,72 @@ select {
line-height: 1.6;
}
+.hero-collapse-bar {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding-bottom: 16px;
+ margin-bottom: 18px;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.14);
+}
+
+.hero-collapse-summary {
+ display: grid;
+ gap: 8px;
+ min-width: 0;
+}
+
+.hero-summary-topline {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.hero-section-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ min-height: 30px;
+ padding: 6px 12px;
+ border-radius: 999px;
+ border: 1px solid rgba(77, 141, 255, 0.28);
+ background: rgba(77, 141, 255, 0.12);
+ color: #dce7ff;
+ font-size: 0.82rem;
+ font-weight: 600;
+}
+
+.hero-summary-title {
+ display: block;
+ font-size: 1.02rem;
+}
+
+.hero-summary-detail {
+ color: var(--text-secondary);
+ line-height: 1.5;
+ font-size: 0.86rem;
+}
+
+.hero-collapse-button {
+ min-width: 128px;
+}
+
+.hero-collapse-icon {
+ transition: transform 0.2s ease;
+}
+
+.hero-foldable {
+ display: grid;
+ max-height: 2200px;
+ opacity: 1;
+ overflow: hidden;
+ transform: translateY(0);
+ transition: max-height 0.28s ease, opacity 0.22s ease, transform 0.22s ease;
+}
+
.hero-actions {
position: relative;
display: grid;
@@ -329,6 +395,31 @@ body.hero-compact .hero-glow {
opacity: 0.55;
}
+body.hero-folded .hero {
+ padding: 16px 18px;
+}
+
+body.hero-folded .hero-collapse-bar {
+ margin-bottom: 0;
+ padding-bottom: 0;
+ border-bottom-color: transparent;
+}
+
+body.hero-folded .hero-collapse-icon {
+ transform: rotate(-90deg);
+}
+
+body.hero-folded .hero-foldable {
+ max-height: 0;
+ opacity: 0;
+ transform: translateY(-10px);
+ pointer-events: none;
+}
+
+body.hero-folded .hero-glow {
+ opacity: 0.42;
+}
+
.metrics-row {
position: relative;
display: grid;
@@ -1454,6 +1545,8 @@ body.hero-compact .hero-glow {
}
.hero-links,
+ .hero-collapse-bar,
+ .hero-summary-topline,
.route-note,
.detail-actions,
.tag-row,
@@ -1469,6 +1562,10 @@ body.hero-compact .hero-glow {
display: grid;
grid-template-columns: 1fr;
}
+
+ .hero-collapse-button {
+ width: 100%;
+ }
}
@media (max-width: 640px) {
diff --git a/08-threat-intel/generated/dashboard/data/index.html b/08-threat-intel/generated/dashboard/data/index.html
index f9494447..2ee2c71c 100644
--- a/08-threat-intel/generated/dashboard/data/index.html
+++ b/08-threat-intel/generated/dashboard/data/index.html
@@ -13,58 +13,78 @@
-
-
-
-
- 授权攻防实验工作台
-
-
本地攻防实证工作台
-
-
-
-
-