matt HOFFNER
different model
fc46386
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. Always import both React and ReactDOM.
2. All logic should be encapsulated within a single app component file.
3. Always use inline styles for styling.
4. Code snippets should conclude with rendering to the DOM's root element: ReactDOM.render(<App />, document.getElementById('root'));
5. Ensure the code is optimized for a live browser environment and adheres strictly to React best practices.
Write the code with the above guidelines in mind.`;
const Playground = () => {
const dispatch = useAppDispatch();
const formRef = useRef<HTMLFormElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(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<string>("openai");
const [urlOption, setUrlOption] = useState<string | any>(null);
const [isChatVisible, setIsChatVisible] = useState(true);
const [systemMessage, setSystemMessage] = useState(
DEFAULT_PROMPT
);
const isValidCodeBlock = (markdownCode: string) => {
return markdownCode && markdownCode.length > 10 && markdownCode.includes('\n');
}
const modifiedHandleSubmit = async (e: FormEvent<HTMLFormElement>, chatRequestOptions?: ChatRequestOptions) => {
e.preventDefault();
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,
url: urlOption
},
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 (
<div style={{ background: theme.background }}>
<div className="flex flex-col">
<div className="px-4 pb-2 pt-3 shadow-lg sm:pb-3 sm:pt-4">
<span className="text-lg font-semibold">System Prompt</span>
<button
onClick={() => setSystemInputVisible(!isSystemInputVisible)}
className="p-2 rounded-full hover:bg-gray-200 transition duration-300"
>
{isSystemInputVisible ? '−' : '+'}
</button>
<span className="text-lg font-semibold">Model</span>
<button
onClick={() => setModelInputVisible(!isModelInputVisible)}
className="p-2 rounded-full hover:bg-gray-200 transition duration-300"
>
{isModelInputVisible ? '−' : '+'}
</button>
<span className="text-lg font-semibold">Chat</span>
<button
onClick={() => setIsChatVisible(!isChatVisible)}
className="p-2 rounded-full hover:bg-gray-200 transition duration-300"
>
{isChatVisible ? '−' : '+'}
</button>
{isSystemInputVisible && (
<textarea
className="textarea my-4 border p-2 rounded-md shadow-sm w-full resize-none"
value={systemMessage || ''}
onChange={e => 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 && (
<div className="my-4">
<select
value={aiProvider}
onChange={(e) => setAIProvider(e.target.value)}
className="border p-2 rounded-md shadow-sm w-full bg-transparent text-gray-700 mt-2"
>
<option value="openai">gpt-4</option>
<option value="meta-llama/Meta-Llama-3-8B">meta-llama/Meta-Llama-3-8B</option>
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="url-option">
Model URL Option:
</label>
</select>
<input
id="url-option"
type="url"
placeholder="Enter URL"
value={urlOption}
onChange={(e) => setUrlOption(e.target.value)}
className="border p-2 rounded-md shadow-sm w-full bg-transparent text-gray-700 mt-2"
/>
</div>
)}
{isChatVisible &&
<form ref={formRef} onSubmit={modifiedHandleSubmit} className="relative w-full">
<textarea ref={inputRef} onChange={(e) => 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} />
<button
type="submit"
className="absolute inset-y-0 right-3 my-auto flex h-8 w-8 items-center justify-center rounded-md transition-all"
>
<SendIcon
className={"h-4 w-4"}
/>
</button>
</form>
}
</div>
{isChatVisible && (
<div className="flex flex-col items-start space-y-4 overflow-y-auto max-h-[20vh]">
{messages?.map((message, index) => (
<p key={index} className="messages-text">
<ReactMarkdown
className="prose mt-1 w-full break-words prose-p:leading-relaxed"
components={{
a: (props) => (
<a {...props} target="_blank" rel="noopener noreferrer" />
),
// @ts-ignore
code: ({node, ...props}) => {
const codeValue = props.children[0] || '';
setMarkdownCode(codeValue as any);
return null;
}
}}
>
{message.content}
</ReactMarkdown>
</p>
))}
</div>
)}
</div>
<Pane
panelA={<InputCodeTab editorValue={editorValue} />}
panelB={
<Iframe
tabs={editorValue.tabs}
output={output}
isCompiling={isCompiling}
esbuildStatus={esbuildStatus}
/>
}
panelC={<ConsoleLog logs={logs} />}
lastPanelVisibility={isLogTabOpen}
/>
<Footer />
</div>
);
};
export default Playground;