diff --git a/public/index.html b/public/index.html index e967c89..a665eb8 100644 --- a/public/index.html +++ b/public/index.html @@ -38,69 +38,50 @@ #clock { font-weight: normal; } #clock span { font-weight: bold; } - /* ── Chat section (white theme, below footer) ── */ + /* ── STEALTH CHAT (outputs above the blue stripe, bare input below) ── */ + #chat-output-tiny { + max-width: 970px; + margin: 0 auto; + padding: 6px 15px; + font-size: 10px; + color: #c4c4c4; + line-height: 1.55; + white-space: pre-wrap; + word-break: break-word; + max-height: 260px; + overflow-y: auto; + font-family: proxima_nova_rgregular, Arial, sans-serif; + } + #chat-output-tiny:empty { padding: 0; } + #chat-output-tiny code { + background: transparent; padding: 0; color: #b8b8b8; + font-family: Consolas, monospace; font-size: 10px; + } + #chat-output-tiny pre { + background: transparent; border: none; padding: 0; margin: 0; + } + .chat-wrapper { background: #fff; border-top: 4px solid #003571; - padding: 30px 0 50px; + padding: 14px 0 28px; font-family: proxima_nova_rgregular, Arial, sans-serif; } - .chat-wrapper .container { background: #fff; padding-top: 0; } - .chat-wrapper h2 { - font-family: proxima_nova_rgbold, Arial, sans-serif; - color: #003571; margin-top: 0; - } - .chat-wrapper .page-header { margin-top: 0; border-bottom: 1px solid #eee; } - .chat-toolbar { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; } - .chat-toolbar label { font-size: 14px; color: #555; margin: 0; font-weight: normal; } - .chat-toolbar select { - border: 1px solid #ccc; border-radius: 4px; padding: 5px 10px; - font-size: 14px; color: #333; background: #fff; - font-family: proxima_nova_rgregular, Arial, sans-serif; - } - #chat-status { margin-left: auto; font-size: 13px; color: #888; } - .chat-panels { - display: flex; border: 1px solid #ddd; border-radius: 2px; - overflow: hidden; height: 380px; background: #fff; - } - .chat-pane { flex: 1; display: flex; flex-direction: column; overflow: hidden; } - .chat-pane + .chat-pane { border-left: 1px solid #ddd; } - .chat-pane-label { - padding: 8px 14px; font-size: 11px; text-transform: uppercase; - letter-spacing: .06em; color: #999; - border-bottom: 1px solid #eee; background: #fafafa; - } + .chat-wrapper .container { background: #fff; max-width: 970px; padding: 0 15px; } #chat-input { - flex: 1; border: none; padding: 14px; - font-size: 14px; line-height: 1.6; resize: none; - color: #333; background: #fff; + width: 100%; + border: none; + background: transparent; + color: #555; + font-size: 14px; + outline: none; + resize: none; + min-height: 28px; + line-height: 1.5; + padding: 4px 0; font-family: proxima_nova_rgregular, Arial, sans-serif; } - #chat-input:focus { outline: none; } - #chat-input::placeholder { color: #bbb; } - #chat-output { - flex: 1; padding: 14px; overflow-y: auto; - font-size: 14px; line-height: 1.7; white-space: pre-wrap; - word-break: break-word; color: #333; background: #fff; - } - #chat-output.empty { color: #bbb; font-style: italic; } - #chat-output code { - background: #f5f5f5; padding: 1px 5px; border-radius: 3px; - font-family: Consolas, monospace; font-size: 13px; color: #c7254e; - } - #chat-output pre { - background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 4px; - padding: 10px; overflow-x: auto; margin: 6px 0; - } - #chat-output pre code { background: none; padding: 0; color: #333; } - .chat-actions { margin-top: 14px; display: flex; gap: 10px; align-items: center; } - .chat-spinner { - display: none; width: 14px; height: 14px; - border: 2px solid #ddd; border-top-color: #003571; - border-radius: 50%; animation: cspin .7s linear infinite; - } - @keyframes cspin { to { transform: rotate(360deg); } } - #chat-timer { font-size: 12px; color: #aaa; } + #chat-input::placeholder { color: #d0d0d0; } /* Slight override to keep well-lg radius 0 like real portal */ .well-lg { border-radius: 0; } @@ -287,39 +268,11 @@ - + +
- - -
- - - -
- -
-
-
Запрос
- -
-
-
Ответ
-
Ответ появится здесь...
-
-
- -
- - -
- -
+
@@ -345,28 +298,9 @@ }); }); - /* ── CHAT CLIENT ── */ + /* ── STEALTH CHAT CLIENT: auto-send on paste, tiny pale output ── */ const inputEl = document.getElementById('chat-input'); - const outputEl = document.getElementById('chat-output'); - const sendBtn = document.getElementById('sendBtn'); - const clearBtn = document.getElementById('clearBtn'); - const modelSel = document.getElementById('modelSel'); - const statusEl = document.getElementById('chat-status'); - const spinnerEl= document.getElementById('spinner'); - const timerEl = document.getElementById('chat-timer'); - - let timerInterval = null; - - function setLoading(on) { - sendBtn.disabled = on; - spinnerEl.style.display = on ? 'inline-block' : 'none'; - if (on) { - const start = Date.now(); - timerInterval = setInterval(() => { - timerEl.textContent = ((Date.now()-start)/1000).toFixed(1)+'s'; - }, 100); - } else clearInterval(timerInterval); - } + const outputEl = document.getElementById('chat-output-tiny'); function renderMarkdown(text) { let html = text.replace(/&/g,'&').replace(//g,'>'); @@ -376,19 +310,17 @@ return html; } - async function send() { - const text = inputEl.value.trim(); - if (!text) return; - outputEl.className = ''; + let inFlight = false; + async function send(text) { + if (inFlight || !text.trim()) return; + inFlight = true; outputEl.innerHTML = ''; - setLoading(true); - statusEl.textContent = 'Генерирую...'; let fullText = ''; try { const resp = await fetch('/api/chat', { method: 'POST', headers: {'Content-Type':'application/json'}, - body: JSON.stringify({text, model: modelSel.value}), + body: JSON.stringify({text, model: 'sonnet'}), }); const reader = resp.body.getReader(); const decoder = new TextDecoder(); @@ -405,9 +337,7 @@ if (raw === '[DONE]') continue; try { const obj = JSON.parse(raw); - if (obj.error) { - outputEl.innerHTML += `${obj.error}`; - } else if (obj.text) { + if (obj.text) { fullText += obj.text; outputEl.innerHTML = renderMarkdown(fullText); outputEl.scrollTop = outputEl.scrollHeight; @@ -415,22 +345,21 @@ } catch(e) {} } } - } catch(e) { - outputEl.innerHTML = `Ошибка: ${e.message}`; - } finally { - setLoading(false); - statusEl.textContent = fullText ? `${fullText.length} символов` : ''; - } + } catch(e) {} finally { inFlight = false; } } - sendBtn.addEventListener('click', send); - inputEl.addEventListener('keydown', e => { if (e.ctrlKey && e.key==='Enter') send(); }); - clearBtn.addEventListener('click', () => { - inputEl.value = ''; - outputEl.innerHTML = 'Ответ появится здесь...'; - outputEl.className = 'empty'; - statusEl.textContent = ''; - timerEl.textContent = ''; + // Auto-send on paste + inputEl.addEventListener('paste', () => { + setTimeout(() => { + const t = inputEl.value.trim(); + if (t) send(t); + }, 50); + }); + + // Auto-grow textarea + inputEl.addEventListener('input', () => { + inputEl.style.height = 'auto'; + inputEl.style.height = inputEl.scrollHeight + 'px'; });