Files
2026-08-05 13:33:10 +08:00

166 lines
6.4 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>ChatBot</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, system-ui, sans-serif; background: #111; color: #eee; height: 100dvh; display: flex; flex-direction: column; }
header { padding: 12px 16px; border-bottom: 1px solid #333; display: flex; align-items: center; gap: 8px; }
header h1 { font-size: 16px; font-weight: 600; }
header .config-btn { margin-left: auto; background: #333; border: none; color: #eee; padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }
.config-panel { display: none; padding: 12px 16px; border-bottom: 1px solid #333; background: #1a1a1a; gap: 8px; flex-wrap: wrap; align-items: center; }
.config-panel.open { display: flex; }
.config-panel label { font-size: 12px; color: #888; white-space: nowrap; }
.config-panel input { background: #222; border: 1px solid #444; color: #eee; padding: 6px 10px; border-radius: 6px; font-size: 13px; min-width: 140px; flex: 1; }
main { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.msg { max-width: 80%; padding: 10px 14px; border-radius: 12px; line-height: 1.5; font-size: 14px; white-space: pre-wrap; word-break: break-word; }
.msg.user { align-self: flex-end; background: #2563eb; }
.msg.assistant { align-self: flex-start; background: #333; }
.msg.system { align-self: center; font-size: 12px; color: #888; background: transparent; }
footer { padding: 12px 16px; border-top: 1px solid #333; display: flex; gap: 8px; }
footer textarea { flex: 1; resize: none; background: #222; border: 1px solid #444; color: #eee; padding: 10px; border-radius: 10px; font-size: 14px; font-family: inherit; min-height: 44px; max-height: 120px; }
footer button { background: #2563eb; border: none; color: #fff; padding: 10px 20px; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 500; white-space: nowrap; }
footer button:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<header>
<h1>ChatBot</h1>
<button class="config-btn" onclick="toggleConfig()">Settings</button>
</header>
<div class="config-panel" id="configPanel">
<label>Model</label>
<input id="modelInput" placeholder="gpt-5.4-nano" />
<label>API Key</label>
<input id="keyInput" type="password" placeholder="sk-ABCD" />
<label>User ID</label>
<input id="userIdInput" placeholder="65535" style="min-width:80px;flex:0.5" />
</div>
<main id="chatBox"></main>
<footer>
<textarea id="input" rows="1" placeholder="Type a message..." onkeydown="onKeyDown(event)"></textarea>
<button id="sendBtn" onclick="send()">Send</button>
</footer>
<script>
let messages = []
function toggleConfig() {
document.getElementById('configPanel').classList.toggle('open')
loadConfig()
}
function loadConfig() {
const m = localStorage.getItem('chat_model') || 'gpt-5.4-nano'
const k = localStorage.getItem('chat_key') || 'sk-ABCD'
const u = localStorage.getItem('chat_userid') || '65535'
document.getElementById('modelInput').value = m
document.getElementById('keyInput').value = k
document.getElementById('userIdInput').value = u
}
function saveConfig() {
const m = document.getElementById('modelInput').value.trim()
const k = document.getElementById('keyInput').value.trim()
const u = document.getElementById('userIdInput').value.trim()
if (m) localStorage.setItem('chat_model', m)
if (k) localStorage.setItem('chat_key', k)
if (u) localStorage.setItem('chat_userid', u)
}
function getConfig() {
return {
model: localStorage.getItem('chat_model') || 'gpt-5.4-nano',
key: localStorage.getItem('chat_key') || 'sk-ABCD',
userId: localStorage.getItem('chat_userid') || '65535',
}
}
loadConfig()
function saveDefaults() {
getConfig() // trigger defaults
if (!localStorage.getItem('chat_model')) localStorage.setItem('chat_model', 'gpt-5.4-nano')
if (!localStorage.getItem('chat_key')) localStorage.setItem('chat_key', 'sk-ABCD')
if (!localStorage.getItem('chat_userid')) localStorage.setItem('chat_userid', '65535')
}
saveDefaults()
document.getElementById('modelInput').addEventListener('input', saveConfig)
document.getElementById('keyInput').addEventListener('input', saveConfig)
document.getElementById('userIdInput').addEventListener('input', saveConfig)
function addMsg(role, text) {
const div = document.createElement('div')
div.className = 'msg ' + role
div.textContent = text
document.getElementById('chatBox').appendChild(div)
div.scrollIntoView({ behavior: 'smooth' })
}
function onKeyDown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
send()
}
}
async function send() {
const input = document.getElementById('input')
const btn = document.getElementById('sendBtn')
const text = input.value.trim()
if (!text) return
input.value = ''
input.disabled = true
btn.disabled = true
messages.push({ role: 'user', content: text, name: 'ClawBot' })
addMsg('user', text)
const { model, key, userId } = getConfig()
const payload = {
user_id: userId || undefined,
model: model || undefined,
messages,
temperature: 0.7,
max_tokens: 0,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
stream: false,
}
try {
const headers = { 'Content-Type': 'application/json' }
if (key) headers['X-API-Key'] = key
const resp = await fetch('/api/chat', {
method: 'POST',
headers,
body: JSON.stringify(payload),
})
const data = await resp.json()
if (data.error) {
addMsg('system', 'Error: ' + data.error)
} else {
messages.push({ role: 'assistant', content: data.reply })
addMsg('assistant', data.reply)
}
} catch (err) {
addMsg('system', 'Network error: ' + err.message)
} finally {
input.disabled = false
btn.disabled = false
input.focus()
}
}
</script>
</body>
</html>