Prechádzať zdrojové kódy

feat: chatSender comp

Carson 2 mesiacov pred
rodič
commit
a13c08ee82

+ 16 - 32
app/run-agent-flow/components/ASideType/Agent.tsx

@@ -4,11 +4,8 @@ import React, { useEffect, useRef, useState } from "react"
 import * as R from 'ramda'
 import markdownit from 'markdown-it'
 import { v4 as uuid4 } from 'uuid'
-import TextareaAutosize from 'react-textarea-autosize';
-import { BsCapslockFill, BsFillStopFill } from "react-icons/bs";
-
-
 import Chater from './Chater'
+import Sender from '@/components/ChatSender'
 import { useReducerAtom } from "jotai/utils"
 import { getChatResponse, template } from "@/lib/utils"
 import { useSession } from "next-auth/react"
@@ -37,13 +34,13 @@ const Agent = ({ node, asideInstantAtom }) => {
 
   const onSend = async ({ text, ignoreQuestionMessage = false }: { text: string, ignoreQuestionMessage?: boolean }) => {
     const assistantId = R.path(['properties', 'item', 'assistant_id'], node)
-    const newMessages = messages
+    const newMessages = []
     if (!ignoreQuestionMessage) {
       newMessages.push({ type: 'md', role: 'user', content: text })
     }
     const message = { type: 'md', role: 'assistant', content: '', isLoading: true }
     newMessages.push(message)
-    setMessages(() => [...newMessages])
+    setMessages((prev) => [...prev, ...newMessages])
     // messageItem.current = message
     const [chunks, ctrl] = getChatResponse({
       text,
@@ -56,10 +53,9 @@ const Agent = ({ node, asideInstantAtom }) => {
     ctrlRef.current = ctrl
     for await (const chunk of chunks) {
       message.content += chunk;
-      setMessages(() => [...newMessages])
+      setMessages((prev) => [...prev])
     }
     message.isLoading = false
-    setMessages(() => [...newMessages])
     setIsSending(false)
     return message
   }
@@ -92,10 +88,8 @@ const Agent = ({ node, asideInstantAtom }) => {
     dispatchCardInstantData({ content: markdownit().render(message.content) })
   }
 
-  let firstMark = true
   useEffect(() => {
-    if (!messages?.length && firstMark) {
-      firstMark = false
+    if (!messages?.length) {
       onFirstSend()
     }
   }, [])
@@ -123,32 +117,22 @@ const Agent = ({ node, asideInstantAtom }) => {
   const onStop = async () => {
     ctrlRef.current?.abort()
   }
-  const onKeyDown = (e) => {
-    if (e.key === 'Enter' && R.none(R.equals(true), R.props(['altKey', 'shiftKey', 'ctrlKey', 'metaKey'], e))) {
-      onCommit()
-      e.preventDefault()
-    }
+
+  const effectButtons = []
+  if (!isSending) {
+    effectButtons.push(
+      <button key="rerun" className='btn btn-xs' onClick={() => {
+        setMessages([])
+        onFirstSend()
+      }}>重新运行</button>,
+    )
   }
 
   return (
     <div className="w-full h-full flex relative">
       <Chater messages={messages} node={node} onAccept={onAccept}></Chater>
-      <div className="absolute flex inset-x-2.5 bottom-2.5 w-auto">
-        <TextareaAutosize className="textarea textarea-bordered pr-12 w-full resize-none" value={input} onChange={ev => setInput(ev.target.value)} maxRows={4} onKeyDown={onKeyDown} />
-        {
-          isSending
-            ? (
-              <button className="btn btn-secondary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" onClick={onStop}>
-                <BsFillStopFill size={18} />
-              </button>
-            )
-            : (
-              <button className="btn btn-primary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" disabled={!input} onClick={onCommit}>
-                <BsCapslockFill size={18} />
-              </button>
-            )
-        }
-      </div>
+
+      <Sender input={input} onInput={setInput} isSending={isSending} onStop={onStop} onCommit={onCommit} effectButtons={effectButtons} />
     </div>
   )
 }

+ 1 - 1
app/run-agent-flow/components/ASideType/Chater.tsx

@@ -23,7 +23,7 @@ const Chater = ({ messages, node, onAccept }: { messages: IMessages }) => {
 
 
   return (
-    <div className="flex flex-col-reverse w-full overflow-auto pb-[80px]">
+    <div className="flex flex-col-reverse w-full overflow-auto pb-[90px]">
       {
         reversedMessages.map((message, i) => (
           <div key={i} className={twMerge("chat ", message.role === 'assistant' ? "chat-start" : 'chat-end')}>

+ 6 - 26
app/run-agent-flow/components/ASideType/Form.tsx

@@ -4,12 +4,10 @@ import React, { useEffect, useRef, useState } from "react"
 import { v4 as uuid4 } from 'uuid'
 import { useReducerAtom } from "jotai/utils";
 import { useSession } from "next-auth/react";
-import TextareaAutosize from 'react-textarea-autosize';
 import Chater from "./Chater";
 import { getChatResponse } from "@/lib/utils";
-import { BsCapslockFill } from "react-icons/bs";
 import * as R from 'ramda'
-import { BsFillStopFill } from "react-icons/bs";
+import Sender from '@/components/ChatSender'
 
 
 const Form = ({ node, asideInstantAtom }) => {
@@ -67,33 +65,15 @@ const Form = ({ node, asideInstantAtom }) => {
   const onStop = async () => {
     ctrlRef.current?.abort()
   }
-  const onKeyDown = (e) => {
-    if (e.key === 'Enter' && R.none(R.equals(true), R.props(['altKey', 'shiftKey', 'ctrlKey', 'metaKey'], e))) {
-      onCommit()
-      e.preventDefault()
-    }
-  }
+
 
   return (
     <div className="w-full h-full flex relative">
       <Chater messages={messages} node={node}></Chater>
-      <div className="absolute flex inset-x-2.5 bottom-2.5 w-auto">
-        <TextareaAutosize className="textarea textarea-bordered pr-12 w-full resize-none" value={input} onChange={ev => setInput(ev.target.value)} maxRows={4} onKeyDown={onKeyDown} />
-        {
-          isSending
-            ? (
-              <button className="btn btn-secondary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" onClick={onStop}>
-                <BsFillStopFill size={18} />
-              </button>
-            )
-            : (
-              <button className="btn btn-primary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" disabled={!input} onClick={onCommit}>
-                <BsCapslockFill size={18} />
-              </button>
-            )
-        }
-      </div>
-    </div>
+
+      <Sender input={input} onInput={setInput} isSending={isSending} onStop={onStop} onCommit={onCommit} />
+
+    </div >
   )
 }
 

+ 47 - 0
components/ChatSender.tsx

@@ -0,0 +1,47 @@
+'use client';
+import TextareaAutosize from 'react-textarea-autosize';
+import { BsCapslockFill } from "react-icons/bs";
+import * as R from 'ramda'
+import { BsFillStopFill } from "react-icons/bs";
+import React, { useMemo } from 'react';
+
+const Sender = ({ input, onInput, isSending, onCommit, onStop, effectButtons = [] }) => {
+  const onKeyDown = (e) => {
+    if (e.key === 'Enter' && R.none(R.equals(true), R.props(['altKey', 'shiftKey', 'ctrlKey', 'metaKey'], e))) {
+      onCommit()
+      e.preventDefault()
+    }
+  }
+  const EffectElement = useMemo(() => {
+    if (effectButtons?.length) {
+      return (
+        <div className="flex justify-start gap-1">
+          {effectButtons}
+        </div>
+      )
+    }
+    return null
+  }, [effectButtons])
+  return (
+    <div
+      className="absolute flex flex-col gap-2 inset-x-2.5 bottom-2.5 w-auto"
+    >
+      {EffectElement}
+      <TextareaAutosize className="textarea textarea-bordered pr-12 w-full resize-none" value={input} onChange={ev => onInput(ev.target.value)} maxRows={4} onKeyDown={onKeyDown} />
+      {
+        isSending
+          ? (
+            <button className="btn btn-secondary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" onClick={onStop}>
+              <BsFillStopFill size={18} />
+            </button>
+          )
+          : (
+            <button className="btn btn-primary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" disabled={!input} onClick={onCommit}>
+              <BsCapslockFill size={18} />
+            </button>
+          )
+      }
+    </div>
+  )
+}
+export default React.memo(Sender)