73
74function Avatar ({ user }) {
75 if (!user?.profileImageUrl) {
76 return (
77 <div className="avatar" />
81 return (
82 <img
83 src={user.profileImageUrl}
84 alt={user.username}
85 width="32"
10 return new Response(svg, {
11 headers: {
12 "Content-Type": "image/svg+xml",
13 },
14 });
12 total_price: number;
13 inference_price: number;
14 total_images: number;
15 used_inference_data?: boolean;
16}
44 <th>Total Price</th>
45 <th>Inference Price</th>
46 <th>Images</th>
47 </tr>
48 </thead>
59 <td class="price">${formatPrice(row.total_price)} ${row.used_inference_data ? '<span class="badge badge-info" title="Using inference data">I</span>' : ''}</td>
60 <td class="price">${formatPrice(row.inference_price || 0)}</td>
61 <td>${formatNumber(row.total_images)}</td>
62 </tr>
63 `).join("")}
178
179- **Redirects:** Use `return new Response(null, { status: 302, headers: { Location: "/place/to/redirect" }})` instead of `Response.redirect` which is broken
180- **Images:** Avoid external images or base64 images. Use emojis, unicode symbols, or icon fonts/libraries instead
181- **AI Image:** To inline generate an AI image use: `<img src="https://maxm-imggenurl.web.val.run/the-description-of-your-image" />`
182- **Storage:** DO NOT use the Deno KV module for storage
183- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
10import { useCreditBalance } from "../hooks/useCreditBalance.tsx";
11import { Messages } from "./Messages.tsx";
12import { InputBox, ImageDropContainer } from "./InputBox.tsx";
13import { PreviewFrame } from "./PreviewFrame.tsx";
14import { BranchSelect } from "./BranchSelect.tsx";
68 refetch: () => void;
69}) {
70 const [images, setImages] = useState<(string|null)[]>([]);
71 const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
72 const { audio, user } = useContext(AppContext);
88 branchId,
89 selectedFiles,
90 images,
91 soundEnabled: audio,
92 });
137
138 return (
139 <ImageDropContainer
140 running={running}
141 images={images}
142 setImages={setImages}>
143 <div className="single-column-container">
144 <div className="single-sticky-header">
148 rel="norefferer"
149 className="block-link text-link lockup">
150 {project.imageUrl ? (
151 <img src={project.imageUrl} className="image-thumbnail" />
152 ) : user?.profileImageUrl ? (
153 <img
154 src={user.profileImageUrl}
155 className="avatar"
156 alt={user.username}
159 />
160 ) : (
161 <div className="image-placeholder" />
162 )}
163 <div>{project.name}</div>
211 onSubmit={e => {
212 handleSubmit(e);
213 setImages([]);
214 }}
215 onCancel={handleStop}
216 running={running}
217 error={error}
218 images={images}
219 setImages={setImages}
220 />
221 )}
223 </div>
224 </div>
225 </ImageDropContainer>
226 );
227}
65 type: itemCategory, // Verwende hier die Kategorie, die Teachable Machine liefert
66 wardrobe_item_id: wardrobeItem ? wardrobeItem.id : null,
67 // Optional: imageUrl: wardrobeItem ? wardrobeItem.imageUrl : null,
68 // Wenn du Bilder der vorgeschlagenen Items im Frontend anzeigen willst,
69 // müsste diese Information hier mitgegeben und im Frontend verarbeitet werden.
11 * Fügt ein Kleidungsstück zum Kleiderschrank des Benutzers hinzu.
12 * Wird vom Frontend aufgerufen, wenn ein Benutzer ein Bild hochlädt.
13 * @param {string} imageUrl - Die Base64-kodierte Data URL des Bildes.
14 * @param {string} description - Eine kurze, vom Benutzer eingegebene Beschreibung des Kleidungsstücks (z.B. "Blaues T-Shirt").
15 * @returns {Promise<any>} Das gespeicherte Kleidungsstück-Objekt.
16 */
17export async function addWardrobeItem(imageUrl: string, description: string) {
18 if (!imageUrl || !description || description.trim() === "") {
19 throw new Error("imageUrl und description sind erforderlich.");
20 }
21 const item = {
22 id: crypto.randomUUID(), // Generiert eine eindeutige ID für jedes Kleidungsstück.
23 userId: USER_ID, // Weist das Item dem aktuellen Benutzer zu.
24 imageUrl: imageUrl, // Speichert die Bild-URL.
25 description: description.trim(), // Speichert die bereinigte Beschreibung.
26 timestamp: new Date().toISOString(), // Speichert den Zeitpunkt des Uploads.
65 if (req.method === "POST") {
66 try {
67 const { imageUrl, description } = await req.json();
68 const newItem = await addWardrobeItem(imageUrl, description);
69 // Erfolgreiche Antwort mit dem neuen Item.
70 return new Response(JSON.stringify(newItem), { status: 201, headers: { "Content-Type": "application/json" } });
6 <title>React Hono Val Town Starter</title>
7 <script src="https://cdn.tailwindcss.com"></script>
8 <link rel="icon" href="/public/favicon.svg" sizes="any" type="image/svg+xml">
9 </head>
10 <body class="bg-gray-100 font-sans">
3It's common to have code and types that are needed on both the frontend and the backend. It's important that you write this code in a particularly defensive way because it's limited by what both environments support:
4
5
6
7For example, you *cannot* use the `Deno` keyword. For imports, you can't use `npm:` specifiers, so we reccomend `https://esm.sh` because it works on the server & client. You *can* use TypeScript because that is transpiled in `/backend/index.ts` for the frontend. Most code that works on the frontend tends to work in Deno, because Deno is designed to support "web-standards", but there are definitely edge cases to look out for.
21## `favicon.svg`
22
23As of this writing Val Town only supports text files, which is why the favicon is an SVG and not an .ico or any other binary image format. If you need binary file storage, check out [Blob Storage](https://docs.val.town/std/blob/).
24
25## `components/`