10app.get("*", async (c, next) => {
11 const path = c.req.path;
12 if (path.startsWith("/api/") || c.req.header("Accept")?.includes("application/json")) {
13 return next();
14 }
20});
21
22app.route("/api", backend);
23app.get("/frontend/*", c => {
24 return serveFile(c.req.path, import.meta.url);
41 </h2>
42 <ol className="list-decimal pl-5 space-y-2 text-gray-700">
43 <li>Login with your Val Town API token</li>
44 <li>Add your Anthropic API key</li>
45 <li>Select a project to work on</li>
46 <li>Chat with Claude about your code</li>
90 </div>
91 <h3 className="font-medium text-gray-800 mb-1">Cost Tracking</h3>
92 <p className="text-sm text-gray-600">See estimated API usage costs for each interaction</p>
93 </div>
94 </div>
124 <ul className="list-disc pl-5 space-y-1 text-gray-700 mb-4">
125 <li>React frontend with TypeScript</li>
126 <li>Hono API server backend</li>
127 <li>Tailwind CSS for styling</li>
128 <li>Web Audio API for sound notifications</li>
129 <li>AI SDK for Claude integration</li>
130 </ul>
131 <p className="text-gray-700">
132 The application proxies requests to the Anthropic API and Val Town API, allowing Claude to view and edit your project files directly.
133 </p>
134 </div>
236 ];
237
238 // Capitalize first letter
239 if (num >= 0 && num <= 10) {
240 const word = words[num];
32
33 try {
34 const response = await fetch("/api/create-project", {
35 method: "POST",
36 headers: {
43
44 try {
45 const response = await fetch("/api/create-branch", {
46 method: "POST",
47 headers: {
8import { ChatMessages } from "./ChatMessages.tsx";
9import { ChatInput } from "./ChatInput.tsx";
10import { ApiKeyWarning } from "./ApiKeyWarning.tsx";
11import { processFiles } from "./ImageUpload.tsx";
12import { Preview } from "./Preview.tsx";
15 project,
16 bearerToken,
17 anthropicApiKey,
18 setProject,
19}: {
20 project: any;
21 bearerToken: string;
22 anthropicApiKey: string;
23 setProject: (project: any) => void;
24}) {
57 project,
58 branchId,
59 anthropicApiKey,
60 bearerToken,
61 selectedFiles,
170
171 <div className="p-6 flex flex-col h-full w-full">
172 <ApiKeyWarning show={!anthropicApiKey} />
173
174 <div className="flex flex-col lg:flex-row gap-4">
37 setIsLoadingBranches(true);
38 try {
39 const response = await fetch(`/api/project-branches?projectId=${projectId}`, {
40 headers: {
41 "Authorization": `Bearer ${bearerToken}`,
107 const fetchBranches = async () => {
108 try {
109 const response = await fetch(`/api/project-branches?projectId=${projectId}`, {
110 headers: {
111 "Authorization": `Bearer ${bearerToken}`,
17export function App() {
18 const [bearerToken, setBearerToken] = useLocalStorage("bearer", "");
19 const [anthropicApiKey, setAnthropicApiKey] = useLocalStorage("anthropic_api_key", "");
20 const [projectJSON, setProjectJSON] = useLocalStorage("project", "");
21 const [project, setProject_] = useState(safeParse(projectJSON));
30 setBearerToken("");
31 setShowLogin(false);
32 // Keep the anthropic API key in case the user wants to reuse it
33 }
34
41 <Chat
42 bearerToken={bearerToken}
43 anthropicApiKey={anthropicApiKey}
44 project={project}
45 setProject={setProject}
3 { bearerToken, projectId, branchId }: { bearerToken: string; projectId: string; branchId?: string },
4) {
5 const url = new URL("/api/project-files", window.location.origin);
6 url.searchParams.append("projectId", projectId);
7 if (branchId) {
2import React from "https://esm.sh/react@18.2.0?dev";
3
4interface ApiKeyWarningProps {
5 show: boolean;
6}
7
8export function ApiKeyWarning({ show }: ApiKeyWarningProps) {
9 if (!show) return null;
10
11 return (
12 <div className="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-4 rounded">
13 <p className="font-bold">Anthropic API Key Missing</p>
14 <p>Please log out and add your Anthropic API key to use this app.</p>
15 </div>
16 );