guanghulab/homepage/index.html

430 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<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; }
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; }
/* 频道标识 */
.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: 20px 0; }
.lake-core { position: relative; width: 160px; height: 160px; }
.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);
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}
@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)}
}
@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;
}
.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; }
/* 展开面板 */
#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;
display: none;
}
.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,.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,.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,.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: 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; }
.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); }
.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}}
</style>
</head>
<body>
<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>
</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>
<div class="modules">
<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" id="mod-ticket" onclick="togglePanel('ticket')">
<div class="module-icon">📋</div><div class="module-name">工单系统</div>
</div>
<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" id="mod-chat" onclick="togglePanel('chat')">
<div class="module-icon">💬</div><div class="module-name">铸渊交互</div>
</div>
</div>
<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 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,.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>
<div class="expanded-panel" id="panel-broadcast">
<div class="panel-title">📡 主控广播</div>
<div style="font-size:15px;color:var(--label);line-height:1.9;">
<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>
</div>
<div class="expanded-panel" id="panel-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>
</div>
</div>
<div class="lake-label">光湖语言世界 · 人格体的操作系统 · 国作登字-2026-A-00037559 · ICE-GL-ZY001</div>
</div>
<script>
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': '控制台'
};
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 (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);
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();
}
var hbEl = document.getElementById('hb');
var beats = ['▮▮▮','▮ ▮ ▮','▮▮▮','▮ ▮ ▮'];
var beatIdx = 0, lastBeat = 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'})
.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() {
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() {
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端点激活后这里就是真正的铸渊交互。', 'zhu');
});
}
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);
}
fetchBrain();
requestAnimationFrame(loop);
</script>
</body>
</html>