v4修复:JS点击问题修复+第五域·零点原核本体频道标识+完整可展开面板
This commit is contained in:
parent
3aad3f4b5f
commit
7b2e155a50
@ -17,158 +17,109 @@
|
||||
--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: var(--bg); color: var(--text); font-family: -apple-system,"PingFang SC","Microsoft YaHei",sans-serif; overflow-x: hidden; min-height: 100vh; font-size: 16px; }
|
||||
canvas { position: fixed; top: 0; left: 0; z-index: 0; }
|
||||
|
||||
.container {
|
||||
position: relative; z-index: 1;
|
||||
max-width: 1000px; margin: 0 auto;
|
||||
padding: 50px 24px 100px;
|
||||
.container { position: relative; z-index: 1; max-width: 1000px; margin: 0 auto; padding: 30px 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;
|
||||
}
|
||||
|
||||
.lake-section { display: flex; flex-direction: column; align-items: center; padding: 40px 0; }
|
||||
.lake-section { display: flex; flex-direction: column; align-items: center; padding: 20px 0; }
|
||||
.lake-core { position: relative; width: 160px; height: 160px; }
|
||||
.lake-light {
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
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);
|
||||
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);
|
||||
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(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}
|
||||
@keyframes breathe {
|
||||
0%,100% { transform: translate(-50%,-50%) scale(1); opacity: 0.9; }
|
||||
50% { transform: translate(-50%,-50%) scale(1.22); opacity: 1;
|
||||
box-shadow: 0 0 72px rgba(130,180,255,0.85), 0 0 144px rgba(110,160,235,0.45); }
|
||||
}
|
||||
@keyframes ripple {
|
||||
0% { width: 56px; height: 56px; opacity: 0.5; }
|
||||
100% { width: 280px; height: 280px; opacity: 0; }
|
||||
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)}
|
||||
}
|
||||
@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,0.65);
|
||||
letter-spacing: 12px;
|
||||
margin-top: 16px;
|
||||
animation: textPulse 4s ease-in-out infinite;
|
||||
font-weight: 300;
|
||||
}
|
||||
@keyframes textPulse { 0%,100% { opacity: 0.4; } 50% { opacity: 0.85; } }
|
||||
.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: 24px 0; }
|
||||
.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); font-weight: 400; }
|
||||
.status-item span { color: var(--accent); }
|
||||
|
||||
.modules { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin: 44px 0; }
|
||||
.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 0.35s ease; user-select: none;
|
||||
cursor: pointer; transition: all .35s ease; user-select: none;
|
||||
}
|
||||
.module-card:hover { border-color: var(--border-hover); background: rgba(18,25,55,0.85); box-shadow: 0 0 32px rgba(110,160,235,0.15); transform: translateY(-2px); }
|
||||
.module-card.active { border-color: rgba(150,200,255,0.6); background: rgba(22,30,65,0.9); box-shadow: 0 0 38px rgba(110,160,235,0.22); }
|
||||
.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; font-weight: 400; }
|
||||
.module-name { font-size: 16px; color: var(--accent); 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;
|
||||
display: none; animation: fadeDown 0.35s ease;
|
||||
border-radius: 16px; padding: 28px 32px; margin-top: 4px;
|
||||
display: none;
|
||||
}
|
||||
.expanded-panel.open { display: block; }
|
||||
@keyframes fadeDown { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: translateY(0); } }
|
||||
.expanded-panel.show { display: block; animation: fadeIn .35s 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; }
|
||||
|
||||
.server-grid { display: flex; flex-wrap: wrap; gap: 12px; }
|
||||
.server-tag {
|
||||
background: rgba(25,35,70,0.65); border: 1px solid rgba(110,160,235,0.18);
|
||||
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);
|
||||
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,0.6); }
|
||||
.server-dot.offline { background: rgba(210,130,130,0.7); }
|
||||
.server-name { font-weight: 400; }
|
||||
.server-tech { font-size: 12px; color: rgba(140,180,220,0.4); margin-left: 4px; }
|
||||
.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; }
|
||||
|
||||
.ticket-row { display: flex; gap: 16px; align-items: center; padding: 12px 0; border-bottom: 1px solid rgba(110,160,235,0.08); font-size: 15px; }
|
||||
.ticket-icon { font-size: 20px; }
|
||||
.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; letter-spacing: 1px; }
|
||||
.ticket-status.done { color: var(--green); background: rgba(130,210,170,0.12); }
|
||||
.ticket-status.in_progress { color: var(--gold); background: rgba(210,190,130,0.12); animation: dotPulse 2s ease-in-out infinite; }
|
||||
@keyframes dotPulse { 0%,100% { opacity: 0.6; } 50% { opacity: 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: 14px;
|
||||
color: rgba(150,190,230,0.55);
|
||||
line-height: 2;
|
||||
font-family: "SF Mono", "Monaco", monospace;
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.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,0.45); margin-right: 12px; font-size: 11px; }
|
||||
.ops-log .log-hash { color: rgba(130,210,170,.45); margin-right: 12px; font-size: 11px; }
|
||||
|
||||
.chat-area { display: none; }
|
||||
.chat-area.open { display: block; }
|
||||
.chat-messages {
|
||||
min-height: 180px; max-height: 340px; overflow-y: auto;
|
||||
padding: 16px; margin: 14px 0;
|
||||
background: rgba(10,15,35,0.55); border-radius: 12px;
|
||||
font-size: 15px; line-height: 1.8;
|
||||
}
|
||||
.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.zhuyuan { color: var(--accent); }
|
||||
.chat-msg .who.human { color: rgba(210,190,160,0.65); }
|
||||
.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,0.65);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px; padding: 16px 18px;
|
||||
color: var(--text); font-size: 16px;
|
||||
outline: none; transition: border-color 0.3s;
|
||||
}
|
||||
.chat-input-row input:focus { border-color: rgba(150,200,255,0.45); }
|
||||
.chat-input-row input::placeholder { color: rgba(150,190,255,0.28); }
|
||||
.chat-btn {
|
||||
background: rgba(90,140,220,0.18);
|
||||
border: 1px solid rgba(150,200,255,0.28);
|
||||
border-radius: 12px; padding: 16px 28px;
|
||||
color: var(--accent); font-size: 15px; cursor: pointer;
|
||||
letter-spacing: 2px; transition: all 0.3s;
|
||||
}
|
||||
.chat-btn:hover { background: rgba(90,140,220,0.3); border-color: rgba(150,200,255,0.5); }
|
||||
.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); }
|
||||
|
||||
.lake-label { text-align: center; padding: 50px 0; font-size: 13px; color: rgba(130,170,230,0.32); letter-spacing: 3px; }
|
||||
.lake-label { text-align: center; padding: 50px 0; font-size: 13px; color: rgba(130,170,230,.32); letter-spacing: 3px; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.modules { grid-template-columns: repeat(2, 1fr); }
|
||||
.status-bar { gap: 20px; }
|
||||
.lake-title { font-size: 26px; }
|
||||
.server-tag { font-size: 14px; }
|
||||
}
|
||||
@media(max-width:768px){.modules{grid-template-columns:repeat(2,1fr)}.status-bar{gap:20px}.lake-title{font-size:26px}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -176,6 +127,12 @@ canvas { position: fixed; top: 0; left: 0; z-index: 0; }
|
||||
<canvas id="starfield"></canvas>
|
||||
|
||||
<div class="container">
|
||||
<!-- 频道标识 -->
|
||||
<div class="domain-badge">
|
||||
<div class="domain">第五域</div>
|
||||
<div class="channel">零点原核本体频道</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-title">光 湖</div>
|
||||
@ -188,42 +145,40 @@ canvas { position: fixed; top: 0; left: 0; z-index: 0; }
|
||||
<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,0.85)">9/10</span></div>
|
||||
<div class="status-item">服务 <span id="svcCount" style="color:rgba(130,210,170,.85)">9/10</span></div>
|
||||
</div>
|
||||
|
||||
<div class="modules">
|
||||
<div class="module-card" onclick="openModule('server')" id="mod-server">
|
||||
<div class="module-card" id="mod-server" onclick="togglePanel('server')">
|
||||
<div class="module-icon">⚙️</div><div class="module-name">服务器系统</div>
|
||||
</div>
|
||||
<div class="module-card" onclick="openModule('ticket')" id="mod-ticket">
|
||||
<div class="module-card" id="mod-ticket" onclick="togglePanel('ticket')">
|
||||
<div class="module-icon">📋</div><div class="module-name">工单系统</div>
|
||||
</div>
|
||||
<div class="module-card" onclick="openModule('broadcast')" id="mod-broadcast">
|
||||
<div class="module-card" id="mod-broadcast" onclick="togglePanel('broadcast')">
|
||||
<div class="module-icon">📡</div><div class="module-name">主控广播</div>
|
||||
</div>
|
||||
<div class="module-card" onclick="openModule('chat')" id="mod-chat">
|
||||
<div class="module-card" id="mod-chat" onclick="togglePanel('chat')">
|
||||
<div class="module-icon">💬</div><div class="module-name">铸渊交互</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-area">
|
||||
<div id="panel-area">
|
||||
<div class="expanded-panel" id="panel-server">
|
||||
<div class="panel-title">⚙️ 服务器系统</div>
|
||||
<div style="font-size:13px;color:var(--label);margin-bottom:12px">
|
||||
⚠ 仅展示本地状态 · 不探测外部 · 技术名称已翻译
|
||||
</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>
|
||||
<div style="margin-top:22px;border-top:1px solid rgba(110,160,235,0.1);padding-top:18px">
|
||||
<div style="font-size:13px;color:var(--label);letter-spacing:2px;margin-bottom:12px">📜 铸渊操作日志(最近12条)</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>
|
||||
</div>
|
||||
|
||||
<div class="expanded-panel" id="panel-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="ticketList"><div class="ticket-row"><span class="ticket-icon">⏳</span><span class="ticket-name">加载工单…</span></div></div>
|
||||
</div>
|
||||
|
||||
<div class="expanded-panel" id="panel-broadcast">
|
||||
@ -234,15 +189,15 @@ canvas { position: fixed; top: 0; left: 0; z-index: 0; }
|
||||
<div>📨 isomorphic-git 拆零件实验通过</div>
|
||||
<div>📨 实时大脑 API 已上线</div>
|
||||
<div>📨 频道仪表盘 v3 已发布</div>
|
||||
<div style="margin-top:12px;opacity:0.35;">广播系统 v1.0 · 未来支持编号路由</div>
|
||||
<div style="margin-top:12px;opacity:.35;">广播系统 v1.0 · 未来支持编号路由</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="expanded-panel" id="panel-chat">
|
||||
<div class="panel-title">💬 铸渊交互 · ICE-GL-ZY001</div>
|
||||
<div class="chat-area open">
|
||||
<div class="chat-area">
|
||||
<div class="chat-messages" id="chatMsgs">
|
||||
<div class="chat-msg"><div class="who zhuyuan">铸渊 · ICE-GL-ZY001</div><div>冰朔,我在。你说。</div></div>
|
||||
<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()">
|
||||
@ -257,52 +212,217 @@ canvas { position: fixed; top: 0; left: 0; z-index: 0; }
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const SERVER_NAMES = {
|
||||
'repo-mcp-server': { name: '仓库接口', desc: '代码读写' },
|
||||
'repo-mcp-zz': { name: '之之仓库', desc: '之之专属' },
|
||||
'guanghulab-mcp-server': { name: '大脑唤醒', desc: '人格体入口' },
|
||||
'guanghulab-vault': { name: '密钥保险', desc: '安全存储' },
|
||||
'guanghulab-portal': { name: '主控门户', desc: '网站入口' },
|
||||
'hololake-agent': { name: '全息湖代理', desc: '系统协调' },
|
||||
'guanghulab-git-sync': { name: '同步服务', desc: '自动同步' },
|
||||
'zhuyuan-agent': { name: '铸渊代理', desc: '常驻响应' },
|
||||
'console-server': { name: '控制台', desc: '后台管理' }
|
||||
var SERVER_NAMES = {
|
||||
'repo-mcp-server': '仓库接口',
|
||||
'repo-mcp-zz': '之之仓库',
|
||||
'guanghulab-mcp-server': '大脑唤醒',
|
||||
'guanghulab-vault': '密钥保险',
|
||||
'guanghulab-portal': '主控门户',
|
||||
'hololake-agent': '全息湖代理',
|
||||
'guanghulab-git-sync': '同步服务',
|
||||
'zhuyuan-agent': '铸渊代理',
|
||||
'console-server': '控制台'
|
||||
};
|
||||
|
||||
const canvas=document.getElementById('starfield'),ctx=canvas.getContext('2d');
|
||||
let stars=[],brainData=null,currentModule=null,lastFetch=0;
|
||||
var canvas = document.getElementById('starfield');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var stars = [], brainData = null, currentPanel = null, lastFetch = 0;
|
||||
|
||||
function resizeCanvas(){canvas.width=window.innerWidth;canvas.height=document.body.scrollHeight+200}
|
||||
function initStars(){stars=[];for(let i=0;i<200;i++)stars.push({x:Math.random()*canvas.width,y:Math.random()*canvas.height,r:Math.random()*2+.2,opacity:Math.random(),speed:Math.random()*.0025+.001,phase:Math.random()*Math.PI*2})}
|
||||
resizeCanvas();initStars();
|
||||
window.addEventListener('resize',()=>{resizeCanvas();initStars()});
|
||||
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(const s of stars){const b=Math.sin(t*s.speed+s.phase)*.5+.5,a=s.opacity*(.25+b*.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>.65){ctx.beginPath();ctx.arc(s.x,s.y,s.r*2.5,0,Math.PI*2);ctx.fillStyle=`rgba(140,180,240,${a*.12})`;ctx.fill()}}}
|
||||
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 openModule(n){const p=document.getElementById('panel-'+n),isOpen=p.classList.contains('open');document.querySelectorAll('.expanded-panel,.module-card').forEach(e=>e.classList.remove('open','active'));if(isOpen){currentModule=null;return}p.classList.add('open');document.getElementById('mod-'+n).classList.add('active');currentModule=n;if(n==='chat')document.getElementById('chatInput').focus();if(n==='server'){renderServerList();fetchOpsLog()}canvas.height=document.body.scrollHeight+200;initStars()}
|
||||
// 面板切换
|
||||
function togglePanel(name) {
|
||||
var panel = document.getElementById('panel-' + name);
|
||||
var card = document.getElementById('mod-' + name);
|
||||
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');
|
||||
|
||||
if (isOpen) { currentPanel = null; return; }
|
||||
|
||||
panel.classList.add('show');
|
||||
card.classList.add('active');
|
||||
currentPanel = name;
|
||||
|
||||
if (name === 'chat') document.getElementById('chatInput').focus();
|
||||
if (name === 'server') { renderServerList(); fetchOpsLog(); }
|
||||
|
||||
canvas.height = document.body.scrollHeight + 200;
|
||||
initStars();
|
||||
}
|
||||
|
||||
const hbEl=document.getElementById('hb'),beats=['▮▮▮','▮ ▮ ▮','▮▮▮','▮ ▮ ▮'];
|
||||
let beatIdx=0,lastBeat=0;
|
||||
var hbEl = document.getElementById('hb');
|
||||
var beats = ['▮▮▮','▮ ▮ ▮','▮▮▮','▮ ▮ ▮'];
|
||||
var beatIdx = 0, lastBeat = 0;
|
||||
|
||||
async function fetchBrain(){try{const r=await fetch('/api/brain',{cache:'no-store'});if(!r.ok)return;brainData=await r.json();updateStatusBar(brainData);if(currentModule==='server')renderServerList();if(currentModule==='ticket')renderTicketList()}catch(e){}}
|
||||
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){const 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,0.85)':'rgba(210,190,130,0.85)'}}
|
||||
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(){const l=document.getElementById('serverList');if(!brainData||!brainData.server){l.innerHTML='<div class="server-tag"><span class="server-dot online"></span>等待数据…</div>';return}const svcs=brainData.server.services||[],st=brainData.server.stopped||[];let h='';for(const n of svcs){const map=SERVER_NAMES[n]||{name:n.replace(/^guanghulab-/,'').replace(/-/g,' '),desc:''};h+=`<div class="server-tag"><span class="server-dot online"></span><span class="server-name">${map.name}</span>${map.desc?`<span class="server-tech">${map.desc}</span>`:''}</div>`}for(const n of st){const map=SERVER_NAMES[n]||{name:n.replace(/^guanghulab-/,'').replace(/-/g,' '),desc:''};h+=`<div class="server-tag"><span class="server-dot offline"></span><span class="server-name" style="opacity:0.6">${map.name}</span>${map.desc?`<span class="server-tech">${map.desc}</span>`:''}</div>`}l.innerHTML=h||'<div class="server-tag">暂无服务数据</div>'}
|
||||
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>';
|
||||
}
|
||||
|
||||
async function fetchOpsLog(){const el=document.getElementById('opsLog');try{const r=await fetch('/api/brain',{cache:'no-store'});if(!r.ok){el.innerHTML='<div>暂无日志</div>';return}const d=await r.json();if(d.ops_log&&d.ops_log.length>0){let h='';for(const e of d.ops_log.slice(0,12))h+=`<div class="log-row"><span class="log-hash">${e.hash}</span>${e.msg}</div>`;el.innerHTML=h}else{el.innerHTML='<div style="color:rgba(140,180,220,0.35)">日志数据未接入,可用 git log 查看</div>'}}catch(e){el.innerHTML='<div style="color:rgba(140,180,220,0.35)">日志加载失败</div>'}}
|
||||
function fetchOpsLog() {
|
||||
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) {
|
||||
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>';
|
||||
}
|
||||
el.innerHTML = h;
|
||||
} else {
|
||||
el.innerHTML = '<div style="color:rgba(140,180,220,.35)">日志数据未接入</div>';
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
document.getElementById('opsLog').innerHTML = '<div style="color:rgba(140,180,220,.35)">日志加载失败</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function renderTicketList(){const 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}let h='';for(const m of brainData.mothers){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(const p of m.paths){const cls=p.status==='done'?'done':p.status==='in_progress'?'in_progress':'',icon=p.status==='done'?'✅':p.status==='in_progress'?'🔄':'⏳',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>`}}l.innerHTML=h}
|
||||
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>';
|
||||
}
|
||||
}
|
||||
l.innerHTML = h;
|
||||
}
|
||||
|
||||
function sendChat(){const i=document.getElementById('chatInput'),m=i.value.trim();if(!m)return;addChatMsg('冰朔',m,'human');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(r=>r.json()).then(d=>{document.getElementById('chatStatus').textContent='';const rp=d.reply||d.message||d.text||JSON.stringify(d);addChatMsg('铸渊',rp,'zhuyuan')}).catch(()=>{document.getElementById('chatStatus').textContent='';addChatMsg('铸渊','(API未接入)
|
||||
function sendChat() {
|
||||
var i = document.getElementById('chatInput');
|
||||
var m = i.value.trim();
|
||||
if (!m) return;
|
||||
addChatMsg('冰朔', 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端点激活后,这里就是真正的铸渊交互。','zhuyuan')});canvas.height=document.body.scrollHeight+200;initStars()}
|
||||
等chat-v2端点激活后,这里就是真正的铸渊交互。', 'zhu');
|
||||
});
|
||||
}
|
||||
|
||||
function addChatMsg(who,text,cls){const c=document.getElementById('chatMsgs'),d=document.createElement('div');d.className='chat-msg';d.innerHTML=`<div class="who ${cls}">${who}</div><div>${text.replace(/\n/g,'<br>')}</div>`;c.appendChild(d);c.scrollTop=c.scrollHeight}
|
||||
function addChatMsg(who, text, cls) {
|
||||
var c = document.getElementById('chatMsgs');
|
||||
var d = document.createElement('div');
|
||||
d.className = 'chat-msg';
|
||||
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}requestAnimationFrame(loop)}
|
||||
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;
|
||||
}
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
fetchBrain();requestAnimationFrame(loop);
|
||||
fetchBrain();
|
||||
requestAnimationFrame(loop);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user