22
33import { ChatAreaStateUpdater } from "@/(docs)/chatAreaState" ;
44import { useStreamingChatContext } from "@/(docs)/streamingChatContext" ;
5+ import { useSendChat } from "@/(docs)/useSendChat" ;
56import { deleteChatAction } from "@/actions/deleteChat" ;
67import { ChatWithMessages } from "@/lib/chatHistory" ;
7- import { LangId , MarkdownSection , PageSlug } from "@/lib/docs" ;
8+ import { DynamicMarkdownSection , LangId , MarkdownSection , PageSlug } from "@/lib/docs" ;
89import { Heading } from "@/markdown/heading" ;
910import { StyledMarkdown } from "@/markdown/markdown" ;
1011import { usePagesListForLang } from "@/pagesListContext" ;
@@ -74,9 +75,17 @@ interface Props {
7475 langId : LangId ;
7576 pageSlug : PageSlug ;
7677 targetSection : MarkdownSection | undefined ;
78+ priorSectionContent : DynamicMarkdownSection [ ] ;
7779}
7880export function ChatAreaContent ( props : Props ) {
79- const { chatId, chatData, langId, pageSlug, targetSection } = props ;
81+ const {
82+ chatId,
83+ chatData,
84+ langId,
85+ pageSlug,
86+ targetSection,
87+ priorSectionContent,
88+ } = props ;
8089
8190 const langEntry = usePagesListForLang ( langId ) ;
8291 const pageEntry = langEntry ?. pages . find ( ( p ) => p . slug === pageSlug ) ;
@@ -92,6 +101,23 @@ export function ChatAreaContent(props: Props) {
92101 const router = useRouter ( ) ;
93102 const streamingChatContext = useStreamingChatContext ( ) ;
94103 const isStreamingThis = streamingChatContext . chatId === chatId ;
104+ const { sendChat, isLoading : isRegenerating } = useSendChat ( ) ;
105+
106+ const handleRegenerate = async ( ) => {
107+ if ( ! confirm ( "このチャットを削除して再生成してもよろしいですか?" ) ) {
108+ return ;
109+ }
110+ const firstUserMsg = chatData . messages . find ( ( m ) => m . role === "user" ) ;
111+ const userQuestion = firstUserMsg ? firstUserMsg . content : chatData . title ;
112+
113+ await sendChat ( {
114+ path : { lang : langId , page : pageSlug } ,
115+ userQuestion,
116+ questionScope : "page" ,
117+ sectionContent : priorSectionContent ,
118+ deleteChatOnCreated : chatId ,
119+ } ) ;
120+ } ;
95121
96122 return (
97123 < >
@@ -117,12 +143,55 @@ export function ChatAreaContent(props: Props) {
117143 </ li >
118144 </ ul >
119145 </ div >
120- < div className = "flex flex-wrap items-center" >
146+ < div className = "flex flex-wrap items-center gap-2 " >
121147 < div className = "flex-1 text-sm opacity-40" suppressHydrationWarning >
122148 { chatData . createdAt . toLocaleString ( ) }
123149 </ div >
150+ < button
151+ className = "btn btn-secondary btn-soft btn-sm"
152+ disabled = { isStreamingThis || isRegenerating }
153+ onClick = { handleRegenerate }
154+ >
155+ < svg
156+ className = { clsx ( "w-4 h-4" , isRegenerating && "animate-spin" ) }
157+ viewBox = "0 0 24 24"
158+ fill = "none"
159+ xmlns = "http://www.w3.org/2000/svg"
160+ >
161+ < path
162+ d = "M4.06189 13C4.55399 16.944 7.92083 20 12 20C15.5463 20 18.5721 17.7719 19.5714 14.619"
163+ stroke = "currentColor"
164+ strokeWidth = "2"
165+ strokeLinecap = "round"
166+ strokeLinejoin = "round"
167+ />
168+ < path
169+ d = "M19.9381 11C19.446 7.05601 16.0792 4 12 4C8.45371 4 5.42788 6.22811 4.42857 9.38095"
170+ stroke = "currentColor"
171+ strokeWidth = "2"
172+ strokeLinecap = "round"
173+ strokeLinejoin = "round"
174+ />
175+ < path
176+ d = "M14 14.619H19.5714V20"
177+ stroke = "currentColor"
178+ strokeWidth = "2"
179+ strokeLinecap = "round"
180+ strokeLinejoin = "round"
181+ />
182+ < path
183+ d = "M10 9.38095H4.42857V4"
184+ stroke = "currentColor"
185+ strokeWidth = "2"
186+ strokeLinecap = "round"
187+ strokeLinejoin = "round"
188+ />
189+ </ svg >
190+ 再生成
191+ </ button >
124192 < button
125193 className = "btn btn-error btn-soft btn-sm"
194+ disabled = { isStreamingThis || isRegenerating }
126195 onClick = { async ( ) => {
127196 if ( confirm ( "このチャットを削除してもよろしいですか?" ) ) {
128197 await deleteChatAction ( chatId ) ;
0 commit comments