Parcourir la source

#5406 AI Agents revamp: agents chat

Aleksandr Trofimov il y a 1 mois
Parent
commit
3c462e1b42
3 fichiers modifiés avec 40 ajouts et 43 suppressions
  1. 34 41
      src/chat/index.ts
  2. 3 1
      tailwind.config.full.js
  3. 3 1
      tailwind.config.js

+ 34 - 41
src/chat/index.ts

@@ -13,35 +13,21 @@ export interface ChatOptions {
 
 declare const sUrlRoot: string | undefined;
 
-const CHAT_STYLES = `
-.bx-ai-chat-root .bx-ai-chat{display:flex;flex-direction:column;gap:.75rem;height:100%;min-height:18rem}
-.bx-ai-chat-root .bx-ai-chat-messages{flex:1 1 auto;overflow-y:auto;display:flex;flex-direction:column;gap:.5rem;padding:.75rem;border:1px solid rgba(0,0,0,.08);border-radius:.5rem;background:rgba(255,255,255,.6)}
-.bx-ai-chat-root .bx-ai-chat-message{display:flex}
-.bx-ai-chat-root .bx-ai-chat-message-user{justify-content:flex-end}
-.bx-ai-chat-root .bx-ai-chat-message-assistant{justify-content:flex-start}
-.bx-ai-chat-root .bx-ai-chat-message-inner{max-width:85%;padding:.5rem .75rem;border-radius:.75rem;line-height:1.4;word-break:break-word}
-.bx-ai-chat-root .bx-ai-chat-message-user .bx-ai-chat-message-inner{background:#2563eb;color:#fff}
-.bx-ai-chat-root .bx-ai-chat-message-assistant .bx-ai-chat-message-inner{background:#f3f4f6;color:#111827}
-.bx-ai-chat-root .bx-ai-chat-form{display:flex;gap:.5rem}
-.bx-ai-chat-root .bx-ai-chat-input{flex:1 1 auto}
-.bx-ai-chat-root .bx-ai-chat-error{color:#b91c1c;font-size:.875rem}
-.bx-ai-chat-root.bx-ai-chat-loading .bx-ai-chat-send{opacity:.7}`;
-
-let stylesInjected = false;
-
-function injectStyles()
-{
-    if (stylesInjected || typeof document === 'undefined')
-        return;
-
-    const style = document.createElement('style');
-    style.setAttribute('data-bx-ai-chat', '1');
-    style.textContent = CHAT_STYLES;
-    document.head.appendChild(style);
-    stylesInjected = true;
-}
-
-function escapeHtml(text)
+const CLASSES = {
+    root: 'bx-ai-chat flex flex-col gap-3 h-full min-h-72 text-gray-800 dark:text-gray-200',
+    messages: 'bx-ai-chat-messages flex flex-1 flex-col gap-2 overflow-y-auto p-3 md:p-4 rounded-2xl ring-1 ring-gray-300/50 dark:ring-gray-600/50 bg-gray-50/80 dark:bg-gray-900/50',
+    form: 'bx-ai-chat-form flex items-center gap-2',
+    inputWrapper: 'bx-form-input-wrapper bx-form-input-wrapper-text flex-1 min-w-0',
+    input: 'bx-ai-chat-input bx-def-font-inputs bx-form-input-text',
+    send: 'bx-ai-chat-send bx-btn bx-btn-primary shrink-0',
+    messageUser: 'bx-ai-chat-message bx-ai-chat-message-user flex justify-end',
+    messageAssistant: 'bx-ai-chat-message bx-ai-chat-message-assistant flex justify-start',
+    bubbleUser: 'bx-ai-chat-message-inner max-w-[85%] px-3 py-2 rounded-2xl rounded-br-md text-sm leading-relaxed break-words bg-blue-600 text-white dark:bg-blue-500',
+    bubbleAssistant: 'bx-ai-chat-message-inner max-w-[85%] px-3 py-2 rounded-2xl rounded-bl-md text-sm leading-relaxed break-words bg-white text-gray-800 ring-1 ring-gray-200 dark:bg-gray-700 dark:text-gray-100 dark:ring-gray-600',
+    error: 'bx-ai-chat-error text-sm text-red-600 dark:text-red-400 px-1',
+};
+
+function escapeHtml(text: string)
 {
     return text
         .replace(/&/g, '&')
@@ -71,7 +57,10 @@ function renderMessages(container: HTMLElement, messages: UIMessage[]): void
         const role = message.role === 'user' ? 'user' : 'assistant';
         const text = escapeHtml(getMessageText(message)).replace(/\n/g, '<br>');
 
-        return `<div class="bx-ai-chat-message bx-ai-chat-message-${role}"><div class="bx-ai-chat-message-inner">${text || '&nbsp;'}</div></div>`;
+        const rowClass = role === 'user' ? CLASSES.messageUser : CLASSES.messageAssistant;
+        const bubbleClass = role === 'user' ? CLASSES.bubbleUser : CLASSES.bubbleAssistant;
+
+        return `<div class="${rowClass}"><div class="${bubbleClass}">${text || '&nbsp;'}</div></div>`;
     }).join('');
 
     list.scrollTop = list.scrollHeight;
@@ -79,14 +68,18 @@ function renderMessages(container: HTMLElement, messages: UIMessage[]): void
 
 function setLoading(container: HTMLElement, isLoading: boolean): void
 {
-    const input = container.querySelector('.bx-ai-chat-input');
-    const button = container.querySelector('.bx-ai-chat-send');
+    const input = container.querySelector<HTMLInputElement>('.bx-ai-chat-input');
+    const button = container.querySelector<HTMLButtonElement>('.bx-ai-chat-send');
 
     if (input)
         input.disabled = isLoading;
 
-    if (button)
+    if (button) {
         button.disabled = isLoading;
+        button.classList.toggle('bx-btn-disabled', isLoading);
+        button.classList.toggle('opacity-70', isLoading);
+        button.classList.toggle('cursor-wait', isLoading);
+    }
 
     container.classList.toggle('bx-ai-chat-loading', isLoading);
 }
@@ -94,11 +87,13 @@ function setLoading(container: HTMLElement, isLoading: boolean): void
 function buildMarkup(placeholder: string): string
 {
     return `
-<div class="bx-ai-chat">
-    <div class="bx-ai-chat-messages"></div>
-    <form class="bx-ai-chat-form">
-        <input type="text" class="bx-ai-chat-input bx-form-input" placeholder="${escapeHtml(placeholder)}" autocomplete="off" />
-        <button type="submit" class="bx-ai-chat-send bx-btn">Send</button>
+<div class="${CLASSES.root}">
+    <div class="${CLASSES.messages}"></div>
+    <form class="${CLASSES.form}">
+        <div class="${CLASSES.inputWrapper}">
+            <input type="text" class="${CLASSES.input}" placeholder="${escapeHtml(placeholder)}" autocomplete="off" />
+        </div>
+        <button type="submit" class="${CLASSES.send}">Send</button>
     </form>
 </div>`;
 }
@@ -128,8 +123,6 @@ export class Chat
         if (!el)
             throw new Error('Chat: container not found');
 
-        injectStyles();
-
         this._oContainer = el;
         this._oContainer.innerHTML = buildMarkup(options.placeholder || 'Type a message…');
         this._oContainer.classList.add('bx-ai-chat-root');
@@ -151,7 +144,7 @@ export class Chat
                     return;
 
                 const note = document.createElement('div');
-                note.className = 'bx-ai-chat-error';
+                note.className = CLASSES.error;
                 note.textContent = error.message;
                 list.appendChild(note);
                 list.scrollTop = list.scrollHeight;

+ 3 - 1
tailwind.config.full.js

@@ -17,7 +17,9 @@ module.exports = {
 
     './template/*.html',
     './template/scripts/*.php',
-    './template/scripts_templ/*.php'
+    './template/scripts_templ/*.php',
+
+    './src/**/*.ts'
   ],
   safelist: [
     {

+ 3 - 1
tailwind.config.js

@@ -23,7 +23,9 @@ module.exports = {
 
     './template/*.html',
     './template/scripts/*.php',
-    './template/scripts_templ/*.php'
+    './template/scripts_templ/*.php',
+
+    './src/**/*.ts'
   ],
   safelist: [
     'w-8', 'w-10', 'w-16', 'w-24', 'w-48', 'w-4/6',