Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
HTML
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>judge · Aesthetic Computer</title> <link rel="icon" href="https://aesthetic.computer/icon/128x128/prompt.png" type="image/png" /> <style> * { margin: 0; padding: 0; box-sizing: border-box; } ::-webkit-scrollbar { display: none; }
body { font-family: monospace; font-size: 14px; line-height: 1.5; background: #f5f5f5; color: #000; -webkit-text-size-adjust: none; cursor: url('https://aesthetic.computer/aesthetic.computer/cursors/precise.svg') 12 12, auto; }
.container { padding: 1em; padding-top: 4em; }
header { position: fixed; top: 12px; left: 16px; z-index: 3; background: transparent; border: none; padding: 0; margin: 0; }
header::before { content: ""; height: 3em; width: 100vw; position: absolute; z-index: -1; top: -12px; left: -16px; background: linear-gradient(to bottom, #f5f5f5 50%, transparent 100%); }
.header-left { flex: 1; }
.header-right { display: none; }
h1 { font-size: 22px; font-weight: normal; color: rgb(205, 92, 155); margin: 0; cursor: pointer; user-select: none; }
h1:hover { color: purple; }
h1:active { color: gray; }
.subtitle { font-size: 0.85em; opacity: 0.7; margin-top: 0.2em; }
.status { display: flex; align-items: center; gap: 0.5em; }
.status-dot { width: 10px; height: 10px; border-radius: 50%; background: #999; }
.status-dot.connected { background: #4ade80; box-shadow: 0 0 8px rgba(74, 222, 128, 0.5); animation: pulse 2s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
.status-dot.disconnected { background: #ef4444; }
section { margin: 2em 0; padding: 0 1em; }
h2 { font-size: 1.2em; font-weight: normal; margin-bottom: 0.8em; padding-bottom: 0.3em; border-bottom: 1px solid #ddd; }
.content-wrapper { max-width: 1200px; margin: 0 auto; }
.form-group { margin: 1em 0; }
label { display: block; margin-bottom: 0.5em; font-weight: normal; }
textarea { width: 100%; padding: 0.8em; font-family: monospace; font-size: 14px; border: 1px solid #ccc; background: white; resize: vertical; min-height: 80px; }
textarea:focus { outline: none; border-color: rgb(205, 92, 155); }
.button-group { display: flex; gap: 0.5em; flex-wrap: wrap; margin: 1em 0; }
button { padding: 0.7em 1.2em; font-family: monospace; font-size: 14px; border: 1px solid #000; background: white; cursor: pointer; transition: all 0.2s; position: relative; overflow: hidden; }
button:hover { background: rgb(205, 92, 155); color: white; border-color: rgb(205, 92, 155); }
button:active { transform: translateY(1px); }
button:disabled { opacity: 0.5; cursor: not-allowed; background: #f0f0f0; }
button.testing { background: rgb(205, 92, 155); color: white; border-color: rgb(205, 92, 155); }
button.testing::after { content: ''; position: absolute; bottom: 0; left: 0; height: 3px; background: rgba(255, 255, 255, 0.5); animation: progress 2s ease-in-out infinite; }
@keyframes progress { 0% { width: 0%; } 50% { width: 70%; } 100% { width: 100%; } }
.result { margin: 1em 0; padding: 1em; background: white; border-left: 4px solid #ddd; display: none; animation: slideIn 0.3s ease-out; }
@keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
.result.show { display: block; }
.result.allowed { border-color: #4ade80; background: #f0fdf4; }
.result.blocked { border-color: #ef4444; background: #fef2f2; }
.result-header { font-weight: bold; margin-bottom: 0.5em; font-size: 1.1em; }
.result-details { font-size: 0.9em; opacity: 0.8; margin: 0.3em 0; }
.stream-output { margin: 1em 0; padding: 0.8em; background: #fafafa; border: 1px solid #eee; min-height: 60px; max-height: 200px; overflow-y: auto; font-size: 0.85em; white-space: pre-wrap; font-family: monospace; display: none; animation: fadeIn 0.3s ease-out; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.stream-output.active { display: block; }
.stream-output::before { content: '💬 AI reasoning...'; display: block; margin-bottom: 0.5em; opacity: 0.6; font-size: 0.9em; }
.history { margin: 1em 0; }
.history-item { padding: 0.8em; margin: 0.5em 0; background: white; border-left: 3px solid #ddd; font-size: 0.9em; }
.history-item.allowed { border-color: #4ade80; }
.history-item.blocked { border-color: #ef4444; }
.message-text { margin-bottom: 0.5em; word-wrap: break-word; }
.message-meta { font-size: 0.85em; opacity: 0.6; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.8em; margin: 1em 0; }
.stat-box { padding: 1em; background: white; border: 1px solid #ddd; }
.stat-label { font-size: 0.85em; opacity: 0.7; margin-bottom: 0.3em; }
.stat-value { font-size: 1.8em; font-weight: normal; color: rgb(205, 92, 155); }
@media (max-width: 600px) { .container { padding: 0.5em; } header { flex-direction: column; align-items: flex-start; } .header-right { width: 100%; flex-direction: column; align-items: flex-start; gap: 0.5em; } h1 { font-size: 1.2em; } section { padding: 0 0.5em; } .stats { grid-template-columns: 1fr; } .button-group { flex-direction: column; } button { width: 100%; } } </style></head><body> <div class="container"> <header> <div class="header-left"> <h1>judge</h1> </div> <div class="header-right"> <div class="status"> <div class="status-dot" id="wsStatus"></div> <span id="wsStatusText">Connecting...</span> </div> <div class="status"> <span id="modelInfo">Model: gemma2:2b</span> </div> </div> </header>
<div class="content-wrapper"> <section> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1em; flex-wrap: wrap; gap: 1em;"> <h2 style="margin: 0;">Test Message</h2> <div style="display: flex; gap: 1.5em; align-items: center; font-size: 0.9em;"> <div class="status"> <div class="status-dot" id="wsStatus2"></div> <span id="wsStatusText2">Connecting...</span> </div> <div class="status"> <span>gemma2:2b</span> </div> </div> </div> <div class="form-group"> <label for="messageInput">Enter a message to check:</label> <textarea id="messageInput" placeholder="Type a message here..."></textarea> </div> <div class="button-group"> <button id="testBtn" onclick="testMessage()">Test Message</button> <button onclick="clearForm()">Clear</button> </div>
<div id="streamOutput" class="stream-output"></div> <div id="result" class="result"></div> </section>
<section> <h2>Statistics</h2> <div class="stats"> <div class="stat-box"> <div class="stat-label">Total Tests</div> <div class="stat-value" id="totalTests">0</div> </div> <div class="stat-box"> <div class="stat-label">Allowed</div> <div class="stat-value" id="allowedCount">0</div> </div> <div class="stat-box"> <div class="stat-label">Blocked</div> <div class="stat-value" id="blockedCount">0</div> </div> <div class="stat-box"> <div class="stat-label">Avg Response</div> <div class="stat-value" id="avgResponse">0s</div> </div> </div> </section>
<section> <h2>Recent Tests</h2> <div id="history" class="history"></div> </section> </div> </div>
<script> // WebSocket connection const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws`; let ws = null; let reconnectTimer = null;
console.log('📍 Attempting WebSocket connection to:', wsUrl);
function updateConnectionStatus(connected) { const dot = document.getElementById('wsStatus'); const text = document.getElementById('wsStatusText'); const dot2 = document.getElementById('wsStatus2'); const text2 = document.getElementById('wsStatusText2'); if (connected) { dot?.classList.add('connected'); dot?.classList.remove('disconnected'); if (text) text.textContent = 'Connected'; dot2?.classList.add('connected'); dot2?.classList.remove('disconnected'); if (text2) text2.textContent = 'Connected'; } else { dot?.classList.remove('connected'); dot?.classList.add('disconnected'); if (text) text.textContent = 'Disconnected'; dot2?.classList.remove('connected'); dot2?.classList.add('disconnected'); if (text2) text2.textContent = 'Disconnected'; } }
function connectWebSocket() { try { console.log('🔌 Connecting WebSocket...'); ws = new WebSocket(wsUrl); ws.onopen = () => { console.log('🟢 WebSocket connected successfully'); updateConnectionStatus(true); }; ws.onclose = (event) => { console.log('🔴 WebSocket disconnected', event.code, event.reason); updateConnectionStatus(false); // Reconnect after 2 seconds reconnectTimer = setTimeout(connectWebSocket, 2000); }; ws.onerror = (error) => { console.error('❌ WebSocket error:', error); updateConnectionStatus(false); };
ws.onmessage = (event) => { const data = JSON.parse(event.data); handleWebSocketMessage(data); }; } catch (error) { console.error('❌ Failed to create WebSocket:', error); updateConnectionStatus(false); } }
// Initialize on load connectWebSocket();
// Statistics tracking let stats = { total: 0, allowed: 0, blocked: 0, responseTimes: [] };
function updateStats() { document.getElementById('totalTests').textContent = stats.total; document.getElementById('allowedCount').textContent = stats.allowed; document.getElementById('blockedCount').textContent = stats.blocked; if (stats.responseTimes.length > 0) { const avg = stats.responseTimes.reduce((a, b) => a + b, 0) / stats.responseTimes.length; document.getElementById('avgResponse').textContent = avg.toFixed(2) + 's'; } }
function handleWebSocketMessage(data) { const streamOutput = document.getElementById('streamOutput'); const testBtn = document.getElementById('testBtn'); if (data.type === 'start') { streamOutput.classList.add('active'); streamOutput.textContent = ''; testBtn.classList.add('testing'); testBtn.disabled = true; testBtn.textContent = 'Testing...'; } else if (data.type === 'chunk') { streamOutput.textContent = data.full || data.chunk; streamOutput.scrollTop = streamOutput.scrollHeight; } else if (data.type === 'complete') { testBtn.classList.remove('testing'); testBtn.disabled = false; testBtn.textContent = 'Test Message'; showResult(data); // Update stats stats.total++; if (data.decision === 't') stats.allowed++; else stats.blocked++; if (data.responseTime) stats.responseTimes.push(data.responseTime); updateStats(); // Add to history addToHistory(currentMessage, data); // Hide stream output after showing result setTimeout(() => { streamOutput.classList.remove('active'); }, 1000); } }
let currentMessage = '';
function testMessage() { const message = document.getElementById('messageInput').value.trim(); if (!message) { alert('Please enter a message'); return; }
currentMessage = message;
if (!ws || ws.readyState !== WebSocket.OPEN) { alert('WebSocket not connected. Please wait...'); return; }
// Clear previous results document.getElementById('result').classList.remove('show'); ws.send(JSON.stringify({ message })); }
function showResult(data) { const resultDiv = document.getElementById('result'); const allowed = data.decision === 't'; resultDiv.className = 'result show ' + (allowed ? 'allowed' : 'blocked'); const emoji = allowed ? '✓' : '✗'; const status = allowed ? 'Allowed' : 'Blocked'; resultDiv.innerHTML = ` <div class="result-header">${emoji} ${status}</div> <div class="result-details">Sentiment: ${data.sentiment || '(yes)'}</div> ${data.reason ? `<div class="result-details">Reason: ${data.reason}</div>` : ''} <div class="result-details">Response time: ${data.responseTime ? data.responseTime.toFixed(3) + 's' : 'N/A'}</div> `; }
function addToHistory(message, data) { const historyDiv = document.getElementById('history'); const allowed = data.decision === 't'; const item = document.createElement('div'); item.className = 'history-item ' + (allowed ? 'allowed' : 'blocked'); item.innerHTML = ` <div class="message-text">${escapeHtml(message)}</div> <div class="message-meta"> ${allowed ? '✓ Allowed' : '✗ Blocked'} · ${data.responseTime ? data.responseTime.toFixed(2) + 's' : 'N/A'} · ${new Date().toLocaleTimeString()} </div> `; historyDiv.insertBefore(item, historyDiv.firstChild); // Keep only last 10 items while (historyDiv.children.length > 10) { historyDiv.removeChild(historyDiv.lastChild); } }
function clearForm() { document.getElementById('messageInput').value = ''; document.getElementById('result').classList.remove('show'); document.getElementById('streamOutput').classList.remove('active'); const testBtn = document.getElementById('testBtn'); testBtn.classList.remove('testing'); testBtn.disabled = false; testBtn.textContent = 'Test Message'; }
function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }
// Wire up back button to go to aesthetic.computer document.querySelector('h1').onclick = () => { if (window.location.host !== 'judge.aesthetic.computer') { window.location.href = '/'; } else { window.location.href = 'https://aesthetic.computer'; } }; </script></body></html>