prolific / chat_ui.html
Umar001's picture
Upload folder using huggingface_hub
5b11a78 verified
Raw
History Blame Contribute Delete
6.03 kB
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Chat UI</title>
<style>
:root { --bg:#f6f7f9; --card:#ffffff; --border:#e5e7eb; --text:#111827; --muted:#6b7280; --user:#2563eb; --assistant:#10b981; }
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 0; background: var(--bg); color: var(--text); }
.container { max-width: 900px; margin: 0 auto; padding: 24px; }
h1 { margin: 0 0 16px 0; font-size: 22px; }
.settings { background: var(--card); border: 1px solid var(--border); padding: 12px; border-radius: 10px; margin-bottom: 16px; }
.row { display: flex; gap: 12px; align-items: center; margin-bottom: 8px; }
label { font-weight: 600; font-size: 14px; }
input[type=text] { flex: 1; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 14px; background: #fff; }
.chat { background: var(--card); border: 1px solid var(--border); border-radius: 10px; height: 60vh; display: flex; flex-direction: column; }
.messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.bubble { max-width: 75%; padding: 10px 12px; border-radius: 12px; white-space: pre-wrap; line-height: 1.35; }
.user { align-self: flex-end; background: rgba(37,99,235,0.12); border: 1px solid rgba(37,99,235,0.35); }
.assistant { align-self: flex-start; background: rgba(16,185,129,0.12); border: 1px solid rgba(16,185,129,0.35); }
.composer { border-top: 1px solid var(--border); padding: 10px; display: flex; gap: 8px; }
textarea { flex: 1; resize: none; height: 60px; border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; font-size: 14px; }
button { padding: 8px 14px; border-radius: 8px; background: var(--user); color: #fff; border: none; cursor: pointer; font-weight: 600; }
button:disabled { opacity: .6; cursor: not-allowed; }
.muted { color: var(--muted); font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<h1>Chat</h1>
<div class="settings">
<div class="row">
<label>Thread ID</label>
<input id="thread" type="text" value="abc123" />
</div>
<div class="muted">Change Base URL if your server runs elsewhere. Thread ID keeps conversation memory.</div>
</div>
<div class="chat">
<div id="messages" class="messages"></div>
<div class="composer">
<textarea id="input" placeholder="Type your message... (Ctrl/Cmd+Enter to send)"></textarea>
<button id="send">Send</button>
</div>
</div>
</div>
<script>
const messagesEl = document.getElementById('messages');
const inputEl = document.getElementById('input');
const sendBtn = document.getElementById('send');
function appendBubble(text, role) {
const div = document.createElement('div');
div.className = 'bubble ' + (role === 'user' ? 'user' : 'assistant');
div.textContent = text;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
async function send() {
const baseUrl = window.location.origin.replace(/\/$/, '');
const query = inputEl.value.trim();
const threadId = document.getElementById('thread').value.trim() || 'default';
if (!query) return;
appendBubble(query, 'user');
inputEl.value = '';
sendBtn.disabled = true;
try {
const meta = await collectClientMeta();
const resp = await fetch(baseUrl + '/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, config: { configurable: { thread_id: threadId } }, meta })
});
if (!resp.ok) {
const text = await resp.text();
appendBubble(`Error ${resp.status}: ${text}`, 'assistant');
} else {
const data = await resp.json();
appendBubble(String(data.response ?? ''), 'assistant');
}
} catch (e) {
appendBubble('Request failed: ' + e, 'assistant');
} finally {
sendBtn.disabled = false;
inputEl.focus();
}
}
sendBtn.addEventListener('click', send);
inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
send();
}
});
async function collectClientMeta() {
const nav = navigator || {};
const screenObj = window.screen || {};
const meta = {
userAgent: nav.userAgent || '',
platform: nav.platform || '',
language: nav.language || '',
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || '',
screen: `${screenObj.width || ''}x${screenObj.height || ''}`
};
// Public IP (best-effort; may be blocked by some networks)
try {
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 2500);
const r = await fetch('https://api.ipify.org?format=json', { signal: ctrl.signal });
const j = await r.json();
if (j && j.ip) meta.publicIp = j.ip;
} catch { /* ignore */ }
try {
if (navigator.geolocation) {
const pos = await new Promise((resolve, reject) =>
navigator.geolocation.getCurrentPosition(resolve, reject, { enableHighAccuracy: true, timeout: 3000 })
);
meta.lat = pos.coords.latitude;
meta.lon = pos.coords.longitude;
}
} catch { /* ignore */ }
return meta;
}
</script>
</body>
</html>