super-chat / index.html
Murugesaninyt's picture
<div class="typing"><span></span><span></span><span></span></div> and also the same error - Initial Deployment
1b748d5 verified
Raw
History Blame Contribute Delete
19.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Fullscreen AI Chatbot — Structured Output + Copy Code</title>
<!-- Highlight.js styles -->
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
<style>
:root{
--bg: #0b1220;
--panel: #0f1724;
--muted: #98a0b3;
--accent: #38bdf8;
--card: #0b1228;
--bot-bg: #0f1724;
--user-bg: #0369a1;
--text: #e6eef8;
--radius: 12px;
--glass: rgba(255,255,255,0.03);
}
*{box-sizing:border-box}
html,body{height:100%; margin:0; font-family:Inter,ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',Arial; background:var(--bg); color:var(--text);}
/* Layout: full screen split - left conversation, right optional panel (hidden on small screens) */
.app {
height:100vh;
display:grid;
grid-template-columns: 1fr 360px;
gap:18px;
padding:18px;
}
/* mobile: single column */
@media (max-width:980px){
.app { grid-template-columns: 1fr; padding:10px; }
.sidebar{ display:none; }
}
/* chat column */
.chat-area {
background: linear-gradient(180deg, rgba(255,255,255,0.02), transparent);
border-radius: var(--radius);
padding: 14px;
display:flex;
flex-direction:column;
min-width:0;
box-shadow: 0 8px 30px rgba(2,6,23,0.6);
border:1px solid rgba(255,255,255,0.03);
overflow:hidden;
}
.topbar {
display:flex;
align-items:center;
gap:12px;
padding:8px 6px;
border-bottom:1px solid rgba(255,255,255,0.03);
}
.logo {
display:flex;
align-items:center;
gap:10px;
font-weight:700;
font-size:16px;
}
.logo .dot { width:10px; height:10px; border-radius:50%; background:var(--accent); box-shadow:0 0 12px var(--accent); }
.controls { margin-left:auto; display:flex; gap:8px; align-items:center; color:var(--muted); }
.clear-btn, .copy-convo-btn {
background:transparent;
border:1px solid rgba(255,255,255,0.03);
color:var(--muted);
padding:6px 10px;
border-radius:8px;
cursor:pointer;
font-size:13px;
}
/* messages list */
.messages {
flex:1;
overflow:auto;
padding:18px 8px;
display:flex;
flex-direction:column;
gap:12px;
}
.msg {
display:inline-block;
max-width:85%;
padding:12px 14px;
border-radius:12px;
line-height:1.45;
white-space:pre-wrap;
word-break:break-word;
}
.msg.user {
align-self:flex-end;
background:linear-gradient(180deg,var(--user-bg), #065f87);
color: #fff;
border-bottom-right-radius:6px;
box-shadow: 0 6px 18px rgba(3,105,161,0.15);
}
.msg.bot {
align-self:flex-start;
background:var(--bot-bg);
border:1px solid rgba(255,255,255,0.03);
color:var(--text);
border-bottom-left-radius:6px;
}
/* markdown inside bot messages */
.msg.bot h1, .msg.bot h2, .msg.bot h3 {
color:var(--accent); margin:8px 0;
}
.msg.bot p { color: #dbe8f8; margin:6px 0; }
.msg.bot ul, .msg.bot ol { margin:8px 0 8px 20px; color: #cfe9fb; }
.msg.bot blockquote { border-left:3px solid rgba(56,189,248,0.18); padding-left:12px; color:#9fbfd8; margin:8px 0; font-style:italic; }
.msg.bot table { border-collapse:collapse; width:100%; margin:8px 0; }
.msg.bot th, .msg.bot td { border:1px solid rgba(255,255,255,0.04); padding:8px; text-align:left; }
.msg.bot code.inline { background:rgba(255,255,255,0.03); padding:2px 6px; border-radius:6px; color:#9ff; font-family:monospace; }
.msg.bot pre { background:#021022; border-radius:8px; padding:12px; overflow:auto; position:relative; margin:8px 0; }
/* copy button for code blocks */
.code-copy {
position:absolute;
right:10px; top:10px;
background:var(--accent);
border:none;
color:#00131b;
padding:6px 9px;
border-radius:8px;
cursor:pointer;
font-weight:600;
font-size:12px;
}
/* input area */
.input-area {
margin-top:12px;
display:flex;
gap:12px;
align-items:center;
padding:12px;
border-radius:10px;
background: linear-gradient(180deg, rgba(255,255,255,0.01), transparent);
border:1px solid rgba(255,255,255,0.02);
}
.input-area textarea {
flex:1;
min-height:48px;
max-height:160px;
resize:none;
padding:10px 12px;
border-radius:10px;
border:none;
outline:none;
font-size:15px;
background:transparent;
color:var(--text);
}
.send-row { display:flex; gap:8px; align-items:center; }
.send-btn {
background:var(--accent);
border:none;
color:#00131b;
font-weight:700;
padding:10px 14px;
border-radius:10px;
cursor:pointer;
box-shadow:0 8px 24px rgba(56,189,248,0.12);
font-size:15px;
}
.secondary {
background:transparent;
border:1px solid rgba(255,255,255,0.04);
color:var(--muted);
padding:8px 10px;
border-radius:10px;
cursor:pointer;
font-size:13px;
}
/* right sidebar (instructions / system prompt / toggles) */
.sidebar {
background:var(--panel);
border-radius:12px;
padding:12px;
min-height:0;
border:1px solid rgba(255,255,255,0.03);
display:flex;
flex-direction:column;
gap:12px;
}
.sidebar h3 { color:var(--accent); margin:0; font-size:14px; }
.sidebar textarea {
width:100%;
height:120px;
resize:none;
background:transparent;
border:1px solid rgba(255,255,255,0.03);
color:var(--text);
padding:8px;
border-radius:8px;
font-size:13px;
}
.meta-row { display:flex; gap:8px; align-items:center; }
.small { font-size:13px; color:var(--muted); }
/* typing indicator style */
.typing {
display: flex;
gap: 8px;
padding: 14px 16px;
align-items: center;
}
.typing span {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--accent);
animation: blink 1.4s infinite ease-in-out;
opacity: 0.4;
}
.typing span:nth-child(1) { animation-delay: 0s }
.typing span:nth-child(2) { animation-delay: 0.2s }
.typing span:nth-child(3) { animation-delay: 0.4s }
@keyframes blink {
0%, 100% { opacity: 0.2; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1); }
}
/* small helper */
.muted { color:var(--muted); font-size:13px; }
</style>
</head>
<body>
<div class="app">
<!-- Chat area -->
<div class="chat-area" role="main" aria-label="Chat area">
<div class="topbar">
<div class="logo"><span class="dot"></span> Super Chat</div>
<div class="controls">
<button class="clear-btn" id="clearBtn" title="Clear conversation">Clear</button>
<button class="copy-convo-btn" id="copyConvoBtn" title="Copy entire conversation">Copy convo</button>
</div>
</div>
<div class="messages" id="messages" aria-live="polite"></div>
<div style="margin-top:10px"></div>
<div class="input-area" role="region" aria-label="Message input">
<textarea id="userInput" placeholder="Ask me anything... (Shift+Enter for newline). Use the sidebar to add system instructions." spellcheck="true"></textarea>
<div class="send-row">
<button class="secondary" id="toggleSystemBtn" title="Toggle system prompt area">System</button>
<button class="send-btn" id="sendBtn">Send</button>
</div>
</div>
</div>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div>
<h3>System prompt</h3>
<textarea id="systemPrompt">You are a helpful assistant. Provide structured, well-formatted responses. When returning code blocks, include language hints (e.g., ```js). Use headings, lists, tables where appropriate.</textarea>
<div class="meta-row">
<button class="secondary" id="applySystemBtn">Apply</button>
<button class="secondary" id="saveSystemBtn">Save</button>
<div style="flex:1"></div>
<div class="small">Model: <strong>cognitivecomputations/dolphin-mistral-24b-venice-edition:free</strong></div>
</div>
</div>
<div>
<h3>Settings</h3>
<label class="small">Max tokens: <input type="number" id="maxTokens" value="1000" style="width:80px; margin-left:8px"></label>
<div style="height:8px"></div>
<label class="small">Temperature: <input type="number" id="temperature" value="0.2" step="0.1" min="0" max="1" style="width:60px; margin-left:6px"></label>
</div>
<div>
<h3>Shortcuts</h3>
<div class="small">Press <kbd>Enter</kbd> to send, <kbd>Shift+Enter</kbd> newline.</div>
<div style="height:6px"></div>
<div class="small">Click the copy button inside code blocks to copy the code.</div>
</div>
</aside>
</div>
<!-- Libraries -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<script>
/*********************************************************
* Fullscreen Chatbot — client-side demo
*
* IMPORTANT: This demo puts your API key directly in the
* client. For production, proxy requests through a backend.
*********************************************************/
// === Replace with your key (you provided it). Keep it secret in real apps. ===
const OPENROUTER_API_KEY = "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";
const API_URL = "https://openrouter.ai/api/v1/chat/completions";
const MODEL_NAME = "cognitivecomputations/dolphin-mistral-24b-venice-edition:free";
// UI elements
const messagesEl = document.getElementById('messages');
const userInput = document.getElementById('userInput');
const sendBtn = document.getElementById('sendBtn');
const clearBtn = document.getElementById('clearBtn');
const copyConvoBtn = document.getElementById('copyConvoBtn');
const sidebar = document.getElementById('sidebar');
const toggleSystemBtn = document.getElementById('toggleSystemBtn');
const systemPromptEl = document.getElementById('systemPrompt');
const applySystemBtn = document.getElementById('applySystemBtn');
const maxTokensEl = document.getElementById('maxTokens');
const temperatureEl = document.getElementById('temperature');
// conversation memory
let conversation = [];
// by default include system prompt if any
let systemPrompt = systemPromptEl.value.trim();
if(systemPrompt) conversation.push({ role: "system", content: systemPrompt });
// Small helper to create elements
function el(tag, props={}, ...children){
const d = document.createElement(tag);
for(const k in props) {
if(k === 'class') d.className = props[k];
else if(k === 'html') d.innerHTML = props[k];
else d.setAttribute(k, props[k]);
}
for(const c of children) if(typeof c === 'string') d.appendChild(document.createTextNode(c)); else d.appendChild(c);
return d;
}
// Render a message (role: 'user' | 'assistant')
function renderMessage(role, htmlContent, rawText) {
const m = el('div', { class: 'msg ' + (role === 'user' ? 'user' : 'bot') });
// if assistant -> content is HTML (from marked)
m.innerHTML = htmlContent;
messagesEl.appendChild(m);
messagesEl.scrollTop = messagesEl.scrollHeight;
// After adding, find code blocks and add copy buttons & syntax highlight
m.querySelectorAll('pre').forEach(pre => {
// avoid adding multiple buttons
if(pre.querySelector('.code-copy')) return;
const lang = pre.getAttribute('data-lang') || 'code';
const btn = el('button', { class: 'code-copy' }, `Copy ${lang}`);
pre.style.position = 'relative';
pre.prepend(btn); // place inside pre (left) — absolute will move it to top-right
// fix absolute location: move btn outside text but top-right visually
btn.style.position = 'absolute';
btn.style.right = '10px';
btn.style.top = '10px';
btn.addEventListener('click', async () => {
// copy code text
const codeEl = pre.querySelector('code');
const textToCopy = codeEl ? codeEl.innerText : pre.innerText;
try{
await navigator.clipboard.writeText(textToCopy);
btn.textContent = 'Copied!';
setTimeout(()=> btn.textContent = 'Copy', 1600);
}catch(err){
btn.textContent = 'Copy';
}
});
});
// run highlight.js on any code blocks inside this message
m.querySelectorAll('pre code').forEach((block) => {
try { hljs.highlightElement(block); } catch(e) { /* ignore */ }
});
return m;
}
// Convert assistant content (markdown) to sanitized HTML with marked
const markedRenderer = new marked.Renderer();
// Customize code rendering so we wrap language classes (highlight.js picks them)
markedRenderer.code = function(code, infostring, escaped) {
const lang = (infostring || '').match(/\S*/)[0];
const langClass = lang ? `language-${lang}` : '';
// Add data-lang attribute for the copy button
return `<pre data-lang="${infostring || ''}"><code class="${langClass}">${escapeHtml(code)}</code></pre>`;
};
marked.setOptions({
renderer: markedRenderer,
breaks: true,
gfm: true
});
function escapeHtml(unsafe){
if (typeof unsafe !== 'string') return unsafe;
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
// Send message handler
async function sendMessage() {
const text = userInput.value.trim();
if(!text) return;
// Add to UI (user)
renderMessage('user', `<div>${escapeHtml(text)}</div>`, text);
// add to conversation history
conversation.push({ role: "user", content: text });
// clear input
userInput.value = '';
// show typing placeholder from assistant
const typingNode = el('div', { class: 'msg bot' }, `<div class="typing"><span></span><span></span><span></span></div>`);
messagesEl.appendChild(typingNode);
messagesEl.scrollTop = messagesEl.scrollHeight;
// prepare payload
const payload = {
model: MODEL_NAME,
messages: conversation,
// pass settings from sidebar
max_tokens: Number(maxTokensEl.value) || 1000,
temperature: Number(temperatureEl.value) || 0.2
};
try {
const res = await fetch(API_URL, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${OPENROUTER_API_KEY}`,
// optional headers that were used earlier
"HTTP-Referer": window.location.origin,
"X-Title": "Super Chat"
},
body: JSON.stringify(payload)
});
if(!res.ok){
const errText = await res.text();
throw new Error(`API error: ${res.status} ${errText}`);
}
const data = await res.json();
// remove typing node
typingNode.remove();
// get assistant message content
const assistantText = data?.choices?.[0]?.message?.content || "(no response)";
// push assistant to conversation
conversation.push({ role: "assistant", content: assistantText });
// render assistant markdown
const html = marked.parse(assistantText);
renderMessage('assistant', html, assistantText);
} catch (err) {
// remove typing node if present
typingNode.remove();
const errHtml = `<div><strong style="color:#ff7a7a">Error:</strong> ${escapeHtml(err.message)}</div>`;
renderMessage('assistant', errHtml, err.message);
}
}
// UI wiring
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keydown', (e) => {
if(e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
clearBtn.addEventListener('click', () => {
if(!confirm('Clear conversation?')) return;
conversation = [];
// re-add system prompt if present
systemPrompt = systemPromptEl.value.trim();
if(systemPrompt) conversation.push({ role: "system", content: systemPrompt });
messagesEl.innerHTML = '';
userInput.focus();
});
copyConvoBtn.addEventListener('click', async () => {
const text = conversation.map(m => `${m.role.toUpperCase()}: ${m.content}`).join('\n\n');
try {
await navigator.clipboard.writeText(text);
copyConvoBtn.textContent = 'Copied!';
setTimeout(()=> copyConvoBtn.textContent = 'Copy convo', 1400);
} catch {
alert('Copy failed — please allow clipboard permission.');
}
});
toggleSystemBtn.addEventListener('click', () => {
sidebar.style.display = sidebar.style.display === 'none' ? 'flex' : 'flex';
// on small screens, toggle visibility
if(window.innerWidth < 980){
sidebar.style.display = sidebar.style.display === 'none' ? 'flex' : (sidebar.style.display === 'flex' ? 'none' : 'flex');
}
});
applySystemBtn.addEventListener('click', () => {
systemPrompt = systemPromptEl.value.trim();
// remove old system messages from conversation
conversation = conversation.filter(m => m.role !== 'system');
if(systemPrompt) conversation.unshift({ role: "system", content: systemPrompt });
alert('Applied system prompt to conversation. (It will be sent on next request.)');
});
// load saved system prompt if any (localStorage optional)
try {
const saved = localStorage.getItem('superchat_system');
if(saved) systemPromptEl.value = saved;
document.getElementById('saveSystemBtn').addEventListener('click', () => {
localStorage.setItem('superchat_system', systemPromptEl.value);
alert('System prompt saved locally.');
});
} catch(e){ /* ignore localStorage errors */ }
// initial focus
userInput.focus();
// helpful dev message (optional)
// renderMessage('assistant', marked.parse('Hello! I am ready. Send a message and I will reply with structured & formatted output.'));
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Murugesaninyt/super-chat" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>