Преглед на файлове

modification du chatRoom

WebUI
maela преди 6 дни
родител
ревизия
28475a8324
променени са 5 файла, в които са добавени 1077 реда и са изтрити 705 реда
  1. +2
    -3
      WebUI/ReAct_PME.WebUI/Layout/NavMenu.razor
  2. +140
    -71
      WebUI/ReAct_PME.WebUI/Pages/ChatRoom/ChatRoom_base.razor
  3. +88
    -27
      WebUI/ReAct_PME.WebUI/Pages/ChatRoom/ChatRoom_base.razor.cs
  4. +294
    -260
      WebUI/ReAct_PME.WebUI/Pages/ChatRoom/ChatRoom_base.razor.css
  5. +553
    -344
      WebUI/ReAct_PME.WebUI/wwwroot/css/app.css

+ 2
- 3
WebUI/ReAct_PME.WebUI/Layout/NavMenu.razor Целия файл

@@ -187,7 +187,7 @@

protected override async Task OnInitializedAsync()
{
var id = AuthService.ID;

await LoadChatConversations();

@@ -288,8 +288,7 @@

private void OnSelectConversation(string conversationId, int typeLLM)
{
CurrentConversationId = conversationId;
Navigation.NavigateTo($"chatroom_nav/{typeLLM}?conversationId={conversationId}");
Navigation.NavigateTo($"chatroom_base/{typeLLM}/{conversationId}", true);
}

private void NavigateTo(string route)

+ 140
- 71
WebUI/ReAct_PME.WebUI/Pages/ChatRoom/ChatRoom_base.razor Целия файл

@@ -1,98 +1,167 @@
@page "/chatroom_base/{typellm:int}"
@page "/chatroom_base/{typellm:int}/{conversationid}"

<div class="chatroom-container">
<!-- Zone de messages -->
<div class="chatroom-messages">
@if (SelectedConversation == null || !Messages.Any())
{
<div class="chatroom-empty">
<div class="empty-icon">💬</div>
<h3>Commencez une conversation</h3>
<p>Posez une question ou envoyez un message pour démarrer</p>
</div>
}
else
{
@foreach (var msg in Messages)
{
<div class="message-row @(msg.IsUser ? "user" : "assistant")">
<div class="avatar @(msg.IsUser ? "avatar-user" : "avatar-assistant")">
@if (msg.IsUser)
{
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0" />
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1" />
</svg>
}
else
{
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
</svg>
}
</div>

<!-- Liste des conversations -->
<div class="conversation-list">
<button class="btn btn-primary" @onclick="CreateConversation">
Nouvelle conversation
</button>
<div class="message-content-wrapper">
<div class="bubble @(msg.IsUser ? "bubble-user" : "bubble-assistant")">
@msg.Text
</div>

<ul>
@foreach (var conv in Conversations)
@if (!msg.IsUser)
{
<div class="message-actions">
<button class="btn-message-action" @onclick="() => CopyMessage(msg.Text)" title="Copier">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1z" />
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0z" />
</svg>
</button>
</div>
}
</div>
</div>
}

@if (isTyping)
{
<li @onclick="() => SelectConversation(conv.Id)"
class="@(conv.Id == SelectedConversationId ? "selected" : "")">
@conv.Title
</li>
<div class="message-row assistant">
<div class="avatar avatar-assistant">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
</svg>
</div>
<div class="message-content-wrapper">
<div class="bubble bubble-assistant">
<div class="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
}
</ul>
}
</div>

<!-- Zone principale -->
<div class="chat-area">
@if (SelectedConversation != null)
<!-- Zone de saisie moderne -->
<div class="chat-input-container">
<!-- Fichiers attachés -->
@if (UploadedDocuments.Any())
{
<h3>@SelectedConversation.Title</h3>

<div class="messages">
@foreach (var msg in Messages)
<div class="attached-files">
@foreach (var doc in UploadedDocuments)
{
<div class="message-row @(msg.IsUser ? "user" : "assistant")">
<div class="avatar @(msg.IsUser ? "avatar-user" : "avatar-assistant")">
@(msg.IsUser ? "👤" : "🤖")
<div class="file-item">
<div class="file-preview">
<div class="file-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5z" />
</svg>
</div>
</div>

<div class="bubble @(msg.IsUser ? "bubble-user" : "bubble-assistant")">
@msg.Text
<div class="file-info">
<span class="file-name">@doc.FileName</span>
<span class="file-size">@FormatFileSize(doc.Bytes.Length)</span>
</div>
<button class="remove-file-button" @onclick="() => RemoveDocument(doc)" aria-label="Supprimer le fichier">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z" />
</svg>
</button>
</div>
}
</div>
}

<div class="chat-input">
<textarea @bind="CurrentInput"></textarea>

<div class="options">
<select @bind="SelectedMode" disabled>
<option value="llm">LLM</option>
<option value="rag">RAG</option>
<option value="code">Code</option>
<option value="image">Image</option>
</select>
<!-- Input wrapper -->
<div class="input-wrapper @(UploadedDocuments.Any() ? "has-files" : "")">
<textarea @bind="CurrentInput"
@bind:event="oninput"
@onkeydown="HandleKeyPress"
placeholder="Nouvelle conversation..."
rows="1"></textarea>

<select @bind="SelectedModel" hidden>
@foreach (var model in OllamaModels)
{
<option value="@model">@model</option>
}
</select>
<div class="actions-row">
<!-- Bouton attachement -->
<label class="attach-button" for="fileUpload">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M4.5 3a2.5 2.5 0 0 1 5 0v9a1.5 1.5 0 0 1-3 0V5a.5.5 0 0 1 1 0v7a.5.5 0 0 0 1 0V3a1.5 1.5 0 1 0-3 0v9a2.5 2.5 0 0 0 5 0V5a.5.5 0 0 1 1 0v7a3.5 3.5 0 1 1-7 0z" />
</svg>
<span>Importer un fichier</span>
<InputFile id="fileUpload" OnChange="UploadDocuments" multiple style="display: none;" />
</label>

<label for="SelectedDomain" class="col-form-label" hidden="@hiddenDomain">Domaine RAG :</label>
<select @bind="SelectedDomain" hidden="@hiddenDomain">
<option value="">(Aucun domaine)</option>
@foreach (var d in RagDomains)
{
<option value="@d">@d</option>
}
</select>

<label for="IsWithHistorique" class="col-form-label">Inclure l'historique ? :</label>
<input id="IsWithHistorique" @bind="IsWithHistorique" type="checkbox" class="form-check-input" />
</div>
<div class="document-upload">
<InputFile OnChange="UploadDocuments" multiple />

@if (UploadedDocuments.Any())
<div class="right-actions">
<!-- Sélecteur de domaine RAG -->
@if (!hiddenDomain && RagDomains.Any())
{
<div class="doc-list">
@foreach (var doc in UploadedDocuments)
<select class="domain-select" @bind="SelectedDomain">
@foreach (var domain in RagDomains)
{
<div class="doc-item">
<span>@doc.FileName (@(doc.Bytes.Length / 1024) Ko)</span>
<button class="btn btn-sm btn-danger" @onclick="() => RemoveDocument(doc)">X</button>
</div>
<option value="@domain">@domain</option>
}
</div>
</select>
}

<!-- Bouton historique -->
<button class="history-button @(IsWithHistorique ? "active" : "desactive")" @onclick="ToggleHistory" title="@(IsWithHistorique ? "Historique activé" : "Historique désactivé")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
<path d="M8.515 1.019A7 7 0 0 0 8 1V0a8 8 0 0 1 .589.022zm2.004.45a7 7 0 0 0-.985-.299l.219-.976q.576.129 1.126.342zm1.37.71a7 7 0 0 0-.439-.27l.493-.87a8 8 0 0 1 .979.654l-.615.789a7 7 0 0 0-.418-.302zm1.834 1.79a7 7 0 0 0-.653-.796l.724-.69q.406.429.747.91zm.744 1.352a7 7 0 0 0-.214-.468l.893-.45a8 8 0 0 1 .45 1.088l-.95.313a7 7 0 0 0-.179-.483m.53 2.507a7 7 0 0 0-.1-1.025l.985-.17q.1.58.116 1.17zm-.131 1.538q.05-.254.081-.51l.993.123a8 8 0 0 1-.23 1.155l-.964-.267q.069-.247.12-.501m-.952 2.379q.276-.436.486-.908l.914.405q-.24.54-.555 1.038zm-.964 1.205q.183-.183.35-.378l.758.653a8 8 0 0 1-.401.432z" />
<path d="M8 1a7 7 0 1 0 4.95 11.95l.707.707A8.001 8.001 0 1 1 8 0z" />
<path d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5" />
</svg>
</button>
<button class="voice-button" @onclick="RecordVoice" aria-label="Message vocal">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5m-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5m12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5" />
</svg>
</button>

<!-- Bouton envoyer -->
<button class="send-button"
@onclick="SendMessage"
disabled="@(string.IsNullOrWhiteSpace(CurrentInput) && !UploadedDocuments.Any())"
aria-label="Envoyer le message">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5" />
</svg>
</button>
</div>
<button class="btn btn-primary" @onclick="SendMessage">Envoyer</button>
</div>
}
else
{
<p>Sélectionne ou crée une conversation.</p>
}
</div>
</div>
</div>


+ 88
- 27
WebUI/ReAct_PME.WebUI/Pages/ChatRoom/ChatRoom_base.razor.cs Целия файл

@@ -2,6 +2,8 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web; // ← AJOUT pour KeyboardEventArgs
using Microsoft.JSInterop;
using ReAct_PME.Domain;
using ReAct_PME.WebUI.ServicesUI;
using System.Net.Http.Json;
@@ -10,9 +12,11 @@ using System.Text.Json;

namespace ReAct_PME.WebUI.Pages.ChatRoom
{
public partial class ChatRoom_base: ComponentBase
public partial class ChatRoom_base : ComponentBase
{
[Parameter] public int TypeLLM { get; set; }
[Parameter] public string? ConversationId { get; set; }


[Inject] public HttpClient Http { get; set; } = default!;
[Inject] private ReAct_PME.WebUI.ServicesUI.AuthService AuthService { get; set; } = default!;
@@ -20,6 +24,8 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
[Inject] private AuthenticationStateProvider AuthenticationStateProvider { get; set; } = default!;
[Inject] private NavigationManager Navigation { get; set; } = default!;
[Inject] private IToastService ToastService { get; set; } = default!;
[Inject] private IJSRuntime JSRuntime { get; set; } = default!;


public class UploadedDoc
{
@@ -27,12 +33,14 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
public byte[] Bytes { get; set; } = Array.Empty<byte>();
}
public List<UploadedDoc> UploadedDocuments { get; set; } = new();

// État de l'UI
public List<ConversationDto> Conversations { get; set; } = new();
public ConversationDto? SelectedConversation { get; set; }
public string SelectedConversationId { get; set; } = "";
public List<MessageDto> Messages { get; set; } = new();
private bool IsWithHistorique { get; set; }
private bool isTyping { get; set; } = false;

public string CurrentInput { get; set; } = "";

@@ -47,7 +55,7 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom

protected override async Task OnInitializedAsync()
{
#region Vérification si utilisateur est connecté et s''il a les droits
#region Vérification si utilisateur est connecté et s'il a les droits

var tok = await PagesInitializer.VerifConnexionAndRules(AuthenticationStateProvider, Navigation, Http, ToastService, "/api/ChatRoom/rag/listeDomaines");
if (tok == null || tok.Length == 0)
@@ -65,9 +73,9 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
if (TypeLLM == 1)
{
SelectedMode = "llm";
IsWithHistorique= true;
IsWithHistorique = true;
}
else if(TypeLLM == 2)
else if (TypeLLM == 2)
{
SelectedMode = "rag";
IsWithHistorique = false;
@@ -85,13 +93,23 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
SelectedMode = "image";
IsWithHistorique = false;
}
else
else
{
SelectedMode = "llm";
IsWithHistorique = true;
}


var uri = new Uri(Navigation.Uri);
if (!string.IsNullOrEmpty(ConversationId))
{
SelectConversation(ConversationId);
}
else
{
Console.WriteLine("No conversationId in URL");
// IsNewConversation reste false par défaut
}
}

private async Task UploadDocuments(InputFileChangeEventArgs e)
@@ -111,7 +129,7 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom

StateHasChanged();
}
private void RemoveDocument(UploadedDoc doc)
{
UploadedDocuments.Remove(doc);
@@ -161,9 +179,9 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
{
SelectedConversationId = id;

SelectedConversation = Conversations.Where(c => c.Id == id).FirstOrDefault();
SelectedConversation = Conversations.Where(c => c.Id == id).FirstOrDefault();

Messages = SelectedConversation?.Messages.OrderBy(m=>m.CreatedAt).ToList() ?? new();
Messages = SelectedConversation?.Messages.OrderBy(m => m.CreatedAt).ToList() ?? new();

StateHasChanged();
}
@@ -175,6 +193,9 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
if (string.IsNullOrWhiteSpace(CurrentInput) || SelectedConversation == null)
return;

isTyping = true;
StateHasChanged();

// Ajoute ton message dans l'UI immédiatement
var userMsg = new MessageDto
{
@@ -199,22 +220,12 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom

};



CurrentInput = "";

/*
var response = await Http.PostAsJsonAsync("/api/ChatRoom/sendMessage", payload);
if (response.IsSuccessStatusCode)
{
var assistantMsg = await response.Content.ReadFromJsonAsync<MessageDto>();
if (assistantMsg != null)
{
Messages.Add(assistantMsg);
}
}
*/
var assistantMsg = await ApiService.EnvoiRequete<MessageDto>("/api/ChatRoom/sendMessage", payload);

isTyping = false;

if (assistantMsg != null)
{
Messages.Add(assistantMsg);
@@ -228,16 +239,66 @@ namespace ReAct_PME.WebUI.Pages.ChatRoom
}
}



UploadedDocuments.Clear();
StateHasChanged();
}
catch (Exception ex)
{
catch (Exception ex)
{
Console.WriteLine(ex.Message);
isTyping = false;
StateHasChanged();
}
}


private async Task HandleKeyPress(KeyboardEventArgs e)
{
if (e.Key == "Enter" && !e.ShiftKey)
{
await SendMessage();
}
}

private void GoBack()
{
Navigation.NavigateTo("/");
}

private void ToggleHistory()
{
IsWithHistorique = !IsWithHistorique;
ToastService.ShowInfo(IsWithHistorique ? "Historique activé" : "Historique désactivé");
}

private async Task CopyMessage(string text)
{
try
{
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
ToastService.ShowSuccess("Message copié !");
}
catch (Exception)
{
// Fallback si clipboard API n'est pas disponible
ToastService.ShowWarning("Impossible de copier le message");
}
}
private void RecordVoice()
{
ToastService.ShowInfo("Fonctionnalité d'enregistrement vocal en cours de développement");
}

private string FormatFileSize(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
double len = bytes;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}
return $"{len:0.##} {sizes[order]}";
}
}
}


+ 294
- 260
WebUI/ReAct_PME.WebUI/Pages/ChatRoom/ChatRoom_base.razor.css Целия файл

@@ -1,105 +1,36 @@
/* ========================================
CHATROOM - AI DASHBOARD
======================================== */


.chatroom-container {
display: flex;
flex-direction: column;
height: 100vh;
background: var(--ai-bg, #eaf8ff);
}

/* ========================================
HEADER
======================================== */

.chatroom-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: white;
border-bottom: 1px solid var(--ai-border, #e0e0e0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.chatroom-header-left {
display: flex;
align-items: center;
gap: 1rem;
}

.chatroom-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--ai-text-dark, #243b5d);
margin: 0;
}

.chatroom-header-right {
display: flex;
align-items: center;
gap: 1rem;
height: 100%;
background: var(--bg-light);
}

.domain-selector {
.chatroom-messages {
flex: 1;
overflow-y: auto;
padding: 2rem;
display: flex;
align-items: center;
gap: 0.5rem;
flex-direction: column;
gap: 1.5rem;
}

.domain-selector label {
font-size: 0.9rem;
color: var(--ai-text-light, #6c757d);
margin: 0;
.chatroom-messages::-webkit-scrollbar {
width: 8px;
}

.domain-selector select {
padding: 0.5rem 2rem 0.5rem 0.75rem;
border: 1px solid var(--ai-border, #e0e0e0);
border-radius: 8px;
font-size: 0.9rem;
background: white;
cursor: pointer;
min-width: 150px;
.chatroom-messages::-webkit-scrollbar-track {
background: transparent;
}

.btn-icon {
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: transparent;
color: var(--ai-text-dark, #243b5d);
font-size: 1.1rem;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}

.btn-icon:hover {
background: var(--ai-bg, #eaf8ff);
.chatroom-messages::-webkit-scrollbar-thumb {
background: var(--secondary);
border-radius: 4px;
}

.btn-back:hover {
background: var(--ai-primary, #243b5d);
color: white;
}

/* ========================================
MESSAGES AREA
======================================== */

.chatroom-messages {
flex: 1;
overflow-y: auto;
padding: 2rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.chatroom-messages::-webkit-scrollbar-thumb:hover {
background: var(--primary);
}

.chatroom-empty {
display: flex;
@@ -107,20 +38,20 @@
align-items: center;
justify-content: center;
height: 100%;
color: var(--ai-text-light, #6c757d);
color: var(--text-light);
text-align: center;
}

.empty-icon {
font-size: 4rem;
color: var(--ai-secondary, #2984a1);
color: var(--secondary);
margin-bottom: 1rem;
opacity: 0.5;
}

.chatroom-empty h3 {
font-size: 1.5rem;
color: var(--ai-text-dark, #243b5d);
color: var(--text-dark);
margin-bottom: 0.5rem;
}

@@ -159,7 +90,6 @@
justify-content: flex-start;
}

/* Avatars */
.avatar {
width: 40px;
height: 40px;
@@ -167,21 +97,19 @@
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
flex-shrink: 0;
}

.avatar-user {
background: linear-gradient(135deg, var(--ai-secondary, #2984a1) 0%, var(--ai-primary, #243b5d) 100%);
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
color: white;
}

.avatar-assistant {
background: linear-gradient(135deg, var(--ai-accent, #f63e63) 0%, #e02851 100%);
background: linear-gradient(135deg, var(--accent) 0%, #e02851 100%);
color: white;
}

/* Message content */
.message-content-wrapper {
display: flex;
flex-direction: column;
@@ -207,19 +135,18 @@
}

.bubble-user {
background: linear-gradient(135deg, var(--ai-secondary, #2984a1) 0%, var(--ai-primary, #243b5d) 100%);
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
color: white;
border-bottom-right-radius: 4px;
}

.bubble-assistant {
background: white;
color: var(--ai-text-dark, #243b5d);
color: var(--text-dark);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-bottom-left-radius: 4px;
}

/* Message actions */
.message-actions {
display: flex;
gap: 0.5rem;
@@ -231,21 +158,19 @@
border-radius: 6px;
border: none;
background: transparent;
color: var(--ai-text-light, #6c757d);
color: var(--text-light);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
transition: all 0.2s ease;
}

.btn-message-action:hover {
background: var(--ai-bg, #eaf8ff);
color: var(--ai-primary, #243b5d);
background: var(--bg-light);
color: var(--primary);
}

/* Typing indicator */
.typing-indicator {
display: flex;
gap: 0.4rem;
@@ -256,7 +181,7 @@
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--ai-secondary, #2984a1);
background-color: var(--secondary);
animation: typing 1.4s infinite;
}

@@ -280,235 +205,355 @@
}
}

/* ========================================
UPLOADED DOCUMENTS PREVIEW
======================================== */

.uploaded-docs-preview {
.chat-input-container {
width: 100%;
padding: 16px 24px;
background: transparent;
display: flex;
gap: 0.75rem;
padding: 0.75rem 2rem;
background: white;
border-top: 1px solid var(--ai-border, #e0e0e0);
overflow-x: auto;
flex-direction: column;
justify-content: center;
position: relative;
}

.doc-preview-item {
/* Fichiers attachés */
.attached-files {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 12px 16px;
max-width: 1000px;
margin: 0 auto;
width: 100%;
}

.file-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
background: var(--ai-bg, #eaf8ff);
gap: 8px;
padding: 8px 12px;
background: #ffffff;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 0.9rem;
white-space: nowrap;
max-width: 32%;
transition: all 0.2s ease;
animation: fileSlideIn 0.3s ease;
}

.doc-preview-item i {
color: var(--ai-secondary, #2984a1);
font-size: 1rem;
.file-item:hover {
border-color: #d1d5db;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.btn-remove-doc {
width: 20px;
height: 20px;
border-radius: 50%;
border: none;
background: rgba(0, 0, 0, 0.1);
color: var(--ai-text-dark, #243b5d);
cursor: pointer;
.file-preview {
flex-shrink: 0;
width: 40px;
height: 40px;
border-radius: 6px;
overflow: hidden;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
border: 1px solid var(--border);
}

.file-icon {
font-size: 20px;
color: var(--secondary);
}

.file-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}

.file-name {
font-size: 13px;
font-weight: 500;
color: #374151;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.file-size {
font-size: 11px;
color: #9ca3af;
}

.remove-file-button {
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
border-radius: 4px;
color: #9ca3af;
cursor: pointer;
transition: all 0.2s ease;
padding: 0;
}

.btn-remove-doc:hover {
background: var(--ai-accent, #f63e63);
color: white;
.remove-file-button:hover {
background: #fee2e2;
color: #ef4444;
}

/* ========================================
INPUT AREA
======================================== */
@keyframes fileSlideIn {
from {
opacity: 0;
transform: translateY(-10px);
}

.chatroom-input-container {
padding: 1.5rem 2rem;
background: white;
border-top: 1px solid var(--ai-border, #e0e0e0);
to {
opacity: 1;
transform: translateY(0);
}
}

.chatroom-input-wrapper {
max-width: 1200px;
/* Input wrapper */
.input-wrapper {
width: 100%;
max-width: 1000px;
margin: 0 auto;
background: var(--ai-bg, #eaf8ff);
background: #ffffff;
border: 1px solid var(--border);
border-radius: 16px;
padding: 1rem 1.25rem;
padding: 16px 20px;
display: flex;
flex-direction: column;
gap: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
}

.chatroom-input {
width: 100%;
border: none;
outline: none;
font-size: 1rem;
padding: 0.5rem 0;
background: transparent;
color: var(--ai-text-dark, #243b5d);
font-family: 'Montserrat', sans-serif;
margin-bottom: 0.75rem;
resize: none;
min-height: 24px;
max-height: 150px;
}
.input-wrapper.has-files {
border-radius: 0 0 16px 16px;
border-top: none;
padding-top: 12px;
}

.chatroom-input::placeholder {
color: #a0a0a0;
.input-wrapper:focus-within {
border-color: var(--secondary);
box-shadow: 0 4px 12px rgba(41, 132, 161, 0.12);
}

.chatroom-input:disabled {
opacity: 0.6;
cursor: not-allowed;
.input-wrapper textarea {
width: 100%;
border: none;
outline: none;
resize: none;
font-size: 15px;
font-family: inherit;
color: #1f2937;
background: transparent;
min-height: 24px;
max-height: 200px;
line-height: 1.5;
padding: 0;
}

.chatroom-input-actions {
.input-wrapper textarea::placeholder {
color: #9ca3af;
}

.input-wrapper textarea:disabled {
opacity: 0.6;
cursor: not-allowed;
}

/* Actions row */
.actions-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding-top: 4px;
}

.btn-chat-action {
.attach-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 8px;
border: none;
gap: 8px;
padding: 0;
background: transparent;
color: var(--ai-text-light, #6c757d);
border: none;
color: #6b7280;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s ease;
font-family: 'Montserrat', sans-serif;
font-size: 13px;
font-weight: 500;
transition: color 0.2s ease;
}

.btn-chat-action:hover:not(:disabled) {
background: white;
color: var(--ai-primary, #243b5d);
.attach-button:hover {
color: var(--secondary);
}

.btn-chat-action:disabled {
opacity: 0.5;
cursor: not-allowed;
.attach-button span {
display: none;
}

.btn-chat-label {
display: none;
}

@media (min-width: 768px) {
.btn-chat-label {
@media (min-width: 640px) {
.attach-button span {
display: inline;
}
}

.btn-chat-submit {
width: 40px;
height: 40px;
border-radius: 50%;
.right-actions {
display: flex;
align-items: center;
gap: 8px;
}

.domain-select {
padding: 6px 12px;
border: 1px solid var(--border);
border-radius: 8px;
background: white;
color: var(--text-dark);
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
}

.domain-select:hover {
border-color: var(--secondary);
}

.domain-select:focus {
outline: none;
border-color: var(--secondary);
box-shadow: 0 0 0 3px rgba(41, 132, 161, 0.1);
}

.voice-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: transparent;
border: none;
background: var(--ai-accent, #f63e63);
color: white;
color: #6b7280;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;
}

.voice-button:hover {
background: #f3f4f6;
color: var(--text-dark);
}




.send-button {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
transition: all 0.3s ease;
flex-shrink: 0;
width: 36px;
height: 36px;
background: var(--accent);
border: none;
color: #ffffff;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;
}

.btn-chat-submit:hover:not(:disabled) {
background: #e02851;
.send-button:hover:not(:disabled) {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(246, 62, 99, 0.3);
}

.btn-chat-submit:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
.send-button:active:not(:disabled) {
transform: scale(0.98);
}

/* ========================================
SCROLLBAR
======================================== */
.send-button:disabled {
background: #e5e7eb;
cursor: not-allowed;
opacity: 0.6;
}

.chatroom-messages::-webkit-scrollbar {
width: 8px;
}
.send-button:disabled svg {
color: #9ca3af;
}

.chatroom-messages::-webkit-scrollbar-track {
background: transparent;
}

.chatroom-messages::-webkit-scrollbar-thumb {
background: var(--ai-secondary, #2984a1);
border-radius: 4px;
}
@media (max-width: 768px) {
.chatroom-messages {
padding: 1rem;
}

.chatroom-messages::-webkit-scrollbar-thumb:hover {
background: var(--ai-primary, #243b5d);
.message-content-wrapper {
max-width: 85%;
}

.uploaded-docs-preview::-webkit-scrollbar {
height: 6px;
}
.chat-input-container {
padding: 12px 16px;
}

.uploaded-docs-preview::-webkit-scrollbar-track {
background: transparent;
}
.attached-files {
padding: 10px 12px;
gap: 6px;
}

.uploaded-docs-preview::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.file-item {
padding: 6px 10px;
max-width: 200px;
}

/* ========================================
RESPONSIVE
======================================== */
.file-preview {
width: 36px;
height: 36px;
}

@media (max-width: 768px) {
.chatroom-header {
padding: 1rem;
.file-name {
font-size: 12px;
}

.chatroom-title {
font-size: 1.25rem;
.file-size {
font-size: 10px;
}

.chatroom-messages {
padding: 1rem;
.remove-file-button {
width: 20px;
height: 20px;
}

.message-content-wrapper {
max-width: 85%;
.input-wrapper {
padding: 12px 16px;
border-radius: 14px;
}

.chatroom-input-container {
padding: 1rem;
.input-wrapper.has-files {
border-radius: 0 0 14px 14px;
}

.input-wrapper textarea {
font-size: 14px;
}

.actions-row .right-actions {
gap: 6px;
}

.chatroom-input-wrapper {
padding: 0.75rem 1rem;
.voice-button,
.send-button {
width: 32px;
height: 32px;
}

.domain-selector {
flex-direction: column;
align-items: flex-start;
gap: 0.25rem;
.domain-select {
font-size: 12px;
padding: 4px 8px;
}
}

@@ -521,16 +566,5 @@
.avatar {
width: 36px;
height: 36px;
font-size: 1.1rem;
}

.chatroom-header-right {
gap: 0.5rem;
}

.btn-icon {
width: 36px;
height: 36px;
font-size: 1rem;
}
}

+ 553
- 344
WebUI/ReAct_PME.WebUI/wwwroot/css/app.css Целия файл

@@ -1664,7 +1664,35 @@ details[open] .nav-section-content {
align-items: center;
gap: 8px;
}
.history-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: transparent;
border: none;
color: #6b7280;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;
}

.history-button:hover {
background: #f3f4f6;
color: var(--text-dark);
}

.active {
transform:scale(1.05);
background-color:var(--main-color);
color:white;

}

.desactive {
transform: scale(0.95);
}
.voice-button {
display: flex;
align-items: center;
@@ -1919,376 +1947,312 @@ details[open] .nav-section-content {



.chatroom-container {
display: flex;
flex-direction: column;
height: 100%;
background: var(--bg-light);
}

.chat-input-container {
width: 100%;
padding: 16px 24px;
background: transparent;
.chatroom-messages {
flex: 1;
overflow-y: auto;
padding: 2rem;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
gap: 1.5rem;
}

.attached-files {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 12px 16px;
max-width: 1000px;
margin: 0 auto;
width: 100%;
.chatroom-messages::-webkit-scrollbar {
width: 8px;
}

.file-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 8px;
max-width: 32%;
transition: all 0.2s ease;

&:hover {
border-color: #d1d5db;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.file-preview {
flex-shrink: 0;
width: 40px;
height: 40px;
border-radius: 6px;
overflow: hidden;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #e5e7eb;

img {
width: 100%;
height: 100%;
object-fit: cover;
}

.file-icon {
font-size: 20px;
}
}

.file-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;

.file-name {
font-size: 13px;
font-weight: 500;
color: #374151;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.file-size {
font-size: 11px;
color: #9ca3af;
}
}

.remove-file-button {
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
border-radius: 4px;
color: #9ca3af;
cursor: pointer;
transition: all 0.2s ease;

&:hover {
background: #ffffff;
color: #ef4444;
}

fa-icon {
font-size: 14px;
}
}
}
.chatroom-messages::-webkit-scrollbar-track {
background: transparent;
}

.input-wrapper {
width: 100%;
max-width: 1000px;
margin: 0 auto;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 16px;
padding: 16px 20px;
display: flex;
flex-direction: column;
gap: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;

&.has-files {
border-radius: 0 0 16px 16px;
border-top: none;
padding-top: 12px;
}
.chatroom-messages::-webkit-scrollbar-thumb {
background: var(--secondary);
border-radius: 4px;
}

&:focus-within {
border-color: #3498db;
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.12);
.chatroom-messages::-webkit-scrollbar-thumb:hover {
background: var(--primary);
}

textarea {
width: 100%;
border: none;
outline: none;
resize: none;
font-size: 15px;
font-family: inherit;
color: #1f2937;
background: transparent;
min-height: 24px;
max-height: 200px;
line-height: 1.5;
padding: 0;

&::placeholder {
color: #9ca3af;
}

&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.chatroom-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: var(--text-light);
text-align: center;
}

.actions-row {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 4px;

.attach-button {
display: flex;
align-items: center;
gap: 8px;
padding: 0;
background: transparent;
border: none;
color: #6b7280;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: color 0.2s ease;

&:hover {
color: #3498db;
}

fa-icon {
font-size: 16px;
}

span {
@media (max-width: 480px) {
display: none;
}
}
}

.right-actions {
display: flex;
align-items: center;
gap: 8px;

.voice-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: transparent;
border: none;
color: #6b7280;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;

&:hover {
background: #f3f4f6;
color: #1f2937;
}

svg {
width: 18px;
height: 18px;
}
}

.send-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: #f63e63;
border: none;
color: #ffffff;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;

&:hover:not(:disabled) {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(246, 62, 99, 0.3);
}

&:active:not(:disabled) {
transform: scale(0.98);
}

&:disabled {
background: #e5e7eb;
cursor: not-allowed;
opacity: 0.6;

svg {
color: #9ca3af;
}
}

svg {
width: 16px;
height: 16px;
}
}
}
}
.empty-icon {
font-size: 4rem;
color: var(--secondary);
margin-bottom: 1rem;
opacity: 0.5;
}

.chatroom-empty h3 {
font-size: 1.5rem;
color: var(--text-dark);
margin-bottom: 0.5rem;
}

.chatroom-empty p {
font-size: 1rem;
margin: 0;
}

/* Messages */
.message-row {
display: flex;
align-items: flex-start;
gap: 0.75rem;
animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}

input[type="file"] {
display: none;
to {
opacity: 1;
transform: translateY(0);
}
}

.chat-input-container.drag-over {
&::before {
content: 'Déposez vos fichiers ici';
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border: 2px dashed #3498db;
border-radius: 16px;
background: rgba(52, 152, 219, 0.05);
color: #3498db;
font-size: 16px;
font-weight: 600;
pointer-events: none;
z-index: 10;
}
.message-row.user {
flex-direction: row-reverse;
justify-content: flex-start;
}

.message-row.assistant {
flex-direction: row;
justify-content: flex-start;
}

.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}

.avatar-user {
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
color: white;
}

.avatar-assistant {
background: linear-gradient(135deg, var(--accent) 0%, #e02851 100%);
color: white;
}

.message-content-wrapper {
display: flex;
flex-direction: column;
max-width: 70%;
gap: 0.5rem;
}

.message-row.user .message-content-wrapper {
align-items: flex-end;
}

.message-row.assistant .message-content-wrapper {
align-items: flex-start;
}

.bubble {
padding: 1rem 1.25rem;
border-radius: 16px;
font-size: 1rem;
line-height: 1.6;
word-wrap: break-word;
white-space: pre-wrap;
}

.bubble-user {
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
color: white;
border-bottom-right-radius: 4px;
}

.bubble-assistant {
background: white;
color: var(--text-dark);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-bottom-left-radius: 4px;
}

.message-actions {
display: flex;
gap: 0.5rem;
}

.btn-message-action {
width: 32px;
height: 32px;
border-radius: 6px;
border: none;
background: transparent;
color: var(--text-light);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}

.btn-message-action:hover {
background: var(--bg-light);
color: var(--primary);
}

// Responsive
@media (max-width: 768px) {
.chat-input-container {
padding: 12px 16px;
.typing-indicator {
display: flex;
gap: 0.4rem;
padding: 0.5rem 0;
}

.typing-indicator span {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--secondary);
animation: typing 1.4s infinite;
}

.attached-files {
padding: 10px 12px;
border-radius: 10px 10px 0 0;
gap: 6px;

.file-item {
padding: 6px 10px;
max-width: 200px;

.file-preview {
width: 36px;
height: 36px;
}

.file-info {
.file-name {
font-size: 12px;
}

.file-size {
font-size: 10px;
}
}

.remove-file-button {
width: 20px;
height: 20px;

fa-icon {
font-size: 12px;
}
}
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}

.input-wrapper {
padding: 12px 16px;
border-radius: 14px;

&.has-files {
border-radius: 0 0 14px 14px;
}

textarea {
font-size: 14px;
}

.actions-row {
.attach-button {
font-size: 12px;

fa-icon {
font-size: 14px;
}
}

.right-actions {
gap: 6px;

.voice-button,
.send-button {
width: 32px;
height: 32px;

svg {
width: 15px;
height: 15px;
}
}
}
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}

@keyframes typing {
0%, 60%, 100% {
transform: translateY(0);
opacity: 0.7;
}

30% {
transform: translateY(-10px);
opacity: 1;
}
}

// Auto-expand textarea
@media (min-width: 769px) {
.chat-input-container .input-wrapper textarea {
overflow-y: auto;

.chat-input-container {
width: 100%;
padding: 16px 24px;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
position: fixed;
}

/* Fichiers attachés */
.attached-files {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 12px 16px;
max-width: 1000px;
margin: 0 auto;
width: 100%;
}

.file-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: #ffffff;
border: 1px solid var(--border);
border-radius: 8px;
max-width: 32%;
transition: all 0.2s ease;
animation: fileSlideIn 0.3s ease;
}

.file-item:hover {
border-color: #d1d5db;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.file-preview {
flex-shrink: 0;
width: 40px;
height: 40px;
border-radius: 6px;
overflow: hidden;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
}

.file-icon {
font-size: 20px;
color: var(--secondary);
}

.file-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}

.file-name {
font-size: 13px;
font-weight: 500;
color: #374151;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.file-size {
font-size: 11px;
color: #9ca3af;
}

.remove-file-button {
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
border-radius: 4px;
color: #9ca3af;
cursor: pointer;
transition: all 0.2s ease;
}

.remove-file-button:hover {
background: #fee2e2;
color: #ef4444;
}

@keyframes fileSlideIn {
from {
opacity: 0;
@@ -2301,10 +2265,255 @@ details[open] .nav-section-content {
}
}

.attached-files .file-item {
animation: fileSlideIn 0.3s ease;
/* Input wrapper */
.input-wrapper {
width: 100%;
max-width: 1000px;
margin: 0 auto;
background: #ffffff;
border: 1px solid var(--border);
border-radius: 16px;
padding: 16px 20px;
display: flex;
flex-direction: column;
gap: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
}

.input-wrapper.has-files {
border-radius: 0 0 16px 16px;
border-top: none;
padding-top: 12px;
}

.input-wrapper:focus-within {
border-color: var(--secondary);
box-shadow: 0 4px 12px rgba(41, 132, 161, 0.12);
}

.input-wrapper textarea {
width: 100%;
border: none;
outline: none;
resize: none;
font-size: 15px;
font-family: inherit;
color: #1f2937;
background: transparent;
min-height: 24px;
max-height: 200px;
line-height: 1.5;
padding: 0;
}

.input-wrapper textarea::placeholder {
color: #9ca3af;
}

.input-wrapper textarea:disabled {
opacity: 0.6;
cursor: not-allowed;
}

/* Actions row */
.actions-row {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 4px;
}

.attach-button {
display: flex;
align-items: center;
gap: 8px;
padding: 0;
background: transparent;
border: none;
color: #6b7280;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: color 0.2s ease;
}

.attach-button:hover {
color: var(--secondary);
}

.attach-button span {
display: none;
}

@media (min-width: 640px) {
.attach-button span {
display: inline;
}
}

.right-actions {
display: flex;
align-items: center;
gap: 8px;
}

.domain-select {
padding: 6px 12px;
border: 1px solid var(--border);
border-radius: 8px;
background: white;
color: var(--text-dark);
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
}

.domain-select:hover {
border-color: var(--secondary);
}

.domain-select:focus {
outline: none;
border-color: var(--secondary);
box-shadow: 0 0 0 3px rgba(41, 132, 161, 0.1);
}

.voice-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: transparent;
border: none;
color: #6b7280;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;
}

.voice-button:hover {
background: #f3f4f6;
color: var(--text-dark);
}

.send-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: var(--accent);
border: none;
color: #ffffff;
cursor: pointer;
border-radius: 8px;
transition: all 0.2s ease;
}

.send-button:hover:not(:disabled) {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(246, 62, 99, 0.3);
}

.send-button:active:not(:disabled) {
transform: scale(0.98);
}

.send-button:disabled {
background: #e5e7eb;
cursor: not-allowed;
opacity: 0.6;
}

.send-button:disabled svg {
color: #9ca3af;
}



@media (max-width: 768px) {
.chatroom-messages {
padding: 1rem;
}

.message-content-wrapper {
max-width: 85%;
}

.chat-input-container {
padding: 12px 16px;
}

.attached-files {
padding: 10px 12px;
gap: 6px;
}

.file-item {
padding: 6px 10px;
max-width: 200px;
}

.file-preview {
width: 36px;
height: 36px;
}

.file-name {
font-size: 12px;
}

.file-size {
font-size: 10px;
}

.remove-file-button {
width: 20px;
height: 20px;
}

.input-wrapper {
padding: 12px 16px;
border-radius: 14px;
}

.input-wrapper.has-files {
border-radius: 0 0 14px 14px;
}

.input-wrapper textarea {
font-size: 14px;
}

.actions-row .right-actions {
gap: 6px;
}

.voice-button,
.send-button {
width: 32px;
height: 32px;
}

.domain-select {
font-size: 12px;
padding: 4px 8px;
}
}

@media (max-width: 480px) {
.bubble {
padding: 0.875rem 1rem;
font-size: 0.95rem;
}

.avatar {
width: 36px;
height: 36px;
}
}




Loading…
Отказ
Запис