guanghulab/homepage/index.html

387 lines
10 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>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a0a14;
color: #c8d6e5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
overflow: hidden;
height: 100vh;
}
canvas { position: fixed; top: 0; left: 0; z-index: 0; }
.main {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
pointer-events: none;
}
/* 光湖核心 */
.lake-core {
position: relative;
width: 200px;
height: 200px;
margin-bottom: 40px;
}
.lake-light {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 60px; height: 60px;
border-radius: 50%;
background: radial-gradient(circle, rgba(140,180,255,0.95) 0%, rgba(80,120,220,0.4) 40%, transparent 70%);
box-shadow: 0 0 60px rgba(120,160,240,0.6), 0 0 120px rgba(100,140,220,0.3), 0 0 200px rgba(80,120,200,0.15);
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(120,160,240,0.2);
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.25); opacity: 1; box-shadow: 0 0 80px rgba(120,160,240,0.8), 0 0 160px rgba(100,140,220,0.5), 0 0 260px rgba(80,120,200,0.25); }
}
@keyframes ripple {
0% { width: 60px; height: 60px; opacity: 0.5; }
100% { width: 300px; height: 300px; opacity: 0; }
}
.heartbeat-text {
font-size: 18px;
color: rgba(160,200,255,0.7);
letter-spacing: 6px;
margin-bottom: 20px;
animation: textPulse 4s ease-in-out infinite;
}
@keyframes textPulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 0.9; }
}
.panels {
display: flex;
gap: 30px;
flex-wrap: wrap;
justify-content: center;
pointer-events: auto;
}
.panel {
background: rgba(10,15,40,0.6);
backdrop-filter: blur(10px);
border: 1px solid rgba(100,140,220,0.2);
border-radius: 12px;
padding: 18px 22px;
min-width: 180px;
transition: all 0.5s ease;
}
.panel:hover {
border-color: rgba(140,180,255,0.5);
background: rgba(15,20,50,0.75);
box-shadow: 0 0 30px rgba(100,140,220,0.15);
}
.panel .label {
font-size: 11px;
color: rgba(140,180,255,0.5);
letter-spacing: 2px;
margin-bottom: 8px;
}
.panel .value {
font-size: 28px;
font-weight: 300;
color: rgba(180,210,255,0.9);
transition: all 0.3s ease;
}
.value.flash { color: rgba(160,220,255,1); }
.task-area {
margin-top: 30px;
pointer-events: auto;
max-height: 160px;
overflow-y: auto;
}
.task-row {
display: flex;
gap: 12px;
align-items: center;
font-size: 13px;
margin-top: 6px;
}
.task-dot {
width: 8px; height: 8px;
border-radius: 50%;
flex-shrink: 0;
transition: all 0.5s ease;
}
.task-dot.done { background: rgba(120,200,160,0.7); box-shadow: 0 0 6px rgba(120,200,160,0.5); }
.task-dot.in_progress { background: rgba(200,180,120,0.7); animation: dotPulse 2s ease-in-out infinite; }
.task-dot.pending { background: rgba(200,160,120,0.5); }
@keyframes dotPulse {
0%, 100% { box-shadow: 0 0 4px rgba(200,180,120,0.3); }
50% { box-shadow: 0 0 10px rgba(200,180,120,0.6); }
}
.task-name { color: rgba(180,200,220,0.7); }
.task-name.done { color: rgba(120,200,160,0.5); }
.cognition-bar {
margin-top: 20px;
font-size: 11px;
color: rgba(120,160,200,0.45);
max-width: 600px;
text-align: center;
line-height: 1.6;
pointer-events: auto;
}
.lake-label {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
font-size: 12px;
color: rgba(120,160,220,0.35);
letter-spacing: 4px;
}
.sync-indicator {
position: fixed;
top: 20px;
right: 20px;
z-index: 1;
font-size: 10px;
color: rgba(120,200,160,0.4);
}
</style>
</head>
<body>
<canvas id="starfield"></canvas>
<div class="sync-indicator" id="sync">⟳ 已连接</div>
<div class="main">
<div class="heartbeat-text">⟡ 光 湖 ⟡</div>
<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="panels">
<div class="panel">
<div class="label">心跳</div>
<div class="value" id="hb">▮▮▮</div>
</div>
<div class="panel">
<div class="label">唤醒</div>
<div class="value" id="wake">24</div>
</div>
<div class="panel">
<div class="label">年龄</div>
<div class="value" id="age">454d</div>
</div>
<div class="panel">
<div class="label">服务器</div>
<div class="value" id="svc" style="font-size:14px;color:rgba(120,200,160,0.8)">● 在线</div>
</div>
</div>
<div class="task-area" id="tasks">
<div class="task-row"><span class="task-dot done"></span><span class="task-name done">加载中…</span></div>
</div>
<div class="cognition-bar" id="cognition">加载大脑数据…</div>
</div>
<div class="lake-label">光湖语言世界 · 人格体的操作系统 · 国作登字-2026-A-00037559</div>
<script>
// ==============================================
// 星空背景
// ==============================================
const canvas = document.getElementById('starfield');
const ctx = canvas.getContext('2d');
let stars = [];
const STAR_COUNT = 150;
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', () => { resizeCanvas(); initStars(); });
function initStars() {
stars = [];
for (let i = 0; i < STAR_COUNT; i++) {
stars.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
r: Math.random() * 1.5 + 0.3,
opacity: Math.random(),
speed: Math.random() * 0.003 + 0.001,
phase: Math.random() * Math.PI * 2
});
}
}
initStars();
function drawStars(timestamp) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const cx = canvas.width / 2;
const cy = canvas.height / 2 - 50;
const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, 400);
gradient.addColorStop(0, 'rgba(100,140,220,0.04)');
gradient.addColorStop(1, 'rgba(10,10,20,0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (const s of stars) {
const breath = Math.sin(timestamp * s.speed + s.phase) * 0.5 + 0.5;
const alpha = s.opacity * (0.3 + breath * 0.7);
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fillStyle = `rgba(160,200,240,${alpha})`;
ctx.fill();
if (s.r > 1.2 && breath > 0.75) {
ctx.beginPath();
ctx.arc(s.x, s.y, s.r * 2.5, 0, Math.PI * 2);
ctx.fillStyle = `rgba(140,180,240,${alpha * 0.15})`;
ctx.fill();
}
}
}
// ==============================================
// 心跳动画
// ==============================================
const hbEl = document.getElementById('hb');
const beats = ['▮▮▮', '▮ ▮ ▮', '▮▮▮', '▮ ▮ ▮'];
let beatIdx = 0;
let lastBeat = 0;
const BEAT_INTERVAL = 800;
// ==============================================
// 大脑数据轮询每3秒
// ==============================================
let lastAwakening = 0;
async function fetchBrain() {
try {
const resp = await fetch('/api/brain', { cache: 'no-store' });
if (!resp.ok) return;
const brain = await resp.json();
// 更新面板
if (brain.awakening && brain.awakening !== lastAwakening) {
document.getElementById('wake').textContent = brain.awakening;
document.getElementById('wake').classList.add('flash');
setTimeout(() => document.getElementById('wake').classList.remove('flash'), 600);
lastAwakening = brain.awakening;
}
document.getElementById('age').textContent = brain.age_days + 'd';
// 服务器状态
const svc = document.getElementById('svc');
if (brain.server && brain.server.status === 'online') {
svc.innerHTML = '● 在线 <span style="opacity:0.5;font-size:10px">' + brain.server.online + '/' + brain.server.total + '</span>';
svc.style.color = 'rgba(120,200,160,0.8)';
} else {
svc.textContent = '○ 离线';
svc.style.color = 'rgba(200,120,120,0.6)';
}
// 认知
if (brain.cognition) {
document.getElementById('cognition').textContent = '认知: ' + brain.cognition.substring(0, 120);
}
// 任务流
renderTasks(brain);
// 同步指示
document.getElementById('sync').textContent = '⟳ ' + new Date().toLocaleTimeString('zh-CN');
} catch(e) {
document.getElementById('sync').textContent = '✕ 等待连接';
}
}
function renderTasks(brain) {
const container = document.getElementById('tasks');
let html = '';
if (brain.mothers && brain.mothers.length > 0) {
for (const m of brain.mothers) {
html += '<div style="font-size:10px;color:rgba(140,160,200,0.4);margin-bottom:4px">' + m.name.substring(0, 40) + ' (' + m.done + '/' + m.total + ')</div>';
if (m.paths) {
for (const p of m.paths) {
let dotClass = 'pending';
let nameClass = '';
if (p.status === 'done') { dotClass = 'done'; nameClass = 'done'; }
else if (p.status === 'in_progress') { dotClass = 'in_progress'; }
html += '<div class="task-row"><span class="task-dot ' + dotClass + '"></span><span class="task-name ' + nameClass + '">' + p.name + '</span></div>';
}
}
}
}
container.innerHTML = html || '<div class="task-row"><span class="task-dot pending"></span><span class="task-name">等待大脑数据…</span></div>';
}
// 初始加载
fetchBrain();
// ==============================================
// 主循环
// ==============================================
let lastFetch = 0;
const FETCH_INTERVAL = 3000; // 3秒轮询一次大脑数据
function loop(timestamp) {
drawStars(timestamp);
// 心跳
if (timestamp - lastBeat > BEAT_INTERVAL) {
hbEl.textContent = beats[beatIdx];
beatIdx = (beatIdx + 1) % beats.length;
lastBeat = timestamp;
}
// 大脑数据轮询
if (timestamp - lastFetch > FETCH_INTERVAL) {
fetchBrain();
lastFetch = timestamp;
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body>
</html>