|
@@ -1,20 +1,23 @@
|
|
|
'use client'
|
|
|
|
|
|
-import React, { useEffect, useState } from "react"
|
|
|
+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 { BsFillSendFill } from "react-icons/bs";
|
|
|
+import { BsCapslockFill } from "react-icons/bs";
|
|
|
import * as R from 'ramda'
|
|
|
+import { BsFillStopFill } from "react-icons/bs";
|
|
|
+
|
|
|
|
|
|
const Form = ({ node, asideInstantAtom }) => {
|
|
|
const { data: session } = useSession()
|
|
|
const [asideInstant, dispatchAsideInstant] = useReducerAtom(asideInstantAtom, (prev, payload) => ({ ...prev, ...payload }))
|
|
|
- const [messages, setMessages] = useState(asideInstant?.messages ?? [])
|
|
|
+ const [messages, setMessages] = useState(asideInstant?.messages ?? [{ type: 'md', role: 'assistant', content: 'Hi~' }])
|
|
|
// const messageItem = useRef<unknown>(null)
|
|
|
+ const ctrlRef = useRef<AbortController>(null)
|
|
|
const [sessionName, setSessionName] = useState(asideInstant?.sessionName ?? uuid4())
|
|
|
|
|
|
const [input, setInput] = useState('')
|
|
@@ -43,6 +46,7 @@ const Form = ({ node, asideInstantAtom }) => {
|
|
|
// FIXME
|
|
|
userId: '1c9dc4b-d95f-11ea-af4c-52540005ab01'
|
|
|
})
|
|
|
+ ctrlRef.current = ctrl
|
|
|
for await (const chunk of chunks) {
|
|
|
message.content += chunk;
|
|
|
setMessages(() => [...newMessages])
|
|
@@ -60,12 +64,34 @@ const Form = ({ node, asideInstantAtom }) => {
|
|
|
await onSend({ text })
|
|
|
}
|
|
|
|
|
|
+ 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} />
|
|
|
- <button className="btn btn-active btn-primary btn-sm absolute right-[8px] bottom-[8px] w-[2rem] px-0" disabled={isSending} onClick={onCommit}><BsFillSendFill size={18} /></button>
|
|
|
+ <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>
|
|
|
)
|