Files
HomeAgent/internal/plugins/webui/dashboard.html
HomeAgent Agent 764939ed90 refactor(webui): dashboard.html 6637 行拆成「外壳 + 样式 + 脚本」
前端刻意没有构建链(纯 CSS + Vanilla JS,go:embed 进二进制),所以拆法是:
外壳 dashboard.html 留 {{DASHBOARD_CSS}} / {{DASHBOARD_JS}} 两个占位符,
init() 启动时把两份资产原样填回去 —— **发出的 HTML 与拆分前逐字节一致**,
但 6637 行的单文件变成三份,便于编辑与评审。

  dashboard.html   168 行   外壳(head/body 结构 + 两个占位符)
  dashboard.css   2099 行   样式
  dashboard.js    4371 行   脚本

逐字节校验(三重):
  * 组装结果 sha256 == git HEAD 里拆分前的 dashboard.html;
  * 真实实例 GET /(带 API key)返回体 sha256 同上:0ef14b49…c053;
  * 新增 TestDashboardAssetsSplit:占位符必须存在、样式/脚本不得再内联回外壳、
    组装结果不得残留占位符且必须含样式与脚本特征串。

按行号切片时踩过一次坑并已修正:`</style>`/`</script>` 两个闭合标签被切掉
(正好少 27 字节)——正是因为当时少了逐字节校验,现在把它固化成断言。
2026-09-14 07:17:23 +08:00

169 lines
5.2 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HomeAgent Dashboard</title>
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<script
src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"
onerror="window._THREE_FAILED = true"
></script>
<script
src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"
onerror="window._THREE_FAILED = true"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"
onerror="console.warn('marked CDN failed')"
></script>
<script
src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"
onerror="console.warn('DOMPurify CDN failed')"
></script>
<script>
setTimeout(function () {
if (!window.THREE) window._THREE_FAILED = true;
}, 8000);
</script>
<style>
{{DASHBOARD_CSS}}
</style>
</head>
<body>
<div id="bg-layer"></div>
<div class="palette-pop" id="palette-pop"></div>
<nav id="sidebar">
<h1>
<img
src="/logo.svg"
alt="HomeAgent"
style="
height: 22px;
width: 22px;
vertical-align: middle;
margin-right: 6px;
flex-shrink: 0;
border-radius: 6px;
"
/>HomeAgent
</h1>
<div class="nav-links">
<a
class="active"
onclick="switchTab('overview')"
data-i18n="navOverview"
>概览</a
>
<a onclick="switchTab('chat')" data-i18n="navChat">对话</a>
<a onclick="switchTab('plugins')" data-i18n="navPlugins">插件</a>
<a onclick="switchTab('settings')" data-i18n="navSettings">设置</a>
<a onclick="switchTab('adapters')" data-i18n="navAdapters">适配器</a>
<a onclick="switchTab('kernel')" data-i18n="navKernel">内核</a>
</div>
<div class="nav-footer">
<div style="display: flex; align-items: center; gap: 4px">
<button
class="theme-btn"
onclick="toggleTheme()"
id="theme-btn"
title="切换亮色/暗色模式"
>
<svg
viewBox="0 0 24 24"
width="15"
height="15"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
>
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
</svg>
</button>
<button
class="theme-btn"
onclick="toggleAppearance()"
id="palette-btn"
title="外观:主题色 / 背景图"
>
<svg
viewBox="0 0 24 24"
width="15"
height="15"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M12 22a10 10 0 1 1 10-10c0 2.2-1.8 4-4 4h-2a3 3 0 0 0-3 3c0 1.1.9 2 2 2 1.4 0 2.5-1.1 3.6-2.1C19.3 18.5 20 20 21.5 20.5A1 1 0 0 0 22 19c0-4-3.6-7-10-7-5.5 0-9 3.8-9 9z"
/>
<circle cx="7.5" cy="10.5" r="1" />
<circle cx="12" cy="7.5" r="1" />
<circle cx="16.5" cy="10.5" r="1" />
</svg>
</button>
<button
class="btn btn-ghost btn-sm"
onclick="toggleLang()"
id="lang-btn"
title="切换语言/Language"
style="font-size: 13px; min-width: 28px"
>
EN
</button>
<button
class="btn btn-ghost btn-sm"
onclick="logout()"
data-i18n="navLogout"
>
退出登录
</button>
</div>
</div>
</nav>
<div class="main-wrap">
<header class="topbar">
<button
class="sidebar-toggle"
onclick="toggleSidebar()"
id="sidebar-toggle"
title="切换侧边栏"
aria-label="切换侧边栏"
>
<svg
viewBox="0 0 24 24"
width="15"
height="15"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
>
<path d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<div class="breadcrumb" id="breadcrumb">
<span class="crumb-home">HomeAgent</span>
<span class="crumb-sep">/</span>
<span class="crumb-current" data-i18n="navOverview">概览</span>
</div>
</header>
<div class="container" id="app">
<div id="tab-overview" class="tab-content active"></div>
<div id="tab-chat" class="tab-content"></div>
<div id="tab-plugins" class="tab-content"></div>
<div id="tab-settings" class="tab-content"></div>
<div id="tab-adapters" class="tab-content"></div>
<div id="tab-kernel" class="tab-content"></div>
</div>
</div>
<div id="toast" class="toast"></div>
<script>
{{DASHBOARD_JS}}
</script>
</body>
</html>