From 4040b5f306e6b403eba8dd325897b415c09619de Mon Sep 17 00:00:00 2001 From: Server Deploy Date: Thu, 19 Mar 2026 23:16:17 +0100 Subject: [PATCH] Assistent: Layout-Fix, Auth-Fix, Session-Reaktivierung - CSS: Feste Hoehe, overflow hidden, Input-Bar immer sichtbar - Auth: Prueft username UND displayName gegen E-Mail und Name - Beendete Sessions werden automatisch reaktiviert beim Senden Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/css/assistant.css | 29 +++++++++++------- frontend/index.html | 18 +++++------ frontend/js/assistant.js | 62 ++++++++++++++++++++++++++++++++++---- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/frontend/css/assistant.css b/frontend/css/assistant.css index af598bf..7cf8a8c 100644 --- a/frontend/css/assistant.css +++ b/frontend/css/assistant.css @@ -5,10 +5,18 @@ */ /* Layout */ +.view.view-assistant { + height: calc(100vh - var(--header-height) - 52px); + overflow: hidden; + display: flex; + flex-direction: column; +} + .assistant-layout { display: grid; grid-template-columns: 280px 1fr; - height: calc(100vh - 120px); + flex: 1; + min-height: 0; overflow: hidden; } @@ -110,6 +118,7 @@ .assistant-chat { display: flex; flex-direction: column; + height: 100%; overflow: hidden; position: relative; background: var(--bg-main); @@ -201,6 +210,7 @@ /* Messages Area */ .assistant-messages { flex: 1; + min-height: 0; overflow-y: auto; padding: 20px; display: flex; @@ -365,10 +375,12 @@ /* Empty State */ .assistant-empty { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; text-align: center; color: var(--text-muted); pointer-events: none; @@ -391,12 +403,7 @@ } /* Hide empty state when messages present */ -.assistant-messages:not(:empty) ~ .assistant-empty { - display: none; -} - -/* Hide input bar when no session */ -.assistant-chat.no-session .assistant-input-bar { +.assistant-messages:not(:empty) + .assistant-empty { display: none; } diff --git a/frontend/index.html b/frontend/index.html index 0aebf85..0885bbd 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -714,15 +714,6 @@
-
- - -
@@ -732,6 +723,15 @@

Claude Assistent

Starte eine neue Session oder waehle eine bestehende aus.

+
+ + +
diff --git a/frontend/js/assistant.js b/frontend/js/assistant.js index ffd47a6..c26ac81 100644 --- a/frontend/js/assistant.js +++ b/frontend/js/assistant.js @@ -94,10 +94,12 @@ class AssistantManager { this.setStatus(data.status); - // If session ended, finalize streaming - if (data.status === 'ended' || data.status === 'stopped') { + // Finalize streaming when message processing completes + if (data.status === 'ended' || data.status === 'stopped' || data.status === 'active') { this.finalizeStreaming(); - this.loadSessions(); + if (data.status !== 'active') { + this.loadSessions(); + } } }); } @@ -178,7 +180,16 @@ class AssistantManager { if (session) { this.chatTitleEl.textContent = session.title; - this.setStatus(session.status === 'active' ? 'ended' : session.status); + + if (session.status === 'active') { + // Aktive Session mit Backend verbinden + const socket = syncManager.socket; + if (socket) { + socket.emit('assistant:start', { sessionId: id }); + } + } else { + this.setStatus(session.status); + } } this.renderSessionsList(); @@ -211,7 +222,7 @@ class AssistantManager { this.currentSessionId = session.id; this.chatTitleEl.textContent = session.title; this.messagesEl.innerHTML = ''; - this.setStatus('running'); + this.setStatus('active'); this.updateChatState(); // Start Claude process via Socket @@ -261,10 +272,22 @@ class AssistantManager { // MESSAGING // ===================== - sendMessage() { + async sendMessage() { const text = this.inputEl?.value?.trim(); if (!text || !this.currentSessionId) return; + // Beendete/gestoppte Session zuerst reaktivieren + if (this.sessionStatus === 'ended' || this.sessionStatus === 'stopped') { + try { + await this._reactivateSession(); + this.loadSessions(); + } catch (err) { + console.error('[Assistant] Reaktivierung fehlgeschlagen:', err); + this.showToast('Session konnte nicht reaktiviert werden', 'error'); + return; + } + } + // Render user message this.renderMessage('user', text); this.inputEl.value = ''; @@ -285,6 +308,32 @@ class AssistantManager { this.setStatus('thinking'); } + _reactivateSession() { + return new Promise((resolve, reject) => { + const socket = syncManager.socket; + if (!socket) return reject(new Error('Kein Socket')); + + const onStatus = (data) => { + if (data.sessionId !== this.currentSessionId) return; + socket.off('assistant:status', onStatus); + clearTimeout(timeout); + if (data.status === 'error') { + reject(new Error(data.error || 'Fehler')); + } else { + resolve(); + } + }; + + const timeout = setTimeout(() => { + socket.off('assistant:status', onStatus); + reject(new Error('Timeout')); + }, 10000); + + socket.on('assistant:status', onStatus); + socket.emit('assistant:start', { sessionId: this.currentSessionId }); + }); + } + handleOutput(content) { if (!this.streamingMessageEl) { // Create streaming bubble if not exists @@ -401,6 +450,7 @@ class AssistantManager { } const labels = { + active: 'Bereit', running: 'Aktiv', thinking: 'Denkt...', ended: 'Beendet',