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=5&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 6398 results for "image"(1043ms)

Townieimages.ts12 matches

@charmaine•Updated 7 hours 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

@charmaine•Updated 7 hours ago
13 return new Response(svg, {
14 headers: {
15 "Content-Type": "image/svg+xml",
16 },
17 });

TownieChatRoute.tsx13 matches

@charmaine•Updated 7 hours 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

@devto•Updated 7 hours 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

@devto•Updated 7 hours 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

@devto•Updated 7 hours 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

@devto•Updated 7 hours 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

@devto•Updated 7 hours 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

@devto•Updated 7 hours 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>

adportalREADME.md9 matches

@devto•Updated 7 hours ago
10- Email functionality to send users a unique link
11- Continuation page for adding more information
12- Image upload and storage functionality
13
14## Project Structure
20- `landing.html` - Main landing page
21- `continue.html` - Page for adding additional information
22- `image-storage.ts` - Image storage and retrieval functionality
23
24## Database Schema
445. User can click the link to add more information
456. Additional information is saved to the database
467. User can upload and manage images related to their submission
47
48## URL Structure
51- Continuation page: `https://[username].web.val.run?action=continue&id=[unique_id]`
52
53## Image Storage
54
55Images are stored using Val Town's blob storage:
56- Images are stored with a unique ID
57- Metadata is stored alongside each image
58- Users can upload, view, and delete images
59- Images are associated with a specific submission
60
61## Development Notes

image-inpainting1 file match

@themichaellai•Updated 1 day ago

brainrot_image_gen1 file match

@dcm31•Updated 1 week ago
Generate images for Italian Brainrot characters using FAL AI
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