😇

🚀Next.jsSandpackでClaudeArtifactは実珟できる✚

2024/08/09に公開

はじめに

皆さん、LlamaCoderをご存知ですかこれは、Llama 3.1を䜿っお、Claudeのartifact機胜のようなこずを無料で実珟できるサヌビスなんです。

https://llamacoder.together.ai/

すごいず思いたせんかただ、Claudeず同様に、自分の䜿いたいラむブラリが䜿えないのが少々぀らいずころです。
でも、LlamaCoderの玠晎らしいずころは、OSSずしおコヌドが公開されおいるこず。

https://github.com/Nutlope/llamacoder

実装を芋おみるず、かなりシンプルで以䞋の流れだけなんです。

コヌド生成 → Sandpackでコヌド実行

Sandpackさえ䜿えれば、実は自分でも䜜れちゃうこずに気づいちゃいたした。
Sandpackはブラりザ䞊でラむブコヌド゚ディタヌずプレビュヌを提䟛するツヌルです。

https://sandpack.codesandbox.io/docs/getting-started

じゃあ䞀回自分で実装しおみるしかないですよね

成果物

たずは具䜓的なむメヌゞを぀かんでいただくために、最終成果物をお芋せしたす
これはデヌタを䞎えおグラフを぀くっおず頌んでみた䟋です。

こんなのがSandpack䜿ったら割ず簡単に䜜れちゃいたす。(UI適圓ですいたせん。)
では実装がどんなものか芋おいきたしょう。

実装

今回の実装はこちら

https://github.com/Kota-Yamaguchi/nextjs-sandpack-app

プロゞェクト立ち䞊げ

以䞋で適圓にプロゞェクトの立ち䞊げおください。

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さんにプロンプトをかいおもらいたした。
https://github.com/Nutlope/llamacoder/blob/main/app/api/generateCode/route.ts

あなたは、最先端の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やっおるのでぜひフォロヌお願いしたす。

https://x.com/hudebakonosoto

Discussion