159 lines
6.0 KiB
HTML
159 lines
6.0 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') || '',
|
|
key: localStorage.getItem('chat_key') || '',
|
|
userId: localStorage.getItem('chat_userid') || '',
|
|
}
|
|
}
|
|
|
|
loadConfig()
|
|
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>
|