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 @@
-
+
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',