Explorar o código

updated chat component to use socket only

Dr-Swopt hai 2 semanas
pai
achega
3a9007e9e9

+ 210 - 60
src/app/chat/chat.component.css

@@ -1,93 +1,243 @@
-.chat-wrapper {
+/* 1. Container Setup - Fixed to Viewport */
+.chat-container {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  /* Use 100% of the parent tab's height, not vh */
+  height: 100%;
+  width: 100%;
+  overflow: hidden;
+  background-color: #f0f2f5;
+}
+
+/* Ensure the cards take up exactly the height of the container */
+.chat-card,
+.agent-card {
+  display: flex !important;
+  flex-direction: column !important;
+  height: 100% !important;
+  min-height: 0;
+  border-radius: 0 !important;
+}
+
+.message-area,
+.log-area {
+  flex: 1;
+  overflow-y: auto !important;
+  min-height: 0;
+  /* Prevents content from pushing parent height */
+}
+
+.card-header {
+  flex-shrink: 0;
+  /* Header stays at the top */
+  padding: 16px 24px;
   display: flex;
-  width: 100vw;
-  height: 80vh; /* full viewport height minus some margin */
-  gap: 12px;
-  padding: 12px;
+  align-items: center;
+  justify-content: space-between;
+  height: 64px;
   box-sizing: border-box;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+/* 3. Left Panel: Chat */
+.chat-card {
+  background: #ffffff !important;
+  border-right: 1px solid #e0e0e0 !important;
 }
 
-.panel {
-  flex: 1; /* each panel takes equal width */
+/* This is the magic part that fixes your scroll issue */
+.message-area {
+  flex: 1 1 auto;
+  /* Grow and shrink as needed */
+  overflow-y: auto !important;
+  /* Only this part scrolls */
+  min-height: 0;
+  padding: 24px !important;
   display: flex;
   flex-direction: column;
+  gap: 16px;
+  background-color: #ffffff;
+}
+
+.input-section {
+  flex-shrink: 0;
+  /* Input stays at the bottom */
+  padding: 16px 24px !important;
+  border-top: 1px solid #f0f0f0;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin: 0 !important;
+  /* Material actions sometimes have weird margins */
+}
+
+/* 4. Right Panel: Trace & Logic */
+.agent-card {
+  background: #1e1e1e !important;
+  color: #d4d4d4;
+}
+
+.agent-card .card-header {
+  background: #252526;
+  border-bottom: 1px solid #333;
+}
+
+/* --- Rest of your visual styles remain the same --- */
+
+.message-bubble {
+  max-width: 80%;
+  padding: 12px 16px;
   border-radius: 12px;
-  box-shadow: 0 6px 16px rgba(0,0,0,0.15);
-  background-color: #fafafa;
-  overflow: hidden;
+  font-size: 0.95rem;
+  line-height: 1.5;
+}
+
+.message-bubble.user {
+  align-self: flex-end;
+  background-color: #007bff;
+  color: #ffffff;
+  border-bottom-right-radius: 2px;
 }
 
-mat-card-title {
-  font-size: 1.4rem;
+.message-bubble.bot {
+  align-self: flex-start;
+  background-color: #f1f3f4;
+  color: #202124;
+  border-bottom-left-radius: 2px;
+}
+
+.timestamp {
+  font-size: 0.7rem;
+  margin-top: 6px;
+  opacity: 0.6;
   font-weight: 600;
-  text-align: center;
-  padding: 12px 0;
-  border-bottom: 1px solid #ddd;
+  text-transform: uppercase;
 }
 
-mat-card-content {
+.input-section mat-form-field {
   flex: 1;
-  overflow-y: auto;
-  padding: 12px;
 }
 
-/* Chat messages */
-.messages {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
+/* Log Entry Container */
+.log-entry {
+  border-bottom: 1px solid #333;
+  padding: 16px 20px;
+  background: #1e1e1e;
 }
 
-.message {
-  padding: 10px 16px;
-  border-radius: 20px;
-  max-width: 80%;
-  word-wrap: break-word;
+/* Individual Sections (Input vs Message) */
+.log-section {
+  margin-top: 12px;
+}
+
+/* Metadata Labels (INPUT, MESSAGE) */
+.log-section .label {
+  display: block;
+  font-family: 'Segoe UI', sans-serif;
+  font-size: 0.65rem;
+  font-weight: 800;
+  color: #888;
+  margin-bottom: 4px;
+  letter-spacing: 1px;
+}
+
+/* Code block for raw input data */
+.code-block {
+  margin: 0;
+  font-family: 'Fira Code', 'Consolas', monospace;
+  font-size: 0.8rem;
+  background: #121212;
+  color: #9cdcfe;
+  padding: 8px;
+  border-radius: 4px;
+  border: 1px solid #333;
+  overflow-x: auto;
   white-space: pre-wrap;
+  /* Wraps long JSON strings */
+}
+
+/* Text block for human-readable messages */
+.message-text {
+  font-family: 'Segoe UI', sans-serif;
+  font-size: 0.9rem;
   line-height: 1.4;
+  color: #d4d4d4;
+  padding-left: 4px;
 }
 
-.user {
-  background-color: #1976d2;
-  color: white;
-  align-self: flex-end;
-  text-align: right;
+/* Logic Status Pilling */
+.status-pill.completed {
+  background: #2e7d32;
+  color: #fff;
 }
 
-.bot {
-  background-color: #e0e0e0;
-  color: #333;
-  align-self: flex-start;
-  text-align: left;
+.status-pill.processing {
+  background: #e65100;
+  color: #fff;
 }
 
-.bot-typing {
-  font-style: italic;
-  color: #666;
+.status-pill.error {
+  background: #c62828;
+  color: #fff;
 }
 
-/* Chat input */
-.chat-input {
-  display: flex;
-  gap: 10px;
+.node-badge {
+  font-family: 'Roboto Mono', monospace;
+  font-size: 0.75rem;
+  color: #4ec9b0;
+  background: rgba(78, 201, 176, 0.1);
+  padding: 4px 8px;
+  border-radius: 4px;
+}
+
+.log-body {
+  margin-top: 10px;
+  font-family: 'Fira Code', monospace;
+  font-size: 0.85rem;
+  color: #9cdcfe;
+  background: #121212;
   padding: 12px;
-  border-top: 1px solid #ddd;
-  background-color: #f5f5f5;
+  border-radius: 6px;
+  white-space: pre-wrap;
 }
 
-.chat-input mat-form-field {
-  flex: 1;
-  margin: 0;
+.typing-indicator {
+  display: flex;
+  gap: 4px;
+  padding: 8px;
 }
 
-/* JSON output pre blocks */
-pre {
-  white-space: pre-wrap;
-  word-break: break-word;
-  font-size: 0.85rem;
-  background-color: #f5f5f5;
-  padding: 10px;
-  border-radius: 8px;
-  max-height: 100%;
-  overflow: auto;
+.typing-indicator span {
+  width: 6px;
+  height: 6px;
+  background: #90a4ae;
+  border-radius: 50%;
+  animation: pulse 1.5s infinite;
+}
+
+@keyframes pulse {
+
+  0%,
+  100% {
+    transform: translateY(0);
+    opacity: 0.4;
+  }
+
+  50% {
+    transform: translateY(-4px);
+    opacity: 1;
+  }
+}
+
+.log-area::-webkit-scrollbar {
+  width: 8px;
+}
+
+.log-area::-webkit-scrollbar-track {
+  background: #1e1e1e;
 }
+
+.log-area::-webkit-scrollbar-thumb {
+  background: #333;
+  border-radius: 4px;
+}

+ 47 - 28
src/app/chat/chat.component.html

@@ -1,37 +1,56 @@
-<div class="chat-wrapper">
-  <!-- Chat panel -->
-  <mat-card class="panel chat-panel">
-    <mat-card-title>Chat</mat-card-title>
-    <mat-card-content>
-      <div class="messages">
-        <div *ngFor="let msg of messages" [ngClass]="['message', msg.sender]">
-          <strong>{{ msg.sender === 'user' ? 'You' : 'Bot' }}:</strong>
-          <span>{{ msg.content }}</span>
-        </div>
+<div class="chat-container">
+  <mat-card class="chat-card">
+    <div class="card-header">
+      <h2>Chat</h2>
+      <span class="status-indicator" [class.active]="loading"></span>
+    </div>
+
+    <mat-card-content class="message-area">
+      <div *ngFor="let msg of messages" [ngClass]="['message-bubble', msg.sender]">
+        <div class="content">{{ msg.content }}</div>
+        <div class="timestamp">{{ msg.sender === 'user' ? 'You' : 'Assistant' }}</div>
+      </div>
+
+      <div *ngIf="loading" class="typing-indicator">
+        <span></span><span></span><span></span>
       </div>
-      <div *ngIf="loading" class="bot-typing">Bot is typing...</div>
     </mat-card-content>
-    <mat-card-actions class="chat-input">
-      <mat-form-field appearance="outline">
-        <input matInput placeholder="Type a message" [(ngModel)]="inputMessage" (keyup.enter)="sendMessage()" />
+
+    <mat-card-actions class="input-section">
+      <mat-form-field appearance="outline" subscriptSizing="dynamic">
+        <input matInput placeholder="Ask anything..." [(ngModel)]="inputMessage" (keyup.enter)="sendMessage()" />
       </mat-form-field>
-      <button mat-raised-button color="primary" (click)="sendMessage()">Send</button>
+      <button mat-flat-button color="primary" (click)="sendMessage()" [disabled]="!inputMessage.trim()">
+        Send
+      </button>
     </mat-card-actions>
   </mat-card>
 
-  <!-- Planner output panel -->
-  <mat-card class="panel planner-panel">
-    <mat-card-title>Planner Output</mat-card-title>
-    <mat-card-content>
-      <pre>{{ plannerOutput | json }}</pre>
-    </mat-card-content>
-  </mat-card>
+  <mat-card class="agent-card">
+    <div class="card-header">
+      <h2>Trace & Logic</h2>
+    </div>
+    <mat-card-content class="log-area">
+      <div *ngFor="let thought of agentThoughts" class="log-entry">
+        <div class="log-header">
+          <span class="node-badge">{{ thought.node }}</span>
+          <span class="status-pill" [ngClass]="thought.status">{{ thought.status }}</span>
+        </div>
+
+        <div class="log-body">
+          <div class="log-section">
+            <span class="label">INPUT</span>
+            <pre class="code-block">{{ thought.input ? (thought.input | json) : 'N/A' }}</pre>
+          </div>
 
-  <!-- Executor output panel -->
-  <mat-card class="panel executor-panel">
-    <mat-card-title>Executor Output</mat-card-title>
-    <mat-card-content>
-      <pre>{{ executorOutput | json }}</pre>
+          <div class="log-section">
+            <span class="label">MESSAGE</span>
+            <div class="message-text">
+              {{ thought.message || 'Processing node output...' }}
+            </div>
+          </div>
+        </div>
+      </div>
     </mat-card-content>
   </mat-card>
-</div>
+</div>

+ 26 - 23
src/app/chat/chat.component.ts

@@ -1,13 +1,13 @@
 import { Component } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { HttpClient, HttpClientModule } from '@angular/common/http';
 import { MatCardModule } from '@angular/material/card';
 import { MatInputModule } from '@angular/material/input';
 import { MatButtonModule } from '@angular/material/button';
 import { MatListModule } from '@angular/material/list';
 import { webConfig } from '../config';
 import { io, Socket } from 'socket.io-client';
+
 interface ChatMessage {
   content: string;
   sender: 'user' | 'bot';
@@ -16,7 +16,7 @@ interface ChatMessage {
 @Component({
   selector: 'app-chat',
   standalone: true,
-  imports: [CommonModule, FormsModule, HttpClientModule, MatCardModule, MatInputModule, MatButtonModule, MatListModule],
+  imports: [CommonModule, FormsModule, MatCardModule, MatInputModule, MatButtonModule, MatListModule],
   templateUrl: './chat.component.html',
   styleUrls: ['./chat.component.css']
 })
@@ -25,23 +25,33 @@ export class ChatComponent {
   inputMessage: string = '';
   loading = false;
 
-  plannerOutput: any = null;
-  executorOutput: any = null;
+  agentThoughts: any[] = [];
 
   private socket: Socket;
 
-  constructor(private http: HttpClient) {
+  constructor() {
     // Connect to FFB socket namespace
     this.socket = io(`${webConfig.exposedUrl}/ffb`);
 
-    // Listen for planner output
-    this.socket.on('planner.output', (payload) => {
-      this.plannerOutput = payload;
+    this.socket.on('connect', () => {
+      console.log('Connected to FFB Gateway');
+    });
+
+    this.socket.on('disconnect', () => {
+      console.log('Disconnected from FFB Gateway');
     });
 
-    // Listen for executor events
-    this.socket.on('executor.result', (payload) => {
-      this.executorOutput = { ...this.executorOutput, pipeline: payload.pipeline, results: payload.results, count: payload.count };
+    // Listen for agent output
+    this.socket.on('agent_thought', (payload) => {
+      console.log('Received agent thought:', payload);
+      this.agentThoughts.push(payload);
+    });
+
+    // Listen for chat response
+    this.socket.on('chat_response', (payload: { message: string }) => {
+      console.log('Received chat response:', payload);
+      this.messages.push({ content: payload.message, sender: 'bot' });
+      this.loading = false;
     });
 
     // Optional: handle errors
@@ -60,17 +70,10 @@ export class ChatComponent {
     this.inputMessage = '';
     this.loading = true;
 
-    this.http.post<{ answer: string }>(`${webConfig.exposedUrl}/api/ffb-production/query`, { message: messageToSend })
-      .subscribe({
-        next: (res) => {
-          this.messages.push({ content: res.answer, sender: 'bot' });
-          this.loading = false;
-        },
-        error: (err) => {
-          console.error(err);
-          this.messages.push({ content: 'Error: Could not get response.', sender: 'bot' });
-          this.loading = false;
-        }
-      });
+    // Clear previous agent thoughts
+    this.agentThoughts = [];
+
+    // Emit chat event via socket
+    this.socket.emit('chat', { message: messageToSend });
   }
 }

+ 30 - 0
src/app/dashboard/dashboard.component.css

@@ -2,6 +2,7 @@
 button {
   margin-top: 16px;
 }
+
 .header {
   display: flex;
   justify-content: flex-end;
@@ -22,3 +23,32 @@ button {
 .logout-button:hover {
   background-color: #b71c1c;
 }
+
+/* Force the parent to not scroll */
+:host {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  overflow: hidden;
+}
+
+/* Make the tab group take up all remaining space below toolbar */
+mat-tab-group {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+/* IMPORTANT: Angular Material puts tab content inside a specific 
+   wrapper class. We must force that wrapper to be full height.
+*/
+::ng-deep .mat-mdc-tab-body-wrapper {
+  flex: 1 !important;
+  display: flex !important;
+  flex-direction: column !important;
+}
+
+::ng-deep .mat-mdc-tab-body {
+  height: 100% !important;
+}