From 5200a4c980882ab62a6af8050a8903d5c24cf10a Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 14 Aug 2025 01:28:43 +0000 Subject: [PATCH] Action Commit --- docs/index.html | 90 +++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/docs/index.html b/docs/index.html index 7f371f0..52e03c6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -19,14 +19,20 @@
- - - - + + + -
-
- Enter for newline -
+
@@ -87,7 +90,6 @@ stopBtn: document.getElementById('stopBtn'), clearBtn: document.getElementById('clearBtn'), newChatBtn: document.getElementById('newChatBtn'), - modelName: document.getElementById('modelName'), modelBadge: document.getElementById('modelBadge'), apiBadge: document.getElementById('apiBadge'), statusText: document.getElementById('statusText'), @@ -98,7 +100,14 @@ get apiKey() { return localStorage.getItem('openrouter_api_key') || DEFAULT_API_KEY || ''; }, set apiKey(v) { localStorage.setItem('openrouter_api_key', v || ''); updateStatus(); }, get model() { return localStorage.getItem('openrouter_model') || DEFAULT_MODEL; }, - set model(v) { localStorage.setItem('openrouter_model', v || DEFAULT_MODEL); el.modelName.textContent = store.model; }, + set model(v) { localStorage.setItem('openrouter_model', v || DEFAULT_MODEL); }, + }; + + // === Helpers === + const getModelShort = () => { + const m = store.model || ''; + const name = m.includes('/') ? m.split('/').pop() : m; + return name; }; // === Runtime state === @@ -108,14 +117,29 @@ function addMessage(role, content) { const row = document.createElement('div'); row.className = 'flex gap-3'; + const avatar = document.createElement('div'); avatar.className = 'shrink-0 h-8 w-8 rounded-full flex items-center justify-center ' + (role === 'user' ? 'bg-gray-900 text-white' : 'bg-gray-200 text-gray-900'); avatar.textContent = role === 'user' ? '🧑' : '🤖'; + + const right = document.createElement('div'); + right.className = 'flex flex-col'; + + if (role !== 'user') { + const name = document.createElement('div'); + name.className = 'text-xs font-medium text-gray-500 mb-0.5'; + name.textContent = getModelShort(); + right.appendChild(name); + } + const bubble = document.createElement('div'); bubble.className = 'rounded-2xl px-4 py-3 text-[15px] leading-relaxed whitespace-pre-wrap ' + (role === 'user' ? 'bg-gray-50 border border-gray-200' : 'bg-gray-100'); bubble.textContent = content; + + right.appendChild(bubble); row.appendChild(avatar); - row.appendChild(bubble); + row.appendChild(right); + el.messages.appendChild(row); state.messages.push({ role, content }); queueMicrotask(() => el.chat.scrollTo({ top: el.chat.scrollHeight, behavior: 'smooth' })); @@ -125,14 +149,27 @@ function addAssistantBubbleStreaming() { const row = document.createElement('div'); row.className = 'flex gap-3'; + const avatar = document.createElement('div'); avatar.className = 'shrink-0 h-8 w-8 rounded-full flex items-center justify-center bg-gray-200 text-gray-900'; avatar.textContent = '🤖'; + + const right = document.createElement('div'); + right.className = 'flex flex-col'; + + const name = document.createElement('div'); + name.className = 'text-xs font-medium text-gray-500 mb-0.5'; + name.textContent = getModelShort(); + const bubble = document.createElement('div'); bubble.className = 'rounded-2xl px-4 py-3 text-[15px] leading-relaxed bg-gray-100 text-gray-800 whitespace-pre-wrap'; bubble.textContent = ''; + + right.appendChild(name); + right.appendChild(bubble); row.appendChild(avatar); - row.appendChild(bubble); + row.appendChild(right); + el.messages.appendChild(row); queueMicrotask(() => el.chat.scrollTo({ top: el.chat.scrollHeight })); return bubble; @@ -144,7 +181,7 @@ if (intro) { const introRow = document.createElement('div'); introRow.className = 'flex gap-3'; - introRow.innerHTML = `
🤖
New chat
You're in a fresh conversation. Ask away.
`; + introRow.innerHTML = `
🤖
New chat
You're in a fresh conversation. Ask away.
`; el.messages.appendChild(introRow); } } @@ -162,7 +199,6 @@ const apiKey = store.apiKey; const model = store.model; if (!apiKey) { - // Local demo fallback const text = localDemoReply(state.messages[state.messages.length - 1]?.content || ''); onDelta(text, true); return { ok: true, text }; @@ -188,7 +224,6 @@ throw new Error(errText || ('HTTP ' + res.status)); } - // Stream via SSE-style chunks const reader = res.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; @@ -226,7 +261,6 @@ } catch (e) { console.error(e); const msg = String(e?.message || e); - // Minimal triage let hint = 'Request failed.'; if (/401|unauthorized/i.test(msg)) hint = 'Unauthorized (check API key).'; else if (/429|rate/i.test(msg)) hint = 'Rate limited (slow down or upgrade).'; @@ -242,7 +276,7 @@ function localDemoReply(prompt) { const tips = [ 'Tip: click the key badge to set your OpenRouter API key.', - 'Model is clickable at the top—tap the model badge to change it.', + 'Click the robot to change the model.', 'New chats are stateless here—no history is kept.' ]; const tip = tips[Math.floor(Math.random() * tips.length)]; @@ -266,20 +300,13 @@ const assistantBubble = addAssistantBubbleStreaming(); - const res = await askOpenRouterStreaming((delta, done) => { + await askOpenRouterStreaming((delta, done) => { assistantBubble.textContent += delta; if (done) { el.sendBtn.disabled = false; el.stopBtn.classList.add('hidden'); state.busy = false; - // ensure assistant message stored - const lastIdx = state.messages.length - 1; - // replace temp assistant if last is not assistant yet - if (state.messages[lastIdx]?.role !== 'assistant') { - state.messages.push({ role: 'assistant', content: assistantBubble.textContent }); - } else { - state.messages[lastIdx].content = assistantBubble.textContent; - } + state.messages.push({ role: 'assistant', content: assistantBubble.textContent }); queueMicrotask(() => el.chat.scrollTo({ top: el.chat.scrollHeight, behavior: 'smooth' })); } }); @@ -302,9 +329,9 @@ // Clear / New el.clearBtn.addEventListener('click', () => clearChat(false)); - document.getElementById('newChatBtn')?.addEventListener('click', () => clearChat(true)); + el.newChatBtn.addEventListener('click', () => clearChat(true)); - // Model badge click + // Model badge click (robot) el.modelBadge.addEventListener('click', () => { const input = prompt('Enter model name (e.g., openai/gpt-4o):', store.model); if (input === null) return; @@ -325,14 +352,13 @@ const url = new URL(location.href); const key = url.searchParams.get('key'); const model = url.searchParams.get('model'); - if (key) store.apiKey = key; // transient: saved to localStorage by design + if (key) store.apiKey = key; if (model) store.model = model; } function init() { initFromQuery(); updateStatus(); - el.modelName.textContent = store.model; } init();