Open gfish-git opened 1 month ago
Maybe I'm doing something wrong, but nothing is rendering in the "React Rendering Area" even after I resolve the "cross-origin" error.
I was able to resolve this particular error with
import React, { useState, useEffect, useCallback } from "react"; import { Sun, Moon, Send } from "lucide-react"; import { Button } from "./components/ui/button"; import { Input } from "./components/ui/input"; import { Textarea } from "./components/ui/textarea"; import { Card, CardContent } from "./components/ui/card"; import { Switch } from "./components/ui/switch"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; import DynamicReactRenderer from "./DynamicReactRenderer";
const SonnetWebUI = () => { const [darkMode, setDarkMode] = useState(false); const [message, setMessage] = useState(""); const [chat, setChat] = useState([]); const [apiKey, setApiKey] = useState( process.env.REACT_APP_OPENROUTER_API_KEY || "" ); const [apiKeyModified, setApiKeyModified] = useState(false); const [reactCode, setReactCode] = useState("");
const toggleDarkMode = () => { setDarkMode(!darkMode); };
useEffect(() => { const savedApiKey = localStorage.getItem("openRouterApiKey"); if (savedApiKey) { setApiKey(savedApiKey); } }, []);
const handleApiKeyChange = (e) => { const newValue = e.target.value; setApiKey(newValue); setApiKeyModified(true); };
const saveApiKey = () => {
fetch("/update-env", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ REACT_APP_OPENROUTER_API_KEY: apiKey }),
})
.then((response) => {
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status}
);
}
return response.text();
})
.then((text) => {
try {
return JSON.parse(text);
} catch (e) {
throw new Error(Server response is not valid JSON: ${text}
);
}
})
.then((data) => {
if (data.success) {
setApiKeyModified(false);
console.log("API key saved successfully");
setChat((prev) => [
...prev,
{
role: "assistant",
content: "API key saved successfully.",
isCode: false,
},
]);
} else {
throw new Error("Failed to save API key");
}
})
.catch((error) => {
console.error("Error saving API key:", error);
setChat((prev) => [
...prev,
{
role: "assistant",
content: Error saving API key: ${error.message}
,
isCode: false,
},
]);
});
};
const sendMessage = async () => { if (message.trim()) { const userMessage = { role: "user", content: message }; setChat((prev) => [...prev, userMessage]);
try {
const response = await fetch(
"https://openrouter.ai/api/v1/chat/completions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
model: "anthropic/claude-3-sonnet-20240320",
messages: [...chat, userMessage],
}),
}
);
if (!response.ok) {
throw new Error("API request failed");
}
const data = await response.json();
const content = data.choices[0].message.content;
const isCode = content.trim().startsWith("```");
const assistantMessage = {
role: "assistant",
content: content,
isCode: isCode,
};
setChat((prev) => [...prev, assistantMessage]);
if (assistantMessage.isCode) {
const codeMatch = content.match(/```(?:jsx?|react)?\s*([\s\S]*?)```/);
if (codeMatch) {
const extractedCode = codeMatch[1].trim();
setReactCode(extractedCode);
console.log("React code updated:", extractedCode);
}
}
} catch (error) {
console.error("Error calling OpenRouter API:", error);
setChat((prev) => [
...prev,
{
role: "assistant",
content: "Sorry, there was an error processing your request.",
isCode: false,
},
]);
}
setMessage("");
}
};
const validateReactCode = (code) => { try { if (code.includes("import ")) { throw new Error( "Import statements are not allowed in this context. Please define your component without using imports." ); }
// eslint-disable-next-line no-new-func
new Function("React", `return ${code}`);
if (!code.includes("return")) {
throw new Error(
"The code does not appear to be a valid React component. Make sure it includes a return statement with JSX."
);
}
return { isValid: true, error: null, code };
} catch (error) {
console.error("Invalid React code:", error);
let errorMessage = error.message;
if (error instanceof SyntaxError) {
if (error.message.includes("Unexpected token")) {
errorMessage = `Syntax Error: Unexpected token found. This often means you have a typo or misplaced character in your code. Check for missing semicolons, parentheses, or brackets.`;
} else if (error.message.includes("Unexpected identifier")) {
errorMessage = `Syntax Error: Unexpected identifier found. This could mean you're using a variable or function name in an unexpected place, or you might be missing an operator or keyword.`;
}
}
return { isValid: false, error: errorMessage, code: null };
}
};
useEffect(() => { document.body.classList.toggle("dark", darkMode); }, [darkMode]);
useEffect(() => {
if (reactCode.trim()) {
const { isValid, error } = validateReactCode(reactCode);
if (!isValid) {
setChat((prev) => [
...prev,
{
role: "assistant",
content: ⚠️ React Code Error ⚠️\n\n${error}\n\nPlease review your code and fix the issue. Remember:\n- Don't use import statements\n- Ensure you have a valid React component structure\n- Check for syntax errors like missing brackets or semicolons
,
isCode: false,
},
]);
}
}
}, [reactCode]);
return (
<div
className={flex h-screen ${ darkMode ? "dark" : "" } bg-background-light dark:bg-background-dark
}
{/ Left side - Chat interface /}
Chat with Claude 3.5 Sonnet
<div className="flex mb-2"> <Input type="password" value={apiKey} onChange={handleApiKeyChange} placeholder="Enter your OpenRouter API key" className="flex-grow mr-2 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 rounded-lg shadow-sm" /> <Button onClick={saveApiKey} disabled={!apiKeyModified} className="bg-primary hover:bg-primary-dark text-white dark:bg-button-dark dark:hover:bg-button-dark-hover dark:text-button-dark-text disabled:opacity-50 disabled:cursor-not-allowed" > Save Key </Button> </div> {/* Chat messages */} <div className="flex-grow overflow-y-auto mb-6 custom-scrollbar"> {chat.map((msg, index) => ( <div key={index} className={`mb-4 ${ msg.role === "user" ? "text-right" : "text-left" }`} > <div className={`inline-block p-3 rounded-lg shadow-custom ${ msg.role === "user" ? "bg-primary text-white" : "bg-white dark:bg-gray-800 text-text-light dark:text-text-dark" }`} > {msg.role === "assistant" ? ( msg.content.split("```").map((part, index) => index % 2 === 0 ? ( <span key={index}>{part}</span> ) : ( <SyntaxHighlighter key={index} language="javascript" style={vscDarkPlus} customStyle={{ padding: "1em", borderRadius: "0.5em", fontSize: "0.9em", marginTop: "0.5em", marginBottom: "0.5em", }} > {part} </SyntaxHighlighter> ) ) ) : ( <span>{msg.content}</span> )} </div> </div> ))} </div> {/* Message input */} <div className="flex flex-col"> <div className="flex mb-2"> <Input type="text" value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Type your message..." className="flex-grow mr-2 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 rounded-lg shadow-sm" /> <Button onClick={sendMessage} className="bg-primary hover:bg-primary-dark text-white dark:bg-button-dark dark:hover:bg-button-dark-hover dark:text-button-dark-text" > <Send className="mr-2" /> Send </Button> </div> </div> </div> {/* Right side - React rendering area */} <div className="w-1/2 p-6"> <h2 className="text-3xl font-bold mb-6 text-primary dark:text-primary-dark"> React Rendering Area </h2> <Textarea value={reactCode} readOnly placeholder="React component code will appear here automatically..." className="mb-4 h-1/3 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 rounded-lg shadow-sm" /> <div className="mb-6 text-sm text-gray-500 dark:text-gray-400"> The React component will automatically render and update when new code is received from the AI. </div> <Card className="bg-white dark:bg-gray-800 shadow-custom"> <CardContent> <DynamicReactRenderer code={reactCode} /> </CardContent> </Card> </div> </div>
); };
export default SonnetWebUI;
DynamicReactRenderer.js
import React, { useState, useEffect } from "react";
const DynamicReactRenderer = ({ code }) => { const [Component, setComponent] = useState(null); const [error, setError] = useState(null);
useEffect(() => { try { // eslint-disable-next-line no-new-func const ComponentFunction = new Function("React",
return ${code}
); const WrappedComponent = ComponentFunction(React); setComponent(() => WrappedComponent); setError(null); } catch (err) { console.error("Error creating component:", err); setError(err.toString()); setComponent(null); } }, [code]);if (error) { return
Error rendering component: {error}; }return Component ?
: null; }; export default DynamicReactRenderer;
@gfish-git Yeah its not working right. I could need some help tho.
This looks super promising, but I can't get the rendering to work in dev due to what may be a cross-origin error:![Screenshot 2024-07-17 at 10 18 18 PM](https://github.com/user-attachments/assets/725dc93e-13a1-4fb7-805d-c554f4c1eb8c)
Console logs:
"Error: UserComponent(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null. at reconcileChildFibers (react-dom.development.js:14199:25) at reconcileChildren (react-dom.development.js:17020:30) at mountIndeterminateComponent (react-dom.development.js:17920:7) at beginWork (react-dom.development.js:19079:18) at HTMLUnknownElement.callCallback (react-dom.development.js:3942:16) at Object.invokeGuardedCallbackDev (react-dom.development.js:3991:18) at invokeGuardedCallback (react-dom.development.js:4053:33) at beginWork$1 (react-dom.development.js:23994:9) at performUnitOfWork (react-dom.development.js:22809:14) at workLoopSync (react-dom.development.js:22737:7) reconcileChildFibers @ react-dom.development.js:14199 reconcileChildren @ react-dom.development.js:17020 mountIndeterminateComponent @ react-dom.development.js:17920 beginWork @ react-dom.development.js:19079 callCallback @ react-dom.development.js:3942 invokeGuardedCallbackDev @ react-dom.development.js:3991 invokeGuardedCallback @ react-dom.development.js:4053 beginWork$1 @ react-dom.development.js:23994 performUnitOfWork @ react-dom.development.js:22809 workLoopSync @ react-dom.development.js:22737 renderRootSync @ react-dom.development.js:22700 performSyncWorkOnRoot @ react-dom.development.js:22323 scheduleUpdateOnFiber @ react-dom.development.js:21911 updateContainer @ react-dom.development.js:25512 (anonymous) @ react-dom.development.js:26051 unbatchedUpdates @ react-dom.development.js:22461 legacyRenderSubtreeIntoContainer @ react-dom.development.js:26050 render @ react-dom.development.js:26133 (anonymous) @ Inline Babel script:31 i @ babel.min.js:24 r @ babel.min.js:24 o @ babel.min.js:24 u @ babel.min.js:24 f @ babel.min.js:1 (anonymous) @ babel.min.js:1Understand this error react-dom.development.js:20115 The above error occurred in the component:
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary. logCapturedError @ react-dom.development.js:20115 update.payload @ react-dom.development.js:20163 getStateFromUpdate @ react-dom.development.js:12132 processUpdateQueue @ react-dom.development.js:12280 resumeMountClassInstance @ react-dom.development.js:12951 updateClassComponent @ react-dom.development.js:17460 beginWork @ react-dom.development.js:19103 beginWork$1 @ react-dom.development.js:23970 performUnitOfWork @ react-dom.development.js:22809 workLoopSync @ react-dom.development.js:22737 renderRootSync @ react-dom.development.js:22700 performSyncWorkOnRoot @ react-dom.development.js:22323 scheduleUpdateOnFiber @ react-dom.development.js:21911 updateContainer @ react-dom.development.js:25512 (anonymous) @ react-dom.development.js:26051 unbatchedUpdates @ react-dom.development.js:22461 legacyRenderSubtreeIntoContainer @ react-dom.development.js:26050 render @ react-dom.development.js:26133 (anonymous) @ Inline Babel script:31 i @ babel.min.js:24 r @ babel.min.js:24 o @ babel.min.js:24 u @ babel.min.js:24 f @ babel.min.js:1 (anonymous) @ babel.min.js:1Understand this error"