16name: string; // Name of the fantasy race
17description: string; // Short description (1-2 sentences)
18styleHint: string; // Keywords for image generation (e.g., "elven archer forest")
19effectColor: string; // CSS hex color for UI effects (e.g., "#4A90E2")
20}
70- 'name': The name of the race (string).
71- 'description': A brief description (string, 1-2 sentences maximum).
72- 'styleHint': 3-5 descriptive keywords for generating an image (string).
73- 'effectColor': A valid CSS hexadecimal color code string (e.g., "#RRGGBB").
74218.race-card img {
219width: 100%;
220height: 60%; /* Image takes upper portion */
221object-fit: cover;
222display: block;
355}
356.race-card img {
357height: 320px; /* Fixed image height on desktop */
358border-radius: 16px 16px 0 0;
359}
466card.style.setProperty('--effect-color', safeEffectColor); // Set the CSS variable for this card
467468// --- Image Generation ---
469// Construct URL for Val Town image generator (ensure this endpoint exists and works)
470// Encode components to handle spaces and special characters in prompts
471const prompt = encodeURIComponent(\`fantasy character portrait, \${safeStyleHint}, \${safeName}, detailed illustration, cinematic lighting\`);
472const imageUrl = \`https://maxm-imggenurl.web.val.run/\${prompt}\`; // Replace with your actual image generation endpoint if different
473474// Fallback placeholder image using placehold.co (useful for debugging/offline)
475const fallbackColor = safeEffectColor.substring(1); // Remove '#'
476const fallbackUrl = \`https://placehold.co/380x320/\${fallbackColor}/1A1A1E?text=\${encodeURIComponent(safeName)}\`;
478// --- Card HTML Structure ---
479// Use template literal for clean HTML construction.
480// Includes the image, effect container, and content area.
481// The 'onerror' attribute on the img tag handles image loading failures gracefully.
482card.innerHTML = \`
483<img src="\${imageUrl}"
484alt="Image of \${safeName}"
485loading="lazy" onerror="this.onerror=null; this.src='\${fallbackUrl}'; console.warn('Image failed to load for \${safeName} (Prompt: \${decodeURIComponent(prompt)}). Using fallback.');">
486<div class="\${EFFECT_CONTAINER_CLASS}">
487<div class="energy-surge"></div>
660function handleDragStart(e) {
661if (isTransitioning) return; // Ignore drags during transitions
662// Prevent dragging text/images on the page
663if (e.target.tagName === 'IMG' || e.target.tagName === 'H3' || e.target.tagName === 'P') {
664e.preventDefault();
reactHonoStarterindex.html1 match
6<title>React Hono Val Town Starter</title>
7<link rel="stylesheet" href="/public/style.css">
8<link rel="icon" href="/public/favicon.svg" sizes="any" type="image/svg+xml">
9</head>
10<body>
701<meta name="viewport" content="width=device-width, initial-scale=1.0">
702<title>🔗 Policy Analysis Tool</title>
703<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🔗</text></svg>">
704<style>${embeddedCss}</style>
705</head>
faithfulTanEelmain.tsx7 matches
9name: string;
10description: string;
11styleHint: string; // Keywords for image generation prompt
12effectColor: string; // CSS hex color for effects (e.g., "#4A90E2")
13}
55const prompt = `
56Generate a list of 4 distinct fantasy character races for an RPG character creator carousel.
57For each race, provide: 'name', 'description' (2 short sentences), 'styleHint' (3-5 keywords for image generation), and 'effectColor' (a valid CSS hex color code string like "#RRGGBB").
5859Return the result STRICTLY as a single JSON object with one key "races", where the value is an array of objects, each representing a race.
244const safeStyleHint = race.styleHint || safeName; // Fallback style hint to name
245246// Generate image URL using the Val Town image generator endpoint
247const prompt = encodeURIComponent(\`fantasy character portrait \${safeStyleHint}, \${safeName}, high detail\`);
248const imageUrl = \`https://maxm-imggenurl.web.val.run/\${prompt}\`;
249// Fallback placeholder image URL
250const fallbackUrl = \`https://placehold.co/380x320/\${effectColor.substring(1)}/ffffff?text=\${encodeURIComponent(safeName)}\`;
251252// Use template literal for card's inner HTML
253card.innerHTML = \`
254<img src="\${imageUrl}"
255alt="\${safeName}"
256onerror="this.onerror=null; this.src='\${fallbackUrl}'; console.warn('Image failed for \${safeName}, prompt: \${prompt}')">
257<div class="\${EFFECT_CONTAINER_CLASS}">
258<div class="energy-surge"></div>
21</div>
22<div className="item-detail-content">
23{item.images && item.images.length > 0 && (
24<div className="item-images-gallery">
25{item.images.map((image, index) => (
26<img
27key={index}
28src={image}
29alt={`${item.title} image ${index + 1}`}
30className="item-gallery-image"
31/>
32))}
49description: '',
50price: '',
51images: []
52});
5354const handleImageUpload = async (e) => {
55const files = Array.from(e.target.files).slice(0, 5);
56const base64Images = await Promise.all(
57files.map(file => new Promise((resolve, reject) => {
58const reader = new FileReader();
62}))
63);
64setFormData(prev => ({ ...prev, images: base64Images }));
65};
6676description: '',
77price: '',
78images: []
79});
80};
134</div>
135<div className="form-group">
136<label htmlFor="images">Upload Images (Max 5)</label>
137<input
138id="images"
139type="file"
140multiple
141accept="image/*"
142onChange={handleImageUpload}
143max={5}
144/>
145{formData.images.length > 0 && (
146<div className="image-preview">
147{formData.images.map((image, index) => (
148<img
149key={index}
150src={image}
151alt={`Preview ${index + 1}`}
152className="preview-thumbnail"
197<h3>{item.title}</h3>
198<p>Price: ${item.price}</p>
199{item.images && item.images.length > 0 && (
200<img
201src={item.images[0]}
202alt={item.title}
203className="item-thumbnail"
411}
412413.image-preview {
414margin-top: 10px;
415display: flex;
426}
427428.image-count {
429background-color: var(--secondary-color);
430color: white;
463}
464465.item-images-gallery {
466display: flex;
467flex-wrap: wrap;
470}
471472.item-gallery-image {
473max-width: 100%;
474max-height: 400px;
stevensDemoREADME.md1 match
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:
45
67For 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.
stevensDemoREADME.md1 match
21## `favicon.svg`
2223As 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/).
2425## `components/`
stevensDemoindex.ts15 matches
73});
7475// --- Blob Image Serving Routes ---
7677// GET /api/images/:filename - Serve images from blob storage
78app.get("/api/images/:filename", async (c) => {
79const filename = c.req.param("filename");
8081try {
82// Get image data from blob storage
83const imageData = await blob.get(filename);
8485if (!imageData) {
86return c.json({ error: "Image not found" }, 404);
87}
8890let contentType = "application/octet-stream"; // Default
91if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) {
92contentType = "image/jpeg";
93} else if (filename.endsWith(".png")) {
94contentType = "image/png";
95} else if (filename.endsWith(".gif")) {
96contentType = "image/gif";
97} else if (filename.endsWith(".svg")) {
98contentType = "image/svg+xml";
99}
100101// Return the image with appropriate headers
102return new Response(imageData, {
103headers: {
104"Content-Type": contentType,
107});
108} catch (error) {
109console.error(`Error serving image ${filename}:`, error);
110return c.json(
111{ error: "Failed to load image", details: error.message },
112500,
113);
stevensDemoindex.html3 matches
10href="/public/favicon.svg"
11sizes="any"
12type="image/svg+xml"
13/>
14<link rel="preconnect" href="https://fonts.googleapis.com" />
36height: 100%;
37font-family: "Pixelify Sans", sans-serif;
38image-rendering: pixelated;
39}
40body::before {
50/* For pixel art aesthetic */
51* {
52image-rendering: pixelated;
53}
54</style>
stevensDemohandleUSPSEmail.ts12 matches
12}
1314type ImageSummary = {
15sender: string;
16recipient: (typeof RECIPIENTS)[number] | "both" | "other";
22anthropic: Anthropic,
23htmlContent: string,
24imageSummaries: ImageSummary[]
25) {
26try {
36text: `Analyze the following content from an email and provide a response as a JSON blob (only JSON, no other text) with two parts.
3738The 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.
3940Your response should include:
66And here is info about the mail pieces:
6768${JSON.stringify(imageSummaries)}`,
69},
70],
95const anthropic = new Anthropic({ apiKey });
9697// Process each image attachment serially
98const summaries = [];
99for (const [index, attachment] of e.attachments.entries()) {
100try {
101const imageData = await attachment.arrayBuffer();
102const base64Image = btoa(
103String.fromCharCode(...new Uint8Array(imageData))
104);
105112content: [
113{
114type: "image",
115source: {
116type: "base64",
117media_type: attachment.type,
118data: base64Image,
119},
120},
148summaries.push(parsedResponse);
149} catch (error) {
150console.error(`Image analysis error:`, error);
151summaries.push({
152sender: "Error",
153recipient: "Error",
154type: "error",
155notes: `Image ${index + 1} Analysis Failed: ${error.message}`,
156});
157}