| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						import { useStore } from '@nanostores/react'; | 
					
					
						
						| 
							 | 
						import type { Message } from 'ai'; | 
					
					
						
						| 
							 | 
						import { useChat } from 'ai/react'; | 
					
					
						
						| 
							 | 
						import { useAnimate } from 'framer-motion'; | 
					
					
						
						| 
							 | 
						import { memo, useCallback, useEffect, useRef, useState } from 'react'; | 
					
					
						
						| 
							 | 
						import { cssTransition, toast, ToastContainer } from 'react-toastify'; | 
					
					
						
						| 
							 | 
						import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks'; | 
					
					
						
						| 
							 | 
						import { description, useChatHistory } from '~/lib/persistence'; | 
					
					
						
						| 
							 | 
						import { chatStore } from '~/lib/stores/chat'; | 
					
					
						
						| 
							 | 
						import { workbenchStore } from '~/lib/stores/workbench'; | 
					
					
						
						| 
							 | 
						import { DEFAULT_MODEL, DEFAULT_PROVIDER, PROMPT_COOKIE_KEY, PROVIDER_LIST } from '~/utils/constants'; | 
					
					
						
						| 
							 | 
						import { cubicEasingFn } from '~/utils/easings'; | 
					
					
						
						| 
							 | 
						import { createScopedLogger, renderLogger } from '~/utils/logger'; | 
					
					
						
						| 
							 | 
						import { BaseChat } from './BaseChat'; | 
					
					
						
						| 
							 | 
						import Cookies from 'js-cookie'; | 
					
					
						
						| 
							 | 
						import type { ProviderInfo } from '~/utils/types'; | 
					
					
						
						| 
							 | 
						import { debounce } from '~/utils/debounce'; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						const toastAnimation = cssTransition({ | 
					
					
						
						| 
							 | 
						  enter: 'animated fadeInRight', | 
					
					
						
						| 
							 | 
						  exit: 'animated fadeOutRight', | 
					
					
						
						| 
							 | 
						}); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						const logger = createScopedLogger('Chat'); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						export function Chat() { | 
					
					
						
						| 
							 | 
						  renderLogger.trace('Chat'); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						  const { ready, initialMessages, storeMessageHistory, importChat, exportChat } = useChatHistory(); | 
					
					
						
						| 
							 | 
						  const title = useStore(description); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						  return ( | 
					
					
						
						| 
							 | 
						    <> | 
					
					
						
						| 
							 | 
						      {ready && ( | 
					
					
						
						| 
							 | 
						        <ChatImpl | 
					
					
						
						| 
							 | 
						          description={title} | 
					
					
						
						| 
							 | 
						          initialMessages={initialMessages} | 
					
					
						
						| 
							 | 
						          exportChat={exportChat} | 
					
					
						
						| 
							 | 
						          storeMessageHistory={storeMessageHistory} | 
					
					
						
						| 
							 | 
						          importChat={importChat} | 
					
					
						
						| 
							 | 
						        /> | 
					
					
						
						| 
							 | 
						      )} | 
					
					
						
						| 
							 | 
						      <ToastContainer | 
					
					
						
						| 
							 | 
						        closeButton={({ closeToast }) => { | 
					
					
						
						| 
							 | 
						          return ( | 
					
					
						
						| 
							 | 
						            <button className="Toastify__close-button" onClick={closeToast}> | 
					
					
						
						| 
							 | 
						              <div className="i-ph:x text-lg" /> | 
					
					
						
						| 
							 | 
						            </button> | 
					
					
						
						| 
							 | 
						          ); | 
					
					
						
						| 
							 | 
						        }} | 
					
					
						
						| 
							 | 
						        icon={({ type }) => { | 
					
					
						
						| 
							 | 
						          /** | 
					
					
						
						| 
							 | 
						           * @todo Handle more types if we need them. This may require extra color palettes. | 
					
					
						
						| 
							 | 
						           */ | 
					
					
						
						| 
							 | 
						          switch (type) { | 
					
					
						
						| 
							 | 
						            case 'success': { | 
					
					
						
						| 
							 | 
						              return <div className="i-ph:check-bold text-bolt-elements-icon-success text-2xl" />; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						            case 'error': { | 
					
					
						
						| 
							 | 
						              return <div className="i-ph:warning-circle-bold text-bolt-elements-icon-error text-2xl" />; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						          } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						          return undefined; | 
					
					
						
						| 
							 | 
						        }} | 
					
					
						
						| 
							 | 
						        position="bottom-right" | 
					
					
						
						| 
							 | 
						        pauseOnFocusLoss | 
					
					
						
						| 
							 | 
						        transition={toastAnimation} | 
					
					
						
						| 
							 | 
						      /> | 
					
					
						
						| 
							 | 
						    </> | 
					
					
						
						| 
							 | 
						  ); | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						interface ChatProps { | 
					
					
						
						| 
							 | 
						  initialMessages: Message[]; | 
					
					
						
						| 
							 | 
						  storeMessageHistory: (messages: Message[]) => Promise<void>; | 
					
					
						
						| 
							 | 
						  importChat: (description: string, messages: Message[]) => Promise<void>; | 
					
					
						
						| 
							 | 
						  exportChat: () => void; | 
					
					
						
						| 
							 | 
						  description?: string; | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						export const ChatImpl = memo( | 
					
					
						
						| 
							 | 
						  ({ description, initialMessages, storeMessageHistory, importChat, exportChat }: ChatProps) => { | 
					
					
						
						| 
							 | 
						    useShortcuts(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const textareaRef = useRef<HTMLTextAreaElement>(null); | 
					
					
						
						| 
							 | 
						    const [chatStarted, setChatStarted] = useState(initialMessages.length > 0); | 
					
					
						
						| 
							 | 
						    const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);  | 
					
					
						
						| 
							 | 
						    const [imageDataList, setImageDataList] = useState<string[]>([]);  | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const [model, setModel] = useState(() => { | 
					
					
						
						| 
							 | 
						      const savedModel = Cookies.get('selectedModel'); | 
					
					
						
						| 
							 | 
						      return savedModel || DEFAULT_MODEL; | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						    const [provider, setProvider] = useState(() => { | 
					
					
						
						| 
							 | 
						      const savedProvider = Cookies.get('selectedProvider'); | 
					
					
						
						| 
							 | 
						      return PROVIDER_LIST.find((p) => p.name === savedProvider) || DEFAULT_PROVIDER; | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const { showChat } = useStore(chatStore); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const [animationScope, animate] = useAnimate(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const [apiKeys, setApiKeys] = useState<Record<string, string>>({}); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const { messages, isLoading, input, handleInputChange, setInput, stop, append } = useChat({ | 
					
					
						
						| 
							 | 
						      api: '/api/chat', | 
					
					
						
						| 
							 | 
						      body: { | 
					
					
						
						| 
							 | 
						        apiKeys, | 
					
					
						
						| 
							 | 
						      }, | 
					
					
						
						| 
							 | 
						      onError: (error) => { | 
					
					
						
						| 
							 | 
						        logger.error('Request failed\n\n', error); | 
					
					
						
						| 
							 | 
						        toast.error( | 
					
					
						
						| 
							 | 
						          'There was an error processing your request: ' + (error.message ? error.message : 'No details were returned'), | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						      }, | 
					
					
						
						| 
							 | 
						      onFinish: () => { | 
					
					
						
						| 
							 | 
						        logger.debug('Finished streaming'); | 
					
					
						
						| 
							 | 
						      }, | 
					
					
						
						| 
							 | 
						      initialMessages, | 
					
					
						
						| 
							 | 
						      initialInput: Cookies.get(PROMPT_COOKIE_KEY) || '', | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const { enhancingPrompt, promptEnhanced, enhancePrompt, resetEnhancer } = usePromptEnhancer(); | 
					
					
						
						| 
							 | 
						    const { parsedMessages, parseMessages } = useMessageParser(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    useEffect(() => { | 
					
					
						
						| 
							 | 
						      chatStore.setKey('started', initialMessages.length > 0); | 
					
					
						
						| 
							 | 
						    }, []); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    useEffect(() => { | 
					
					
						
						| 
							 | 
						      parseMessages(messages, isLoading); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      if (messages.length > initialMessages.length) { | 
					
					
						
						| 
							 | 
						        storeMessageHistory(messages).catch((error) => toast.error(error.message)); | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						    }, [messages, isLoading, parseMessages]); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const scrollTextArea = () => { | 
					
					
						
						| 
							 | 
						      const textarea = textareaRef.current; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      if (textarea) { | 
					
					
						
						| 
							 | 
						        textarea.scrollTop = textarea.scrollHeight; | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const abort = () => { | 
					
					
						
						| 
							 | 
						      stop(); | 
					
					
						
						| 
							 | 
						      chatStore.setKey('aborted', true); | 
					
					
						
						| 
							 | 
						      workbenchStore.abortAllActions(); | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    useEffect(() => { | 
					
					
						
						| 
							 | 
						      const textarea = textareaRef.current; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      if (textarea) { | 
					
					
						
						| 
							 | 
						        textarea.style.height = 'auto'; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const scrollHeight = textarea.scrollHeight; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        textarea.style.height = `${Math.min(scrollHeight, TEXTAREA_MAX_HEIGHT)}px`; | 
					
					
						
						| 
							 | 
						        textarea.style.overflowY = scrollHeight > TEXTAREA_MAX_HEIGHT ? 'auto' : 'hidden'; | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						    }, [input, textareaRef]); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const runAnimation = async () => { | 
					
					
						
						| 
							 | 
						      if (chatStarted) { | 
					
					
						
						| 
							 | 
						        return; | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      await Promise.all([ | 
					
					
						
						| 
							 | 
						        animate('#examples', { opacity: 0, display: 'none' }, { duration: 0.1 }), | 
					
					
						
						| 
							 | 
						        animate('#intro', { opacity: 0, flex: 1 }, { duration: 0.2, ease: cubicEasingFn }), | 
					
					
						
						| 
							 | 
						      ]); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      chatStore.setKey('started', true); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      setChatStarted(true); | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const sendMessage = async (_event: React.UIEvent, messageInput?: string) => { | 
					
					
						
						| 
							 | 
						      const _input = messageInput || input; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      if (_input.length === 0 || isLoading) { | 
					
					
						
						| 
							 | 
						        return; | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						      await workbenchStore.saveAllFiles(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      const fileModifications = workbenchStore.getFileModifcations(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      chatStore.setKey('aborted', false); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      runAnimation(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      if (fileModifications !== undefined) { | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        append({ | 
					
					
						
						| 
							 | 
						          role: 'user', | 
					
					
						
						| 
							 | 
						          content: [ | 
					
					
						
						| 
							 | 
						            { | 
					
					
						
						| 
							 | 
						              type: 'text', | 
					
					
						
						| 
							 | 
						              text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${_input}`, | 
					
					
						
						| 
							 | 
						            }, | 
					
					
						
						| 
							 | 
						            ...imageDataList.map((imageData) => ({ | 
					
					
						
						| 
							 | 
						              type: 'image', | 
					
					
						
						| 
							 | 
						              image: imageData, | 
					
					
						
						| 
							 | 
						            })), | 
					
					
						
						| 
							 | 
						          ] as any,  | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        workbenchStore.resetAllFileModifications(); | 
					
					
						
						| 
							 | 
						      } else { | 
					
					
						
						| 
							 | 
						        append({ | 
					
					
						
						| 
							 | 
						          role: 'user', | 
					
					
						
						| 
							 | 
						          content: [ | 
					
					
						
						| 
							 | 
						            { | 
					
					
						
						| 
							 | 
						              type: 'text', | 
					
					
						
						| 
							 | 
						              text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${_input}`, | 
					
					
						
						| 
							 | 
						            }, | 
					
					
						
						| 
							 | 
						            ...imageDataList.map((imageData) => ({ | 
					
					
						
						| 
							 | 
						              type: 'image', | 
					
					
						
						| 
							 | 
						              image: imageData, | 
					
					
						
						| 
							 | 
						            })), | 
					
					
						
						| 
							 | 
						          ] as any,  | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      setInput(''); | 
					
					
						
						| 
							 | 
						      Cookies.remove(PROMPT_COOKIE_KEY); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						      setUploadedFiles([]); | 
					
					
						
						| 
							 | 
						      setImageDataList([]); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      resetEnhancer(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      textareaRef.current?.blur(); | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    const onTextareaChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => { | 
					
					
						
						| 
							 | 
						      handleInputChange(event); | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    const debouncedCachePrompt = useCallback( | 
					
					
						
						| 
							 | 
						      debounce((event: React.ChangeEvent<HTMLTextAreaElement>) => { | 
					
					
						
						| 
							 | 
						        const trimmedValue = event.target.value.trim(); | 
					
					
						
						| 
							 | 
						        Cookies.set(PROMPT_COOKIE_KEY, trimmedValue, { expires: 30 }); | 
					
					
						
						| 
							 | 
						      }, 1000), | 
					
					
						
						| 
							 | 
						      [], | 
					
					
						
						| 
							 | 
						    ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const [messageRef, scrollRef] = useSnapScroll(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    useEffect(() => { | 
					
					
						
						| 
							 | 
						      const storedApiKeys = Cookies.get('apiKeys'); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      if (storedApiKeys) { | 
					
					
						
						| 
							 | 
						        setApiKeys(JSON.parse(storedApiKeys)); | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						    }, []); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const handleModelChange = (newModel: string) => { | 
					
					
						
						| 
							 | 
						      setModel(newModel); | 
					
					
						
						| 
							 | 
						      Cookies.set('selectedModel', newModel, { expires: 30 }); | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const handleProviderChange = (newProvider: ProviderInfo) => { | 
					
					
						
						| 
							 | 
						      setProvider(newProvider); | 
					
					
						
						| 
							 | 
						      Cookies.set('selectedProvider', newProvider.name, { expires: 30 }); | 
					
					
						
						| 
							 | 
						    }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    return ( | 
					
					
						
						| 
							 | 
						      <BaseChat | 
					
					
						
						| 
							 | 
						        ref={animationScope} | 
					
					
						
						| 
							 | 
						        textareaRef={textareaRef} | 
					
					
						
						| 
							 | 
						        input={input} | 
					
					
						
						| 
							 | 
						        showChat={showChat} | 
					
					
						
						| 
							 | 
						        chatStarted={chatStarted} | 
					
					
						
						| 
							 | 
						        isStreaming={isLoading} | 
					
					
						
						| 
							 | 
						        enhancingPrompt={enhancingPrompt} | 
					
					
						
						| 
							 | 
						        promptEnhanced={promptEnhanced} | 
					
					
						
						| 
							 | 
						        sendMessage={sendMessage} | 
					
					
						
						| 
							 | 
						        model={model} | 
					
					
						
						| 
							 | 
						        setModel={handleModelChange} | 
					
					
						
						| 
							 | 
						        provider={provider} | 
					
					
						
						| 
							 | 
						        setProvider={handleProviderChange} | 
					
					
						
						| 
							 | 
						        messageRef={messageRef} | 
					
					
						
						| 
							 | 
						        scrollRef={scrollRef} | 
					
					
						
						| 
							 | 
						        handleInputChange={(e) => { | 
					
					
						
						| 
							 | 
						          onTextareaChange(e); | 
					
					
						
						| 
							 | 
						          debouncedCachePrompt(e); | 
					
					
						
						| 
							 | 
						        }} | 
					
					
						
						| 
							 | 
						        handleStop={abort} | 
					
					
						
						| 
							 | 
						        description={description} | 
					
					
						
						| 
							 | 
						        importChat={importChat} | 
					
					
						
						| 
							 | 
						        exportChat={exportChat} | 
					
					
						
						| 
							 | 
						        messages={messages.map((message, i) => { | 
					
					
						
						| 
							 | 
						          if (message.role === 'user') { | 
					
					
						
						| 
							 | 
						            return message; | 
					
					
						
						| 
							 | 
						          } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						          return { | 
					
					
						
						| 
							 | 
						            ...message, | 
					
					
						
						| 
							 | 
						            content: parsedMessages[i] || '', | 
					
					
						
						| 
							 | 
						          }; | 
					
					
						
						| 
							 | 
						        })} | 
					
					
						
						| 
							 | 
						        enhancePrompt={() => { | 
					
					
						
						| 
							 | 
						          enhancePrompt( | 
					
					
						
						| 
							 | 
						            input, | 
					
					
						
						| 
							 | 
						            (input) => { | 
					
					
						
						| 
							 | 
						              setInput(input); | 
					
					
						
						| 
							 | 
						              scrollTextArea(); | 
					
					
						
						| 
							 | 
						            }, | 
					
					
						
						| 
							 | 
						            model, | 
					
					
						
						| 
							 | 
						            provider, | 
					
					
						
						| 
							 | 
						            apiKeys, | 
					
					
						
						| 
							 | 
						          ); | 
					
					
						
						| 
							 | 
						        }} | 
					
					
						
						| 
							 | 
						        uploadedFiles={uploadedFiles} | 
					
					
						
						| 
							 | 
						        setUploadedFiles={setUploadedFiles} | 
					
					
						
						| 
							 | 
						        imageDataList={imageDataList} | 
					
					
						
						| 
							 | 
						        setImageDataList={setImageDataList} | 
					
					
						
						| 
							 | 
						      /> | 
					
					
						
						| 
							 | 
						    ); | 
					
					
						
						| 
							 | 
						  }, | 
					
					
						
						| 
							 | 
						); | 
					
					
						
						| 
							 | 
						
 |