57return new Response(svg, {
58headers: {
59"Content-Type": "image/svg+xml",
60"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
61"Pragma": "no-cache",
41return new Response(svg, {
42headers: {
43"Content-Type": "image/svg+xml",
44"Cache-Control": "public, max-age=86400",
45"Access-Control-Allow-Origin": "*"
mynewprojectjxnredirects1 match
2425redirects.set("/avatar", "https://jxnblk-avatar.web.val.run/");
26redirects.set("/images/avatar.png", "https://jxnblk-avatar.web.val.run/");
27redirects.set("/avatar.png", "https://jxnblk-avatar.web.val.run/");
28
openTownieMadeThis1style.css1 match
158/* Hero Section Styles */
159.hero-section {
160background-image: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url('https://maxm-imggenurl.web.val.run/disneyland-castle-at-night-with-purple-lighting');
161background-size: cover;
162background-position: center;
svgRenderersvgRenderer.ts1 match
25return new Response(svgContent, {
26headers: {
27"Content-Type": "image/svg+xml",
28"Cache-Control": "public, max-age=86400",
29"Access-Control-Allow-Origin": "*"
OpenTowniesystem_prompt.txt10 matches
12- If a section of code that you're working on is getting too complex, consider refactoring it into subcomponents
1314## Image Handling
1516- When users upload images, carefully analyze them to understand their content
17- Reference specific details from the images in your responses
18- If multiple images are uploaded, consider their relationship to each other
19- For code-related images (screenshots, diagrams), extract relevant information and incorporate it into your solutions
20- For UI mockups or design images, use them as reference for layout and design, use the colors from the image
21- When images contain text or code, transcribe relevant portions as needed
22- If image content is unclear, ask clarifying questions about what the user wants you to focus on
2324## Technical Requirements
95## Val Town Platform Specifics
96- **Redirects:** Use `return new Response(null, { status: 302, headers: { Location: "/place/to/redirect" }})` instead of `Response.redirect` which is broken
97- **Images:** Avoid external images or base64 images. Use emojis, unicode symbols, or icon fonts/libraries instead
98- For AI-generated images, use: `https://maxm-imggenurl.web.val.run/the-description-of-your-image`
99- **Storage:** DO NOT use the Deno KV module for storage
100- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
OpenTownieImageUpload.tsx49 matches
2import React, { useRef, useState } from "https://esm.sh/react@18.2.0?dev";
34// Maximum number of images that can be uploaded
5export const PROMPT_IMAGE_LIMIT = 5;
67interface ImageUploadProps {
8images: (string | null)[];
9setImages: (images: (string | null)[]) => void;
10}
1112export function ImageUpload({ images, setImages }: ImageUploadProps) {
13const fileInputRef = useRef<HTMLInputElement>(null);
14const [isDragging, setIsDragging] = useState(false);
23// Process the selected files
24const processFiles = async (files: File[]) => {
25// Filter for image files only
26const imageFiles = files.filter(file => file.type.startsWith('image/'));
27
28// Limit the number of images
29const filesToProcess = imageFiles.slice(0, PROMPT_IMAGE_LIMIT - images.filter(Boolean).length);
30
31if (filesToProcess.length === 0) return;
3233// Add null placeholders for loading state
34const newImages = [...images, ...Array(filesToProcess.length).fill(null)];
35setImages(newImages);
3637// Process each file
38const processedImages = await Promise.all(
39filesToProcess.map(async (file) => {
40return await readFileAsDataURL(file);
42);
4344// Replace null placeholders with actual images
45const updatedImages = [...images];
46processedImages.forEach((dataUrl, index) => {
47updatedImages[images.length + index] = dataUrl;
48});
49
50setImages(updatedImages.slice(0, PROMPT_IMAGE_LIMIT));
51};
5257reader.onload = () => {
58const result = reader.result as string;
59console.log("Image loaded, size:", result.length, "bytes");
60resolve(result);
61};
93};
9495// Handle removing an image
96const removeImage = (index: number) => {
97const newImages = [...images];
98newImages.splice(index, 1);
99setImages(newImages);
100};
101102// Check if we've reached the image limit
103const isAtLimit = images.filter(Boolean).length >= PROMPT_IMAGE_LIMIT;
104105return (
106<div className="w-full">
107{/* Image previews */}
108{images.length > 0 && (
109<div className="flex flex-wrap gap-2 mb-2">
110{images.map((image, index) => (
111<div key={index} className="relative">
112{image ? (
113<div className="relative group">
114<img
115src={image}
116alt={`Uploaded ${index + 1}`}
117className="h-16 w-16 object-cover rounded border border-gray-300"
119<button
120className="absolute top-0 right-0 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
121onClick={() => removeImage(index)}
122title="Remove image"
123>
124×
151ref={fileInputRef}
152onChange={handleFileChange}
153accept="image/*"
154multiple
155className="hidden"
157<div className="text-sm text-gray-500">
158{isDragging ? (
159"Drop images here"
160) : (
161<>
162<span className="text-blue-500">Upload images</span> or drag and drop
163<div className="text-xs mt-1">
164{images.filter(Boolean).length}/{PROMPT_IMAGE_LIMIT} images
165</div>
166</>
173}
174175// Component to display images in messages
176export function ImagePreview({ images }: { images: string[] }) {
177const [expandedImage, setExpandedImage] = useState<string | null>(null);
178179if (!images || images.length === 0) return null;
180181return (
182<div className="mt-2">
183<div className="flex flex-wrap gap-2">
184{images.map((image, index) => (
185<div key={index} className="relative">
186<img
187src={image}
188alt={`Image ${index + 1}`}
189className="max-h-32 max-w-32 object-contain rounded cursor-pointer"
190onClick={() => setExpandedImage(image)}
191/>
192</div>
194</div>
195196{/* Modal for expanded image */}
197{expandedImage && (
198<div
199className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50"
200onClick={() => setExpandedImage(null)}
201>
202<div className="max-w-[90%] max-h-[90%]">
203<img
204src={expandedImage}
205alt="Expanded view"
206className="max-w-full max-h-full object-contain"
md_crawlindex.html1 match
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>MD Crawl - Website to Markdown Crawler</title>
7<link rel="icon" href="/public/favicon.svg" type="image/svg+xml">
8<script src="https://cdn.twind.style" crossorigin></script>
9<script src="https://esm.town/v/std/catch"></script>
svgEditorSvgEditor.tsx5 matches
84// Handle downloading the SVG
85const handleDownload = () => {
86const blob = new Blob([svgContent], { type: 'image/svg+xml' });
87const url = URL.createObjectURL(blob);
88const a = document.createElement('a');
95};
9697// Image to SVG conversion
98const handleImageToSvg = (imageDataUrl: string) => {
99// Create a placeholder SVG that embeds the image
100// This is a simple embedding, not a true vector conversion
101const imgSvg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 600">
102<image href="${imageDataUrl}" width="100%" height="100%" />
103</svg>`;
104
4import React, { useEffect, useRef, useState } from "https://esm.sh/react@18.2.0?dev";
5import { playBellSound } from "../utils/soundEffects.ts";
6import { ImageUpload, PROMPT_IMAGE_LIMIT } from "./ImageUpload.tsx";
7import { MessagePart } from "./MessagePart.tsx";
8import { SvgEditor } from "./SvgEditor.tsx";
22const [usages, setUsages] = useState<Record<string, any>>({});
23const [soundEnabled, setSoundEnabled] = useLocalStorage<boolean>("soundEnabled", true);
24const [images, setImages] = useState<(string | null)[]>([]);
25const [currentSvg, setCurrentSvg] = useState<string>("");
26const [showSvgEditor, setShowSvgEditor] = useState<boolean>(true);
63project,
64anthropicApiKey,
65images: images
66.filter((img): img is string => {
67// Make sure it's a string and a valid data URL
68const isValid = typeof img === "string" && img.startsWith("data:");
69if (!isValid && img !== null) {
70console.warn("Invalid image format:", img?.substring(0, 50) + "...");
71}
72return isValid;
102const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
103e.preventDefault();
104const validImages = images.filter((img): img is string => typeof img === "string");
105console.log("Submitting with images:", validImages);
106if (input.trim() || validImages.length > 0) {
107originalHandleSubmit(e);
108setImages([]);
109}
110};
253onSubmit={handleSubmit}
254>
255{(images.length > 0 || !running) && <ImageUpload images={images} setImages={setImages} />}
256257<div className="flex gap-2">
268if (e.key === "Enter" && !e.shiftKey && !isMobile) {
269e.preventDefault();
270if (input.trim() || images.filter(Boolean).length > 0) {
271handleSubmit(e as any);
272}
291}
292}}
293title="Attach images"
294>
295📎