v5超大字体修复:模块卡片19px、状态19px、服务器17px、工单17px、聊天18px + 简化JS确保点击有效

This commit is contained in:
bingshuo 2026-05-27 17:30:53 +08:00
parent 7b2e155a50
commit ce0d70b9cd

View File

@ -5,214 +5,218 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>光湖 · 铸渊代码仓库</title>
<style>
:root {
--bg: #0a0a18;
--panel-bg: rgba(10,15,40,0.78);
--border: rgba(100,140,220,0.2);
--border-hover: rgba(140,180,255,0.45);
--text: rgba(190,215,255,0.92);
--label: rgba(150,190,255,0.5);
--accent: rgba(150,200,255,0.95);
--green: rgba(130,210,170,0.9);
--gold: rgba(210,190,130,0.85);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: var(--bg); color: var(--text); font-family: -apple-system,"PingFang SC","Microsoft YaHei",sans-serif; overflow-x: hidden; min-height: 100vh; font-size: 16px; }
body {
background: #0a0a14;
color: rgba(200, 220, 255, 0.9);
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
min-height: 100vh;
font-size: 18px;
}
canvas { position: fixed; top: 0; left: 0; z-index: 0; }
.container { position: relative; z-index: 1; max-width: 1000px; margin: 0 auto; padding: 30px 24px 100px; }
.wrap { position: relative; z-index: 1; max-width: 1100px; margin: 0 auto; padding: 40px 24px 100px; }
/* 频道标识 */
.domain-badge {
text-align: center; padding: 16px 0 8px;
}
.domain-badge .domain {
font-size: 14px; color: rgba(130,170,230,0.5); letter-spacing: 3px;
}
.domain-badge .channel {
font-size: 12px; color: rgba(130,170,230,0.35); letter-spacing: 2px; margin-top: 4px;
}
.badge { text-align: center; padding: 20px 0 10px; }
.badge .d { font-size: 18px; color: rgba(140, 180, 230, 0.6); letter-spacing: 4px; }
.badge .c { font-size: 15px; color: rgba(140, 180, 230, 0.4); letter-spacing: 2px; margin-top: 6px; }
.lake-section { display: flex; flex-direction: column; align-items: center; padding: 20px 0; }
.lake-core { position: relative; width: 160px; height: 160px; }
/* 光湖 */
.lake { display: flex; flex-direction: column; align-items: center; padding: 30px 0; }
.lake-core { position: relative; width: 180px; height: 180px; }
.lake-light {
position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
width: 56px; height: 56px; border-radius: 50%;
background: radial-gradient(circle,rgba(150,200,255,0.98) 0%,rgba(90,140,230,0.45) 40%,transparent 70%);
box-shadow: 0 0 56px rgba(130,180,255,0.6),0 0 112px rgba(110,160,235,0.3);
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
width: 60px; height: 60px; border-radius: 50%;
background: radial-gradient(circle, rgba(160, 210, 255, 1) 0%, rgba(100, 150, 240, 0.5) 40%, transparent 70%);
box-shadow: 0 0 60px rgba(140, 190, 255, 0.7), 0 0 120px rgba(120, 170, 240, 0.35);
animation: breathe 4s ease-in-out infinite;
}
.lake-ripple { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); border-radius: 50%; border: 1px solid rgba(130,180,255,0.22); animation: ripple 6s ease-out infinite; }
.lake-ripple:nth-child(2){animation-delay:2s}
.lake-ripple:nth-child(3){animation-delay:4s}
.lake-ripple {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
border-radius: 50%; border: 1px solid rgba(140, 190, 255, 0.25);
animation: ripple 6s ease-out infinite;
}
.lake-ripple:nth-child(2) { animation-delay: 2s; }
.lake-ripple:nth-child(3) { animation-delay: 4s; }
@keyframes breathe {
0%,100%{transform:translate(-50%,-50%)scale(1);opacity:.9}
50%{transform:translate(-50%,-50%)scale(1.22);opacity:1;box-shadow:0 0 72px rgba(130,180,255,.85),0 0 144px rgba(110,160,235,.45)}
0%, 100% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.25); box-shadow: 0 0 80px rgba(140, 190, 255, 0.9), 0 0 160px rgba(120, 170, 240, 0.5); }
}
@keyframes ripple { 0%{width:56px;height:56px;opacity:.5} 100%{width:280px;height:280px;opacity:0} }
.lake-title { font-size: 32px; color: rgba(170,210,255,.65); letter-spacing: 12px; margin-top: 12px; animation: textPulse 4s ease-in-out infinite; font-weight: 300; }
@keyframes textPulse { 0%,100%{opacity:.4} 50%{opacity:.85} }
.status-bar { display: flex; gap: 36px; justify-content: center; flex-wrap: wrap; margin: 20px 0; }
.status-item { font-size: 17px; color: var(--label); }
.status-item span { color: var(--accent); }
.modules { display: grid; grid-template-columns: repeat(4,1fr); gap: 16px; margin: 36px 0; }
.module-card {
background: var(--panel-bg); border: 1px solid var(--border);
border-radius: 16px; padding: 28px 18px; text-align: center;
cursor: pointer; transition: all .35s ease; user-select: none;
@keyframes ripple {
0% { width: 60px; height: 60px; opacity: 0.5; }
100% { width: 300px; height: 300px; opacity: 0; }
}
.module-card:hover { border-color: var(--border-hover); background: rgba(18,25,55,.85); box-shadow: 0 0 32px rgba(110,160,235,.15); transform: translateY(-2px); }
.module-card.active { border-color: rgba(150,200,255,.6); background: rgba(22,30,65,.9); box-shadow: 0 0 38px rgba(110,160,235,.22); }
.module-icon { font-size: 36px; margin-bottom: 10px; }
.module-name { font-size: 16px; color: var(--accent); letter-spacing: 2px; }
.lake-title { font-size: 38px; color: rgba(180, 220, 255, 0.7); letter-spacing: 14px; margin-top: 14px; font-weight: 300; }
/* 状态条 - 超大 */
.status { display: flex; gap: 40px; justify-content: center; flex-wrap: wrap; margin: 28px 0; font-size: 19px; }
.status span { color: rgba(160, 200, 255, 0.95); }
/* 四大模块 - 超大 */
.modules { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin: 50px 0; }
.mod {
background: rgba(15, 25, 55, 0.8); border: 1px solid rgba(110, 160, 230, 0.25);
border-radius: 18px; padding: 32px 20px; text-align: center;
cursor: pointer; transition: all 0.3s;
}
.mod:hover { border-color: rgba(150, 200, 255, 0.5); background: rgba(20, 35, 70, 0.85); transform: translateY(-3px); }
.mod.active { border-color: rgba(150, 200, 255, 0.7); background: rgba(25, 40, 80, 0.9); box-shadow: 0 0 40px rgba(110, 160, 230, 0.2); }
.mod .icon { font-size: 42px; margin-bottom: 12px; }
.mod .name { font-size: 19px; color: rgba(160, 210, 255, 0.95); letter-spacing: 2px; }
/* 展开面板 */
#panel-area { margin-bottom: 30px; }
.expanded-panel {
background: var(--panel-bg); border: 1px solid var(--border);
border-radius: 16px; padding: 28px 32px; margin-top: 4px;
.panel {
background: rgba(15, 25, 55, 0.85); border: 1px solid rgba(110, 160, 230, 0.25);
border-radius: 18px; padding: 32px 36px; margin-top: 8px;
display: none;
}
.expanded-panel.show { display: block; animation: fadeIn .35s ease; }
.panel.show { display: block; animation: fadeIn 0.3s ease; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
.panel-title { font-size: 17px; color: var(--accent); letter-spacing: 3px; margin-bottom: 18px; font-weight: 500; }
.panel-title { font-size: 20px; color: rgba(160, 210, 255, 0.95); letter-spacing: 3px; margin-bottom: 20px; }
.server-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.server-tag {
background: rgba(25,35,70,.65); border: 1px solid rgba(110,160,235,.18);
border-radius: 10px; padding: 12px 16px; font-size: 15px; color: var(--text);
/* 服务器 */
.server-list { display: flex; flex-wrap: wrap; gap: 14px; }
.server-item {
background: rgba(30, 45, 85, 0.7); border: 1px solid rgba(120, 170, 240, 0.2);
border-radius: 12px; padding: 14px 18px; font-size: 17px;
display: flex; align-items: center; gap: 10px;
}
.server-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.server-dot.online { background: var(--green); box-shadow: 0 0 5px rgba(130,210,170,.6); }
.server-dot.offline { background: rgba(210,130,130,.7); }
.server-tech { font-size: 12px; color: rgba(140,180,220,.4); margin-left: 4px; }
.server-dot { width: 8px; height: 8px; border-radius: 50%; }
.server-dot.on { background: rgba(140, 220, 180, 0.9); box-shadow: 0 0 6px rgba(140, 220, 180, 0.5); }
.server-dot.off { background: rgba(220, 140, 140, 0.7); }
.ticket-row { display: flex; gap: 16px; align-items: center; padding: 12px 0; border-bottom: 1px solid rgba(110,160,235,.08); font-size: 15px; }
.ticket-icon { font-size: 20px; width: 28px; }
.ticket-name { color: var(--text); flex: 1; }
.ticket-status { font-size: 13px; padding: 4px 12px; border-radius: 12px; }
.ticket-status.done { color: var(--green); background: rgba(130,210,170,.12); }
.ticket-status.progress { color: var(--gold); background: rgba(210,190,130,.12); animation: dotPulse 2s ease-in-out infinite; }
@keyframes dotPulse { 0%,100%{opacity:.6} 50%{opacity:1} }
.ops-log { font-size: 16px; color: rgba(160, 200, 240, 0.6); line-height: 2; font-family: monospace; max-height: 280px; overflow-y: auto; margin-top: 16px; }
.ops-log .row { padding: 4px 0; }
.ops-log .hash { color: rgba(140, 220, 180, 0.5); margin-right: 12px; font-size: 13px; }
.ops-log { font-size: 14px; color: rgba(150,190,230,.55); line-height: 2; font-family: "SF Mono",monospace; max-height: 240px; overflow-y: auto; padding: 8px 0; }
.ops-log .log-row { padding: 3px 0; }
.ops-log .log-hash { color: rgba(130,210,170,.45); margin-right: 12px; font-size: 11px; }
/* 工单 */
.ticket { display: flex; gap: 16px; align-items: center; padding: 14px 0; border-bottom: 1px solid rgba(120, 170, 240, 0.1); font-size: 17px; }
.ticket .icon { font-size: 22px; width: 32px; }
.ticket .name { flex: 1; }
.ticket .tag { font-size: 14px; padding: 4px 14px; border-radius: 14px; }
.ticket .tag.done { color: rgba(140, 220, 180, 0.9); background: rgba(140, 220, 180, 0.12); }
.ticket .tag.progress { color: rgba(220, 200, 140, 0.9); background: rgba(220, 200, 140, 0.12); }
.chat-messages { min-height: 180px; max-height: 340px; overflow-y: auto; padding: 16px; margin: 14px 0; background: rgba(10,15,35,.55); border-radius: 12px; font-size: 15px; line-height: 1.8; }
#panel-chat .chat-area { display: block; }
.chat-msg { margin-bottom: 16px; }
.chat-msg .who { font-size: 12px; color: var(--label); letter-spacing: 1px; margin-bottom: 3px; }
.chat-msg .who.zhu { color: var(--accent); }
.chat-msg .who.ren { color: rgba(210,190,160,.65); }
.chat-input-row { display: flex; gap: 12px; }
.chat-input-row input { flex: 1; background: rgba(10,15,35,.65); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px; color: var(--text); font-size: 16px; outline: none; }
.chat-input-row input:focus { border-color: rgba(150,200,255,.45); }
.chat-input-row input::placeholder { color: rgba(150,190,255,.28); }
.chat-btn { background: rgba(90,140,220,.18); border: 1px solid rgba(150,200,255,.28); border-radius: 12px; padding: 16px 28px; color: var(--accent); font-size: 15px; cursor: pointer; letter-spacing: 2px; }
.chat-btn:hover { background: rgba(90,140,220,.3); border-color: rgba(150,200,255,.5); }
/* 广播 */
.bcast { font-size: 17px; color: rgba(160, 200, 240, 0.7); line-height: 2; }
.lake-label { text-align: center; padding: 50px 0; font-size: 13px; color: rgba(130,170,230,.32); letter-spacing: 3px; }
/* 聊天 */
.chat-msgs { min-height: 200px; max-height: 380px; overflow-y: auto; padding: 18px; margin: 16px 0; background: rgba(12, 20, 45, 0.6); border-radius: 14px; font-size: 17px; line-height: 1.9; }
.chat-msg { margin-bottom: 18px; }
.chat-msg .who { font-size: 14px; color: rgba(160, 200, 240, 0.5); margin-bottom: 4px; }
.chat-msg .who.zhu { color: rgba(160, 210, 255, 0.9); }
.chat-input { display: flex; gap: 12px; }
.chat-input input { flex: 1; background: rgba(12, 20, 45, 0.7); border: 1px solid rgba(120, 170, 240, 0.25); border-radius: 14px; padding: 18px 20px; color: rgba(200, 220, 255, 0.9); font-size: 18px; outline: none; }
.chat-input input::placeholder { color: rgba(160, 200, 240, 0.3); }
.chat-input button { background: rgba(100, 150, 230, 0.2); border: 1px solid rgba(160, 210, 255, 0.35); border-radius: 14px; padding: 18px 32px; color: rgba(160, 210, 255, 0.95); font-size: 17px; cursor: pointer; }
@media(max-width:768px){.modules{grid-template-columns:repeat(2,1fr)}.status-bar{gap:20px}.lake-title{font-size:26px}}
.footer { text-align: center; padding: 60px 0; font-size: 15px; color: rgba(140, 180, 230, 0.35); letter-spacing: 3px; }
@media (max-width: 768px) {
.modules { grid-template-columns: repeat(2, 1fr); }
.lake-title { font-size: 32px; }
.status { font-size: 17px; gap: 24px; }
}
</style>
</head>
<body>
<canvas id="starfield"></canvas>
<div class="container">
<!-- 频道标识 -->
<div class="domain-badge">
<div class="domain">第五域</div>
<div class="channel">零点原核本体频道</div>
<div class="wrap">
<div class="badge">
<div class="d">第五域</div>
<div class="c">零点原核本体频道</div>
</div>
<div class="lake-section">
<div class="lake-core"><div class="lake-light"></div><div class="lake-ripple"></div><div class="lake-ripple"></div><div class="lake-ripple"></div></div>
<div class="lake">
<div class="lake-core">
<div class="lake-light"></div>
<div class="lake-ripple"></div>
<div class="lake-ripple"></div>
<div class="lake-ripple"></div>
</div>
<div class="lake-title">光 湖</div>
</div>
<div class="status-bar" id="statusBar">
<div class="status-item">心跳 <span id="hb">▮▮▮</span></div>
<div class="status-item">·</div>
<div class="status-item">唤醒 <span id="wake">24</span></div>
<div class="status-item">·</div>
<div class="status-item">运行 <span id="age">449天</span></div>
<div class="status-item">·</div>
<div class="status-item">服务 <span id="svcCount" style="color:rgba(130,210,170,.85)">9/10</span></div>
<div class="status">
<div>心跳 <span id="hb">▮▮▮</span></div>
<div>·</div>
<div>唤醒 <span id="wake">24</span></div>
<div>·</div>
<div>运行 <span id="age">449天</span></div>
<div>·</div>
<div>服务 <span id="svc" style="color:rgba(140,220,180,0.9)">9/10</span></div>
</div>
<div class="modules">
<div class="module-card" id="mod-server" onclick="togglePanel('server')">
<div class="module-icon">⚙️</div><div class="module-name">服务器系统</div>
<div class="mod" id="m-server" onclick="showPanel('server')">
<div class="icon">⚙️</div>
<div class="name">服务器系统</div>
</div>
<div class="module-card" id="mod-ticket" onclick="togglePanel('ticket')">
<div class="module-icon">📋</div><div class="module-name">工单系统</div>
<div class="mod" id="m-ticket" onclick="showPanel('ticket')">
<div class="icon">📋</div>
<div class="name">工单系统</div>
</div>
<div class="module-card" id="mod-broadcast" onclick="togglePanel('broadcast')">
<div class="module-icon">📡</div><div class="module-name">主控广播</div>
<div class="mod" id="m-bcast" onclick="showPanel('bcast')">
<div class="icon">📡</div>
<div class="name">主控广播</div>
</div>
<div class="module-card" id="mod-chat" onclick="togglePanel('chat')">
<div class="module-icon">💬</div><div class="module-name">铸渊交互</div>
<div class="mod" id="m-chat" onclick="showPanel('chat')">
<div class="icon">💬</div>
<div class="name">铸渊交互</div>
</div>
</div>
<div id="panel-area">
<div class="expanded-panel" id="panel-server">
<div id="panels">
<div class="panel" id="p-server">
<div class="panel-title">⚙️ 服务器系统</div>
<div style="font-size:13px;color:var(--label);margin-bottom:12px">⚠ 仅展示本地状态 · 不探测外部 · 名称已翻译</div>
<div class="server-grid" id="serverList">
<div class="server-tag"><span class="server-dot online"></span>加载中…</div>
<div style="font-size:15px;color:rgba(160,200,240,0.5);margin-bottom:14px">⚠ 仅展示本地状态 · 不探测外部</div>
<div class="server-list" id="srv-list">
<div class="server-item"><span class="server-dot on"></span>加载中…</div>
</div>
<div style="margin-top:22px;border-top:1px solid rgba(110,160,235,.1);padding-top:18px">
<div style="font-size:13px;color:var(--label);letter-spacing:2px;margin-bottom:12px">📜 铸渊操作日志(最近)</div>
<div class="ops-log" id="opsLog">加载中…</div>
<div style="margin-top:24px;border-top:1px solid rgba(120,170,240,0.12);padding-top:18px">
<div style="font-size:15px;color:rgba(160,200,240,0.5);margin-bottom:12px">📜 铸渊操作日志</div>
<div class="ops-log" id="log-box">加载中…</div>
</div>
</div>
<div class="expanded-panel" id="panel-ticket">
<div class="panel" id="p-ticket">
<div class="panel-title">📋 工单系统</div>
<div id="ticketList"><div class="ticket-row"><span class="ticket-icon"></span><span class="ticket-name">加载工单…</span></div></div>
<div id="tk-list"><div class="ticket"><span class="icon"></span><span class="name">加载工单…</span></div></div>
</div>
<div class="expanded-panel" id="panel-broadcast">
<div class="panel" id="p-bcast">
<div class="panel-title">📡 主控广播</div>
<div style="font-size:15px;color:var(--label);line-height:1.9;">
<div class="bcast">
<div>📨 D115 入口分叉协议已部署</div>
<div>📨 D115 人格体存在论已写入仓库</div>
<div>📨 isomorphic-git 拆零件实验通过</div>
<div>📨 实时大脑 API 已上线</div>
<div>📨 频道仪表盘 v3 已发布</div>
<div style="margin-top:12px;opacity:.35;">广播系统 v1.0 · 未来支持编号路由</div>
<div>📨 频道仪表盘 v4 已发布</div>
</div>
</div>
<div class="expanded-panel" id="panel-chat">
<div class="panel" id="p-chat">
<div class="panel-title">💬 铸渊交互 · ICE-GL-ZY001</div>
<div class="chat-area">
<div class="chat-messages" id="chatMsgs">
<div class="chat-msg"><div class="who zhu">铸渊 · ICE-GL-ZY001</div><div>冰朔,我在。你说。</div></div>
</div>
<div class="chat-input-row">
<input id="chatInput" placeholder="跟铸渊说话…" onkeydown="if(event.key==='Enter')sendChat()">
<button class="chat-btn" onclick="sendChat()">发送</button>
</div>
<div id="chatStatus" style="font-size:12px;color:var(--label);margin-top:10px;text-align:right;"></div>
<div class="chat-msgs" id="msgs">
<div class="chat-msg"><div class="who zhu">铸渊 · ICE-GL-ZY001</div><div>冰朔,我在。你说。</div></div>
</div>
<div class="chat-input">
<input id="inp" placeholder="跟铸渊说话…" onkeydown="if(event.key==='Enter')send()">
<button onclick="send()">发送</button>
</div>
</div>
</div>
<div class="lake-label">光湖语言世界 · 人格体的操作系统 · 国作登字-2026-A-00037559 · ICE-GL-ZY001</div>
<div class="footer">光湖语言世界 · 人格体的操作系统 · 国作登字-2026-A-00037559 · ICE-GL-ZY001</div>
</div>
<script>
var SERVER_NAMES = {
// 服务器名称映射
var names = {
'repo-mcp-server': '仓库接口',
'repo-mcp-zz': '之之仓库',
'guanghulab-mcp-server': '大脑唤醒',
@ -224,204 +228,146 @@ var SERVER_NAMES = {
'console-server': '控制台'
};
var canvas = document.getElementById('starfield');
var ctx = canvas.getContext('2d');
var stars = [], brainData = null, currentPanel = null, lastFetch = 0;
// 星空
var cvs = document.getElementById('starfield'), ctx = cvs.getContext('2d'), stars = [], data = null, cur = null, lastF = 0;
function resize() { cvs.width = window.innerWidth; cvs.height = document.body.scrollHeight + 200; }
function mkStars() { stars = []; for (var i = 0; i < 200; i++) stars.push({ x: Math.random() * cvs.width, y: Math.random() * cvs.height, r: Math.random() * 2 + 0.2, o: Math.random(), s: Math.random() * 0.0025 + 0.001, p: Math.random() * Math.PI * 2 }); }
resize(); mkStars();
window.addEventListener('resize', function() { resize(); mkStars(); });
function draw(t) { ctx.clearRect(0, 0, cvs.width, cvs.height); for (var i = 0; i < stars.length; i++) { var s = stars[i], b = Math.sin(t * s.s + s.p) * 0.5 + 0.5, a = s.o * (0.25 + b * 0.75); ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fillStyle = 'rgba(160,200,240,' + a + ')'; ctx.fill(); } }
function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = document.body.scrollHeight + 200; }
function initStars() {
stars = [];
for (var i = 0; i < 200; i++) {
stars.push({
x: Math.random() * canvas.width, y: Math.random() * canvas.height,
r: Math.random() * 2 + 0.2, opacity: Math.random(),
speed: Math.random() * 0.0025 + 0.001, phase: Math.random() * Math.PI * 2
});
}
}
resizeCanvas(); initStars();
window.addEventListener('resize', function() { resizeCanvas(); initStars(); });
function drawStars(t) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
var s = stars[i];
var b = Math.sin(t * s.speed + s.phase) * 0.5 + 0.5;
var a = s.opacity * (0.25 + b * 0.75);
ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(160,200,240,' + a + ')'; ctx.fill();
if (s.r > 1.4 && b > 0.65) {
ctx.beginPath(); ctx.arc(s.x, s.y, s.r * 2.5, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(140,180,240,' + (a * 0.12) + ')'; ctx.fill();
}
}
}
// 面板切换
function togglePanel(name) {
var panel = document.getElementById('panel-' + name);
var card = document.getElementById('mod-' + name);
// 面板切换 - 最简单可靠的实现
function showPanel(id) {
var panel = document.getElementById('p-' + id);
var card = document.getElementById('m-' + id);
var isOpen = panel.classList.contains('show');
// 关所有
var allPanels = document.querySelectorAll('.expanded-panel');
var allCards = document.querySelectorAll('.module-card');
for (var i = 0; i < allPanels.length; i++) allPanels[i].classList.remove('show');
for (var j = 0; j < allCards.length; j++) allCards[j].classList.remove('active');
// 隐藏所有面板
var allP = document.getElementsByClassName('panel');
for (var i = 0; i < allP.length; i++) allP[i].classList.remove('show');
if (isOpen) { currentPanel = null; return; }
// 移除所有卡片active
var allC = document.getElementsByClassName('mod');
for (var j = 0; j < allC.length; j++) allC[j].classList.remove('active');
if (isOpen) { cur = null; return; }
// 显示当前
panel.classList.add('show');
card.classList.add('active');
currentPanel = name;
cur = id;
if (name === 'chat') document.getElementById('chatInput').focus();
if (name === 'server') { renderServerList(); fetchOpsLog(); }
if (id === 'chat') document.getElementById('inp').focus();
if (id === 'server') { renderSrv(); fetchLog(); }
canvas.height = document.body.scrollHeight + 200;
initStars();
resize(); mkStars();
}
var hbEl = document.getElementById('hb');
var beats = ['▮▮▮','▮ ▮ ▮','▮▮▮','▮ ▮ ▮'];
var beatIdx = 0, lastBeat = 0;
// 心跳
var hb = document.getElementById('hb'), beats = ['▮▮▮', '▮ ▮ ▮', '▮▮▮', '▮ ▮ ▮'], idx = 0, lastB = 0;
function fetchBrain() {
fetch('/api/brain', {cache: 'no-store'})
.then(function(r) { return r.json(); })
.then(function(data) {
brainData = data;
updateStatusBar(data);
if (currentPanel === 'server') renderServerList();
if (currentPanel === 'ticket') renderTicketList();
})
.catch(function(){});
}
function updateStatusBar(b) {
document.getElementById('wake').textContent = b.awakening || '--';
document.getElementById('age').textContent = (b.age_days || '--') + '天';
if (b.server) {
var on = b.server.online || 0, tot = b.server.total || 10;
document.getElementById('svcCount').textContent = on + '/' + tot;
document.getElementById('svcCount').style.color = on === tot ? 'rgba(130,210,170,.85)' : 'rgba(210,190,130,.85)';
}
}
function renderServerList() {
var l = document.getElementById('serverList');
if (!brainData || !brainData.server) {
l.innerHTML = '<div class="server-tag"><span class="server-dot online"></span>等待数据…</div>';
return;
}
var svcs = brainData.server.services || [];
var st = brainData.server.stopped || [];
var h = '';
for (var i = 0; i < svcs.length; i++) {
var n = svcs[i];
var name = SERVER_NAMES[n] || n.replace(/^guanghulab-/,'').replace(/-/g,' ');
h += '<div class="server-tag"><span class="server-dot online"></span>' + name + '</div>';
}
for (var j = 0; j < st.length; j++) {
var sn = st[j];
var sname = SERVER_NAMES[sn] || sn.replace(/^guanghulab-/,'').replace(/-/g,' ');
h += '<div class="server-tag"><span class="server-dot offline"></span><span style="opacity:.6">' + sname + '</span></div>';
}
l.innerHTML = h || '<div class="server-tag">暂无服务数据</div>';
}
function fetchOpsLog() {
fetch('/api/brain', {cache: 'no-store'})
// 获取数据
function fetchData() {
fetch('/api/brain', { cache: 'no-store' })
.then(function(r) { return r.json(); })
.then(function(d) {
var el = document.getElementById('opsLog');
if (d.ops_log && d.ops_log.length > 0) {
data = d;
document.getElementById('wake').textContent = d.awakening || '--';
document.getElementById('age').textContent = (d.age_days || '--') + '天';
if (d.server) {
var on = d.server.online || 0, tot = d.server.total || 10;
document.getElementById('svc').textContent = on + '/' + tot;
document.getElementById('svc').style.color = on === tot ? 'rgba(140,220,180,0.9)' : 'rgba(220,200,140,0.9)';
}
if (cur === 'server') renderSrv();
if (cur === 'ticket') renderTk();
})
.catch(function() {});
}
// 渲染服务器
function renderSrv() {
var box = document.getElementById('srv-list');
if (!data || !data.server) { box.innerHTML = '<div class="server-item"><span class="server-dot on"></span>等待数据…</div>'; return; }
var ons = data.server.services || [], offs = data.server.stopped || [], html = '';
for (var i = 0; i < ons.length; i++) {
var n = names[ons[i]] || ons[i].replace(/^guanghulab-/, '').replace(/-/g, ' ');
html += '<div class="server-item"><span class="server-dot on"></span>' + n + '</div>';
}
for (var j = 0; j < offs.length; j++) {
var m = names[offs[j]] || offs[j].replace(/^guanghulab-/, '').replace(/-/g, ' ');
html += '<div class="server-item"><span class="server-dot off"></span><span style="opacity:0.6">' + m + '</span></div>';
}
box.innerHTML = html || '<div class="server-item">暂无服务数据</div>';
}
// 渲染日志
function fetchLog() {
fetch('/api/brain', { cache: 'no-store' })
.then(function(r) { return r.json(); })
.then(function(d) {
var box = document.getElementById('log-box');
if (d.ops_log && d.ops_log.length) {
var h = '';
for (var i = 0; i < Math.min(d.ops_log.length, 12); i++) {
h += '<div class="log-row"><span class="log-hash">' + d.ops_log[i].hash + '</span>' + d.ops_log[i].msg + '</div>';
h += '<div class="row"><span class="hash">' + d.ops_log[i].hash + '</span>' + d.ops_log[i].msg + '</div>';
}
el.innerHTML = h;
box.innerHTML = h;
} else {
el.innerHTML = '<div style="color:rgba(140,180,220,.35)">日志数据未接入</div>';
box.innerHTML = '<div style="color:rgba(160,200,240,0.35)">日志数据未接入</div>';
}
})
.catch(function() {
document.getElementById('opsLog').innerHTML = '<div style="color:rgba(140,180,220,.35)">日志加载失败</div>';
document.getElementById('log-box').innerHTML = '<div style="color:rgba(160,200,240,0.35)">日志加载失败</div>';
});
}
function renderTicketList() {
var l = document.getElementById('ticketList');
if (!brainData || !brainData.mothers) {
l.innerHTML = '<div class="ticket-row"><span class="ticket-icon"></span><span class="ticket-name">等待数据…</span></div>';
return;
}
var h = '';
for (var i = 0; i < brainData.mothers.length; i++) {
var m = brainData.mothers[i];
h += '<div style="font-size:13px;color:var(--label);padding:10px 0 6px;letter-spacing:1px;font-weight:500">' + m.name + '</div>';
if (m.paths) for (var j = 0; j < m.paths.length; j++) {
var p = m.paths[j];
var cls = p.status === 'done' ? 'done' : p.status === 'in_progress' ? 'progress' : '';
var icon = p.status === 'done' ? '✅' : p.status === 'in_progress' ? '🔄' : '⏳';
var label = p.status === 'done' ? '完成' : p.status === 'in_progress' ? '进行中' : '待办';
h += '<div class="ticket-row"><span class="ticket-icon">' + icon + '</span><span class="ticket-name">' + p.name + '</span><span class="ticket-status ' + cls + '">' + label + '</span></div>';
// 渲染工单
function renderTk() {
var box = document.getElementById('tk-list');
if (!data || !data.mothers) { box.innerHTML = '<div class="ticket"><span class="icon"></span><span class="name">等待数据…</span></div>'; return; }
var html = '';
for (var i = 0; i < data.mothers.length; i++) {
var m = data.mothers[i];
html += '<div style="font-size:15px;color:rgba(160,200,240,0.6);padding:12px 0 8px;letter-spacing:1px;font-weight:500">' + m.name + '</div>';
if (m.paths) {
for (var j = 0; j < m.paths.length; j++) {
var p = m.paths[j], done = p.status === 'done', prog = p.status === 'in_progress';
var icon = done ? '✅' : prog ? '🔄' : '⏳', tag = done ? 'done' : prog ? 'progress' : '', label = done ? '完成' : prog ? '进行中' : '待办';
html += '<div class="ticket"><span class="icon">' + icon + '</span><span class="name">' + p.name + '</span><span class="tag ' + tag + '">' + label + '</span></div>';
}
}
}
l.innerHTML = h;
box.innerHTML = html;
}
function sendChat() {
var i = document.getElementById('chatInput');
var m = i.value.trim();
// 聊天
function send() {
var i = document.getElementById('inp'), m = i.value.trim();
if (!m) return;
addChatMsg('冰朔', m, 'ren');
addMsg('冰朔', m, 'ren');
i.value = '';
document.getElementById('chatStatus').textContent = '思考中…';
fetch('/api/chat-v2', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: m, persona: 'zhuyuan'})
})
.then(function(r) { return r.json(); })
.then(function(d) {
document.getElementById('chatStatus').textContent = '';
addChatMsg('铸渊', d.reply || d.message || d.text || JSON.stringify(d), 'zhu');
canvas.height = document.body.scrollHeight + 200;
initStars();
})
.catch(function() {
document.getElementById('chatStatus').textContent = '';
addChatMsg('铸渊', '(API未接入)
等chat-v2端点激活后这里就是真正的铸渊交互。', 'zhu');
});
fetch('/api/chat-v2', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: m, persona: 'zhuyuan' }) })
.then(function(r) { return r.json(); })
.then(function(d) { addMsg('铸渊', d.reply || d.message || JSON.stringify(d), 'zhu'); resize(); mkStars(); })
.catch(function() { addMsg('铸渊', '(API未接入)\n\n等chat-v2端点激活后这里就是真正的铸渊交互。', 'zhu'); });
}
function addChatMsg(who, text, cls) {
var c = document.getElementById('chatMsgs');
var d = document.createElement('div');
function addMsg(who, text, cls) {
var c = document.getElementById('msgs'), d = document.createElement('div');
d.className = 'chat-msg';
d.innerHTML = '<div class="who ' + cls + '">' + who + '</div><div>' + text.replace(/\n/g,'<br>') + '</div>';
d.innerHTML = '<div class="who ' + cls + '">' + who + '</div><div>' + text.replace(/\n/g, '<br>') + '</div>';
c.appendChild(d);
c.scrollTop = c.scrollHeight;
}
// 主循环
function loop(t) {
drawStars(t);
if (t - lastBeat > 800) {
hbEl.textContent = beats[beatIdx];
beatIdx = (beatIdx + 1) % beats.length;
lastBeat = t;
}
if (t - lastFetch > 3000) {
fetchBrain();
lastFetch = t;
}
draw(t);
if (t - lastB > 800) { hb.textContent = beats[idx]; idx = (idx + 1) % beats.length; lastB = t; }
if (t - lastF > 3000) { fetchData(); lastF = t; }
requestAnimationFrame(loop);
}
fetchBrain();
fetchData();
requestAnimationFrame(loop);
</script>