@@ -10,13 +10,16 @@ import ConfirmationModal from "@/components/modals/confirmation-modal"
1010import SettingsModal from "@/components/modals/settings-modal"
1111import HistoryModal from "@/components/modals/history-modal"
1212import LoadingQuestionModal from "@/components/modals/loading-question-modal"
13+ import AnswerDisplay from "@/components/answer-display"
1314import { useState , useEffect , useCallback } from "react"
1415import type { Question , UserAnswer , QuestionHistory } from "@/lib/types"
16+ import { QuestionType } from "@/lib/types"
1517import { generateRandomQuestion } from "@/lib/question"
1618import { useTranslation } from "@/lib/i18n"
1719import { useToast } from "@/hooks/use-toast"
1820// Update the onSubmit function to use the OpenAI API for evaluation
1921import { evaluateAnswer , getModelAnswer } from "@/lib/api"
22+ import { cleanupJsonResponse } from "@/lib/utils"
2023
2124export default function Page ( ) {
2225 const { t, language, setLanguage } = useTranslation ( )
@@ -36,8 +39,13 @@ export default function Page() {
3639 const [ isStreaming , setIsStreaming ] = useState ( false )
3740 const { toast } = useToast ( )
3841 const [ questionHistory , setQuestionHistory ] = useState < QuestionHistory [ ] > ( [ ] )
42+
43+ // State for showing answer in the main content area instead of modal
44+ const [ showInlineAnswer , setShowInlineAnswer ] = useState ( false )
45+ const [ parsedAnswer , setParsedAnswer ] = useState < any > ( null )
3946
4047 const [ isLoadingQuestion , setIsLoadingQuestion ] = useState ( true )
48+ const [ forcedQuestionType , setForcedQuestionType ] = useState < string | null > ( null )
4149
4250 // 在组件加载时从本地存储加载历史记录
4351 useEffect ( ( ) => {
@@ -197,47 +205,81 @@ export default function Page() {
197205 setShowConfirmationModal ( true )
198206 }
199207
200- // Updated handleConfirmation function with streaming support
208+ // Updated handleConfirmation function to show answers inline
201209 const handleConfirmation = async ( ) => {
210+ // Make sure modals are properly managed
211+ setShowAnswerModal ( false ) ;
212+
202213 if ( confirmationStep < 2 ) {
203214 setConfirmationStep ( ( prevStep ) => prevStep + 1 )
204- } else {
205- setShowConfirmationModal ( false )
206- setAnswer ( null )
207- setIsStreaming ( true )
208- setShowAnswerModal ( true )
215+ return
216+ }
209217
210- try {
211- // Call OpenAI to get the model answer with streaming
212- await getModelAnswer ( currentQuestion , language , ( streamingAnswer ) => {
213- setAnswer ( streamingAnswer )
214- } )
218+ // Reset to start
219+ setConfirmationStep ( 0 )
220+ setShowConfirmationModal ( false )
221+
222+ if ( ! currentQuestion ) {
223+ return
224+ }
225+
226+ // Set streaming state and prepare to display inline answer
227+ setIsStreaming ( true )
228+ setShowInlineAnswer ( true )
229+ setAnswer ( null )
230+ setParsedAnswer ( null )
215231
216- setIsStreaming ( false )
217- setIsSubmitted ( true )
232+ try {
233+ // Call OpenAI to get the model answer with streaming
234+ await getModelAnswer ( currentQuestion , language , ( streamingAnswer ) => {
235+ setAnswer ( streamingAnswer )
218236
219- // 更新历史记录中的问题状态为已回答
220- if ( currentQuestion ) {
221- setQuestionHistory ( prevHistory => {
222- const updatedHistory = prevHistory . map ( item =>
223- item . id === currentQuestion . id
224- ? { ...item , answered : true }
225- : item
226- ) ;
227- localStorage . setItem ( "questionHistory" , JSON . stringify ( updatedHistory ) ) ;
228- return updatedHistory ;
229- } ) ;
237+ // Try to parse the streaming answer as it comes in
238+ try {
239+ const cleanedAnswer = cleanupJsonResponse ( streamingAnswer )
240+ const parsed = JSON . parse ( cleanedAnswer )
241+ setParsedAnswer ( parsed )
242+ } catch ( e ) {
243+ // It's okay if parsing fails during streaming
230244 }
231- } catch ( error ) {
232- console . error ( "Error getting model answer:" , error )
233- setIsStreaming ( false )
234- toast ( {
235- title : t ( "toast.error.title" ) ,
236- description : t ( "toast.error.description" ) ,
237- variant : "destructive" ,
238- duration : 5000 ,
239- } )
245+ } )
246+
247+ setIsStreaming ( false )
248+ setIsSubmitted ( true )
249+
250+ // Once streaming is complete, try to parse the final answer
251+ try {
252+ if ( answer ) {
253+ const cleanedAnswer = cleanupJsonResponse ( answer )
254+ const parsed = JSON . parse ( cleanedAnswer )
255+ setParsedAnswer ( parsed )
256+ }
257+ } catch ( e ) {
258+ // Keep using the raw answer if parsing fails
259+ console . error ( "Failed to parse answer:" , e )
260+ }
261+
262+ // 更新历史记录中的问题状态为已回答
263+ if ( currentQuestion ) {
264+ setQuestionHistory ( prevHistory => {
265+ const updatedHistory = prevHistory . map ( item =>
266+ item . id === currentQuestion . id
267+ ? { ...item , answered : true }
268+ : item
269+ ) ;
270+ localStorage . setItem ( "questionHistory" , JSON . stringify ( updatedHistory ) ) ;
271+ return updatedHistory ;
272+ } ) ;
240273 }
274+ } catch ( error ) {
275+ console . error ( "Error getting model answer:" , error )
276+ setIsStreaming ( false )
277+ toast ( {
278+ title : t ( "toast.error.title" ) ,
279+ description : t ( "toast.error.description" ) ,
280+ variant : "destructive" ,
281+ duration : 5000 ,
282+ } )
241283 }
242284 }
243285
@@ -270,6 +312,8 @@ export default function Page() {
270312
271313 const onCloseAnswerModal = ( ) => {
272314 setShowAnswerModal ( false )
315+ // For inline answers
316+ setShowInlineAnswer ( false )
273317 }
274318
275319 const onOpenSettings = ( ) => {
@@ -303,6 +347,36 @@ export default function Page() {
303347 setQuestionHistory ( [ ] ) ;
304348 } ;
305349
350+ // Function to handle switching to code editor
351+ const handleSwitchToCode = ( ) => {
352+ if ( currentQuestion && currentQuestion . type !== QuestionType . Coding ) {
353+ // Clone the current question and change the type
354+ const updatedQuestion = {
355+ ...currentQuestion ,
356+ type : QuestionType . Coding
357+ } ;
358+
359+ setCurrentQuestion ( updatedQuestion ) ;
360+ setForcedQuestionType ( "Coding" ) ;
361+
362+ toast ( {
363+ title : t ( "toast.switchedToCode.title" ) || "Switched to Code Editor" ,
364+ description : t ( "toast.switchedToCode.description" ) || "The question type has been changed to coding" ,
365+ duration : 3000 ,
366+ } ) ;
367+ }
368+ } ;
369+
370+ // Add a handler function to close the inline answer display
371+ const handleCloseInlineAnswer = ( ) => {
372+ setShowInlineAnswer ( false ) ;
373+ }
374+
375+ // Add a handler function to edit the answer
376+ const handleEditAnswer = ( ) => {
377+ setShowInlineAnswer ( false ) ;
378+ }
379+
306380 return (
307381 < div className = "flex flex-col min-h-screen" >
308382 < Header
@@ -316,7 +390,20 @@ export default function Page() {
316390 { currentQuestion && ! isLoadingQuestion ? (
317391 < >
318392 < QuestionArea question = { currentQuestion } language = { language } onNotMyStack = { handleNotMyStack } />
319- < AnswerArea question = { currentQuestion } userAnswer = { userAnswer } setUserAnswer = { setUserAnswer } />
393+
394+ { /* Show AI answer if requested */ }
395+ { showInlineAnswer ? (
396+ < AnswerDisplay
397+ answer = { answer }
398+ language = { language }
399+ isStreaming = { isStreaming }
400+ parsedAnswer = { parsedAnswer }
401+ onClose = { handleCloseInlineAnswer }
402+ onEdit = { handleEditAnswer }
403+ />
404+ ) : (
405+ < AnswerArea question = { currentQuestion } userAnswer = { userAnswer } setUserAnswer = { setUserAnswer } />
406+ ) }
320407 </ >
321408 ) : ! currentQuestion && ! isLoadingQuestion ? (
322409 < div className = "flex flex-col items-center justify-center py-16 space-y-4" >
@@ -333,13 +420,16 @@ export default function Page() {
333420 onViewAnswer = { onViewAnswer }
334421 isSubmitted = { isSubmitted }
335422 onNotMyStack = { handleNotMyStack }
423+ showSwitchTypeButton = { currentQuestion ?. type !== QuestionType . Coding }
424+ onSwitchToCode = { handleSwitchToCode }
336425 />
337426
338427 { showResultsModal && (
339428 < ResultsModal results = { results } language = { language } onClose = { onCloseResultsModal } isStreaming = { isStreaming } />
340429 ) }
341430
342- { showAnswerModal && (
431+ { /* AnswerModal is only used if not showing inline answers */ }
432+ { showAnswerModal && ! showInlineAnswer && (
343433 < AnswerModal answer = { answer } language = { language } onClose = { onCloseAnswerModal } isStreaming = { isStreaming } />
344434 ) }
345435
0 commit comments