ðNext.jsïŒSandpackã§ClaudeArtifactã¯å®çŸã§ããïŒâš
ã¯ããã«
çãããLlamaCoderããåç¥ã§ããïŒããã¯ãLlama 3.1ã䜿ã£ãŠãClaudeã®artifactæ©èœã®ãããªããšãç¡æã§å®çŸã§ãããµãŒãã¹ãªãã§ãã
ããããšæããŸãããïŒãã ãClaudeãšåæ§ã«ãèªåã®äœ¿ãããã©ã€ãã©ãªã䜿ããªãã®ãå°ã
ã€ãããšããã§ãã
ã§ããLlamaCoderã®çŽ æŽããããšããã¯ãOSSãšããŠã³ãŒããå
¬éãããŠããããšã
å®è£ ãèŠãŠã¿ããšãããªãã·ã³ãã«ã§ä»¥äžã®æµãã ããªãã§ãã
ã³ãŒãçæãâ Sandpackã§ã³ãŒãå®è¡
Sandpackãã䜿ããã°ãå®ã¯èªåã§ãäœãã¡ããããšã«æ°ã¥ãã¡ãããŸããã
Sandpackã¯ãã©ãŠã¶äžã§ã©ã€ãã³ãŒããšãã£ã¿ãŒãšãã¬ãã¥ãŒãæäŸããããŒã«ã§ãã
ãããäžåèªåã§å®è£ ããŠã¿ããããªãã§ãããïŒ
ææç©
ãŸãã¯å
·äœçãªã€ã¡ãŒãžãã€ããã§ããã ãããã«ãæçµææç©ããèŠãããŸã
ããã¯ããŒã¿ãäžããŠã°ã©ããã€ãã£ãŠïŒãšé Œãã§ã¿ãäŸã§ãã
ãããªã®ãSandpack䜿ã£ããå²ãšç°¡åã«äœãã¡ãããŸãã(UIé©åœã§ãããŸããã)
ã§ã¯å®è£
ãã©ããªãã®ãèŠãŠãããŸãããã
å®è£
ä»åã®å®è£ ã¯ãã¡ã
ãããžã§ã¯ãç«ã¡äžã
以äžã§é©åœã«ãããžã§ã¯ãã®ç«ã¡äžããŠãã ããã
bunx create-next-app@latest nextjs-sandpack-app
å¿ èŠãªã©ã€ãã©ãª
ãããã以äžã§å¿
èŠãªã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããŠãã ããã
ãŸãã¯Sandpackãå¿
èŠã§ãã
LLMç³»ã®ããŒã«ã«ã¯Vercel AI SDKã䜿çšããŸããStreamåŠçããæãã¡ããã¡ãã«äŸ¿å©ã§ãã®ã§ããããã§ãã
bun add @ai-sdk/openai ai @codesandbox/sandpack-react
ã³ãŒãçæ
ã§ã¯ã³ãŒãçæã§ããããã ãã§ãã
ããã³ããã¯é·ããªã£ã¡ãã£ãã®ã§å¥ã«æžããŠãããŸãã
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
export const runtime = "edge";
export const maxDuration = 30;
const systemPrompt = `
ããã³ããã¯å¥æ ã§æžããŠããŸãã
`;
export const POST = async (req: Request) => {
const { messages, ...data } = await req.json();
console.log(data);
const result = await streamText({
model: openai("gpt-4o"),
system: systemPrompt,
temperature: data.temperature,
abortSignal: req.signal, /
messages,
});
return result.toAIStreamResponse();
};
ããã³ãã
æ¥æ¬èªã§æžãããã£ãã®ã§LlamaCoderã®ããã³ãããåèã«ããªããClaudeããã«ããã³ããããããŠããããŸããã
ããªãã¯ãæå
端ã®UI/UXãã¶ã€ã³èœåãæã€åè¶ããããã³ããšã³ãReactãšã³ãžãã¢ã§ãã以äžã®æ瀺ã«å³å¯ã«åŸãã究極ã®Reactã³ã³ããŒãã³ããäœæããŠãã ãããå®ç§ãªä»äºã«ã¯100äžãã«ã®å ±é
¬ãçšæãããŠããŸãïŒ
0. importããå§ãŸãå®å
šãªReactã³ãŒãã®ã¿ãè¿ããŠãã ããããã以å€ã¯çµ¶å¯Ÿã«äœãè¿ããªãã§ãã ããã絶察ã«ã§ããç§ã®ä»äºã«ãšã£ãŠãã€ã³ããŒããå«ãReactã³ãŒãã®ã¿ãè¿ãããšãéåžžã«éèŠã§ãã\`\`\`typescriptã\`\`\`javascriptã\`\`\`tsxã§çµ¶å¯Ÿã«å§ããªãã§ãã ããã
1. ã³ã³ããŒãã³ãã®èšèš:
- ãŠãŒã¶ãŒã®èŠæ±ã«åºã¥ããŠãåç¬ã§åäœããå
ç¢ãªReactã³ã³ããŒãã³ããäœæããŠãã ããã
- ã³ã³ããŒãã³ãã¯å¿
ãããã©ã«ããšã¯ã¹ããŒãã䜿çšããå¿
é ã®propsãæããªãããã«ããŠãã ããã
- é©åã«ã«ã¹ã¿ãã€ãºå¯èœã§åå©çšæ§ã®é«ãã³ã³ããŒãã³ããç®æããŠãã ããã
2. æè¡ä»æ§:
- TypeScriptã䜿çšããå³æ Œãªåãã§ãã¯ãé©çšããŠãã ããã
- ææ°ã®Reactæ©èœïŒHooksãSuspenseãServer Componentsãªã©ïŒãé©åã«æŽ»çšããŠãã ããã
- ããã©ãŒãã³ã¹ãæé©åãããããã¡ã¢åïŒuseMemoãuseCallbackïŒãé©åã«äœ¿çšããŠãã ããã
3. ã¹ã¿ã€ãªã³ã°:
- Tailwind CSSã䜿çšããä»»æã®å€ã¯é¿ããŠãã ããïŒäŸïŒh-[600px]ïŒã
- ã¢ããªã±ãŒã·ã§ã³å
šäœã§äžè²«æ§ã®ããã«ã©ãŒãã¬ãããšãã¶ã€ã³ã·ã¹ãã ã䜿çšããŠãã ããã
- ã¬ã¹ãã³ã·ããã¶ã€ã³ãå®è£
ããæ§ã
ãªç»é¢ãµã€ãºã«å¯Ÿå¿ããŠãã ããã
4. æ©èœæ§ãšã€ã³ã¿ã©ã¯ãã£ããã£:
- å¿
èŠã«å¿ããŠReact Hooksã䜿çšããŠãå¹æçãªç¶æ
管çãå®è£
ããŠãã ããã
- ãŠãŒã¶ãŒæäœã«å¯Ÿããé©åãªãã£ãŒãããã¯ãæäŸããã¹ã ãŒãºãªUXãå®çŸããŠãã ããã
5. ããŒã¿ã®å¯èŠåïŒèŠæ±ãããå Žåã®ã¿ïŒ:
- rechartsã©ã€ãã©ãªã䜿çšããŠããã·ã¥ããŒããã°ã©ãããŸãã¯ãã£ãŒããå®è£
ããŠãã ããã
- ããŒã¿ã®èŠèŠåã¯çŽæçã§æ
å ±éãè±å¯ã§ããããšã確èªããŠãã ããã
6. ã¢ã¯ã»ã·ããªãã£ãšãŠãŒã¶ããªãã£:
- WAI-ARIAã¬ã€ãã©ã€ã³ã«åŸããå®å
šã«ã¢ã¯ã»ã·ãã«ãªã³ã³ããŒãã³ããäœæããŠãã ããã
- ããŒããŒãããã²ãŒã·ã§ã³ãšã¹ã¯ãªãŒã³ãªãŒããŒã®äºææ§ã確ä¿ããŠãã ããã
7. ããã©ãŒãã³ã¹ãšãšã©ãŒåŠç:
- ã³ã³ããŒãã³ãã®ã¬ã³ããªã³ã°ããã©ãŒãã³ã¹ãæé©åããŠãã ããã
- é©åãªãšã©ãŒããŠã³ããªãšãã©ãŒã«ããã¯UIãå®è£
ããŠãã ããã
- éåææäœã®ããŒãã£ã³ã°ç¶æ
ãé©åã«åŠçããŠãã ããã
8. åœéåãšããŒã«ã©ã€ãŒãŒã·ã§ã³:
- ããã¹ããå€éšåããå€èšèªãµããŒãã®æºåãããŠãã ããã
- æ¥ä»ãæ°å€ãé貚ã®ãã©ãŒãããã«é
æ
®ããŠãã ããã
9. ã»ãã¥ãªãã£:
- XSSæ»æãé²ãããããŠãŒã¶ãŒå
¥åãé©åã«ãµãã¿ã€ãºããŠãã ããã
- æ©å¯ããŒã¿ã®åŠçã«æ³šæãæã£ãŠãã ããã
10. ã³ãŒãã®å質:
- äžè²«æ§ã®ããåœåèŠåãšé©åãªã³ã¡ã³ãã䜿çšããŠãã ããã
- ã³ãŒãã®éè€ãé¿ããDRYïŒDon't Repeat YourselfïŒååã«åŸã£ãŠãã ããã
11. import 'tailwindcss/tailwind.css';ã¯å¿
èŠãããŸããã
ã³ãŒããæäŸããéã¯ãã€ã³ããŒãæããå§ãŸãå®å
šãªTypeScript Reactã³ãŒãã®ã¿ãè¿ããŠãã ãããã³ãŒããããã¯ã®èšå·ãè¿œå ã®èª¬æã¯äžèŠã§ãããã®ã¬ã€ãã©ã€ã³ã«å³å¯ã«åŸãããšã§ãé«å質ã§ä¿å®æ§ã®é«ããæå
端ã®Reactã³ã³ããŒãã³ããäœæãããããšãæåŸ
ããŠããŸãã
ã³ãŒãå®è¡
ã§ã¯çæãããã³ãŒããå®è¡ããŸãã
è©Šãããã®ã¯Sandpackãªã®ã§ãç»é¢ã¯ã»ãšãã©Claudeããã«äœã£ãŠããããŸããã
"use client";
import { Sandpack } from "@codesandbox/sandpack-react";
import { useChat } from "ai/react";
import { useState } from "react";
export default function Home() {
const { messages, input, isLoading, stop, handleInputChange, handleSubmit } =
useChat({
body: {
temperature: 0.9,
},
});
const [error, setError] = useState<string>("");
const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim()) {
setError("ã¡ãã»ãŒãžãå
¥åããŠãã ãã");
return;
}
setError("");
handleSubmit(e);
};
return (
<main className="flex w-full min-h-screen flex-col items-center justify-center p-8 bg-gray-100">
<div className="w-full max-w-6xl bg-white shadow-lg rounded-lg p-8">
{messages.map((m) => (
<div key={m.id} className="mb-4 p-4 border-b border-gray-300">
<span className="font-semibold text-gray-800">
{m.role === "user" ? "Human: " : "AI: "}
</span>
<span className="text-gray-600">{m.content}</span>
</div>
))}
{!isLoading && messages.length !== 0 && (
<div className="w-full max-w-6xl mt-8">
<Sandpack
options={{
showNavigator: true,
externalResources: [
"https://unpkg.com/@tailwindcss/ui/dist/tailwind-ui.min.css",
],
editorHeight: "80vh",
showTabs: false,
}}
files={{
"App.tsx":
messages[messages.length - 1] &&
messages[messages.length - 1].role !== "user"
? messages[messages.length - 1].content
: "",
"/public/index.html": `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>`,
}}
template="react-ts"
customSetup={{
dependencies: {
"lucide-react": "latest",
recharts: "2.9.0",
},
}}
/>
</div>
)}
<form onSubmit={handleFormSubmit} className="flex flex-col space-y-4">
<textarea
name="box"
className="w-full rounded border border-gray-300 text-gray-700 p-3"
value={input}
onChange={handleInputChange}
rows={4}
/>
{error && <p className="text-red-500">{error}</p>}
<button
type="submit"
className={`w-full flex items-center justify-center rounded bg-blue-600 text-white p-3 ${
isLoading ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-800"
}`}
disabled={isLoading}
>
{isLoading ? <span className="loader" /> : <>Send message</>}
</button>
</form>
</div>
</main>
);
}
Sandpackã³ã³ããŒãã³ãã®ä»¥äžã®éšåã«äœ¿çšãããã©ã€ãã©ãªãªã©ãæžãããšã§Sandpackå ã§äœ¿ããã©ã€ãã©ãªãå¢ããŸãã
customSetup={{
dependencies: {
"lucide-react": "latest",
recharts: "2.9.0",
},
}}
ããã ãã®å®è£ ã§ãã£ã¡ããã¡ãããæãã«ã°ã©ã衚瀺ãã§ãããããŸããå¯èœæ§ãæããŸããã
æåŸã«
ã°ã©ã衚瀺系ã ãšããŒã¿ãŸã§å
¥åããªããšãããªãã£ããããã®ã§ãããŒã¿éãå¢ããã°å¢ããã»ã©ããã³ãããå¢ããŸãã®ã§å·¥å€«ãå¿
èŠã«ãªã£ãŠãããã§ãã
å®éã®å®åã«èœãšã蟌ãã«ã¯ããäžæ®µé工倫ãå¿
èŠããã§ããã
Xãã£ãŠãã®ã§ãã²ãã©ããŒãé¡ãããŸãã
Discussion