stevensDemoApp.tsx25 matches
82const [cookieAndTeaMode, setCookieAndTeaMode] = useState(false);
8384// Fetch images from backend instead of blob storage directly
85useEffect(() => {
86// Set default background color in case image doesn't load
87if (document.body) {
88document.body.style.backgroundColor = "#2D1700"; // Dark brown leather color
89}
9091// Fetch avatar image
92fetch("/api/images/stevens.jpg")
93.then((response) => {
94if (response.ok) return response.blob();
95throw new Error("Failed to load avatar image");
96})
97.then((imageBlob) => {
98const url = URL.createObjectURL(imageBlob);
99setAvatarUrl(url);
100})
104105// Fetch wood background
106fetch("/api/images/wood.jpg")
107.then((response) => {
108if (response.ok) return response.blob();
109throw new Error("Failed to load wood background");
110})
111.then((imageBlob) => {
112const url = URL.createObjectURL(imageBlob);
113setWoodUrl(url);
114115// Apply wood background to body
116if (document.body) {
117document.body.style.backgroundImage = `url(${url})`;
118}
119})
362return {
363position: SCENE_ELEMENTS.DESK_SITTING,
364image: ASSETS.STEVENS_FRONT,
365highlightElement: SCENE_ELEMENTS.DESK,
366animationClass: "no-animation",
372return {
373position: SCENE_ELEMENTS.DESK_SITTING,
374image: ASSETS.STEVENS_FRONT,
375highlightElement: null,
376animationClass: "no-animation",
387y: SCENE_ELEMENTS.MAILBOX.y - 20,
388},
389image: ASSETS.STEVENS_BACK,
390highlightElement: SCENE_ELEMENTS.MAILBOX,
391animationClass: "walk-to-mailbox",
398y: SCENE_ELEMENTS.CALENDAR.y + 30,
399},
400image: ASSETS.STEVENS_BACK,
401highlightElement: SCENE_ELEMENTS.CALENDAR,
402animationClass: "walk-to-calendar",
409y: SCENE_ELEMENTS.TELEGRAM.y + 10,
410},
411image: ASSETS.STEVENS_BACK,
412highlightElement: SCENE_ELEMENTS.TELEGRAM,
413animationClass: "walk-to-telegram",
417return {
418position: SCENE_ELEMENTS.OUTSIDE,
419image: ASSETS.STEVENS_FRONT,
420highlightElement: null,
421animationClass: "walk-to-outside",
425return {
426position: SCENE_ELEMENTS.DESK_SITTING,
427image: ASSETS.STEVENS_FRONT,
428highlightElement: SCENE_ELEMENTS.DESK,
429animationClass: "walk-to-desk",
623box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.4),
6240 3px 8px rgba(0, 0, 0, 0.5);
625image-rendering: pixelated;
626cursor: pointer;
627transition: transform 0.2s;
634.notebook-pages {
635background-color: #f8f1e0;
636background-image: linear-gradient(#d6c6a5 1px, transparent 1px);
637background-size: 100% 16px;
638box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.3);
639image-rendering: pixelated;
640}
641652653.pixel-character {
654image-rendering: pixelated;
655position: absolute;
656transition: left 0.7s ease-in-out, top 0.7s ease-in-out;
763className="w-[512px] h-[512px] mx-auto relative"
764style={{
765backgroundImage: `url(${ASSETS.BACKGROUND})`,
766backgroundSize: "cover",
767backgroundPosition: "center",
768imageRendering: "pixelated",
769}}
770>
799{/* Stevens character */}
800<img
801src={stevensState.image}
802alt="Stevens"
803className={`pixel-character ${
eink-frameindex.tsx9 matches
4import { Header } from "./components.tsx";
5import hemolog from "./frames/hemolog.tsx";
6import generateImageFromHtml from "./generateImageFromHtml.ts";
78export default async function(req: Request) {
9const url = new URL(req.url);
10const isImageRequest = url.searchParams.get("generate") === "image";
11const isListRequest = url.searchParams.get("generate") === "list";
12const frameId = url.searchParams.get("frame");
68}
6970// get ?&generate=image&frame=S0mth1ingKrAzy
71if (frameId && !frame_list.includes(frameId)) {
72const html = renderToString(
84const generateUrl = frames[frameId as keyof typeof frames];
8586// get ?&generate=image&frame=weather
87if (isImageRequest) {
88const width = 800;
89const height = 480;
9091const imageResponse = await generateImageFromHtml(generateUrl, width, height);
92return new Response(imageResponse.body, {
93status: imageResponse.status,
94headers: {
95"Content-Type": "image/png",
96},
97});
eink-framegenerateImageFromHtml.ts4 matches
2// API key required
34// TODO: Add caching of image
5export default async function generateImageFromHtml(
6valUrl: string,
7width: number = 800,
13const apiKey = Deno.env.get("API_FLASH_KEY");
14const generateUrl =
15`https://api.apiflash.com/v1/urltoimage?access_key=${apiKey}&url=${valUrl}&width=${width}&height=${height}&format=png&fresh=true`;
1617try {
22return response;
23} catch (error) {
24return new Response("Failed to generate image", { status: 500 });
25}
26}
eink-frameapod.ts1 match
8service_version: string;
9title: string;
10url: string; // image to display
11};
12
a4595dc5b24_index.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);
a4595dc5b24_handleUSPSEmail.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}
a4595dc5b24_.cursorrules2 matches
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
a4595dc5b24_index.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>
a4595dc5b24_README.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/`
a4595dc5b24_assets.ts6 matches
2// Background
3BACKGROUND:
4"https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/8b501664-722e-4be8-cf71-83aab7756e00/public",
56// Stevens
7STEVENS_FRONT:
8"https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/8b8432bb-add2-44ad-bb12-44b8ea215500/public",
9STEVENS_BACK:
10"https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/e28da8ab-7710-4b82-8e32-8fdf65c2ed00/public",
11STEVENS_WALKING:
12"https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/bd7b9997-09b2-4b35-6eb9-9975a85bb700/public",
1314// Mailman
15MAILMAN_STANDING:
16"https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/20a6493d-cc31-475e-aa83-ac97d317e400/public",
17MAILMAN_WALKING:
18"https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/61604576-8a83-4d85-d5e4-8e8e26641700/public",
19};
20