1// This program creates an image generation service using the maxm-imggenurl API.
2// It provides a simple HTML interface for users to enter a prompt and generate an image.
3// The generated images are displayed in a grid, and clicking on an image shows a popup with the image and prompt.
45/** @jsxImportSource https://esm.sh/react */
7import { createRoot } from "https://esm.sh/react-dom/client";
89// Define a type for our generated images
10type GeneratedImage = { imageUrl: string; prompt: string };
1112function App() {
13const [prompt, setPrompt] = useState("");
14const [imageUrl, setImageUrl] = useState("");
15const [loading, setLoading] = useState(false);
1617const generateImage = async () => {
18setLoading(true);
19try {
20const response = await fetch(`/generate?prompt=${encodeURIComponent(prompt)}`, { method: 'POST' });
21const data = await response.json();
22setImageUrl(data.imageUrl);
23// After generating, fetch the updated list of images
24fetchImages();
25} catch (error) {
26console.error("Error generating image:", error);
27}
28setPrompt(""); // Clear the prompt after generating
30};
3132const [images, setImages] = useState<GeneratedImage[]>([]);
33const [selectedImage, setSelectedImage] = useState<GeneratedImage | null>(null);
3435const fetchImages = async () => {
36try {
37const response = await fetch('/images');
38const data = await response.json();
39setImages(data);
40} catch (error) {
41console.error("Error fetching images:", error);
42}
43};
4445React.useEffect(() => {
46fetchImages();
47}, []);
4849const openPopup = (image: GeneratedImage) => {
50setSelectedImage(image);
51};
5253const closePopup = () => {
54setSelectedImage(null);
55};
5657return (
58<div className="container">
59<h1>AI Image Generator</h1>
60<div className="input-container">
61<input
63value={prompt}
64onChange={(e) => setPrompt(e.target.value)}
65placeholder="Enter your image prompt"
66/>
67<button onClick={generateImage} disabled={loading || !prompt}>
68{loading ? "Generating..." : "Generate Image"}
69</button>
70</div>
71<div className="image-grid">
72{images.map((image, index) => (
73<div key={index} className="image-item" onClick={() => openPopup(image)}>
74<img src={image.imageUrl} alt={image.prompt} />
75</div>
76))}
77</div>
78{selectedImage && (
79<div className="popup-overlay" onClick={closePopup}>
80<div className="popup-content" onClick={(e) => e.stopPropagation()}>
81<img src={selectedImage.imageUrl} alt={selectedImage.prompt} />
82<p>{selectedImage.prompt}</p>
83<button onClick={closePopup}>Close</button>
84</div>
103const { sqlite } = await import("https://esm.town/v/stevekrouse/sqlite");
104const url = new URL(request.url);
105const KEY = "genimage";
106107// Create table if it doesn't exist
108await sqlite.execute(`
109CREATE TABLE IF NOT EXISTS ${KEY}_images (
110id INTEGER PRIMARY KEY AUTOINCREMENT,
111imageUrl TEXT NOT NULL,
112prompt TEXT NOT NULL
113)
123}
124
125const imageUrl = `https://maxm-imggenurl.web.val.run/${encodeURIComponent(prompt)}`;
126
127// Store the generated image in the database
128await sqlite.execute(`
129INSERT INTO ${KEY}_images (imageUrl, prompt) VALUES (?, ?)
130`, [imageUrl, prompt]);
131132return new Response(JSON.stringify({ imageUrl }), {
133headers: { "Content-Type": "application/json" },
134});
135}
136137if (url.pathname === "/images") {
138const result = await sqlite.execute(`SELECT * FROM ${KEY}_images ORDER BY id DESC`);
139return new Response(JSON.stringify(result.rows), {
140headers: { "Content-Type": "application/json" },
148<meta charset="UTF-8">
149<meta name="viewport" content="width=device-width, initial-scale=1.0">
150<title>AI Image Generator</title>
151<style>${css}</style>
152</head>
204cursor: not-allowed;
205}
206.image-grid {
207display: grid;
208grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
210margin-top: 20px;
211}
212.image-item {
213cursor: pointer;
214transition: transform 0.3s ease;
215}
216.image-item:hover {
217transform: scale(1.05);
218}
219.image-item img {
220width: 100%;
221height: 150px;
261return (
262<div className="container">
263<h1>AI Image Generator</h1>
264<div className="input-container">
265<input
267value={prompt}
268onChange={(e) => setPrompt(e.target.value)}
269placeholder="Enter your image prompt"
270/>
271<button onClick={generateImage} disabled={loading || !prompt}>
272{loading ? "Generating..." : "Generate Image"}
273</button>
274</div>
275<div className="image-grid">
276{images.map((image, index) => (
277padding: 20px;
278background-color: #fff;
307cursor: not-allowed;
308}
309.image-container {
310text-align: center;
311}
7* Type text prompts, select it, press "Q". Select a previous generation with a new text prompt to keep iterating. Selecting shapes doesn't work yet. Have fun!
89<a href="https://x.com/JanPaul123/status/1815502582015754657"><img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/5893dfbf-c2de-4be0-049e-d8fdd1970a00/public"/></a>
10
10* Create a [Val Town API token](https://www.val.town/settings/api), open the browser preview of this val, and use the API token as the password to log in.
1112<img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/7077d1b5-1fa7-4a9b-4b93-f8d01d3e4f00/public"/>
10* Create a [Val Town API token](https://www.val.town/settings/api), open the browser preview of this val, and use the API token as the password to log in.
1112<img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/7077d1b5-1fa7-4a9b-4b93-f8d01d3e4f00/public"/>
7* Type text prompts, select it, press "Q". Select a previous generation with a new text prompt to keep iterating. Selecting shapes doesn't work yet. Have fun!
89<a href="https://x.com/JanPaul123/status/1815502582015754657"><img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/5893dfbf-c2de-4be0-049e-d8fdd1970a00/public"/></a>
10
fluxImageGeneratormain.tsx22 matches
1// This app uses the fal.ai API to generate images based on user prompts.
2// It features a clean UI with an input field for the prompt and a button to generate the image.
3// The generated image is displayed below the input field.
4// React is used for the UI and the fal.ai serverless client for image generation.
5// The app measures and displays the latency for each image generation.
6// The background features randomly placed pixelart lightning bolts in neon yellow.
717const BOLT_SIZE = 40; // Width and height of each bolt
18const DEFAULT_PROMPT = "green grass landscape with text \"val.town\" is engraved, a townhouse in the background";
19const PLACEHOLDER_IMAGE = "https://fal.media/files/penguin/PysYf1-_ddhM7JKiLrkcF.png";
20const RATE_LIMIT = 15; // requests per hour
21const RATE_LIMIT_WINDOW = 5 * 60 * 1000; // 5 minutes in milliseconds
72function App() {
73const [prompt, setPrompt] = useState(DEFAULT_PROMPT);
74const [imageUrl, setImageUrl] = useState(PLACEHOLDER_IMAGE);
75const [latency, setLatency] = useState<number | null>(null);
76const [loading, setLoading] = useState(false);
77const [error, setError] = useState("");
7879const generateImage = async () => {
80setLoading(true);
81setError("");
87});
88const data = await response.json();
89if (!response.ok) throw new Error(data.error || "Failed to generate image");
90setLatency(data.latency);
91setImageUrl(data.imageUrl);
92setError(""); // Clear any previous errors
93} catch (err) {
103<Background />
104<div className="container">
105<h1 className="title">AI Image Generator</h1>
106<div className="input-container">
107<input
113/>
114</div>
115<button onClick={generateImage} disabled={loading || !prompt} className="generate-btn">
116{loading ? "Generating..." : "Generate Image"}
117</button>
118{error && <p className="error">{error}</p>}
119<div className="image-container">
120{latency !== null && (
121<p className="latency">Inference time (doesn't include network): {latency.toFixed(2)} ms</p>
123{loading
124? <LoadingSpinner />
125: <img src={imageUrl} alt="Generated image" className="generated-image" />}
126</div>
127<div className="footer">
193input: {
194prompt,
195image_size: "landscape_4_3",
196num_inference_steps: 4,
197num_images: 1,
198enable_safety_checker: true,
199sync_mode: true,
201});
202return new Response(
203JSON.stringify({ imageUrl: result.images[0].url, latency: result.timings.inference * 1000 }),
204{
205headers: { "Content-Type": "application/json" },
207);
208} catch (error) {
209return new Response(JSON.stringify({ error: "Failed to generate image" }), {
210status: 500,
211headers: { "Content-Type": "application/json" },
221<meta charset="UTF-8">
222<meta name="viewport" content="width=device-width, initial-scale=1.0">
223<title>AI Image Generator</title>
224<style>${css}</style>
225</head>
315text-align: center;
316}
317.image-container {
318margin-top: 20px;
319text-align: center;
322border-radius: 10px;
323}
324.generated-image {
325max-width: 100%;
326height: auto;
7* Type text prompts, select it, press "Q". Select a previous generation with a new text prompt to keep iterating. Selecting shapes doesn't work yet. Have fun!
89<a href="https://x.com/JanPaul123/status/1815502582015754657"><img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/5893dfbf-c2de-4be0-049e-d8fdd1970a00/public"/></a>
10
10* Create a [Val Town API token](https://www.val.town/settings/api), open the browser preview of this val, and use the API token as the password to log in.
1112<img width=500 src="https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/7077d1b5-1fa7-4a9b-4b93-f8d01d3e4f00/public"/>
umbrellaReminderREADME.md1 match
1# ☔️ Umbrella reminder if there's rain today
23
45## Setup