Просмотр исходного кода

add progress label to MessageCard

Yuki Takei 1 год назад
Родитель
Сommit
83c29bbd90

+ 2 - 1
apps/app/public/static/locales/en_US/translation.json

@@ -488,7 +488,8 @@
     "title": "Knowledge Assistant",
     "title_beta_label": "(Beta)",
     "placeholder": "Ask me anything.",
-    "caution_against_hallucination": "Please verify the information and check the sources."
+    "caution_against_hallucination": "Please verify the information and check the sources.",
+    "progress_label": "Generating answers"
   },
   "link_edit": {
     "edit_link": "Edit Link",

+ 2 - 1
apps/app/public/static/locales/fr_FR/translation.json

@@ -482,7 +482,8 @@
     "title": "Assistant de Connaissance",
     "title_beta_label": "(Bêta)",
     "placeholder": "Demandez-moi n'importe quoi.",
-    "caution_against_hallucination": "Veuillez vérifier les informations et consulter les sources."
+    "caution_against_hallucination": "Veuillez vérifier les informations et consulter les sources.",
+    "progress_label": "Génération des réponses"
   },
   "link_edit": {
     "edit_link": "Modifier lien",

+ 2 - 1
apps/app/public/static/locales/ja_JP/translation.json

@@ -521,7 +521,8 @@
     "title": "ナレッジアシスタント",
     "title_beta_label": "(ベータ)",
     "placeholder": "ききたいことを入力してください",
-    "caution_against_hallucination": "情報が正しいか出典を確認しましょう"
+    "caution_against_hallucination": "情報が正しいか出典を確認しましょう",
+    "progress_label": "回答を生成しています"
   },
   "link_edit": {
     "edit_link": "リンク編集",

+ 2 - 1
apps/app/public/static/locales/zh_CN/translation.json

@@ -477,7 +477,8 @@
     "title": "知识助手",
     "title_beta_label": "(测试版)",
     "placeholder": "问我任何问题。",
-    "caution_against_hallucination": "请核实信息并检查来源。"
+    "caution_against_hallucination": "请核实信息并检查来源。",
+    "progress_label": "生成答案中"
   },
   "link_edit": {
     "edit_link": "Edit Link",

+ 12 - 13
apps/app/src/features/openai/chat/components/AiChatModal/AiChatModal.tsx

@@ -70,10 +70,19 @@ const AiChatModalSubstance = (): JSX.Element => {
   const submit = useCallback(async(data: FormData) => {
     const { length: logLength } = messageLogs;
 
+    // add user message to the logs
+    const newUserMessage = { id: logLength.toString(), content: data.input, isUserMessage: true };
+    setMessageLogs(msgs => [...msgs, newUserMessage]);
+
+    // reset form
+    form.reset();
+
+    // add an empty assistant message
+    const newAssistantMessage = { id: (logLength + 1).toString(), content: '' };
+    setLastMessage(newAssistantMessage);
+
     // post message
     try {
-      form.clearErrors();
-
       const response = await fetch('/_api/v3/openai/message', {
         method: 'POST',
         headers: { 'Content-Type': 'application/json' },
@@ -87,20 +96,10 @@ const AiChatModalSubstance = (): JSX.Element => {
           const errors = resJson.errors.map(({ message }) => message).join(', ');
           form.setError('input', { type: 'manual', message: `[${response.status}] ${errors}` });
         }
+        setLastMessage(undefined);
         return;
       }
 
-      // add user message to the logs
-      const newUserMessage = { id: logLength.toString(), content: data.input, isUserMessage: true };
-      setMessageLogs(msgs => [...msgs, newUserMessage]);
-
-      // reset form
-      form.reset();
-
-      // add assistant message
-      const newAssistantMessage = { id: (logLength + 1).toString(), content: '' };
-      setLastMessage(newAssistantMessage);
-
       const reader = response.body?.getReader();
       const decoder = new TextDecoder('utf-8');
 

+ 33 - 0
apps/app/src/features/openai/chat/components/AiChatModal/MessageCard.module.scss

@@ -21,6 +21,39 @@
   }
 }
 
+// text animation
+// refs: https://web.dev/articles/speedy-css-tip-animated-gradient-text?hl=ja
+.assistant-message-card :global {
+  .text-thinking {
+    --bg-size: 400%;
+    --color-one: var(--bs-tertiary-color);
+    --color-two: var(--grw-highlight-300);
+    color: transparent;
+    background: linear-gradient(
+                  -90deg,
+                  var(--color-one),
+                  var(--color-two),
+                  var(--color-one)
+                ) 0 0 / var(--bg-size) 100%;
+    -webkit-background-clip: text;
+    background-clip: text;
+  }
+
+  @media (prefers-reduced-motion: no-preference) {
+    .text-thinking {
+      &:local {
+        animation: move-bg 6s linear infinite;
+      }
+    }
+    @keyframes move-bg {
+      from {
+        background-position: var(--bg-size) 0;
+      }
+    }
+  }
+}
+
+
  /*******************
  * UserMessageCard
  *******************/

+ 28 - 14
apps/app/src/features/openai/chat/components/AiChatModal/MessageCard.tsx

@@ -1,3 +1,4 @@
+import { useTranslation } from 'react-i18next';
 import ReactMarkdown from 'react-markdown';
 
 import styles from './MessageCard.module.scss';
@@ -7,35 +8,48 @@ const moduleClass = styles['message-card'] ?? '';
 
 const userMessageCardModuleClass = styles['user-message-card'] ?? '';
 
-const UserMessageCard = ({ children }: { children?: string }): JSX.Element => (
+const UserMessageCard = ({ children }: { children: string }): JSX.Element => (
   <div className={`card d-inline-flex align-self-end bg-success-subtle bg-info-subtle ${moduleClass} ${userMessageCardModuleClass}`}>
-    { children != null && children.length > 0 && (
-      <div className="card-body">
-        <ReactMarkdown>{children}</ReactMarkdown>
-      </div>
-    ) }
+    <div className="card-body">
+      <ReactMarkdown>{children}</ReactMarkdown>
+    </div>
   </div>
 );
 
 
 const assistantMessageCardModuleClass = styles['assistant-message-card'] ?? '';
 
-const AssistantMessageCard = ({ children }: { children?: string }): JSX.Element => (
-  <div className={`card border-0 ${moduleClass} ${assistantMessageCardModuleClass}`}>
-    { children != null && children.length > 0 && (
+const AssistantMessageCard = ({ children }: { children: string }): JSX.Element => {
+
+  const { t } = useTranslation();
+
+  return (
+    <div className={`card border-0 ${moduleClass} ${assistantMessageCardModuleClass}`}>
       <div className="card-body d-flex">
         <div className="me-2 me-lg-3">
           <span className="material-symbols-outlined grw-ai-icon rounded-pill p-1">psychology</span>
         </div>
-        <ReactMarkdown>{children}</ReactMarkdown>
+
+        <div className="mt-1">
+          { children.length > 0
+            ? (
+              <ReactMarkdown>{children}</ReactMarkdown>
+            )
+            : (
+              <span className="text-thinking">
+                {t('modal_aichat.progress_label')} <span className="material-symbols-outlined">more_horiz</span>
+              </span>
+            )
+          }
+        </div>
       </div>
-    ) }
-  </div>
-);
+    </div>
+  );
+};
 
 type Props = {
   role: 'user' | 'assistant',
-  children?: string,
+  children: string,
 }
 
 export const MessageCard = (props: Props): JSX.Element => {