257 lines
9.9 KiB
HTML
257 lines
9.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>ARIA Diagnostic</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: #0D0D1A; color: #E0E0F0; font-family: 'Courier New', monospace; padding: 16px; }
|
|
h1 { font-size: 20px; margin-bottom: 16px; color: #0096FF; }
|
|
h2 { font-size: 14px; margin-bottom: 8px; color: #8888AA; text-transform: uppercase; letter-spacing: 1px; }
|
|
|
|
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }
|
|
.card { background: #12122A; border: 1px solid #1E1E2E; border-radius: 8px; padding: 12px; }
|
|
.card.full { grid-column: 1 / -1; }
|
|
|
|
.status-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
|
.dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
|
|
.dot.connected { background: #34C759; box-shadow: 0 0 6px #34C759; }
|
|
.dot.connecting { background: #FFD60A; }
|
|
.dot.disconnected { background: #FF3B30; }
|
|
.dot.error { background: #FF3B30; box-shadow: 0 0 6px #FF3B30; }
|
|
.dot.not_configured { background: #555570; }
|
|
|
|
.status-label { font-size: 13px; }
|
|
.error-text { color: #FF6B6B; font-size: 11px; margin-top: 4px; }
|
|
|
|
.btn { background: #0096FF; color: #fff; border: none; border-radius: 6px; padding: 8px 16px;
|
|
font-family: inherit; font-size: 13px; cursor: pointer; margin: 4px 4px 4px 0; }
|
|
.btn:hover { background: #007ADB; }
|
|
.btn:disabled { background: #333; color: #666; cursor: not-allowed; }
|
|
.btn.secondary { background: #1E1E2E; border: 1px solid #333; }
|
|
.btn.secondary:hover { background: #2A2A3E; }
|
|
|
|
.log-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
|
.log-header h2 { margin-bottom: 0; }
|
|
.pause-hint { font-size: 11px; color: #FFD60A; display: none; }
|
|
.pause-hint.visible { display: inline; }
|
|
.log-box { background: #080810; border: 1px solid #1E1E2E; border-radius: 6px;
|
|
height: 300px; overflow-y: auto; padding: 8px; font-size: 11px; line-height: 1.6; position: relative; }
|
|
.log-entry { white-space: pre-wrap; word-break: break-all; }
|
|
.log-entry.error { color: #FF6B6B; }
|
|
.log-entry.warn { color: #FFD60A; }
|
|
.log-entry.info { color: #AAB; }
|
|
.log-entry.debug { color: #555570; }
|
|
|
|
.chat-box { background: #080810; border: 1px solid #1E1E2E; border-radius: 6px;
|
|
min-height: 120px; max-height: 250px; overflow-y: auto; padding: 8px; margin-bottom: 8px; }
|
|
.chat-msg { margin-bottom: 6px; padding: 6px 10px; border-radius: 6px; font-size: 13px; }
|
|
.chat-msg.sent { background: #0096FF; color: #fff; margin-left: 20%; text-align: right; }
|
|
.chat-msg.received { background: #1E1E2E; margin-right: 20%; }
|
|
.chat-msg.error { background: #3B1010; color: #FF6B6B; }
|
|
.chat-msg .meta { font-size: 10px; color: rgba(255,255,255,0.4); margin-top: 2px; }
|
|
|
|
.input-row { display: flex; gap: 6px; }
|
|
.input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px;
|
|
padding: 8px 12px; color: #E0E0F0; font-family: inherit; font-size: 13px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>ARIA Diagnostic</h1>
|
|
|
|
<!-- Verbindungsstatus -->
|
|
<div class="grid">
|
|
<div class="card">
|
|
<h2>OpenClaw Gateway</h2>
|
|
<div class="status-row">
|
|
<div class="dot" id="gw-dot"></div>
|
|
<span class="status-label" id="gw-status">-</span>
|
|
</div>
|
|
<div class="error-text" id="gw-error"></div>
|
|
<button class="btn secondary" onclick="send({action:'reconnect_gateway'})">Reconnect</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>RVS (Rendezvous)</h2>
|
|
<div class="status-row">
|
|
<div class="dot" id="rvs-dot"></div>
|
|
<span class="status-label" id="rvs-status">-</span>
|
|
</div>
|
|
<div class="error-text" id="rvs-error"></div>
|
|
<button class="btn secondary" onclick="send({action:'reconnect_rvs'})">Reconnect</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chat Test -->
|
|
<div class="grid">
|
|
<div class="card full">
|
|
<h2>Chat Test</h2>
|
|
<div class="chat-box" id="chat-box"></div>
|
|
<div class="input-row">
|
|
<input type="text" id="chat-input" value="aria lebst du noch?" placeholder="Nachricht...">
|
|
<button class="btn" id="btn-gw" onclick="testGateway()">Gateway senden</button>
|
|
<button class="btn" id="btn-rvs" onclick="testRVS()">Via RVS senden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Verbindungslog -->
|
|
<div class="card" style="margin-top:12px">
|
|
<div class="log-header">
|
|
<h2>Verbindungslog</h2>
|
|
<span>
|
|
<span class="pause-hint" id="pause-hint">Autoscroll pausiert</span>
|
|
<button class="btn secondary" id="btn-scroll" onclick="resumeScroll()" style="display:none;padding:4px 10px;font-size:11px">Nach unten</button>
|
|
</span>
|
|
</div>
|
|
<div class="log-box" id="log-box"></div>
|
|
</div>
|
|
|
|
<script>
|
|
const logBox = document.getElementById('log-box');
|
|
const chatBox = document.getElementById('chat-box');
|
|
const pauseHint = document.getElementById('pause-hint');
|
|
const btnScroll = document.getElementById('btn-scroll');
|
|
let ws;
|
|
let logAutoScroll = true;
|
|
|
|
// Autoscroll pausiert wenn User hochscrollt
|
|
logBox.addEventListener('scroll', () => {
|
|
const atBottom = logBox.scrollHeight - logBox.scrollTop - logBox.clientHeight < 30;
|
|
logAutoScroll = atBottom;
|
|
pauseHint.classList.toggle('visible', !atBottom);
|
|
btnScroll.style.display = atBottom ? 'none' : 'inline';
|
|
});
|
|
|
|
function resumeScroll() {
|
|
logAutoScroll = true;
|
|
logBox.scrollTop = logBox.scrollHeight;
|
|
pauseHint.classList.remove('visible');
|
|
btnScroll.style.display = 'none';
|
|
}
|
|
|
|
const STATUS_LABELS = {
|
|
connected: 'Verbunden',
|
|
connecting: 'Verbinde...',
|
|
disconnected: 'Getrennt',
|
|
error: 'Fehler',
|
|
not_configured: 'Nicht konfiguriert',
|
|
};
|
|
|
|
function connectWS() {
|
|
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
ws = new WebSocket(`${proto}://${location.host}`);
|
|
|
|
ws.onopen = () => addLog('info', 'browser', 'Verbunden mit Diagnostic Server');
|
|
ws.onclose = () => {
|
|
addLog('warn', 'browser', 'Verbindung zum Diagnostic Server verloren — Reconnect in 2s');
|
|
setTimeout(connectWS, 2000);
|
|
};
|
|
|
|
ws.onmessage = (e) => {
|
|
const msg = JSON.parse(e.data);
|
|
|
|
if (msg.type === 'init') {
|
|
updateState(msg.state);
|
|
msg.logs.forEach(entry => addLog(entry.level, entry.source, entry.message, entry.ts));
|
|
return;
|
|
}
|
|
|
|
if (msg.type === 'state') { updateState(msg.state); return; }
|
|
if (msg.type === 'log') { addLog(msg.entry.level, msg.entry.source, msg.entry.message, msg.entry.ts); return; }
|
|
|
|
if (msg.type === 'chat_final') {
|
|
addChat('received', msg.text, 'chat:final');
|
|
return;
|
|
}
|
|
if (msg.type === 'chat_delta') {
|
|
// Deltas optional anzeigen
|
|
return;
|
|
}
|
|
if (msg.type === 'chat_error') {
|
|
addChat('error', msg.error, 'chat:error');
|
|
return;
|
|
}
|
|
if (msg.type === 'rvs_chat') {
|
|
const p = msg.msg.payload || {};
|
|
addChat('received', p.text || '?', `via RVS (${p.sender || '?'})`);
|
|
return;
|
|
}
|
|
if (msg.type === 'response') {
|
|
// chat.send Ack — nur loggen
|
|
return;
|
|
}
|
|
};
|
|
}
|
|
|
|
function send(obj) {
|
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
ws.send(JSON.stringify(obj));
|
|
}
|
|
}
|
|
|
|
function testGateway() {
|
|
const text = document.getElementById('chat-input').value.trim();
|
|
if (!text) return;
|
|
addChat('sent', text, 'Gateway direkt');
|
|
send({ action: 'test_gateway', text });
|
|
}
|
|
|
|
function testRVS() {
|
|
const text = document.getElementById('chat-input').value.trim();
|
|
if (!text) return;
|
|
addChat('sent', text, 'via RVS');
|
|
send({ action: 'test_rvs', text });
|
|
}
|
|
|
|
function updateState(state) {
|
|
// Gateway
|
|
const gw = state.gateway || {};
|
|
document.getElementById('gw-dot').className = `dot ${gw.status || 'disconnected'}`;
|
|
document.getElementById('gw-status').textContent =
|
|
(STATUS_LABELS[gw.status] || gw.status) + (gw.handshakeOk ? ' (Handshake OK)' : '');
|
|
document.getElementById('gw-error').textContent = gw.lastError || '';
|
|
|
|
// RVS
|
|
const rvs = state.rvs || {};
|
|
document.getElementById('rvs-dot').className = `dot ${rvs.status || 'disconnected'}`;
|
|
document.getElementById('rvs-status').textContent = STATUS_LABELS[rvs.status] || rvs.status;
|
|
document.getElementById('rvs-error').textContent = rvs.lastError || '';
|
|
|
|
// Buttons
|
|
document.getElementById('btn-gw').disabled = gw.status !== 'connected';
|
|
document.getElementById('btn-rvs').disabled = rvs.status !== 'connected';
|
|
}
|
|
|
|
function addLog(level, source, message, ts) {
|
|
const time = ts ? new Date(ts).toLocaleTimeString('de-DE') : new Date().toLocaleTimeString('de-DE');
|
|
const el = document.createElement('div');
|
|
el.className = `log-entry ${level}`;
|
|
el.textContent = `${time} [${source}] ${message}`;
|
|
logBox.appendChild(el);
|
|
if (logAutoScroll) logBox.scrollTop = logBox.scrollHeight;
|
|
}
|
|
|
|
function addChat(type, text, meta) {
|
|
const el = document.createElement('div');
|
|
el.className = `chat-msg ${type}`;
|
|
el.innerHTML = `${escapeHtml(text)}<div class="meta">${escapeHtml(meta)} — ${new Date().toLocaleTimeString('de-DE')}</div>`;
|
|
chatBox.appendChild(el);
|
|
chatBox.scrollTop = chatBox.scrollHeight;
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
// Enter-Taste sendet via Gateway
|
|
document.getElementById('chat-input').addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') testGateway();
|
|
});
|
|
|
|
connectWS();
|
|
</script>
|
|
</body>
|
|
</html>
|