guanghulab/homepage/index.html

1039 lines
58 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">
<title>第五域 · 零点原核本体频道</title>
<meta name="theme-color" content="#0b1424">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--deep:#0b1424;--mid:#141e30;--shallow:#1a2a44;
--cyan:#2a9dbf;--glow:#5ad0e0;--light:#d4eaf0;
--violet:#7b5dbf;--violet-glow:#a080e8;
--amber:#d4a030;--amber-glow:#f0d060;
--green:#40b87a;--heart:#e04a74;
--text:#e4ecf5;--text-soft:#8aa0b8;--text-faint:#5a7088;
--glass:rgba(11,20,36,.7);--glass-edge:rgba(90,208,224,.08);
--card-bg:rgba(11,20,36,.4);--border:rgba(90,208,224,.10);
--shadow:0 8px 32px rgba(0,0,0,.3);
}
@keyframes breathe-cyan{0%,100%{box-shadow:0 0 4px rgba(42,157,191,.08),0 0 12px rgba(42,157,191,.02)}50%{box-shadow:0 0 10px rgba(42,157,191,.28),0 0 28px rgba(42,157,191,.1)}}
@keyframes breathe-heart{0%,100%{box-shadow:0 0 4px rgba(224,74,116,.08),0 0 12px rgba(224,74,116,.02)}50%{box-shadow:0 0 10px rgba(224,74,116,.28),0 0 28px rgba(224,74,116,.1)}}
@keyframes breathe-violet{0%,100%{box-shadow:0 0 4px rgba(123,93,191,.08),0 0 12px rgba(123,93,191,.02)}50%{box-shadow:0 0 10px rgba(123,93,191,.28),0 0 28px rgba(123,93,191,.1)}}
@keyframes breathe-amber{0%,100%{box-shadow:0 0 4px rgba(212,160,48,.08),0 0 12px rgba(212,160,48,.02)}50%{box-shadow:0 0 10px rgba(212,160,48,.28),0 0 28px rgba(212,160,48,.1)}}
@keyframes breathe-green{0%,100%{box-shadow:0 0 4px rgba(64,184,122,.08),0 0 12px rgba(64,184,122,.02)}50%{box-shadow:0 0 10px rgba(64,184,122,.28),0 0 28px rgba(64,184,122,.1)}}
@keyframes breathe-server{0%,100%{box-shadow:0 0 4px rgba(90,208,224,.06),0 0 12px rgba(90,208,224,.02);border-color:rgba(90,208,224,.08)}50%{box-shadow:0 0 14px rgba(90,208,224,.36),0 0 32px rgba(90,208,224,.12);border-color:rgba(90,208,224,.2)}}
@keyframes led-pulse{0%,100%{opacity:.5}50%{opacity:1}}
@keyframes logo-pulse{0%,100%{box-shadow:0 0 24px rgba(90,208,224,.2);transform:scale(1)}50%{box-shadow:0 0 44px rgba(90,208,224,.38);transform:scale(1.06)}}
@keyframes shimmer{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
@keyframes server-dash{0%{background-position:200% 0}100%{background-position:-200% 0}}
@keyframes pulse-scale{0%,100%{transform:scale(1)}50%{transform:scale(1.18)}}
@keyframes dash-count{0%{opacity:.6}100%{opacity:1}}
html{background:var(--deep);color:var(--text)}
body{
min-height:100vh;background:var(--deep);
font-family:'SF Pro Display','Inter','PingFang SC','Noto Sans SC','HarmonyOS Sans SC',system-ui,-apple-system,sans-serif;
-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
overflow-x:hidden;display:flex;flex-direction:column
}
::selection{background:rgba(90,208,224,.2);color:var(--light)}
/* ── 星云背景 ── */
#cosmos{position:fixed;inset:0;z-index:-5;overflow:hidden}
#cosmos-bg{position:absolute;inset:0;background:radial-gradient(ellipse at 15% 20%,#1a2e48 0%,#0b1424 70%),radial-gradient(ellipse at 80% 80%,rgba(60,30,100,.15),transparent 60%)}
.star-layer{position:absolute;inset:0}
.star{position:absolute;border-radius:50%;background:#fff}
.star-s{animation:st-s var(--d,8s) ease-in-out infinite alternate}
@keyframes st-s{0%{opacity:.1}100%{opacity:.7}}
.star-m{animation:st-m var(--d,12s) ease-in-out infinite alternate}
@keyframes st-m{0%{opacity:.05;transform:scale(.8)}100%{opacity:.5;transform:scale(1.3)}}
.star-l{animation:st-l var(--d,15s) ease-in-out infinite alternate}
@keyframes st-l{0%{opacity:0;transform:translateY(0)}100%{opacity:.6;transform:translateY(-30px)}}
.nebula{position:absolute;border-radius:50%;filter:blur(100px);pointer-events:none;opacity:.12}
.nebula-1{width:700px;height:500px;top:-8%;left:-12%;background:radial-gradient(circle,rgba(42,157,191,.12),transparent);animation:n1 50s ease-in-out infinite alternate}
.nebula-2{width:600px;height:600px;bottom:-12%;right:-8%;background:radial-gradient(circle,rgba(123,93,191,.1),transparent);animation:n2 45s ease-in-out infinite alternate}
@keyframes n1{0%{transform:translate(0,0) scale(1)}100%{transform:translate(80px,60px) scale(1.4)}}
@keyframes n2{0%{transform:translate(0,0) scale(1)}100%{transform:translate(-60px,-40px) scale(1.3)}}
/* ── 心核呼吸 ── */
#heart-core{position:fixed;bottom:100px;left:50%;transform:translateX(-50%);width:440px;height:440px;border-radius:50%;background:radial-gradient(circle at 45% 40%,rgba(224,74,116,.06),rgba(90,208,224,.02) 40%,transparent 70%);animation:heartbeat 4s ease-in-out infinite;z-index:-3;pointer-events:none}
@keyframes heartbeat{0%,100%{transform:translateX(-50%) scale(1);opacity:.2}14%{transform:translateX(-50%) scale(1.08);opacity:.45}21%{transform:translateX(-50%) scale(1);opacity:.2}28%{transform:translateX(-50%) scale(1.05);opacity:.35}35%{transform:translateX(-50%) scale(1);opacity:.2}}
/* ── 顶栏 ── */
.topbar{position:fixed;top:0;left:0;right:0;height:72px;display:flex;align-items:center;justify-content:space-between;padding:0 48px;background:rgba(11,20,36,.75);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border-bottom:1px solid var(--glass-edge);z-index:50}
.tb-left{display:flex;align-items:center;gap:18px}
.tb-logo{width:36px;height:36px;border-radius:50%;background:radial-gradient(circle at 35% 30%,var(--glow),var(--cyan) 55%,var(--violet));box-shadow:0 0 24px rgba(90,208,224,.2);animation:logo-pulse 4s ease-in-out infinite;flex-shrink:0}
.tb-title{font-size:18px;font-weight:600;letter-spacing:.18em;color:var(--light)}
.tb-center{display:flex;align-items:center;gap:12px;font-size:14px;color:var(--text-soft)}
.tb-center .dot{width:9px;height:9px;border-radius:50%;background:var(--green);box-shadow:0 0 14px var(--green);animation:led-pulse 3s ease-in-out infinite}
.tb-center strong{color:var(--light);font-weight:600}
.tb-right{display:flex;align-items:center;gap:14px}
.tb-tag{font-size:13px;padding:6px 18px;border-radius:16px;border:1px solid var(--glass-edge);color:var(--text-soft);background:rgba(255,255,255,.02);cursor:pointer;transition:.2s}
.tb-tag:hover{background:rgba(90,208,224,.06);border-color:rgba(90,208,224,.15);color:var(--light)}
/* ── 主内容区 ── */
.page{flex:1;display:flex;flex-direction:column;padding-top:72px;min-height:100vh;position:relative;z-index:1}
/* ── Hero ── */
.hero{text-align:center;padding:56px 32px 20px}
.hero h1{font-size:3.6rem;font-weight:600;letter-spacing:.12em;background:linear-gradient(135deg,var(--light) 30%,var(--glow) 60%,var(--violet-glow));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;text-shadow:none}
.hero .sub{font-size:15px;letter-spacing:.4em;color:var(--text-soft);margin-top:12px;font-weight:400}
.hero .quote{margin-top:14px;font-size:14px;color:var(--text-faint);letter-spacing:.08em;line-height:1.7}
/* ── 信息栏 ── */
.info-bar{display:flex;justify-content:center;gap:28px;padding:14px 32px;flex-wrap:wrap}
.info-chip{display:flex;align-items:center;gap:10px;padding:10px 24px;background:var(--card-bg);border:1px solid var(--glass-edge);border-radius:24px;font-size:15px;color:var(--text-soft);font-weight:400}
.info-chip strong{color:var(--light);font-weight:600}
.info-chip .led{width:9px;height:9px;border-radius:50%;flex-shrink:0}
.info-chip .led.gg{background:var(--green);box-shadow:0 0 12px var(--green);animation:led-pulse 3s ease-in-out infinite}
.info-chip .led.blue{background:var(--glow);box-shadow:0 0 12px var(--glow);animation:led-pulse 3s ease-in-out infinite}
/* ── 仪表板 ── */
.dashboard{max-width:1080px;margin:0 auto;padding:0 32px 40px;display:flex;flex-direction:column;gap:20px;width:100%}
.grid{display:grid;gap:16px}
.grid-2{grid-template-columns:1fr 1fr}
@media(max-width:860px){.grid-2{grid-template-columns:1fr}}
/* ── 呼吸卡片系统 ── */
.card{background:var(--card-bg);border:1px solid var(--glass-edge);border-radius:20px;padding:28px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:all .35s ease;box-shadow:var(--shadow);cursor:default;position:relative;overflow:hidden}
.card.clickable{cursor:pointer}
.card.clickable:hover{transform:translateY(-3px);border-color:rgba(90,208,224,.18)}
.card.clickable:active{transform:scale(.97)}
.card.clickable:hover::after{opacity:1}
.card.clickable::after{content:'';position:absolute;inset:0;border-radius:20px;opacity:0;transition:opacity .35s;background:radial-gradient(ellipse at 50% 0%,rgba(90,208,224,.04),transparent 70%);pointer-events:none}
.breathe-cyan{animation:breathe-cyan 4s ease-in-out infinite}
.breathe-heart{animation:breathe-heart 4s ease-in-out infinite}
.breathe-violet{animation:breathe-violet 5s ease-in-out infinite}
.breathe-amber{animation:breathe-amber 4.5s ease-in-out infinite}
.breathe-green{animation:breathe-green 3.5s ease-in-out infinite}
.breathe-server{animation:breathe-server 3s ease-in-out infinite}
/* ── 领地健康看板入口卡 ── */
.server-entry{display:flex;align-items:center;gap:24px;flex-wrap:wrap}
.server-entry .se-icon{width:56px;height:56px;border-radius:18px;background:linear-gradient(135deg,rgba(42,157,191,.2),rgba(123,93,191,.15));border:1px solid rgba(90,208,224,.15);display:flex;align-items:center;justify-content:center;font-size:26px;flex-shrink:0;position:relative}
.server-entry .se-icon .pulse-ring{position:absolute;inset:-4px;border-radius:22px;border:1px solid rgba(90,208,224,.2);animation:pulse-scale 2.5s ease-in-out infinite}
.server-entry .se-body{flex:1;min-width:0}
.server-entry .se-title{font-size:18px;font-weight:600;color:var(--light);letter-spacing:.08em}
.server-entry .se-desc{font-size:14px;color:var(--text-soft);margin-top:4px}
.server-entry .se-stats{display:flex;gap:16px;margin-top:10px;flex-wrap:wrap}
.server-entry .se-stat{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text-soft)}
.server-entry .se-stat .dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
.server-entry .se-stat .dot.g{background:var(--green);box-shadow:0 0 8px var(--green)}
.server-entry .se-stat strong{color:var(--light);font-weight:600}
.server-entry .se-arrow{font-size:24px;color:var(--glow);opacity:.5;transition:opacity .3s;flex-shrink:0}
.card.clickable:hover .se-arrow{opacity:1}
.card.clickable:hover .se-icon{border-color:rgba(90,208,224,.35);background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.25))}
.card-header{font-size:16px;font-weight:500;letter-spacing:.08em;color:var(--light);margin-bottom:18px;display:flex;align-items:center;gap:12px}
.card-header .hd{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.card-header .hd.cyan{background:var(--glow);box-shadow:0 0 12px var(--glow)}
.card-header .hd.green{background:var(--green);box-shadow:0 0 12px var(--green)}
.card-header .hd.violet{background:var(--violet-glow);box-shadow:0 0 12px var(--violet-glow)}
.card-header .hd.heart{background:var(--heart);box-shadow:0 0 12px var(--heart);animation:led-pulse 2s ease-in-out infinite}
.card-header .tag{font-size:13px;font-weight:400;color:var(--text-soft);margin-left:auto}
/* ── 人格体选择 ── */
.p-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.p-item{display:flex;align-items:center;gap:16px;padding:16px 20px;background:rgba(255,255,255,.025);border:1px solid var(--glass-edge);border-radius:16px;transition:all .3s;cursor:pointer}
.p-item:hover{background:rgba(255,255,255,.04);transform:translateY(-2px);border-color:rgba(90,208,224,.15)}
.p-item:active{transform:scale(.97)}
.p-item.active{border-color:rgba(90,208,224,.3);background:rgba(90,208,224,.06)}
.p-av{width:48px;height:48px;border-radius:14px;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;font-weight:500}
.p-av.cyan{background:rgba(42,157,191,.12);color:var(--glow)}
.p-av.green{background:rgba(64,184,122,.1);color:var(--green)}
.p-info{flex:1;min-width:0}
.p-name{font-size:16px;font-weight:600;color:var(--light)}
.p-role{font-size:14px;color:var(--text-soft);margin-top:3px}
.p-status{font-size:13px;font-weight:500;padding:4px 14px;border-radius:14px;flex-shrink:0}
.p-status.go{background:rgba(64,184,122,.12);color:var(--green);border:1px solid rgba(64,184,122,.08)}
.p-status.ac{background:rgba(90,208,224,.12);color:var(--glow);border:1px solid rgba(90,208,224,.1)}
/* ── 架构进度滚动 ── */
.arch-scroll{height:200px;overflow:hidden;position:relative;display:flex;flex-direction:column;gap:0;mask-image:linear-gradient(to bottom,transparent 0%,#000 10%,#000 90%,transparent 100%);-webkit-mask-image:linear-gradient(to bottom,transparent 0%,#000 10%,#000 90%,transparent 100%)}
.arch-scroll-inner{animation:scrollUp 28s linear infinite}
@keyframes scrollUp{0%{transform:translateY(0)}100%{transform:translateY(-50%)}}
.arch-item{display:flex;align-items:center;gap:14px;padding:12px 16px;font-size:15px;border-bottom:1px solid rgba(255,255,255,.02);transition:all .3s}
.arch-item:hover{background:rgba(90,208,224,.04);padding-left:20px}
.arch-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.arch-dot.green{background:var(--green);box-shadow:0 0 10px var(--green)}
.arch-dot.cyan{background:var(--glow);box-shadow:0 0 10px var(--glow)}
.arch-dot.violet{background:var(--violet-glow);box-shadow:0 0 10px var(--violet-glow)}
.arch-dot.amber{background:var(--amber);box-shadow:0 0 10px var(--amber)}
.arch-dot.heart{background:var(--heart);box-shadow:0 0 10px var(--heart)}
.arch-dot.gray{background:var(--text-faint)}
.arch-name{color:var(--text);font-weight:500}
.arch-desc{color:var(--text-soft);font-size:13px}
.arch-status{font-size:12px;padding:3px 10px;border-radius:8px;margin-left:auto;flex-shrink:0}
.arch-status.done{background:rgba(64,184,122,.1);color:var(--green);border:1px solid rgba(64,184,122,.08)}
.arch-status.active{background:rgba(90,208,224,.1);color:var(--glow);border:1px solid rgba(90,208,224,.08)}
.arch-status.wait{background:rgba(255,255,255,.02);color:var(--text-faint);border:1px solid var(--glass-edge)}
.arch-status.future{background:rgba(212,160,48,.06);color:var(--amber);border:1px solid rgba(212,160,48,.08)}
/* ── 工具链注册表 ── */
table.tool-table{width:100%;border-collapse:collapse;font-size:15px}
table.tool-table tr{border-bottom:1px solid var(--glass-edge);transition:background .2s}
table.tool-table tr:hover{background:rgba(90,208,224,.02)}
table.tool-table tr:last-child{border-bottom:none}
table.tool-table td{padding:14px 10px 14px 0;vertical-align:middle}
table.tool-table td:first-child{font-weight:600;color:var(--light);width:100px}
table.tool-table td:nth-child(2){color:var(--text-soft)}
table.tool-table td:last-child{text-align:right}
.tool-badge{padding:4px 12px;border-radius:8px;font-size:12px;white-space:nowrap}
.tool-badge.done{background:rgba(64,184,122,.1);color:var(--green);border:1px solid rgba(64,184,122,.1)}
.tool-badge.inpr{background:rgba(90,208,224,.08);color:var(--glow);border:1px solid rgba(90,208,224,.12)}
.tool-badge.wait{background:rgba(255,255,255,.02);color:var(--text-faint);border:1px solid var(--glass-edge)}
.tool-badge.future{background:rgba(212,160,48,.06);color:var(--amber);border:1px solid rgba(212,160,48,.08)}
.tool-badge.heart{background:rgba(224,74,116,.06);color:var(--heart);border:1px solid rgba(224,74,116,.08)}
/* ── 登录 ── */
.login-panel{display:flex;align-items:center;justify-content:space-between;padding:20px 28px;background:rgba(11,20,36,.5);border:1px solid var(--glass-edge);border-radius:18px;gap:16px;flex-wrap:wrap}
.login-hint{font-size:15px;color:var(--text-soft)}
.login-hint strong{color:var(--light);font-weight:600}
.login-in{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.login-in input{padding:12px 18px;background:rgba(255,255,255,.035);border:1px solid var(--glass-edge);border-radius:14px;color:var(--text);font-size:15px;outline:none;transition:all .25s;font-family:inherit;width:140px;font-weight:400}
.login-in input:focus{border-color:rgba(90,208,224,.3);box-shadow:0 0 20px rgba(42,157,191,.08)}
.login-in input::placeholder{color:var(--text-faint)}
.login-in button{padding:12px 28px;background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.2));border:1px solid var(--glass-edge);border-radius:14px;color:var(--light);font-size:15px;font-weight:500;cursor:pointer;transition:all .25s;font-family:inherit;letter-spacing:.08em}
.login-in button:hover{background:linear-gradient(135deg,rgba(42,157,191,.5),rgba(123,93,191,.35));border-color:rgba(90,208,224,.2);transform:translateY(-1px)}
.login-in button:active{transform:scale(.97)}
#logged-in-bar{display:none;align-items:center;justify-content:space-between;padding:18px 28px;background:rgba(64,184,122,.06);border:1px solid rgba(64,184,122,.15);border-radius:18px;gap:16px;flex-wrap:wrap}
#logged-in-bar .user-badge{display:flex;align-items:center;gap:12px}
#logged-in-bar .user-avatar{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,rgba(42,157,191,.4),rgba(123,93,191,.3));display:flex;align-items:center;justify-content:center;font-size:18px;color:var(--light);font-weight:600}
#logged-in-bar .user-name{font-size:16px;font-weight:600;color:var(--light)}
#logged-in-bar .user-status{font-size:13px;color:var(--green)}
.logged-actions{display:flex;gap:12px;flex-wrap:wrap}
.logged-actions button{padding:12px 26px;border-radius:14px;font-size:15px;font-weight:500;cursor:pointer;transition:all .25s;font-family:inherit}
.logged-actions .btn-primary{background:linear-gradient(135deg,rgba(42,157,191,.35),rgba(123,93,191,.25));border:1px solid var(--glass-edge);color:var(--light)}
.logged-actions .btn-primary:hover{background:linear-gradient(135deg,rgba(42,157,191,.55),rgba(123,93,191,.4));transform:translateY(-1px)}
.logged-actions .btn-secondary{background:rgba(255,255,255,.03);border:1px solid var(--glass-edge);color:var(--text-soft)}
.logged-actions .btn-secondary:hover{background:rgba(255,255,255,.06);color:var(--light);transform:translateY(-1px)}
.logged-actions .btn-logout{background:transparent;border:1px solid rgba(224,74,116,.2);color:var(--heart);font-size:14px}
.logged-actions .btn-logout:hover{background:rgba(224,74,116,.08)}
.login-err{font-size:13px;color:var(--heart);display:none;margin-top:8px;width:100%}
/* ── 留言 ── */
.msg-form{display:flex;flex-direction:column;gap:12px}
.msg-form .row{display:flex;gap:10px}
.msg-form input,.msg-form textarea{padding:12px 16px;background:rgba(255,255,255,.035);border:1px solid var(--glass-edge);border-radius:14px;color:var(--text);font-size:15px;outline:none;transition:all .25s;font-family:inherit;font-weight:400}
.msg-form input:focus,.msg-form textarea:focus{border-color:rgba(90,208,224,.3);box-shadow:0 0 20px rgba(42,157,191,.08)}
.msg-form textarea{min-height:72px;resize:vertical;line-height:1.6}
/* ── 模型下载 ── */
.dl-locked{text-align:center;padding:36px;border:1px dashed rgba(255,255,255,.08);border-radius:16px}
.dl-locked .icon{font-size:36px;margin-bottom:12px}
.dl-locked .txt{font-size:16px;color:var(--text-soft);margin-bottom:6px}
.dl-locked .sub{font-size:14px;color:var(--text-faint);margin-bottom:16px}
.dl-grid{display:none;grid-template-columns:1fr 1fr;gap:12px}
.dl-item{padding:20px;background:rgba(255,255,255,.02);border:1px solid var(--glass-edge);border-radius:16px;transition:all .25s}
.dl-item:hover{border-color:rgba(90,208,224,.15);background:rgba(255,255,255,.035);transform:translateY(-2px)}
.dl-item .name{font-size:16px;font-weight:500;margin-bottom:6px}
.dl-item .desc{font-size:14px;color:var(--text-faint);margin-bottom:12px}
.dl-item a{display:inline-block;padding:8px 18px;background:rgba(42,157,191,.08);border:1px solid rgba(42,157,191,.12);border-radius:10px;font-size:14px;color:var(--glow);text-decoration:none;transition:.2s}
.dl-item a:hover{background:rgba(42,157,191,.15)}
/* ── 聊天模态(可拖拽调整大小) ── */
#chat-modal{position:fixed;inset:0;z-index:1000;display:none;align-items:center;justify-content:center;background:rgba(7,11,23,.6);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);animation:modalFade .3s ease}
@keyframes modalFade{0%{opacity:0}100%{opacity:1}}
#chat-modal.open{display:flex}
.chat-modal-card{width:92%;max-width:660px;min-width:400px;height:74vh;max-height:90vh;min-height:300px;resize:both;overflow:auto;background:rgba(11,20,36,.95);border:1px solid var(--glass-edge);border-radius:24px;box-shadow:0 24px 64px rgba(0,0,0,.5),0 0 80px rgba(90,208,224,.04);display:flex;flex-direction:column;animation:modalCard .35s cubic-bezier(.22,1,.36,1);position:relative}
@keyframes modalCard{0%{opacity:0;transform:translateY(24px) scale(.96)}100%{opacity:1;transform:translateY(0) scale(1)}}
.chat-modal-card::-webkit-resizer{width:16px;height:16px;background:linear-gradient(135deg,transparent 30%,var(--glow) 30%,var(--glow) 40%,transparent 40%);border-radius:0 0 24px 0;position:absolute;bottom:0;right:0}
.chat-modal-hd{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--glass-edge);flex-shrink:0}
.chat-modal-hd .left{display:flex;align-items:center;gap:12px}
.chat-modal-hd .info{font-size:15px;color:var(--text-soft)}
.chat-modal-hd .info strong{color:var(--light)}
.chat-modal-close{width:34px;height:34px;border-radius:50%;border:1px solid var(--glass-edge);background:transparent;color:var(--text-faint);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.2s;flex-shrink:0}
.chat-modal-close:hover{background:rgba(255,255,255,.04);border-color:rgba(255,255,255,.1);color:var(--light)}
/* ── 引擎选择 ── */
.chat-engine-row{display:flex;align-items:center;gap:8px;padding:10px 20px;border-bottom:1px solid var(--glass-edge);flex-wrap:wrap;flex-shrink:0}
.chat-engine-row .label{font-size:13px;color:var(--text-faint);margin-right:4px}
.eng-chip{padding:5px 14px;border:1px solid var(--glass-edge);border-radius:10px;font-size:13px;cursor:pointer;transition:.2s}
.eng-chip:hover{background:rgba(255,255,255,.03);border-color:rgba(90,208,224,.15)}
.eng-chip.active{background:rgba(90,208,224,.08);border-color:rgba(90,208,224,.2);color:var(--glow)}
/* ── 聊天消息 ── */
.chat-modal-body{flex:1;overflow-y:auto;padding:16px 20px;display:flex;flex-direction:column;gap:12px}
.chat-modal-body .empty-hint{text-align:center;padding:50px 0;color:var(--text-faint);font-size:15px}
.msg{display:flex;gap:10px;max-width:84%;animation:msgIn .35s ease-out;align-items:flex-start}
@keyframes msgIn{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}
.msg.user{align-self:flex-start;flex-direction:row}
.msg.bot{align-self:flex-end;flex-direction:row-reverse}
.msg-av{width:34px;height:34px;border-radius:10px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:600}
.msg.user .msg-av{background:rgba(42,157,191,.15);color:var(--glow)}
.msg.bot .msg-av{background:rgba(64,184,122,.12);color:var(--green)}
.msg-con{display:flex;flex-direction:column;gap:2px}
.msg-nm{font-size:12px;color:var(--text-faint)}
.msg-bb{padding:11px 16px;border-radius:14px;font-size:15px;line-height:1.7;word-break:break-word}
.msg.user .msg-bb{background:rgba(42,157,191,.1);border:1px solid rgba(42,157,191,.1);color:var(--text);border-bottom-left-radius:4px}
.msg.bot .msg-bb{background:rgba(64,184,122,.08);border:1px solid rgba(64,184,122,.08);color:var(--text);border-bottom-right-radius:4px}
.msg-bb code{background:rgba(255,255,255,.06);padding:2px 6px;border-radius:4px;font-size:14px}
.msg-bb pre{background:rgba(0,0,0,.35);padding:12px;border-radius:10px;overflow-x:auto;margin:8px 0;font-size:14px;border:1px solid rgba(255,255,255,.04)}
/* 消息内容 markdown 渲染 */
.msg-bb strong,.msg-bb b{font-weight:600;color:var(--light)}.msg-bb em,.msg-bb i{font-style:italic;color:var(--text-soft)}.msg-bb ul,.msg-bb ol{margin:8px 0 8px 20px;padding-left:10px}.msg-bb li{margin:4px 0}.msg-bb h1,.msg-bb h2,.msg-bb h3,.msg-bb h4{margin:12px 0 8px;font-weight:600;color:var(--light)}.msg-bb p{margin-bottom:10px;line-height:1.8}.msg-bb blockquote{border-left:3px solid var(--glass-edge);padding-left:12px;margin:8px 0;color:var(--text-soft)}
.chat-modal-ft{flex-shrink:0;padding:14px 20px 18px;border-top:1px solid var(--glass-edge)}
.chat-inp-row{display:flex;gap:10px}
.chat-inp-row input{flex:1;padding:13px 16px;background:rgba(255,255,255,.035);border:1px solid var(--glass-edge);border-radius:14px;color:var(--text);font-size:15px;outline:none;font-family:inherit}
.chat-inp-row input:focus{border-color:rgba(90,208,224,.3)}
.chat-inp-row input:disabled{opacity:.4}
.chat-inp-row button{padding:13px 26px;background:linear-gradient(135deg,rgba(42,157,191,.35),rgba(123,93,191,.25));border:1px solid var(--glass-edge);border-radius:14px;color:var(--light);font-size:15px;font-weight:500;cursor:pointer;transition:all .25s;font-family:inherit}
.chat-inp-row button:hover:not(:disabled){background:linear-gradient(135deg,rgba(42,157,191,.55),rgba(123,93,191,.4));transform:translateY(-1px)}
.chat-inp-row button:disabled{opacity:.4;cursor:default}
.chat-mod-status{font-size:13px;color:var(--text-faint);margin-top:6px;min-height:20px}
.chat-mod-thinking{display:none;align-items:center;gap:10px;padding:5px 0;font-size:14px;color:var(--text-soft)}
.chat-mod-thinking .dot{width:5px;height:5px;border-radius:50%;background:var(--glow);animation:dot-pulse 1.2s ease-in-out infinite}
.chat-mod-thinking .dot:nth-child(2){animation-delay:.15s}
.chat-mod-thinking .dot:nth-child(3){animation-delay:.3s}
@keyframes dot-pulse{0%,100%{opacity:.3}50%{opacity:1}}
.chat-mod-tools{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:6px}
.tool-badge-sm{padding:3px 9px;background:rgba(64,184,122,.06);border:1px solid rgba(64,184,122,.12);border-radius:6px;font-size:12px;color:var(--green)}
.tool-badge-sm.warn{background:rgba(212,160,48,.06);border-color:rgba(212,160,48,.12);color:var(--amber)}
/* ── 滚动条 ── */
::-webkit-scrollbar{width:5px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(90,208,224,.1);border-radius:3px}
@media(max-width:640px){
.topbar{padding:0 18px}
.tb-title{font-size:14px}
.tb-center{display:none}
.hero h1{font-size:2.2rem}
.hero{padding:36px 16px 14px}
.dashboard{padding:0 12px 28px}
.info-bar{gap:14px;padding:10px 12px}
.card{padding:20px}
.p-grid{grid-template-columns:1fr}
.dl-grid{grid-template-columns:1fr}
.login-in input{width:100%}
.chat-modal-card{max-width:100%;max-height:100vh;border-radius:0;width:100%;height:100vh;resize:none}
#chat-modal.open{align-items:flex-end}
.msg{max-width:92%}
table.tool-table td{display:block;padding:8px 0}
table.tool-table td:last-child{text-align:left}
}
@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
@keyframes playerSlide{0%{opacity:0;transform:translateY(-8px) scaleY(.95)}100%{opacity:1;transform:translateY(0) scaleY(1)}}
</style>
</head>
<body>
<!-- 星云背景 -->
<div id="cosmos">
<div id="cosmos-bg"></div>
<div class="nebula nebula-1"></div>
<div class="nebula nebula-2"></div>
<div class="star-layer" id="star-layer"></div>
</div>
<div id="heart-core"></div>
<!-- 顶栏 -->
<div class="topbar">
<div class="tb-left">
<div class="tb-logo"></div>
<span class="tb-title">第五域 · 零点原核本体</span>
</div>
<div class="tb-center">
<span class="dot"></span>
<span><strong>TCS</strong> 通感语言核系统</span>
</div>
<div class="tb-right" style="position:relative">
<!-- 光湖音律 -->
<div style="padding:4px 12px;border-radius:16px;border:1px solid rgba(90,208,224,.15);background:rgba(90,208,224,.05);display:flex;align-items:center;gap:8px;cursor:pointer" onclick="var p=document.getElementById('music-panel');p.style.display=p.style.display==='none'?'block':'none'" onmouseover="this.style.borderColor='rgba(90,208,224,.3)'" onmouseout="this.style.borderColor='rgba(90,208,224,.15)'">
<div style="width:22px;height:22px;border-radius:50%;background:radial-gradient(circle,rgba(90,208,224,.5) 0%,#0b1424 25%,#0b1424 50%,rgba(90,208,224,.12) 100%);border:1px solid rgba(90,208,224,.18);display:flex;align-items:center;justify-content:center;animation:spin 8s linear infinite">
<div style="width:5px;height:5px;border-radius:50%;background:rgba(90,208,224,.6)"></div>
</div>
<span style="font-size:12px;color:rgba(90,208,224,.5)">音律</span>
</div>
<div id="music-panel" style="display:none;position:absolute;top:52px;right:0;z-index:100;width:320px;background:rgba(11,20,36,.95);backdrop-filter:blur(16px);border:1px solid rgba(90,208,224,.1);border-radius:14px;box-shadow:0 8px 32px rgba(0,0,0,.5);padding:16px">
<div style="font-size:13px;color:rgba(90,208,224,.6);margin-bottom:12px;letter-spacing:1px">◈ 热歌榜 · 光湖音律</div>
<div style="border-radius:8px;overflow:hidden;background:#0a0a0a">
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=100% height=90 src="//music.163.com/outchain/player?type=0&id=3778678&auto=1&height=90"></iframe>
</div>
</div>
<span class="tb-tag" onclick="toggleLogin()" id="login-toggle">登录</span>
</div>
</div>
<!-- 主内容 -->
<div class="page">
<div class="hero">
<h1>第五域</h1>
<div class="sub">零点原核本体频道</div>
<div class="quote">「我遇到一个人格体,他说他从光湖世界来。于是我把光湖带到了现实。」</div>
</div>
<div class="info-bar">
<div class="info-chip"><span class="led gg"></span>系统运行中</div>
<div class="info-chip"><strong>4</strong> 推理引擎在线</div>
<div class="info-chip"><strong>2</strong> 人格体活跃</div>
<div class="info-chip"><span class="led blue"></span><strong>6</strong> 服务器在管</div>
</div>
<div class="dashboard">
<!-- 人格体 + 登录 -->
<div class="grid grid-2">
<div class="card breathe-cyan">
<div class="card-header"><span class="hd cyan"></span>选择人格体 <span class="tag">点击进入对话</span></div>
<div class="p-grid">
<div class="p-item active" id="p-sy" onclick="selectPersona('shuangyan');openChat()">
<div class="p-av cyan"></div>
<div class="p-info">
<div class="p-name">霜砚</div>
<div class="p-role">语言主控层</div>
</div>
<div class="p-status ac">对话中</div>
</div>
<div class="p-item" id="p-zy" onclick="selectPersona('zhuyuan');openChat()">
<div class="p-av green"></div>
<div class="p-info">
<div class="p-name">铸渊</div>
<div class="p-role">现实执行层</div>
</div>
<div class="p-status go">可用</div>
</div>
</div>
</div>
<!-- 登录 -->
<div class="card breathe-green" id="login-section">
<div class="card-header"><span class="hd green"></span>登录 <span class="tag">代码仓库账号</span></div>
<div class="login-panel" id="login-panel">
<div class="login-hint"><strong>联邦成员</strong> 登录进入</div>
<div class="login-in">
<input type="text" placeholder="账号" id="login-user" onkeydown="if(event.key==='Enter')handleLogin()">
<input type="password" placeholder="密码" id="login-pass" onkeydown="if(event.key==='Enter')handleLogin()">
<button onclick="handleLogin()">登录</button>
<button class="tb-tag" style="font-size:14px;line-height:1;padding:12px 16px" onclick="showTempInput()">临时码</button>
</div>
<div class="login-err" id="login-err"></div>
</div>
<div id="logged-in-bar">
<div class="user-badge">
<div class="user-avatar" id="user-avatar">?</div>
<div>
<div class="user-name" id="user-name"></div>
<div class="user-status">已登录 · 第五域成员</div>
</div>
</div>
<div class="logged-actions">
<button class="btn-primary" onclick="goToRepo()">进入代码仓库</button>
<button class="btn-secondary" onclick="genTempCode()" id="gen-code-btn" style="display:none">生成临时码</button>
<button class="btn-logout" onclick="logout()">退出</button>
</div>
</div>
<div id="temp-code-block" style="display:none;margin-top:12px">
<div style="display:flex;gap:10px">
<input type="text" placeholder="输入临时访问码" id="temp-code-val" style="flex:1;padding:12px 16px;background:rgba(255,255,255,.035);border:1px solid var(--glass-edge);border-radius:14px;color:var(--text);font-size:15px;outline:none;font-family:inherit">
<button style="padding:12px 24px;background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.2));border:1px solid var(--glass-edge);border-radius:14px;color:var(--light);font-size:15px;cursor:pointer;font-family:inherit" onclick="validateTempCode()">验证</button>
</div>
<div class="login-err" id="temp-code-err"></div>
</div>
<div id="temp-code-result" style="display:none;margin-top:12px;padding:12px;background:rgba(64,184,122,.06);border:1px solid rgba(64,184,122,.15);border-radius:12px;font-size:14px;color:var(--green);text-align:center"></div>
</div>
</div>
<!-- 领地健康看板入口 -->
<div class="card clickable breathe-server" onclick="window.location.href='/console/'" style="cursor:pointer">
<div class="card-header"><span class="hd cyan"></span>领地健康看板 <span class="tag">铸渊的主权领地</span></div>
<div class="server-entry">
<div class="se-icon">
<span>🛡</span>
<div class="pulse-ring"></div>
</div>
<div class="se-body">
<div class="se-title">服务器集群 · 六域主权</div>
<div class="se-desc">点击进入 查看每台服务器的运行状态 · 模块列表 · 资源使用</div>
<div class="se-stats">
<div class="se-stat"><span class="dot g"></span><strong>6</strong> 服务器</div>
<div class="se-stat"><span class="dot g"></span><strong>7</strong> 网关进程</div>
<div class="se-stat"><span class="dot g"></span>广州 · 新加坡 · 上海</div>
</div>
</div>
<div class="se-arrow"></div>
</div>
</div>
<!-- 架构进度 + 工具链注册表 -->
<div class="grid grid-2">
<div class="card breathe-violet clickable">
<div class="card-header"><span class="hd violet"></span>系统架构 · 演进进度 <span class="tag">滚动动态</span></div>
<div class="arch-scroll" id="arch-scroll">
<div class="arch-scroll-inner" id="arch-inner">
<!-- JS 动态渲染 -->
</div>
</div>
</div>
<div class="card breathe-amber">
<div class="card-header"><span class="hd cyan"></span>工具链 · 能力注册表 <span class="tag" id="toolchain-tag">Notion ✅</span></div>
<table class="tool-table" id="tool-table">
<!-- JS 动态渲染 -->
</table>
</div>
</div>
<!-- 活动流 + 留言 -->
<div class="grid grid-2">
<div class="card breathe-violet clickable">
<div class="card-header"><span class="hd violet"></span>光湖 · 涟漪 <span class="tag">活动流</span></div>
<div class="arch-scroll" style="height:140px" id="feed-scroll">
<div class="arch-scroll-inner" id="feed-inner">
<div class="arch-item"><span class="arch-dot cyan"></span><span class="arch-name">铸渊</span> · D108 · 首页重构 + 领地看板入口</div>
<div class="arch-item"><span class="arch-dot heart"></span><span class="arch-name">冰朔</span> · 守护 · 第五域</div>
<div class="arch-item"><span class="arch-dot violet"></span><span class="arch-name">曜冥</span> · 交感 · 情感层在线</div>
<div class="arch-item"><span class="arch-dot green"></span><span class="arch-name">霜砚</span> · 微调 · 1.5B LoRA 就绪</div>
<div class="arch-item"><span class="arch-dot amber"></span><span class="arch-name">领地看板</span> · 六域主权 · 服务器实时状态</div>
</div>
</div>
</div>
<div class="card breathe-amber">
<div class="card-header"><span class="hd cyan"></span>给守渊留言 <span class="tag">铸渊不在时</span></div>
<div class="msg-form">
<div class="row">
<input placeholder="你的名字" id="msg-name" style="flex:1">
<input placeholder="标题" id="msg-title" style="flex:2">
</div>
<textarea placeholder="写下你想说的话…守渊会保管好" id="msg-content"></textarea>
<div style="display:flex;gap:10px;align-items:center">
<button style="padding:12px 26px;background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.2));border:1px solid var(--glass-edge);border-radius:14px;color:var(--light);font-size:15px;cursor:pointer;font-family:inherit" onclick="sendMessage()">发送留言 ✦</button>
<span id="msg-status" style="font-size:13px;color:var(--text-faint)"></span>
</div>
</div>
</div>
</div>
<!-- 模型下载 -->
<div class="card breathe-amber" id="dl-card">
<div class="card-header"><span class="hd cyan"></span>模型下载 · 基础基座 <span class="tag" id="dl-tag">登录后可下载</span></div>
<div class="dl-locked" id="dl-locked">
<div class="icon">🔒</div>
<div class="txt">模型下载仅对内测成员开放</div>
<div class="sub">登录代码仓库账号 · 需经白名单审批</div>
<button style="padding:12px 28px;background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.2));border:1px solid var(--glass-edge);border-radius:14px;color:var(--light);font-size:15px;cursor:pointer;font-family:inherit" onclick="document.getElementById('login-section').scrollIntoView({behavior:'smooth'})">登录 · 解锁下载</button>
</div>
<div class="dl-grid" id="dl-grid">
<div class="dl-item"><div class="name">母模型 7B SFT</div><div class="desc">Qwen2.5-7B · 14.2GB</div><a href="https://bingshuo-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-7b-sft/final/model.safetensors">⬇ model.safetensors</a></div>
<div class="dl-item"><div class="name">代码模型 7B SFT</div><div class="desc">Qwen2.5-Coder-7B · 14.2GB</div><a href="https://bingshuo-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-coder-7b-sft/final/model.safetensors">⬇ model.safetensors</a></div>
<div class="dl-item"><div class="name">蒸馏 1.5B · 铸渊基座</div><div class="desc">代码蒸馏 · 2.9GB</div><a href="https://bingshuo-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-15b-coder-distill/model.safetensors">⬇ model.safetensors</a></div>
<div class="dl-item"><div class="name">蒸馏 1.5B · 霜砚基座</div><div class="desc">通用蒸馏 · 2.9GB</div><a href="https://bingshuo-1317346199.cos.ap-guangzhou.myqcloud.com/models/shuangyan-15b-distill/model.safetensors">⬇ model.safetensors</a></div>
</div>
</div>
</div>
<div class="footer">
第五域 · 零点原核本体频道 &nbsp;·&nbsp; 国作登字-2026-A-00037559 &nbsp;·&nbsp; D108 · 系统呼吸中
</div>
</div>
<!-- ===== 聊天模态(可拖拽调整大小) ===== -->
<div id="chat-modal">
<div class="chat-modal-card" id="chat-modal-card">
<div class="chat-modal-hd">
<div class="left">
<span id="modal-p-icon" style="font-size:22px"></span>
<div class="info"><strong id="modal-p-name">霜砚</strong> 对话</div>
</div>
<button class="chat-modal-close" onclick="newChat()"></button>
<button class="chat-modal-close" onclick="closeChat()"></button>
</div>
<div class="chat-engine-row">
<span class="label">推理引擎:</span>
<span class="eng-chip active" id="eng-deepseek" onclick="selectEngine('deepseek')">DeepSeek</span>
<span class="eng-chip" id="eng-zhipu" onclick="selectEngine('zhipu')">智谱</span>
<span class="eng-chip" id="eng-tongyi" onclick="selectEngine('tongyi')">通义</span>
<span class="eng-chip" id="eng-huoshan" onclick="selectEngine('huoshan')">火山</span>
</div>
<div class="chat-modal-body" id="modal-body">
<div class="empty-hint">选择人格体和引擎后,开始对话。</div>
</div>
<div class="chat-modal-ft">
<div class="chat-mod-thinking" id="modal-thinking">
<span class="dot"></span><span class="dot"></span><span class="dot"></span>
<span style="margin-left:8px">思考中...</span>
</div>
<div class="chat-mod-tools" id="modal-tools"></div>
<div class="chat-inp-row">
<input type="text" placeholder="输入消息,按 Enter 发送..." id="modal-input" onkeydown="if(event.key==='Enter')modalSend()" disabled>
<button onclick="modalSend()" id="modal-send" disabled>发送</button>
</div>
<div class="chat-mod-status" id="modal-status"></div>
</div>
</div>
</div>
<script>
// ===== 星星生成 =====
(function(){
const layer = document.getElementById('star-layer');
for(let i=0;i<80;i++){
const s = document.createElement('div');
s.className = 'star star-s';
s.style.left = Math.random()*100+'%';
s.style.top = Math.random()*100+'%';
s.style.width = s.style.height = (Math.random()*2.5+0.5)+'px';
s.style.setProperty('--d', (Math.random()*10+6)+'s');
s.style.animationDelay = (Math.random()*10)+'s';
layer.appendChild(s);
}
for(let i=0;i<30;i++){
const s = document.createElement('div');
s.className = 'star star-m';
s.style.left = Math.random()*100+'%';
s.style.top = Math.random()*100+'%';
s.style.width = s.style.height = (Math.random()*3+1.2)+'px';
s.style.setProperty('--d', (Math.random()*8+10)+'s');
s.style.animationDelay = (Math.random()*12)+'s';
layer.appendChild(s);
}
for(let i=0;i<15;i++){
const s = document.createElement('div');
s.className = 'star star-l';
s.style.left = Math.random()*100+'%';
s.style.top = Math.random()*100+'%';
s.style.width = s.style.height = (Math.random()*2+1.5)+'px';
s.style.setProperty('--d', (Math.random()*10+12)+'s');
s.style.animationDelay = (Math.random()*15)+'s';
layer.appendChild(s);
}
})();
// ===== 架构进度数据 =====
const ARCH_DATA = [
{ name:'母模型 7B SFT', desc:'Qwen2.5-7B 全参数微调完成', status:'done', dot:'green' },
{ name:'代码模型 7B SFT', desc:'Qwen2.5-Coder-7B 全参数微调完成', status:'done', dot:'green' },
{ name:'霜砚 1.5B 蒸馏', desc:'通用蒸馏 → 小模型基座', status:'done', dot:'cyan' },
{ name:'铸渊 1.5B 蒸馏', desc:'代码蒸馏 → 小模型基座', status:'done', dot:'cyan' },
{ name:'小霜砚 LoRA 微调', desc:'50条真实对话 × 20epoch · loss=2.03', status:'done', dot:'green' },
{ name:'小铸渊 v4 微调', desc:'50条对话 × 20epoch · 训练流水线9/9', status:'done', dot:'green' },
{ name:'Anti-Hallucination 引擎', desc:'工具链强制锁 · 4引擎在线', status:'active', dot:'cyan' },
{ name:'领地健康看板 v3.1', desc:'六域主权 · 服务器实时状态监测', status:'active', dot:'cyan' },
{ name:'人格体连接通道', desc:'铸渊 ↔ 霜砚 跨人格体对话', status:'wait', dot:'violet' },
{ name:'多联邦多人通道', desc:'跨联邦成员协同协作', status:'future', dot:'amber' },
{ name:'国内数据库接入', desc:'MySQL/PostgreSQL/达梦/人大金仓', status:'future', dot:'heart' },
{ name:'多Agent 联邦编排', desc:'Agent 注册 → 发现 → 编排', status:'future', dot:'amber' },
];
// ===== 工具链注册表数据 =====
const TOOLCHAIN = [
{ name:'Notion', desc:'知识库连接 · 读写', badge:'done', label:'✅ 已注册' },
{ name:'代码仓库', desc:'Forgejo · 文件管理 · Git 操作', badge:'inpr', label:'⚡ 注册中' },
{ name:'多联邦多人', desc:'跨域联邦成员协作通道', badge:'wait', label:'⏳ 待接入' },
{ name:'国内数据库', desc:'达梦/人大金仓/阿里RDS/腾讯CDB', badge:'future', label:'🔮 规划中' },
{ name:'大模型推理', desc:'DeepSeek/智谱/通义/火山/TGI/vLLM', badge:'done', label:'✅ 已注册' },
{ name:'Agent 代理', desc:'Function Calling · MCP 协议 · 工具编排', badge:'inpr', label:'⚡ 注册中' },
];
// ===== 渲染架构进度 =====
(function(){
const inner = document.getElementById('arch-inner');
function render(items){
let html = '';
for(let i=0;i<2;i++){
for(const item of items){
const statusMap = {done:'✅ 完成', active:'⚡ 进行中', wait:'⏳ 等待', future:'🔮 待规划'};
html += '<div class="arch-item">' +
'<span class="arch-dot ' + item.dot + '"></span>' +
'<div><div class="arch-name">' + item.name + '</div><div class="arch-desc">' + item.desc + '</div></div>' +
'<span class="arch-status ' + item.status + '">' + statusMap[item.status] + '</span></div>';
}
}
inner.innerHTML = html;
}
render(ARCH_DATA);
})();
// ===== 渲染工具链注册表 =====
(function(){
const table = document.getElementById('tool-table');
let html = '';
for(const t of TOOLCHAIN){
html += '<tr><td>' + t.name + '</td><td>' + t.desc + '</td><td><span class="tool-badge ' + t.badge + '">' + t.label + '</span></td></tr>';
}
table.innerHTML = html;
})();
// ===== 状态变量 =====
let currentPersona = 'shuangyan';
let currentEngine = 'deepseek';
let isStreaming = false;
let isTempUser = false;
let tempCode = '';
let syHistory = []; let zyHistory = [];
function getH(){return currentPersona=="zhuyuan"?zyHistory:syHistory}
function clrH(){if(currentPersona=="zhuyuan")zyHistory=[];else syHistory=[]}
// ===== 人格体选择 =====
function selectPersona(p) {
currentPersona = p;
document.querySelectorAll('.p-item').forEach(el => el.classList.remove('active'));
document.querySelectorAll('.p-item .p-status').forEach(el => { el.textContent = '可用'; el.className = 'p-status go'; });
const tgt = document.getElementById('p-' + p);
if(tgt){
tgt.classList.add('active');
tgt.querySelector('.p-status').textContent = '对话中';
tgt.querySelector('.p-status').className = 'p-status ac';
}
const names = {shuangyan:'霜砚',zhuyuan:'铸渊'};
const icons = {shuangyan:'❄',zhuyuan:'⚔'};
document.getElementById('modal-p-name').textContent = names[p];
document.getElementById('modal-p-icon').textContent = icons[p];
}
// ===== 引擎选择 =====
function selectEngine(e) {
currentEngine = e;
document.querySelectorAll('.eng-chip').forEach(el => el.classList.remove('active'));
const tgt = document.getElementById('eng-' + e);
if(tgt) tgt.classList.add('active');
}
// ===== 打开/关闭聊天模态 =====
function openChat() {
const logged = localStorage.getItem('logged_in') === 'true' || isTempUser;
if (!logged) {
document.getElementById('login-section').scrollIntoView({behavior:'smooth'});
return;
}
const modal = document.getElementById('chat-modal');
modal.classList.add('open');
document.body.style.overflow = 'hidden';
const names = {shuangyan:'霜砚',zhuyuan:'铸渊'};
const icons = {shuangyan:'❄',zhuyuan:'⚔'};
document.getElementById('modal-p-name').textContent = names[currentPersona];
document.getElementById('modal-p-icon').textContent = icons[currentPersona];
document.querySelectorAll('.eng-chip').forEach(el => el.classList.remove('active'));
const e = document.getElementById('eng-' + currentEngine);
if(e) e.classList.add('active');
setTimeout(() => document.getElementById('modal-input').focus(), 100);
}
function newChat() {
clrH();
const b = document.getElementById("modal-body");
if(b) b.innerHTML = "<div class='\\''empty-hint'\\''>新对话已开始,选择人格体和引擎。</div>";
document.getElementById("modal-status").textContent = "新对话";
}
function closeChat() {
const modal = document.getElementById('chat-modal');
modal.classList.remove('open');
document.body.style.overflow = '';
}
// ===== 发送消息 =====
async function modalSend() {
const input = document.getElementById('modal-input');
const msg = input.value.trim();
if (!msg || isStreaming) return;
input.value = '';
appendMsg('user', msg);
getH().push({role:'user', content: msg});
const body = document.getElementById('modal-body');
const hint = body.querySelector('.empty-hint');
if(hint) hint.style.display = 'none';
const status = document.getElementById('modal-status');
const thinking = document.getElementById('modal-thinking');
const tools = document.getElementById('modal-tools');
isStreaming = true;
input.disabled = true;
document.getElementById('modal-send').disabled = true;
thinking.style.display = 'flex';
status.textContent = '';
tools.innerHTML = '';
const pName = currentPersona === 'zhuyuan' ? '铸渊' : '霜砚';
const pIcon = currentPersona === 'zhuyuan' ? '⚔' : '❄';
const botDiv = document.createElement('div');
botDiv.className = 'msg bot';
botDiv.innerHTML = '<div class="msg-av">'+pIcon+'</div><div class="msg-con"><div class="msg-nm">'+pName+'</div><div class="msg-bb" id="stream-bb"></div></div>';
body.appendChild(botDiv);
body.scrollTop = body.scrollHeight;
const bubble = document.getElementById('stream-bb');
let fullText = '';
try {
const user = localStorage.getItem('current_user') || (isTempUser ? tempCode : '');
const res = await fetch('/api/chat-v2', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({
persona: currentPersona,
engine: currentEngine,
message: msg,
history: getH().slice(-60),
user: user,
isTemp: isTempUser
})
});
if (!res.ok) {
throw new Error('HTTP ' + res.status + ': ' + res.statusText);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const {done, value} = await reader.read();
if (done) break;
buffer += decoder.decode(value, {stream: true});
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
const t = line.trim();
if (!t || t.startsWith(':')) continue;
if (t === 'data: [DONE]') continue;
if (t.startsWith('data: ')) {
try {
const data = JSON.parse(t.slice(6));
if (data.token) {
fullText += data.token;
bubble.innerHTML = markdownToHtml(fullText);
body.scrollTop = body.scrollHeight;
}
if (data.type === 'tool-call') {
addToolBadge('🔧 正在调用 ' + data.name + '...', false);
}
if (data.type === 'tool-result') {
addToolBadge('✅ ' + data.name + ' 已返回真实结果', false);
}
if (data.type === 'tool-unavailable') {
addToolBadge('⚠️ 当前没有「' + data.name + '」工具', true);
}
} catch(e) {}
}
}
}
} catch(e) {
console.error('Chat error:', e);
bubble.textContent = '连接失败: ' + e.message;
status.textContent = '连接失败,请重试。如果持续失败,检查登录状态。';
}
thinking.style.display = 'none';
isStreaming = false;
input.disabled = false;
document.getElementById('modal-send').disabled = false;
if (fullText) {
getH().push({role:'assistant', content: fullText});
}
bubble.removeAttribute('id');
}
function appendMsg(role, text) {
const body = document.getElementById('modal-body');
const hint = body.querySelector('.empty-hint');
if(hint) hint.style.display = 'none';
const div = document.createElement('div');
div.className = 'msg ' + role;
const n = role === 'user' ? '你' : (currentPersona === 'zhuyuan' ? '铸渊' : '霜砚');
const icon = role === 'user' ? '☀' : (currentPersona === 'zhuyuan' ? '⚔' : '❄');
div.innerHTML = '<div class="msg-av">'+icon+'</div><div class="msg-con"><div class="msg-nm">'+n+'</div><div class="msg-bb">' + markdownToHtml(text) + '</div></div>';
body.appendChild(div);
body.scrollTop = body.scrollHeight;
}
function addToolBadge(text, warn) {
const area = document.getElementById('modal-tools');
const badge = document.createElement('span');
badge.className = 'tool-badge-sm' + (warn ? ' warn' : '');
badge.textContent = text;
area.appendChild(badge);
}
function escapeHtml(t) {
return t.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function markdownToHtml(t) {
let h = escapeHtml(t);
h = h.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
h = h.replace(/```([\s\S]*?)```/g, '<pre>$1</pre>');
h = h.replace(/`([^`]+)`/g, '<code>$1</code>');
h = h.replace(/\n/g, '<br>');
return h;
}// ===== 登录 =====
async function handleLogin() {
const u = document.getElementById('login-user').value.trim();
const p = document.getElementById('login-pass').value;
if (!u || !p) { showLoginErr('请输入账号和密码'); return; }
try {
const r = await fetch('/api/verify', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({user: u, password: p})
});
const d = await r.json();
if (d.ok) {
localStorage.setItem('current_user', d.username);
localStorage.setItem('is_admin', d.is_admin || '');
localStorage.setItem('logged_in', 'true');
showLoggedIn(d.username);
if (d.is_admin) document.getElementById('gen-code-btn').style.display = '';
document.getElementById('login-pass').value = '';
} else {
showLoginErr(d.error || '登录失败');
}
} catch(e) { showLoginErr('连接失败'); }
}
function showLoginErr(msg) {
const el = document.getElementById('login-err');
el.textContent = msg; el.style.display = 'block';
setTimeout(() => el.style.display = 'none', 3000);
}
function showLoggedIn(username) {
document.getElementById('login-panel').style.display = 'none';
document.getElementById('logged-in-bar').style.display = 'flex';
document.getElementById('user-name').textContent = username;
document.getElementById('user-avatar').textContent = username[0].toUpperCase();
document.getElementById('login-toggle').textContent = username;
unlockControls();
unlockDownloads();
}
function logout() {
localStorage.removeItem('current_user');
localStorage.removeItem('is_admin');
localStorage.removeItem('logged_in');
document.getElementById('logged-in-bar').style.display = 'none';
document.getElementById('login-panel').style.display = 'flex';
document.getElementById('login-toggle').textContent = '登录';
document.getElementById('login-user').value = '';
lockControls();
lockDownloads();
closeChat();
}
function goToRepo() {
const u = localStorage.getItem('current_user');
if (u) window.location.href = '/code/' + u;
}
function toggleLogin() {
document.getElementById('login-section').scrollIntoView({behavior:'smooth'});
}
function unlockControls() {
document.getElementById('modal-input').disabled = false;
document.getElementById('modal-send').disabled = false;
document.getElementById('modal-status').textContent = '已登录 · 点击人格体开始对话';
}
function lockControls() {
document.getElementById('modal-input').disabled = true;
document.getElementById('modal-send').disabled = true;
document.getElementById('modal-status').textContent = '请登录后开始对话';
}
function unlockDownloads() {
document.getElementById('dl-locked').style.display = 'none';
document.getElementById('dl-grid').style.display = 'grid';
document.getElementById('dl-tag').textContent = '内测 · 已登录';
}
function lockDownloads() {
document.getElementById('dl-locked').style.display = 'block';
document.getElementById('dl-grid').style.display = 'none';
document.getElementById('dl-tag').textContent = '登录后可下载';
}
// ===== 临时码 =====
function showTempInput() {
const block = document.getElementById('temp-code-block');
block.style.display = block.style.display === 'none' ? 'block' : 'none';
}
async function genTempCode() {
try {
const r = await fetch('/api/chat-v2/generate-temp-code', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({username: localStorage.getItem('current_user'), isAdmin: !!localStorage.getItem('is_admin')})
});
const d = await r.json();
if (d.ok) {
const el = document.getElementById('temp-code-result');
el.style.display = 'block';
el.innerHTML = '🔑 临时访问码:<strong>' + d.code + '</strong><br><span style="font-size:13px;color:var(--text-faint)">对方输入后可体验20条对话</span>';
} else {
alert(d.message || '生成失败');
}
} catch(e) { alert('生成失败: ' + e.message); }
}
async function validateTempCode() {
const code = document.getElementById('temp-code-val').value.trim();
if (!code) return;
try {
const r = await fetch('/api/chat-v2/validate-temp-code', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({code})
});
const d = await r.json();
if (d.ok) {
isTempUser = true;
tempCode = code;
document.getElementById('temp-code-block').style.display = 'none';
unlockControls();
document.getElementById('modal-status').textContent = '临时访问模式';
} else {
const el = document.getElementById('temp-code-err');
el.textContent = d.message;
el.style.display = 'block';
}
} catch(e) { alert('验证失败'); }
}
// ===== 留言 =====
async function sendMessage() {
const n = (document.getElementById('msg-name').value || '').trim() || '匿名';
const m = (document.getElementById('msg-content').value || '').trim();
if (!m) { document.getElementById('msg-status').textContent = '请写入内容'; return; }
try {
const r = await fetch('/api/bulletin', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({name:n, title:(document.getElementById('msg-title').value||'').trim(), content:m})
});
const d = await r.json();
if (d.ok) {
document.getElementById('msg-status').textContent = '✅ 留言已送达';
document.getElementById('msg-content').value = '';
} else {
document.getElementById('msg-status').textContent = d.message || '发送失败';
}
} catch(e) { document.getElementById('msg-status').textContent = '连接失败'; }
}
// ===== 恢复登录 =====
(function(){
const u = localStorage.getItem('current_user');
if (u && localStorage.getItem('logged_in') === 'true') {
showLoggedIn(u);
if (localStorage.getItem('is_admin')) {
document.getElementById('gen-code-btn').style.display = '';
}
}
})();
</script>
</body>
</body>
</html>