Val Town Code SearchReturn to Val Town

API Access

You can access search results via JSON API by adding format=json to your query:

https://codesearch.val.run/image-url.jpg%20%22Optional%20title%22?q=image&page=16&format=json

For typeahead suggestions, use the /typeahead endpoint:

https://codesearch.val.run/typeahead?q=image

Returns an array of strings in format "username" or "username/projectName"

Found 6504 results for "image"(1153ms)

TownieInputBox.tsx46 matches

@charmaineUpdated 2 days ago
2import { useRef, useState, useEffect } from "https://esm.sh/react@18.2.0?dev";
3import { PlusIcon, ArrowUpIcon, Square, XIcon } from "./icons.tsx";
4import { processFiles } from "../utils/images.ts";
5
6export function InputBox ({
11 running,
12 error,
13 images,
14 setImages,
15} : {
16 value: string;
20 running: boolean;
21 error: any;
22 images: (string|null)[];
23 setImages: (images: (string|null)[]) => void;
24}) {
25 const form = useRef(null);
57 autoFocus={true}
58 />
59 <ImageRow images={images} setImages={setImages} />
60 <div className="toolbar">
61 <UploadButton
62 disabled={running}
63 images={images}
64 setImages={setImages}
65 />
66 <div className="spacer" />
88}
89
90export function ImageDropContainer ({
91 images,
92 setImages,
93 running,
94 children,
95}: {
96 images: (string|null)[];
97 setImages: (images: (string|null)[]) => void;
98 running: boolean;
99 children: React.ReactNode;
100}) {
101 const dragging = useImageDrop({ images, setImages, running });
102
103 return (
105 {children}
106 {dragging && (
107 <div className="image-drop-overlay">
108 <div className="image-drop-inner">
109 Drop images here to upload
110 </div>
111 </div>
115}
116
117export function useImageDrop ({ images, setImages, running }: {
118 images: (string|null)[];
119 setImages(images: (string|null)[]) => void;
120 running: boolean;
121}) {
143 setDragging(false);
144 if (e.dataTransfer?.files && !running) {
145 processFiles(Array.from(e.dataTransfer.files), images, setImages);
146 }
147 }
164}
165
166function ImageRow ({ images, setImages }: {
167 images: (string|null)[];
168 setImages: (images: (string|null)[]) => void;
169}) {
170 return (
171 <div className="image-row">
172 {images.map((image, i) => (
173 <Thumbnail
174 key={i}
175 image={image}
176 onRemove={() => {
177 setImages([
178 ...images.slice(0, i),
179 ...images.slice(i + 1),
180 ]);
181 }}
186}
187
188function Thumbnail ({ image, onRemove }: {
189 image: string|null;
190 onRemove: () => void;
191}) {
192 if (!image) return null;
193
194 return (
195 <div className="input-image">
196 <img
197 src={image}
198 alt="User uploaded image"
199 className="image-thumbnail"
200 />
201 <button
202 type="button"
203 title="Remove image"
204 className="remove-image-button"
205 onClick={onRemove}
206 >
212
213function UploadButton ({
214 images,
215 setImages,
216 disabled,
217}: {
218 images: (string|null)[];
219 setImages: (images: (string|null)[]) => void;
220 disabled: boolean;
221}) {
226 <button
227 type="button"
228 title="Upload image"
229 disabled={disabled}
230 onClick={e => {
234 <PlusIcon />
235 <div className="sr-only">
236 Upload image
237 </div>
238 </button>
243 onChange={e => {
244 if (e.target.files) {
245 processFiles(Array.from(e.target.files), images, setImages);
246 }
247 }}

Townieimages.ts12 matches

@charmaineUpdated 2 days ago
1
2export const PROMPT_IMAGE_LIMIT = 5;
3
4export const processFiles = async (files: File[], images: (string | null)[], setImages: (images: (string | null)[]) => void) => {
5 const imageFiles = files.filter(file => file.type.startsWith('image/'));
6 const filesToProcess = imageFiles.slice(0, PROMPT_IMAGE_LIMIT - images.filter(Boolean).length);
7
8 if (filesToProcess.length === 0) return;
9
10 const newImages = [...images, ...Array(filesToProcess.length).fill(null)];
11 setImages(newImages);
12
13 const processedImages = await Promise.all(
14 filesToProcess.map(async (file) => {
15 return await readFileAsDataURL(file);
17 );
18
19 const updatedImages = [...images];
20 processedImages.forEach((dataUrl, index) => {
21 updatedImages[images.length + index] = dataUrl;
22 });
23
24 setImages(updatedImages.slice(0, PROMPT_IMAGE_LIMIT));
25};
26
30 reader.onload = () => {
31 const result = reader.result as string;
32 console.log("Image loaded, size:", result.length, "bytes");
33 resolve(result);
34 };

Towniefavicon.http.tsx1 match

@charmaineUpdated 2 days ago
13 return new Response(svg, {
14 headers: {
15 "Content-Type": "image/svg+xml",
16 },
17 });

TownieChatRoute.tsx13 matches

@charmaineUpdated 2 days ago
10import { useUsageStats } from "../hooks/useUsageStats.ts";
11import { Messages } from "./Messages.tsx";
12import { InputBox, ImageDropContainer } from "./InputBox.tsx";
13import { PreviewFrame } from "./PreviewFrame.tsx";
14import { BranchSelect } from "./BranchSelect.tsx";
64}) {
65 const { token, anthropicApiKey } = useAuth();
66 const [images, setImages] = useState<(string|null)[]>([]);
67 const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
68 const { audio } = useContext(AppContext);
84 bearerToken: token,
85 selectedFiles,
86 images,
87 soundEnabled: audio,
88 });
108
109 return (
110 <ImageDropContainer
111 running={running}
112 images={images}
113 setImages={setImages}>
114 <div
115 className="chat-container container">
131 onSubmit={e => {
132 handleSubmit(e);
133 setImages([]);
134 }}
135 onCancel={handleStop}
136 running={running}
137 error={error}
138 images={images}
139 setImages={setImages}
140 />
141 </div>
149 rel="norefferer"
150 className="block-link text-link lockup">
151 {project.imageUrl ? (
152 <img src={project.imageUrl} className="image-thumbnail" />
153 ) : (
154 <div className="image-placeholder" />
155 )}
156 <div>
173 </div>
174 <pre hidden>{JSON.stringify(messages, null, 2)}</pre>
175 </ImageDropContainer>
176 );
177}

adportalcontinue.html149 matches

@devtoUpdated 2 days ago
44 cursor: not-allowed;
45 }
46 /* Image upload styles */
47 #imagePreviewContainer {
48 position: relative;
49 display: inline-block;
50 margin-top: 0.5rem;
51 }
52 #imagePreview {
53 border: 1px solid #e5e7eb;
54 border-radius: 0.375rem;
58 line-height: 1;
59 }
60 #imagesList img {
61 transition: transform 0.2s ease-in-out;
62 }
63 #imagesList img:hover {
64 transform: scale(1.05);
65 }
66 .image-card {
67 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
68 transition: box-shadow 0.2s ease-in-out;
69 }
70 .image-card:hover {
71 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
72 }
136
137 <div class="mt-6 border-t pt-6">
138 <label class="block text-lg font-medium text-gray-800 mb-2">Upload Images</label>
139
140 <div id="imageUploadLog" class="mb-4 p-3 bg-gray-50 border border-gray-200 rounded-md text-sm max-h-32 overflow-y-auto">
141 <div class="text-gray-500">Image upload log:</div>
142 </div>
143
144 <div class="mb-4">
145 <div class="flex items-center space-x-2">
146 <label for="imageUpload" class="px-4 py-2 bg-blue-100 hover:bg-blue-200 rounded-md cursor-pointer text-sm font-medium border border-blue-300">
147 Select Image
148 </label>
149 <span id="selectedFileName" class="text-sm text-gray-500">No file selected</span>
151 <input
152 type="file"
153 id="imageUpload"
154 accept="image/*"
155 class="hidden"
156 >
157 </div>
158
159 <div id="imagePreviewContainer" class="hidden mb-4">
160 <div class="relative">
161 <img id="imagePreview" class="max-w-full h-auto max-h-64 rounded-md border-2 border-blue-300" src="" alt="Preview">
162 <button id="cancelUpload" class="absolute top-2 right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center">×</button>
163 </div>
166 <button
167 type="button"
168 id="uploadImageButton"
169 class="px-4 py-2 bg-blue-500 text-white rounded-md disabled:bg-blue-300 disabled:cursor-not-allowed"
170 disabled
171 >
172 Upload Image
173 </button>
174
176 </div>
177
178 <div id="imagesContainer" class="mt-6 mb-6 p-4 bg-gray-50 border border-gray-200 rounded-md">
179 <div class="flex justify-between items-center mb-3">
180 <h3 class="text-lg font-medium text-gray-800">Image Gallery</h3>
181 <div class="flex items-center space-x-2">
182 <span id="imageCount" class="px-2 py-1 bg-blue-100 text-blue-800 rounded-full text-sm">0 images</span>
183 <button id="refreshImages" class="px-2 py-1 bg-green-100 text-green-800 rounded-md text-sm">Refresh</button>
184 <button id="testBlobStorage" class="px-2 py-1 bg-yellow-100 text-yellow-800 rounded-md text-sm">Test Storage</button>
185 </div>
186 </div>
187 <div id="noImagesMessage" class="text-gray-500 text-center py-4">No images uploaded yet</div>
188 <div id="imagesList" class="grid grid-cols-2 gap-4"></div>
189 </div>
190
217 const submissionId = document.getElementById('submissionId').value;
218
219 // Image upload elements
220 const imageUploadInput = document.getElementById('imageUpload');
221 const selectedFileName = document.getElementById('selectedFileName');
222 const imagePreviewContainer = document.getElementById('imagePreviewContainer');
223 const imagePreview = document.getElementById('imagePreview');
224 const cancelUploadButton = document.getElementById('cancelUpload');
225 const uploadImageButton = document.getElementById('uploadImageButton');
226 const uploadStatus = document.getElementById('uploadStatus');
227 const imagesContainer = document.getElementById('imagesContainer');
228 const imagesList = document.getElementById('imagesList');
229 const imageCount = document.getElementById('imageCount');
230 const noImagesMessage = document.getElementById('noImagesMessage');
231 const imageUploadLog = document.getElementById('imageUploadLog');
232
233 // Helper function to log to UI
237 logEntry.className = 'text-xs text-gray-700 mb-1';
238 logEntry.textContent = `${new Date().toLocaleTimeString()}: ${message}`;
239 imageUploadLog.appendChild(logEntry);
240 imageUploadLog.scrollTop = imageUploadLog.scrollHeight;
241 }
242
251 logToUI(`Page loaded for submission ID: ${submissionId}`);
252
253 // Load existing images
254 loadImages();
255
256 // Add refresh button handler
257 document.getElementById('refreshImages').addEventListener('click', () => {
258 logToUI('Manual refresh requested');
259 loadImages();
260 });
261
274 logToUI(`Test key worked: ${result.data.testKeyWorked}`);
275
276 if (result.data.imageKeys && result.data.imageKeys.length > 0) {
277 logToUI(`Found ${result.data.imageKeys.length} image keys:`);
278 result.data.imageKeys.forEach(key => {
279 logToUI(`- ${key}`);
280 });
281 } else {
282 logToUI('No image keys found');
283 }
284 }
290
291 // Handle file selection
292 imageUploadInput.addEventListener('change', (e) => {
293 const file = e.target.files[0];
294 if (!file) {
295 logToUI('File selection canceled');
296 resetImageUpload();
297 return;
298 }
300 logToUI(`File selected: ${file.name} (${file.type}, ${formatFileSize(file.size)})`);
301 selectedFileName.textContent = file.name;
302 uploadImageButton.disabled = false;
303
304 // Show image preview
305 const reader = new FileReader();
306 reader.onload = (e) => {
307 imagePreview.src = e.target.result;
308 imagePreviewContainer.classList.remove('hidden');
309 logToUI('Preview generated');
310 };
315 cancelUploadButton.addEventListener('click', () => {
316 logToUI('Upload canceled by user');
317 resetImageUpload();
318 });
319
320 // Upload image
321 uploadImageButton.addEventListener('click', async () => {
322 const file = imageUploadInput.files[0];
323 if (!file) return;
324
325 uploadImageButton.disabled = true;
326 uploadStatus.textContent = 'Uploading...';
327 uploadStatus.className = 'mt-2 text-sm text-blue-500';
337 logToUI(`Sending file to server...`);
338
339 const response = await fetch('/upload-image', {
340 method: 'POST',
341 body: formData
346
347 if (response.ok && result.success) {
348 logToUI(`Upload successful! Image ID: ${result.data.id}`);
349
350 // Log debug info if available
351 if (result.debug) {
352 logToUI(`Debug info: Found ${result.debug.imagesFound} images`);
353 if (result.debug.imageIds && result.debug.imageIds.length > 0) {
354 result.debug.imageIds.forEach(id => {
355 logToUI(`Debug: Image ID found: ${id}`);
356 });
357 }
358 }
359
360 uploadStatus.textContent = 'Image uploaded successfully!';
361 uploadStatus.className = 'mt-2 text-sm text-green-500';
362 resetImageUpload();
363 loadImages(); // Refresh the images list
364 } else {
365 logToUI(`Upload failed: ${result.message || 'Unknown error'}`);
367 logToUI(`Error details: ${result.error}`);
368 }
369 uploadStatus.textContent = result.message || 'Failed to upload image';
370 uploadStatus.className = 'mt-2 text-sm text-red-500';
371 uploadImageButton.disabled = false;
372 }
373 } catch (error) {
374 console.error('Error uploading image:', error);
375 logToUI(`Upload error: ${error.message}`);
376 uploadStatus.textContent = 'An error occurred while uploading';
377 uploadStatus.className = 'mt-2 text-sm text-red-500';
378 uploadImageButton.disabled = false;
379 }
380 });
381
382 // Reset image upload form
383 function resetImageUpload() {
384 imageUploadInput.value = '';
385 selectedFileName.textContent = 'No file selected';
386 imagePreviewContainer.classList.add('hidden');
387 uploadImageButton.disabled = true;
388 uploadStatus.textContent = '';
389 logToUI('Upload form reset');
390 }
391
392 // Load images for this submission
393 async function loadImages() {
394 logToUI(`Loading images for submission: ${submissionId}`);
395 try {
396 logToUI(`Fetching from /list-images?submissionId=${submissionId}`);
397 const response = await fetch(`/list-images?submissionId=${submissionId}`);
398
399 if (!response.ok) {
400 logToUI(`Error response: ${response.status} ${response.statusText}`);
401 imagesContainer.classList.remove('hidden');
402 noImagesMessage.classList.remove('hidden');
403 noImagesMessage.textContent = `Error: ${response.status} ${response.statusText}`;
404 return;
405 }
409
410 if (result.success) {
411 const imageCount = result.data.length;
412 logToUI(`Found ${imageCount} images`);
413
414 if (result.data && Array.isArray(result.data)) {
415 result.data.forEach((img, idx) => {
416 logToUI(`Image ${idx + 1}: ID=${img.id}, Name=${img.originalName}, Type=${img.contentType}`);
417 });
418 }
419
420 // Update image count display
421 document.getElementById('imageCount').textContent = `${imageCount} image${imageCount !== 1 ? 's' : ''}`;
422
423 if (imageCount > 0) {
424 imagesContainer.classList.remove('hidden');
425 noImagesMessage.classList.add('hidden');
426 renderImages(result.data);
427 } else {
428 imagesContainer.classList.remove('hidden');
429 noImagesMessage.classList.remove('hidden');
430 imagesList.innerHTML = '';
431 logToUI('No images found for this submission');
432 }
433 } else {
434 logToUI(`Error in response: ${result.message || 'Unknown error'}`);
435 imagesContainer.classList.remove('hidden');
436 noImagesMessage.classList.remove('hidden');
437 noImagesMessage.textContent = result.message || 'Error loading images';
438 }
439 } catch (error) {
440 console.error('Error loading images:', error);
441 logToUI(`Error loading images: ${error.message}`);
442 imagesContainer.classList.remove('hidden');
443 noImagesMessage.classList.remove('hidden');
444 noImagesMessage.textContent = 'Error loading images';
445 }
446 }
447
448 // Render images in the list
449 function renderImages(images) {
450 logToUI(`Rendering ${images.length} images in gallery`);
451 imagesList.innerHTML = '';
452
453 images.forEach((image, index) => {
454 logToUI(`Rendering image ${index + 1}: ${image.originalName} (${image.id})`);
455
456 const imageCard = document.createElement('div');
457 imageCard.className = 'relative border rounded-md overflow-hidden image-card';
458 imageCard.dataset.imageId = image.id;
459
460 const img = document.createElement('img');
461 img.src = `/image?id=${image.id}`;
462 img.alt = image.originalName;
463 img.className = 'w-full h-32 object-cover';
464 img.onerror = () => {
465 logToUI(`Failed to load image: ${image.id}`);
466 img.src = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22286%22%20height%3D%22180%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20286%20180%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_17a3f093956%20text%20%7B%20fill%3A%23999%3Bfont-weight%3Anormal%3Bfont-family%3A-apple-system%2CBlinkMacSystemFont%2C%26quot%3BSegoe%20UI%26quot%3B%2CRoboto%2C%26quot%3BHelvetica%20Neue%26quot%3B%2CArial%2C%26quot%3BNoto%20Sans%26quot%3B%2Csans-serif%2C%26quot%3BApple%20Color%20Emoji%26quot%3B%2C%26quot%3BSegoe%20UI%20Emoji%26quot%3B%2C%26quot%3BSegoe%20UI%20Symbol%26quot%3B%2C%26quot%3BNoto%20Color%20Emoji%26quot%3B%2C%20monospace%3Bfont-size%3A14pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_17a3f093956%22%3E%3Crect%20width%3D%22286%22%20height%3D%22180%22%20fill%3D%22%23373940%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22108.5390625%22%20y%3D%2296.3%22%3EImage%20Error%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
467 };
468 img.onload = () => {
469 logToUI(`Image loaded successfully: ${image.id}`);
470 };
471
473 deleteButton.className = 'absolute top-2 right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center';
474 deleteButton.textContent = '×';
475 deleteButton.addEventListener('click', () => deleteImage(image.id));
476
477 const caption = document.createElement('div');
478 caption.className = 'p-2 text-xs truncate';
479 caption.textContent = image.originalName;
480
481 const size = document.createElement('div');
482 size.className = 'px-2 pb-2 text-xs text-gray-500';
483 size.textContent = formatFileSize(image.size);
484
485 imageCard.appendChild(img);
486 imageCard.appendChild(deleteButton);
487 imageCard.appendChild(caption);
488 imageCard.appendChild(size);
489
490 imagesList.appendChild(imageCard);
491 });
492 }
493
494 // Delete an image
495 async function deleteImage(imageId) {
496 if (!confirm('Are you sure you want to delete this image?')) {
497 logToUI(`Deletion canceled for image: ${imageId}`);
498 return;
499 }
500
501 logToUI(`Deleting image: ${imageId}`);
502
503 try {
504 const response = await fetch('/image', {
505 method: 'DELETE',
506 headers: {
508 },
509 body: JSON.stringify({
510 imageId,
511 submissionId
512 })
516
517 if (response.ok && result.success) {
518 logToUI(`Image deleted successfully: ${imageId}`);
519
520 // Remove the image from the list
521 const imageElement = imagesList.querySelector(`[data-image-id="${imageId}"]`);
522 if (imageElement) {
523 imageElement.remove();
524 logToUI('Image removed from gallery');
525 }
526
527 // Update image count
528 const remainingImages = imagesList.children.length;
529 document.getElementById('imageCount').textContent = `${remainingImages} image${remainingImages !== 1 ? 's' : ''}`;
530
531 // If no images left, show the no images message
532 if (remainingImages === 0) {
533 noImagesMessage.classList.remove('hidden');
534 logToUI('No images remaining in gallery');
535 }
536 } else {
537 logToUI(`Failed to delete image: ${result.message || 'Unknown error'}`);
538 alert(result.message || 'Failed to delete image');
539 }
540 } catch (error) {
541 console.error('Error deleting image:', error);
542 logToUI(`Error deleting image: ${error.message}`);
543 alert('An error occurred while deleting the image');
544 }
545 }

adportalindex.ts84 matches

@devtoUpdated 2 days ago
6import { generateUniqueId } from "./utils.ts";
7import {
8 storeImage,
9 getImage,
10 getImageMetadata,
11 listSubmissionImages,
12 deleteImage,
13 ImageMetadata,
14 testBlobStorage
15} from "./image-storage.ts";
16import { blob } from "https://esm.town/v/std/blob";
17
103
104 // Remove id from the fields to update
105 const { id: _, images: __, ...fieldsToUpdate } = body;
106
107 // Update the submission in the database
119});
120
121// Upload an image
122app.post("/upload-image", async (c) => {
123 try {
124 console.log("[SERVER] Received image upload request");
125
126 // Parse the multipart form data
162 console.log(`[SERVER] File read as Uint8Array, length: ${uint8Array.length} bytes`);
163
164 // Store the image
165 console.log(`[SERVER] Calling storeImage function`);
166 const metadata = await storeImage(
167 uint8Array,
168 file.name,
171 );
172
173 console.log(`[SERVER] Image stored successfully with ID: ${metadata.id}`);
174
175 // Immediately try to list images to verify
176 console.log(`[SERVER] Verifying image listing immediately after upload`);
177 const images = await listSubmissionImages(submissionId);
178 console.log(`[SERVER] Verification found ${images.length} images`);
179
180 return c.json({
181 success: true,
182 message: "Image uploaded successfully",
183 data: metadata,
184 debug: {
185 imagesFound: images.length,
186 imageIds: images.map(img => img.id)
187 }
188 });
189 } catch (error) {
190 console.error("[SERVER] Error uploading image:", error);
191 return c.json({
192 success: false,
193 message: "An error occurred uploading your image",
194 error: error.toString()
195 }, 500);
197});
198
199// Get an image by ID
200app.get("/image", async (c) => {
201 try {
202 const imageId = c.req.query("id");
203 console.log(`[SERVER] Image request received for ID: ${imageId}`);
204
205 if (!imageId) {
206 console.log("[SERVER] Missing image ID in request");
207 return c.json({ success: false, message: "Image ID is required" }, 400);
208 }
209
210 // Get image metadata
211 const metadata = await getImageMetadata(imageId);
212 if (!metadata) {
213 console.log(`[SERVER] Metadata not found for image: ${imageId}`);
214 return c.json({ success: false, message: "Image not found" }, 404);
215 }
216
217 console.log(`[SERVER] Metadata retrieved for image: ${imageId}`);
218 console.log(`[SERVER] Image details: name=${metadata.originalName}, type=${metadata.contentType}, size=${metadata.size} bytes`);
219
220 // Get the image data
221 const imageData = await getImage(imageId);
222 if (!imageData) {
223 console.log(`[SERVER] Image data not found for ID: ${imageId}`);
224 return c.json({ success: false, message: "Image data not found" }, 404);
225 }
226
227 console.log(`[SERVER] Image data retrieved successfully, size: ${imageData.length} bytes`);
228
229 // Return the image with proper content type
230 console.log(`[SERVER] Serving image with content type: ${metadata.contentType}`);
231 return new Response(imageData, {
232 headers: {
233 "Content-Type": metadata.contentType,
236 });
237 } catch (error) {
238 console.error("[SERVER] Error getting image:", error);
239 return c.json({ success: false, message: "An error occurred getting the image" }, 500);
240 }
241});
242
243// List images for a submission
244app.get("/list-images", async (c) => {
245 try {
246 const submissionId = c.req.query("submissionId");
247 console.log(`[SERVER] List images request for submission: ${submissionId}`);
248
249 if (!submissionId) {
250 console.log("[SERVER] Missing submission ID in list images request");
251 return c.json({ success: false, message: "Submission ID is required" }, 400);
252 }
253
254 // Get all images for this submission
255 const images = await listSubmissionImages(submissionId);
256 console.log(`[SERVER] Found ${images.length} images for submission: ${submissionId}`);
257
258 // Log details of each image
259 images.forEach((img, index) => {
260 console.log(`[SERVER] Image ${index + 1}: id=${img.id}, name=${img.originalName}, type=${img.contentType}, size=${img.size} bytes`);
261 });
262
263 return c.json({
264 success: true,
265 data: images
266 });
267 } catch (error) {
268 console.error("[SERVER] Error listing images:", error);
269 return c.json({
270 success: false,
271 message: "An error occurred listing images"
272 }, 500);
273 }
274});
275
276// Delete an image
277app.delete("/image", async (c) => {
278 try {
279 const body = await c.req.json();
280 const { imageId, submissionId } = body;
281 console.log(`[SERVER] Delete image request: imageId=${imageId}, submissionId=${submissionId}`);
282
283 if (!imageId || !submissionId) {
284 console.log("[SERVER] Missing required parameters for image deletion");
285 return c.json({
286 success: false,
287 message: "Image ID and Submission ID are required"
288 }, 400);
289 }
290
291 // Get image metadata to verify it belongs to this submission
292 const metadata = await getImageMetadata(imageId);
293 if (!metadata) {
294 console.log(`[SERVER] Metadata not found for image: ${imageId}`);
295 return c.json({
296 success: false,
297 message: "Image not found"
298 }, 404);
299 }
300
301 if (metadata.submissionId !== submissionId) {
302 console.log(`[SERVER] Image ${imageId} does not belong to submission ${submissionId}`);
303 console.log(`[SERVER] Image belongs to submission: ${metadata.submissionId}`);
304 return c.json({
305 success: false,
306 message: "Image does not belong to this submission"
307 }, 403);
308 }
309
310 console.log(`[SERVER] Verified image ${imageId} belongs to submission ${submissionId}`);
311
312 // Delete the image
313 const success = await deleteImage(imageId);
314
315 if (!success) {
316 console.log(`[SERVER] Failed to delete image: ${imageId}`);
317 return c.json({ success: false, message: "Failed to delete image" }, 500);
318 }
319
320 console.log(`[SERVER] Successfully deleted image: ${imageId}`);
321
322 return c.json({
323 success: true,
324 message: "Image deleted successfully"
325 });
326 } catch (error) {
327 console.error("[SERVER] Error deleting image:", error);
328 return c.json({
329 success: false,
330 message: "An error occurred deleting the image"
331 }, 500);
332 }

adportalimage-storage.ts75 matches

@devtoUpdated 2 days ago
1import { blob } from "https://esm.town/v/std/blob";
2
3// Prefix for image keys in blob storage
4const IMAGE_PREFIX = "laura_mepson_image_";
5
6// Interface for image metadata
7export interface ImageMetadata {
8 id: string;
9 originalName: string;
14}
15
16// Generate a unique ID for an image
17export function generateImageId(submissionId: string): string {
18 const timestamp = Date.now().toString(36);
19 const randomPart = Math.random().toString(36).substring(2, 8);
20 return `${IMAGE_PREFIX}${submissionId}_${timestamp}${randomPart}`;
21}
22
23// Store an image in blob storage
24export async function storeImage(
25 imageData: Uint8Array, // Raw image data
26 fileName: string,
27 contentType: string,
28 submissionId: string
29): Promise<ImageMetadata> {
30 try {
31 console.log(`[BLOB STORAGE] Storing image: ${fileName} (${contentType}) for submission: ${submissionId}`);
32 console.log(`[BLOB STORAGE] Image size: ${imageData.length} bytes`);
33
34 // Generate a unique ID for the image
35 const imageId = generateImageId(submissionId);
36 console.log(`[BLOB STORAGE] Generated image ID: ${imageId}`);
37
38 // Store the image data directly
39 await blob.set(imageId, imageData, { contentType });
40 console.log(`[BLOB STORAGE] Image data stored successfully with ID: ${imageId}`);
41
42 // Verify the image was stored
43 const storedData = await blob.get(imageId);
44 if (storedData) {
45 console.log(`[BLOB STORAGE] Verified image data is stored (${storedData.length} bytes)`);
46 } else {
47 console.log(`[BLOB STORAGE] WARNING: Could not verify image data storage`);
48 }
49
50 // Create metadata
51 const metadata: ImageMetadata = {
52 id: imageId,
53 originalName: fileName,
54 contentType,
55 size: imageData.length,
56 uploadedAt: new Date().toISOString(),
57 submissionId
60 console.log(`[BLOB STORAGE] Storing metadata: ${JSON.stringify(metadata)}`);
61
62 // Store metadata alongside the image
63 await blob.setJSON(`${imageId}_metadata`, metadata);
64 console.log(`[BLOB STORAGE] Image metadata stored successfully`);
65
66 // Verify metadata was stored
67 const storedMetadata = await blob.getJSON(`${imageId}_metadata`);
68 if (storedMetadata) {
69 console.log(`[BLOB STORAGE] Verified metadata is stored: ${JSON.stringify(storedMetadata)}`);
79 return metadata;
80 } catch (error) {
81 console.error("[BLOB STORAGE] Error storing image:", error);
82 throw error;
83 }
84}
85
86// Get an image from blob storage
87export async function getImage(imageId: string): Promise<Uint8Array | null> {
88 try {
89 console.log(`[BLOB STORAGE] Retrieving image with ID: ${imageId}`);
90 const imageData = await blob.get(imageId);
91
92 if (imageData) {
93 console.log(`[BLOB STORAGE] Image retrieved successfully, size: ${imageData.length} bytes`);
94 } else {
95 console.log(`[BLOB STORAGE] Image not found with ID: ${imageId}`);
96 }
97
98 return imageData;
99 } catch (error) {
100 console.error(`[BLOB STORAGE] Error getting image ${imageId}:`, error);
101 return null;
102 }
103}
104
105// Get image metadata
106export async function getImageMetadata(imageId: string): Promise<ImageMetadata | null> {
107 try {
108 console.log(`[BLOB STORAGE] Retrieving metadata for image: ${imageId}`);
109 const metadata = await blob.getJSON(`${imageId}_metadata`);
110
111 if (metadata) {
112 console.log(`[BLOB STORAGE] Metadata retrieved successfully for image: ${imageId}`);
113 } else {
114 console.log(`[BLOB STORAGE] Metadata not found for image: ${imageId}`);
115 }
116
117 return metadata as ImageMetadata;
118 } catch (error) {
119 console.error(`[BLOB STORAGE] Error getting image metadata for ${imageId}:`, error);
120 return null;
121 }
122}
123
124// List all images for a submission
125export async function listSubmissionImages(submissionId: string): Promise<ImageMetadata[]> {
126 try {
127 console.log(`[BLOB STORAGE] Listing images for submission: ${submissionId}`);
128 console.log(`[BLOB STORAGE] Using prefix: ${IMAGE_PREFIX}${submissionId}_`);
129
130 // First, list all keys to see what's in the blob storage
133 allKeys.forEach(key => console.log(`[BLOB STORAGE] - ${key}`));
134
135 // List all blobs with the image prefix for this submission
136 const keys = await blob.list(`${IMAGE_PREFIX}${submissionId}_`);
137 console.log(`[BLOB STORAGE] Found ${keys.length} keys for submission: ${submissionId}`);
138 keys.forEach(key => console.log(`[BLOB STORAGE] - ${key}`));
139
140 // Filter out metadata keys and get only image keys
141 const imageKeys = keys.filter(key => !key.endsWith('_metadata'));
142 console.log(`[BLOB STORAGE] Found ${imageKeys.length} image keys after filtering`);
143 imageKeys.forEach(key => console.log(`[BLOB STORAGE] - Image key: ${key}`));
144
145 // Get metadata for each image
146 const metadataPromises = imageKeys.map(key => getImageMetadata(key));
147 const metadataResults = await Promise.all(metadataPromises);
148
149 // Filter out any null results
150 const validMetadata = metadataResults.filter(metadata => metadata !== null) as ImageMetadata[];
151 console.log(`[BLOB STORAGE] Retrieved ${validMetadata.length} valid metadata objects`);
152 validMetadata.forEach(meta => console.log(`[BLOB STORAGE] - Metadata: ${meta.id}, ${meta.originalName}`));
154 return validMetadata;
155 } catch (error) {
156 console.error(`[BLOB STORAGE] Error listing submission images for ${submissionId}:`, error);
157 return [];
158 }
159}
160
161// Delete an image and its metadata
162export async function deleteImage(imageId: string): Promise<boolean> {
163 try {
164 console.log(`[BLOB STORAGE] Deleting image with ID: ${imageId}`);
165
166 await blob.delete(imageId);
167 console.log(`[BLOB STORAGE] Image deleted: ${imageId}`);
168
169 await blob.delete(`${imageId}_metadata`);
170 console.log(`[BLOB STORAGE] Image metadata deleted: ${imageId}_metadata`);
171
172 return true;
173 } catch (error) {
174 console.error(`[BLOB STORAGE] Error deleting image ${imageId}:`, error);
175 return false;
176 }
218 await blob.delete(testKey);
219
220 // 6. List image keys for this submission
221 console.log(`[BLOB STORAGE TEST] Listing image keys for submission: ${submissionId}`);
222 const imageKeys = await blob.list(`${IMAGE_PREFIX}${submissionId}_`);
223 console.log(`[BLOB STORAGE TEST] Found ${imageKeys.length} image keys`);
224
225 return {
229 totalKeys: allKeys.length,
230 prefixedKeys: prefixedKeys.length,
231 imageKeys: imageKeys,
232 testKeyWorked: true
233 }

blob_adminREADME.md1 match

@devtoUpdated 2 days ago
3This is a lightweight Blob Admin interface to view and debug your Blob data.
4
5![Screenshot 2024-11-22 at 15.43.43@2x.png](https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/d075a4ee-93ec-4cdd-4823-7c8aee593f00/public)
6
7To use this, fork it to your account.

blob_adminmain.tsx2 matches

@devtoUpdated 2 days ago
60 const { ValTown } = await import("npm:@valtown/sdk");
61 const vt = new ValTown();
62 const { email: authorEmail, profileImageUrl, username } = await vt.me.profile.retrieve();
63 // const authorEmail = me.email;
64
128
129 c.set("email", email);
130 c.set("profile", { profileImageUrl, username });
131 await next();
132};

blob_adminapp.tsx3 matches

@devtoUpdated 2 days ago
437 {profile && (
438 <div className="flex items-center space-x-4">
439 <img src={profile.profileImageUrl} alt="Profile" className="w-8 h-8 rounded-full" />
440 <span>{profile.username}</span>
441 <a href="/auth/logout" className="text-blue-400 hover:text-blue-300">Logout</a>
580 alt="Blob content"
581 className="max-w-full h-auto"
582 onError={() => console.error("Error loading image")}
583 />
584 </div>
630 <li>Create public shareable links for blobs</li>
631 <li>View and manage public folder</li>
632 <li>Preview images directly in the interface</li>
633 </ul>
634 </div>

gpt-image-test1 file match

@CaptainJackUpdated 21 hours ago
测试 gpt image 的不同 api 能否满足图片生成要求

image-inpainting1 file match

@themichaellaiUpdated 3 days ago
Chrimage
Atiq
"Focal Lens with Atig Wazir" "Welcome to my photography journey! I'm Atiq Wazir, a passionate photographer capturing life's beauty one frame at a time. Explore my gallery for stunning images, behind-the-scenes stories, and tips & tricks to enhance your own