import React, { useRef, useEffect, useState, ChangeEvent, FormEvent } from "react"; import { Message, useChat } from "ai/react"; import { useAppDispatch, useAppSelector } from "../../store/hook"; import { compiler_state, initEsbuild, } from "../../store/features/compilerSlice"; import { editor_state, set_monaco_input_value, update_editor_code } from "../../store/features/editorSlice"; import { theme_state } from "../../store/features/themeSlice"; import { ModalEnum, open_modal } from "../../store/features/modalSlice"; import ConsoleLog from "./ConsoleLog"; import Iframe from "./Iframe"; import InputCodeTab from "./InputCodeTab"; import Footer from "./Footer"; import Pane from "./Pane"; import { SendIcon } from "../../constants/icon"; import ReactMarkdown from "react-markdown"; import { ChatRequestOptions } from "ai"; const DEFAULT_PROMPT = `You are assisting in a WASM-powered live editor, Codetree, where React code is instantly reflected in the browser. Generate React code with the following constraints: 1. All logic should be encapsulated within a single app component file. 2. External dependencies or imports outside of the React core are not permitted. 3. Always use inline styles for styling. 4. Code snippets should conclude with rendering to the DOM's root element: ReactDOM.render(, document.getElementById('root')); 5. Ensure the code is optimized for a live browser environment and adheres strictly to React best practices.`; const Playground = () => { const dispatch = useAppDispatch(); const formRef = useRef(null); const inputRef = useRef(null); const { theme } = useAppSelector(theme_state); const { esbuildStatus, isCompiling, output } = useAppSelector(compiler_state); const { logs, editorValue, isLogTabOpen } = useAppSelector(editor_state); const [markdownCode, setMarkdownCode] = useState(''); const [prevMarkdownCode, setPrevMarkdownCode] = useState(markdownCode); const [isSystemInputVisible, setSystemInputVisible] = useState(false); const [isModelInputVisible, setModelInputVisible] = useState(false); const [aiProvider, setAIProvider] = useState("openai"); const [systemMessage, setSystemMessage] = useState( DEFAULT_PROMPT ); const isValidCodeBlock = (markdownCode: string) => { return markdownCode && markdownCode.length > 10 && markdownCode.includes('\n'); } const modifiedHandleSubmit = async (e: FormEvent, chatRequestOptions?: ChatRequestOptions) => { e.preventDefault(); // Pass the aiProvider in chatRequestOptions await handleSubmit(e, { ...chatRequestOptions, aiProvider } as any); }; useEffect(() => { const timer = setInterval(() => { if (isValidCodeBlock(markdownCode) && markdownCode !== prevMarkdownCode) { dispatch(update_editor_code({ type: 'javascript', content: markdownCode })); setPrevMarkdownCode(markdownCode); } }, 2000); return () => { clearInterval(timer); }; }, [markdownCode, prevMarkdownCode, dispatch]); const { append, messages, input, setInput, handleSubmit, ...rest } = useChat({ body: { systemMessage: systemMessage, aiProvider: aiProvider }, onError: (error) => { console.error(error); }, }); useEffect(() => { if (!esbuildStatus.isReady) { dispatch(initEsbuild()); } }, [dispatch, esbuildStatus]); useEffect(() => { dispatch(open_modal(ModalEnum.TEMPLATE)); }, [dispatch]); useEffect(() => { if(isValidCodeBlock(markdownCode)) { const newEditorValue = { name: "React", description: "By codetree", public: true, iconSrc: "/icons/reactjs.svg", tabs: { javascript: { title: "JS/JSX", entryPoints: "index.js", monacoLanguage: "javascript", data: markdownCode }, html: { title: "index.html", entryPoints: "index.html", monacoLanguage: "html", data: "" }, css: { title: "main.css", entryPoints: "main.css", monacoLanguage: "css", data: "" } } }; dispatch(set_monaco_input_value(newEditorValue as any)); } }, [markdownCode, dispatch]); return ( System Prompt setSystemInputVisible(!isSystemInputVisible)} className="p-2 rounded-full hover:bg-gray-200 transition duration-300" > {isSystemInputVisible ? '−' : '+'} Model setModelInputVisible(!isModelInputVisible)} className="p-2 rounded-full hover:bg-gray-200 transition duration-300" > {isModelInputVisible ? '−' : '+'} {isSystemInputVisible && ( setSystemMessage(e.target.value)} placeholder="Enter custom system prompt here" rows={2} // initial number of rows, can be adjusted style={{ overflowWrap: 'break-word', whiteSpace: 'pre-wrap' }} /> )} {isModelInputVisible && ( setAIProvider(e.target.value)} className="border p-2 rounded-md shadow-sm w-full bg-transparent text-gray-700 mt-2" > OpenAI Hugging Face )} setInput(e.target.value)} placeholder="Enter your message" onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { formRef.current?.requestSubmit(); e.preventDefault(); } }} spellCheck={false} className="textarea" value={input} /> {messages?.map((message, index) => ( ( ), // @ts-ignore code: ({node, ...props}) => { const codeValue = props.children[0] || ''; setMarkdownCode(codeValue as any); return null; } }} > {message.content} ))} } panelB={ } panelC={} lastPanelVisibility={isLogTabOpen} /> ); }; export default Playground;
( ), // @ts-ignore code: ({node, ...props}) => { const codeValue = props.children[0] || ''; setMarkdownCode(codeValue as any); return null; } }} > {message.content}