14 type TEXT,
15 creation_time DATETIME NOT NULL, /* Time from the main Pondiverse */
16 image_url TEXT, /* Direct URL to the main pondiverse image */
17 link TEXT UNIQUE NOT NULL, /* Link to the creation on the main site */
18 received_time DATETIME DEFAULT CURRENT_TIMESTAMP /* When this aggregator received it */
28 const results = await sqlite.execute({
29 // Fetch recent items, ordered by original creation time
30 sql: `SELECT title, type, creation_time, image_url, link
31 FROM ${AGGREGATOR_TABLE_NAME}
32 ORDER BY creation_time DESC
37 for (const item of results.rows) {
38 const pubDate = new Date(item.creation_time).toUTCString();
39 // Create description with type and image preview
40 const description = `<![CDATA[
41 <p>Type: ${escapeXml(item.type)}</p>
42 ${item.image_url ? `<p><img src="${escapeXml(item.image_url)}" alt="Thumbnail" style="max-width: 300px; max-height: 200px;" /></p>` : ''}
43 <p><a href="${escapeXml(item.link)}">View Creation Details</a></p>
44 ]]>`;
3import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";
4
5function Slideshow({ images }) {
6 useEffect(() => {
7 let currentSlideIndex = 0;
57 return (
58 <div className="slideshow-container">
59 {images.map((src, index) => (
60 <img key={index} className={`mySlides ${index === 0 ? 'visible' : ''}`} src={src} alt={`Slide ${index + 1}`} />
61 ))}
286 }, []);
287
288 const slideshowImages = [
289 "https://i.postimg.cc/KvSbQsgQ/download-4.png",
290 "https://i.postimg.cc/NMzVyLRp/download-5.png",
354 </div>
355 </div>
356 <Slideshow images={slideshowImages} />
357 <Card {...loginCardData} />
358 <Card {...scanCodeCardData} />
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/`
73});
74
75// --- Blob Image Serving Routes ---
76
77// GET /api/images/:filename - Serve images from blob storage
78app.get("/api/images/:filename", async (c) => {
79 const filename = c.req.param("filename");
80
81 try {
82 // Get image data from blob storage
83 const imageData = await blob.get(filename);
84
85 if (!imageData) {
86 return c.json({ error: "Image not found" }, 404);
87 }
88
90 let contentType = "application/octet-stream"; // Default
91 if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) {
92 contentType = "image/jpeg";
93 } else if (filename.endsWith(".png")) {
94 contentType = "image/png";
95 } else if (filename.endsWith(".gif")) {
96 contentType = "image/gif";
97 } else if (filename.endsWith(".svg")) {
98 contentType = "image/svg+xml";
99 }
100
101 // Return the image with appropriate headers
102 return new Response(imageData, {
103 headers: {
104 "Content-Type": contentType,
107 });
108 } catch (error) {
109 console.error(`Error serving image ${filename}:`, error);
110 return c.json(
111 { error: "Failed to load image", details: error.message },
112 500,
113 );
10 href="/public/favicon.svg"
11 sizes="any"
12 type="image/svg+xml"
13 />
14 <link rel="preconnect" href="https://fonts.googleapis.com" />
36 height: 100%;
37 font-family: "Pixelify Sans", sans-serif;
38 image-rendering: pixelated;
39 }
40 body::before {
50 /* For pixel art aesthetic */
51 * {
52 image-rendering: pixelated;
53 }
54 </style>
12}
13
14type ImageSummary = {
15 sender: string;
16 recipient: (typeof RECIPIENTS)[number] | "both" | "other";
22 anthropic: Anthropic,
23 htmlContent: string,
24 imageSummaries: ImageSummary[]
25) {
26 try {
36 text: `Analyze the following content from an email and provide a response as a JSON blob (only JSON, no other text) with two parts.
37
38 The email is from the USPS showing mail I'm receiving. Metadata about packages is stored directly in the email. Info about mail pieces is in images, so I've included summaries of those as well.
39
40 Your response should include:
66 And here is info about the mail pieces:
67
68 ${JSON.stringify(imageSummaries)}`,
69 },
70 ],
95 const anthropic = new Anthropic({ apiKey });
96
97 // Process each image attachment serially
98 const summaries = [];
99 for (const [index, attachment] of e.attachments.entries()) {
100 try {
101 const imageData = await attachment.arrayBuffer();
102 const base64Image = btoa(
103 String.fromCharCode(...new Uint8Array(imageData))
104 );
105
112 content: [
113 {
114 type: "image",
115 source: {
116 type: "base64",
117 media_type: attachment.type,
118 data: base64Image,
119 },
120 },
148 summaries.push(parsedResponse);
149 } catch (error) {
150 console.error(`Image analysis error:`, error);
151 summaries.push({
152 sender: "Error",
153 recipient: "Error",
154 type: "error",
155 notes: `Image ${index + 1} Analysis Failed: ${error.message}`,
156 });
157 }
187## Val Town Platform Specifics
188- **Redirects:** Use `return new Response(null, { status: 302, headers: { Location: "/place/to/redirect" }})` instead of `Response.redirect` which is broken
189- **Images:** Avoid external images or base64 images. Use emojis, unicode symbols, or icon fonts/libraries instead
190- For AI-generated images, use: `https://maxm-imggenurl.web.val.run/the-description-of-your-image`
191- **Storage:** DO NOT use the Deno KV module for storage
192- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
2 // Background
3 BACKGROUND:
4 "https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/8b501664-722e-4be8-cf71-83aab7756e00/public",
5
6 // Stevens
7 STEVENS_FRONT:
8 "https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/8b8432bb-add2-44ad-bb12-44b8ea215500/public",
9 STEVENS_BACK:
10 "https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/e28da8ab-7710-4b82-8e32-8fdf65c2ed00/public",
11 STEVENS_WALKING:
12 "https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/bd7b9997-09b2-4b35-6eb9-9975a85bb700/public",
13
14 // Mailman
15 MAILMAN_STANDING:
16 "https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/20a6493d-cc31-475e-aa83-ac97d317e400/public",
17 MAILMAN_WALKING:
18 "https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/61604576-8a83-4d85-d5e4-8e8e26641700/public",
19};
20