guanghulab/homepage/agui.html

44 lines
5.9 KiB
HTML

<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>光湖助手 · Agent</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;background:#030712;color:#e5e7eb;height:100vh;display:flex}
.sb{width:260px;background:rgba(17,17,28,.95);border-right:1px solid rgba(255,255,255,.06);display:flex;flex-direction:column;flex-shrink:0}
.sb-hd{padding:16px;border-bottom:1px solid rgba(255,255,255,.06)}
.sb-hd h1{font-size:15px;color:#fff;margin-bottom:12px}
.nbtn{width:100%;padding:10px;background:#6366f1;border:none;border-radius:8px;color:#fff;font-size:13px;cursor:pointer}.nbtn:hover{background:#4f46e5}
.clist{flex:1;overflow-y:auto;padding:8px}
.ci{padding:10px 12px;border-radius:8px;margin-bottom:4px;cursor:pointer;font-size:13px;color:#9ca3af;display:flex;justify-content:space-between}.ci:hover{background:rgba(255,255,255,.04)}.ci.ac{background:rgba(99,102,241,.1);color:#a5b4fc}
.ci .tt{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ci .dl{opacity:0;cursor:pointer;color:#f87171;font-size:11px}.ci:hover .dl{opacity:.6}
.mn{flex:1;display:flex;flex-direction:column;min-width:0}
.thd{background:rgba(17,17,28,.95);border-bottom:1px solid rgba(255,255,255,.06);padding:12px 24px;font-size:14px;color:#9ca3af}
.chat{flex:1;overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:16px}
.msg{max-width:75%;padding:14px 18px;border-radius:14px;font-size:14px;line-height:1.7;white-space:pre-wrap;word-break:break-word}
.msg.u{align-self:flex-end;background:#6366f1;color:#fff;border-bottom-right-radius:4px}
.msg.a{align-self:flex-start;background:rgba(17,17,28,.9);border:1px solid rgba(255,255,255,.06);border-bottom-left-radius:4px}
.msg.a pre{background:rgba(0,0,0,.3);padding:12px;border-radius:8px;margin:8px 0;overflow-x:auto;font-size:12px}
.msg.a code{font-family:monospace;font-size:12px;background:rgba(0,0,0,.3);padding:2px 6px;border-radius:4px}
.ia{padding:16px 24px;border-top:1px solid rgba(255,255,255,.06);display:flex;gap:12px;background:rgba(17,17,28,.8)}
.ia input{flex:1;padding:14px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:12px;color:#fff;font-size:14px;outline:none}
.ia input:focus{border-color:#6366f1}
.ia button{padding:14px 24px;background:#6366f1;border:none;border-radius:12px;color:#fff;font-weight:600;cursor:pointer}.ia button:hover{background:#4f46e5}
.ia button:disabled{opacity:.5}
.epty{flex:1;display:flex;align-items:center;justify-content:center;color:#374151;font-size:15px}
@keyframes fi{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.msg{animation:fi .3s}
.typ{color:#6b7280;padding:8px 18px;font-size:13px;animation:pulse 1.5s infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
</style></head><body>
<div class="sb"><div class="sb-hd"><h1>光湖助手</h1><button class="nbtn" onclick="nc()">+ 新建对话</button></div><div class="clist" id="cl"></div></div>
<div class="mn"><div class="thd" id="tt">选择或新建对话</div><div class="chat" id="chat"><div class="epty">点击左侧新建对话开始</div></div><div class="ia"><input id="inp" placeholder="问服务器问题..." onkeydown="if(event.key==='Enter')snd()" disabled><button onclick="snd()" id="b" disabled>发送</button></div></div>
<script>
let cc=null;
const A='api/';
async function lc(){const r=await fetch(A+'convs');const d=await r.json();document.getElementById('cl').innerHTML=d.map(c=>`<div class="ci${c.id===cc?' ac':''}" onclick="sc('${c.id}')"><span class="tt">${e(c.title)}</span><span class="dl" onclick="event.stopPropagation();dc('${c.id}')">x</span></div>`).join('')}
function e(s){return(s||'').replace(/</g,'&lt;')}
async function nc(){const r=await fetch(A+'convs',{method:'POST'});const d=await r.json();cc=d.id;document.getElementById('tt').textContent='新对话';document.getElementById('chat').innerHTML='';document.getElementById('inp').disabled=false;document.getElementById('b').disabled=false;document.getElementById('inp').focus();await lc()}
async function sc(id){cc=id;const r=await fetch(A+'convs/'+id);const d=await r.json();document.getElementById('tt').textContent=d.title;const ch=document.getElementById('chat');if(d.messages.length===0)ch.innerHTML='<div class="epty">开始对话</div>';else ch.innerHTML=d.messages.map(m=>`<div class="msg ${m.role==='user'?'u':'a'}">${m.role==='user'?e(m.content):f(m.content)}</div>`).join('');document.getElementById('inp').disabled=false;document.getElementById('b').disabled=false;document.getElementById('inp').focus();ch.scrollTop=ch.scrollHeight;await lc()}
async function dc(id){await fetch(A+'convs/'+id,{method:'DELETE'});if(cc===id){cc=null;document.getElementById('chat').innerHTML='<div class="epty">选择或新建对话</div>';document.getElementById('inp').disabled=true;document.getElementById('b').disabled=true};await lc()}
async function snd(){const i=document.getElementById('inp');const m=i.value.trim();if(!m||!cc)return;const ch=document.getElementById('chat');ch.innerHTML+=`<div class="msg u">${e(m)}</div>`;i.value='';document.getElementById('b').disabled=true;ch.innerHTML+='<div class="typ">查询服务器数据...</div>';ch.scrollTop=ch.scrollHeight;try{const r=await fetch(A+'chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({conversation_id:cc,message:m})});const d=await r.json();document.querySelector('.typ')?.remove();if(d.error||d.e)ch.innerHTML+=`<div class="msg a">错误: ${e(d.error||d.e)}</div>`;else ch.innerHTML+=`<div class="msg a">${f(d.reply)}</div>`;ch.scrollTop=ch.scrollHeight}catch(e){document.querySelector('.typ')?.remove();ch.innerHTML+='<div class="msg a">连接失败</div>'};document.getElementById('b').disabled=false;await lc()}
function f(t){return e(t).replace(/```(\w*)\n?([\s\S]*?)```/g,'<pre><code>$2</code></pre>').replace(/`([^`]+)`/g,'<code>$1</code>').replace(/\n/g,'<br>')}
lc();
</script></body></html>