PierrunoYT / claude-3-artifacts

17 stars 8 forks source link

Rendering not working #2

Open gfish-git opened 1 month ago

gfish-git commented 1 month ago

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

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:

at UserComponent
at ErrorBoundary (<anonymous>:15:5)
at App

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"

gfish-git commented 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.

Screenshot 2024-07-17 at 10 48 24 PM

I was able to resolve this particular error with

Updated SonnetWebUI.js

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;

PierrunoYT commented 1 month ago

@gfish-git Yeah its not working right. I could need some help tho.