diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index 5be0fe6..bc71ab4 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -283,6 +283,8 @@ const ChatScreen: React.FC = () => { if (message.type === 'chat') { const sender = (message.payload.sender as string) || ''; + const dbgText = ((message.payload.text as string) || '').slice(0, 60); + console.log('[Chat] chat-event sender=%s text=%s', sender || '(none)', dbgText); // STT-Ergebnis: Transkribierten Text in die Sprach-Bubble schreiben. // WICHTIG: Nur die ERSTE noch unaufgeloeste Aufnahme matchen — sonst @@ -295,6 +297,9 @@ const ChatScreen: React.FC = () => { const idx = prev.findIndex(m => m.sender === 'user' && m.text.includes('Spracheingabe wird verarbeitet') ); + console.log('[Chat] STT-Result: idx=%d text="%s" placeholders=%d', + idx, sttText.slice(0, 60), + prev.filter(m => m.sender === 'user' && m.text.includes('Spracheingabe wird verarbeitet')).length); const newText = `\uD83C\uDFA4 ${sttText}`; if (idx < 0) { // Defensiv: wenn keine Placeholder im State (z.B. weil sie nie diff --git a/diagnostic/index.html b/diagnostic/index.html index f1696cd..021a116 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -63,24 +63,35 @@ .log-entry.pipeline-sep { color: #333; margin: 6px 0 2px; } .chat-box { background: #080810; border: 1px solid #1E1E2E; border-radius: 6px; - min-height: 120px; max-height: 250px; overflow-y: auto; padding: 8px; margin-bottom: 8px; } - .chat-msg { margin-bottom: 6px; padding: 6px 10px; border-radius: 6px; font-size: 13px; line-height: 1.5; word-wrap: break-word; } - .chat-msg.sent { background: #0096FF; color: #fff; margin-left: 20%; text-align: right; } - .chat-msg.received { background: #1E1E2E; margin-right: 20%; } - .chat-msg.error { background: #3B1010; color: #FF6B6B; } - .chat-msg .meta { font-size: 10px; color: rgba(255,255,255,0.4); margin-top: 2px; } + min-height: 120px; max-height: 250px; overflow-y: auto; + padding: 12px; margin-bottom: 8px; display: flex; flex-direction: column; gap: 8px; } + .chat-msg { padding: 10px 14px; border-radius: 14px; font-size: 14px; line-height: 1.5; + word-wrap: break-word; max-width: 80%; white-space: pre-wrap; + box-shadow: 0 1px 2px rgba(0,0,0,0.4); } + .chat-msg.sent { background: #0096FF; color: #fff; align-self: flex-end; + border-bottom-right-radius: 4px; } + .chat-msg.received { background: #1E1E2E; color: #E8E8F0; align-self: flex-start; + border-bottom-left-radius: 4px; } + .chat-msg.error { background: #3B1010; color: #FF6B6B; align-self: flex-start; } + .chat-msg .meta { font-size: 10px; color: rgba(255,255,255,0.4); margin-top: 4px; + display: block; } .chat-msg a { color: #66BBFF; text-decoration: underline; } .chat-msg.sent a { color: #CCEEFF; } - .chat-msg .chat-media { max-width: 100%; max-height: 200px; border-radius: 4px; margin-top: 4px; cursor: pointer; display: block; } + .chat-msg .chat-media { max-width: 100%; max-height: 200px; border-radius: 8px; + margin-top: 6px; cursor: pointer; display: block; } .chat-msg .chat-media:hover { opacity: 0.85; } .lightbox-overlay { display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.92); z-index:2000; justify-content:center; align-items:center; cursor:pointer; } .lightbox-overlay.open { display:flex; } .lightbox-overlay img, .lightbox-overlay video { max-width:95vw; max-height:95vh; border-radius:8px; } - .input-row { display: flex; gap: 6px; } - .input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px; - padding: 8px 12px; color: #E0E0F0; font-family: inherit; font-size: 13px; } + .input-row { display: flex; gap: 6px; align-items: flex-end; } + .input-row input, .input-row textarea { + flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px; + padding: 8px 12px; color: #E0E0F0; font-family: inherit; font-size: 13px; + } + .input-row textarea { resize: none; min-height: 38px; max-height: 200px; line-height: 1.4; + overflow-y: auto; } /* Terminal Modal */ .modal-overlay { display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.85); @@ -282,7 +293,7 @@ 📎 - + @@ -300,7 +311,7 @@ 💭 ARIA denkt...
- +
@@ -2069,10 +2080,23 @@ return str.replace(/&/g,'&').replace(//g,'>'); } - // Enter-Taste sendet via Gateway - document.getElementById('chat-input').addEventListener('keydown', (e) => { - if (e.key === 'Enter') testRVS(); - }); + // Auto-Resize fuer Textarea — wuchst mit dem Inhalt bis zum max-height + function autoResizeTextarea(el) { + el.style.height = 'auto'; + el.style.height = Math.min(el.scrollHeight, 200) + 'px'; + } + + // Enter sendet, Shift+Enter macht neue Zeile (chat-Standard). + function chatInputKeydown(e, sendFn) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendFn(); + // Textarea zurueck auf 2 rows setzen + e.target.style.height = 'auto'; + } + } + document.getElementById('chat-input').addEventListener('keydown', (e) => chatInputKeydown(e, testRVS)); + document.getElementById('chat-input-fs').addEventListener('keydown', (e) => chatInputKeydown(e, testRVSFS)); // Escape schliesst Lightbox document.addEventListener('keydown', (e) => { diff --git a/diagnostic/server.js b/diagnostic/server.js index 59b1911..50b7485 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -716,11 +716,24 @@ function sendToRVS_withResponse(sendType, sendPayload, expectType, clientWs) { function sendToRVS_raw(msgObj) { if (!RVS_HOST || !RVS_TOKEN) return; + const payload = JSON.stringify(msgObj); + // Persistente Connection bevorzugen — die ist garantiert connected + // und wird vom RVS direkt an alle anderen Clients (App, Bridge) broadcastet. + // Frische Connections hatten Race-Probleme: die WS war nach dem send manchmal + // schon zu, bevor RVS broadcasten konnte → App-Nachrichten verloren. + if (rvsWs && rvsWs.readyState === WebSocket.OPEN) { + try { + rvsWs.send(payload); + return; + } catch (err) { + log("warn", "rvs", `persistente Verbindung send failed (${err.message}) — Fallback frische WS`); + } + } const proto = RVS_TLS === "true" ? "wss" : "ws"; const url = `${proto}://${RVS_HOST}:${RVS_PORT}?token=${RVS_TOKEN}`; const freshWs = new WebSocket(url); freshWs.on("open", () => { - freshWs.send(JSON.stringify(msgObj)); + freshWs.send(payload); setTimeout(() => { try { freshWs.close(); } catch (_) {} }, 5000); }); freshWs.on("error", () => {});