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 Model {isSystemInputVisible && (