@@ -4,9 +4,8 @@ import ArrowRightIcon from "@/components/icons/arrow-right";
44import Spinner from "@/components/spinner" ;
55import assert from "assert" ;
66import { useRouter } from "next/navigation" ;
7- import { useEffect , useRef , useTransition } from "react" ;
8- import TextareaAutosize from "react-textarea-autosize" ;
9- import { createMessage , getNextCompletionStreamPromise } from "../../actions" ;
7+ import { useEffect , useRef , useState , useTransition } from "react" ;
8+ import { createMessage } from "../../actions" ;
109import { type Chat } from "./page" ;
1110
1211export default function ChatBox ( {
@@ -23,6 +22,11 @@ export default function ChatBox({
2322 const disabled = isPending || isStreaming ;
2423 const didFocusOnce = useRef ( false ) ;
2524 const textareaRef = useRef < HTMLTextAreaElement > ( null ) ;
25+ const [ prompt , setPrompt ] = useState ( "" ) ;
26+ const textareaResizePrompt = prompt
27+ . split ( "\n" )
28+ . map ( ( text ) => ( text === "" ? "a" : text ) )
29+ . join ( "\n" ) ;
2630
2731 useEffect ( ( ) => {
2832 if ( ! textareaRef . current ) return ;
@@ -39,42 +43,60 @@ export default function ChatBox({
3943 < div className = "mx-auto mb-5 flex w-full max-w-prose shrink-0 px-8" >
4044 < form
4145 className = "relative flex w-full"
42- action = { async ( formData ) => {
46+ action = { async ( ) => {
4347 startTransition ( async ( ) => {
44- const prompt = formData . get ( "prompt" ) ;
45- assert . ok ( typeof prompt === "string" ) ;
46-
4748 const message = await createMessage ( chat . id , prompt , "user" ) ;
48- const { streamPromise } = await getNextCompletionStreamPromise (
49- message . id ,
50- chat . model ,
51- ) ;
52- onNewStreamPromise ( streamPromise ) ;
49+ const streamPromise = fetch (
50+ "/api/get-next-completion-stream-promise" ,
51+ {
52+ method : "POST" ,
53+ body : JSON . stringify ( {
54+ messageId : message . id ,
55+ model : chat . model ,
56+ } ) ,
57+ } ,
58+ ) . then ( ( res ) => {
59+ if ( ! res . body ) {
60+ throw new Error ( "No body on response" ) ;
61+ }
62+ return res . body ;
63+ } ) ;
5364
54- router . refresh ( ) ;
65+ onNewStreamPromise ( streamPromise ) ;
66+ startTransition ( ( ) => {
67+ router . refresh ( ) ;
68+ setPrompt ( "" ) ;
69+ } ) ;
5570 } ) ;
5671 } }
5772 >
5873 < fieldset className = "w-full" disabled = { disabled } >
5974 < div className = "relative flex rounded-lg border-4 border-gray-300 bg-white" >
60- < TextareaAutosize
61- ref = { textareaRef }
62- placeholder = "Follow up"
63- autoFocus = { ! disabled }
64- required
65- name = "prompt"
66- rows = { 2 }
67- minRows = { 2 }
68- className = "peer relative w-full resize-none bg-transparent p-2 placeholder-gray-500 focus:outline-none disabled:opacity-50"
69- onKeyDown = { ( event ) => {
70- if ( event . key === "Enter" && ! event . shiftKey ) {
71- event . preventDefault ( ) ;
72- const target = event . target ;
73- if ( ! ( target instanceof HTMLTextAreaElement ) ) return ;
74- target . closest ( "form" ) ?. requestSubmit ( ) ;
75- }
76- } }
77- />
75+ < div className = "relative w-full" >
76+ < div className = "w-full p-2" >
77+ < p className = "invisible min-h-[48px] w-full whitespace-pre-wrap" >
78+ { textareaResizePrompt }
79+ </ p >
80+ </ div >
81+ < textarea
82+ ref = { textareaRef }
83+ placeholder = "Follow up"
84+ autoFocus = { ! disabled }
85+ value = { prompt }
86+ onChange = { ( e ) => setPrompt ( e . target . value ) }
87+ required
88+ name = "prompt"
89+ className = "peer absolute inset-0 w-full resize-none bg-transparent p-2 placeholder-gray-500 focus:outline-none disabled:opacity-50"
90+ onKeyDown = { ( event ) => {
91+ if ( event . key === "Enter" && ! event . shiftKey ) {
92+ event . preventDefault ( ) ;
93+ const target = event . target ;
94+ if ( ! ( target instanceof HTMLTextAreaElement ) ) return ;
95+ target . closest ( "form" ) ?. requestSubmit ( ) ;
96+ }
97+ } }
98+ />
99+ </ div >
78100 < div className = "pointer-events-none absolute inset-0 rounded peer-focus:outline peer-focus:outline-offset-0 peer-focus:outline-blue-500" />
79101
80102 < div className = "absolute bottom-1.5 right-1.5 flex has-[:disabled]:opacity-50" >
0 commit comments