campycardsmain.tsx41 matches
257// --- Card Grader Frontend Component ---
258function CardGraderApp() {
259const [imageFile, setImageFile] = useState(null);
260const [imageBase64, setImageBase64] = useState(null);
261const [gradingResult, setGradingResult] = useState(null); // Final displayed result
262const [pastResults, setPastResults] = useState([]);
305const handleFileChange = (event) => {
306const file = event.target.files[0];
307if (file && file.type.startsWith("image/")) {
308setError(null);
309setGradingResult(null);
310setIsRevealingScore(false); // Reset reveal state
311setPendingReportData(null); // Reset pending data
312setImageFile(file);
313314const reader = new FileReader();
315reader.onloadend = () => {
316setImageBase64(reader.result);
317};
318reader.onerror = () => {
319setError("Failed to read the image file.");
320setImageBase64(null);
321setImageFile(null);
322};
323reader.readAsDataURL(file);
324} else {
325setError("Please select a valid image file (jpg, png, webp, etc.).");
326setImageBase64(null);
327setImageFile(null);
328}
329};
330331const handleGradeRequest = useCallback(async () => {
332if (!imageBase64 || isLoading || isRevealingScore) return;
333334setIsLoading(true);
339340try {
341const base64Data = imageBase64.split(",")[1];
342if (!base64Data) {
343throw new Error("Could not extract image data for processing.");
344}
345347method: "POST",
348headers: { "Content-Type": "application/json" },
349body: JSON.stringify({ imageBase64: base64Data }),
350});
351370...data.gradingReport,
371timestamp: generateTimestamp(),
372imagePreview: imageBase64,
373cardName: data.gradingReport?.cardDetails?.name || "Unknown Card",
374};
389setPendingReportData(null);
390}
391}, [imageBase64, isLoading, isRevealingScore]);
392393// --- Handler for when animation completes ---
412413const viewPastResult = (result) => {
414setImageBase64(result.imagePreview);
415setGradingResult(result);
416setError(null);
417setImageFile(null);
418setIsRevealingScore(false); // Ensure reveal is off
419setPendingReportData(null);
482{pastResults.map((result, index) => (
483<li key={result.timestamp || index}>
484<img src={result.imagePreview} alt="Thumbnail" width="40" height="auto" />
485<span>
486{result.cardName || "Graded Card"} - {result?.estimatedGrade?.grade || "N/A"} ({result.timestamp})
520<input
521type="file"
522accept="image/*"
523onChange={handleFileChange}
524disabled={isLoading || isRevealingScore} // Disable during reveal
525/>
526{imageBase64 && !isRevealingScore && ( // Hide preview during reveal
527<div className="image-preview">
528<img src={imageBase64} alt="Card Preview" />
529</div>
530)}
531<button onClick={handleGradeRequest} disabled={!imageBase64 || isLoading || isRevealingScore}>
532{/* Disable during reveal */}
533{isLoading ? "Analyzing Card..." : "Grade This Card"}
537{/* Show loading message *only* if loading AND *not* revealing score */}
538{isLoading && !isRevealingScore && (
539<div className="message loading">Processing image and generating report... This may take a moment.</div>
540)}
541{/* Show error message if error exists AND *not* revealing score */}
592// Check for OpenAI API Key in Val Town secrets
593const openai = new OpenAI(); // Assumes OPENAI_API_KEY is set in Val Town secrets
594const { imageBase64 } = await request.json();
595if (!imageBase64 || typeof imageBase64 !== "string") {
596return Response.json({ error: "Missing or invalid image data (imageBase64)." }, {
597status: 400,
598headers: corsHeaders,
600}
601602console.log("Received image data, preparing request to OpenAI...");
603604const modelChoice = "gpt-4o"; // Or "gpt-4o-mini"
606// --- Construct the detailed prompt for OpenAI ---
607const prompt = `
608You are an expert collectible card grader simulating a PSA-style inspection based SOLELY on the provided image. Analyze the card image and provide a detailed N-point inspection report in JSON format.
609610# PSA Grading Lens - Instructions for Autogrok Relay
611612## Objective:
613Evaluate trading card images based on PSA grading standards, focusing on key attributes such as corners, edges, surface, centering, and overall presentation.
614615## Steps:
6227. **Estimated Grade**: Assign PSA grade (1-10 scale) based on cumulative condition.
623624**Image Analysis Instructions:**
6251. Identify the card (name, set, year) if possible. If not, state 'Unknown'. Ponder its future context if identifiable.
6262. Evaluate condition ONLY from the image. Be critical but fair.
6273. Assess points (score 1-10 AND brief comment):
628* **Centering:** Visual estimate L/R, T/B. (Score: 1-10, Comment)
670role: "user",
671content: [
672{ type: "text", text: "Analyze this card image and provide the report in the specified JSON format." },
673{
674type: "image_url",
675image_url: {
676url: `data:image/jpeg;base64,${imageBase64}`, // Assume jpeg, adjust if needed
677detail: "high",
678},
779.upload-section { display: flex; flex-direction: column; align-items: center; gap: 15px; margin-bottom: 25px; padding: 20px; border: 1px dashed var(--border-color); border-radius: 5px; background-color: #fdfdfd; }
780input[type="file"] { border: 1px solid var(--border-color); padding: 8px; border-radius: 4px; max-width: 300px; }
781.image-preview { max-width: 300px; max-height: 400px; margin-top: 10px; border: 1px solid var(--border-color); background-color: #eee; display: flex; justify-content: center; align-items: center; }
782.image-preview img { max-width: 100%; max-height: 390px; object-fit: contain; border-radius: 3px; }
783button { padding: 10px 20px; font-size: 1em; border-radius: 5px; border: none; cursor: pointer; transition: background-color 0.2s ease; background-color: var(--primary-color); color: white; }
784button:hover:not(:disabled) { background-color: var(--primary-hover); }
806.past-results img { border-radius: 3px; border: 1px solid var(--border-color); object-fit: contain; }
807.past-results span { flex-grow: 1; font-size: 0.9em;}
808@media (max-width: 700px) { body { padding: 10px; } .card-grader-app { padding: 15px; } h1 { font-size: 1.5em; } button { font-size: 0.95em; padding: 8px 15px; } .image-preview { max-width: 100%; } }
809/* --- END Base & Card Grader CSS --- */
810
13// --- Frontend Component ---
14function CardGraderApp() {
15const [imageFile, setImageFile] = useState(null);
16const [imageBase64, setImageBase64] = useState(null);
17const [gradingResult, setGradingResult] = useState(null);
18const [pastResults, setPastResults] = useState([]);
49const handleFileChange = (event) => {
50const file = event.target.files[0];
51if (file && file.type.startsWith("image/")) {
52setError(null); // Clear previous errors
53setGradingResult(null); // Clear previous result display
54setImageFile(file);
5556const reader = new FileReader();
57reader.onloadend = () => {
58setImageBase64(reader.result); // This includes the "data:image/..." prefix
59};
60reader.onerror = () => {
61setError("Failed to read the image file.");
62setImageBase64(null);
63setImageFile(null);
64};
65reader.readAsDataURL(file);
66} else {
67setError("Please select a valid image file (jpg, png, webp, etc.).");
68setImageBase64(null);
69setImageFile(null);
70}
71};
7273const handleGradeRequest = useCallback(async () => {
74if (!imageBase64 || isLoading) return;
7576setIsLoading(true);
80try {
81// Extract base64 data part *without* the prefix
82const base64Data = imageBase64.split(",")[1];
83if (!base64Data) {
84throw new Error("Could not extract image data for processing.");
85}
8688method: "POST",
89headers: { "Content-Type": "application/json" },
90body: JSON.stringify({ imageBase64: base64Data }), // Send only the data part
91});
92105}
106107// Add timestamp and original image to the result before saving
108setGradingResult({
109...data.gradingReport, // The report object from backend
110timestamp: generateTimestamp(),
111imagePreview: imageBase64, // Store preview for history
112cardName: data.gradingReport?.cardDetails?.name || "Unknown Card", // Use for history title
113});
119setIsLoading(false);
120}
121}, [imageBase64, isLoading]);
122123const clearHistory = () => {
134const viewPastResult = (result) => {
135// Display a selected past result in the main view
136setImageBase64(result.imagePreview);
137setGradingResult(result); // Display the past result object
138setError(null);
139setImageFile(null); // Indicate we are viewing a past result, not a new file
140window.scrollTo(0, 0); // Scroll to top
141};
184This is an AI-generated visual estimation for informational purposes only. It does not guarantee a specific
185grade from professional services like PSA and should not substitute professional evaluation. Accuracy depends
186heavily on image quality.
187</div>
188</div>
201{pastResults.map((result, index) => (
202<li key={result.timestamp || index}>
203<img src={result.imagePreview} alt="Thumbnail" width="40" height="auto" />
204<span>
205{result.cardName || "Graded Card"} - {result?.estimatedGrade?.grade || "N/A"} ({result.timestamp})
219<h1>AI Card Grading Assistant (Beta)</h1>
220<p className="instructions">
221Upload a clear, well-lit photo of your card on a plain white or black background. The better the image, the
222better the AI analysis (estimation only!).
223</p>
226<input
227type="file"
228accept="image/*"
229onChange={handleFileChange}
230disabled={isLoading}
231/>
232{imageBase64 && (
233<div className="image-preview">
234<img src={imageBase64} alt="Card Preview" />
235</div>
236)}
237<button
238onClick={handleGradeRequest}
239disabled={!imageBase64 || isLoading}
240>
241{isLoading ? "Analyzing Card..." : "Grade This Card"}
244245{isLoading && (
246<div className="message loading">Processing image and generating report... This may take a moment.</div>
247)}
248{error && <div className="message error">{error}</div>}
283// Check for OpenAI API Key in Val Town secrets
284const openai = new OpenAI();
285const { imageBase64 } = await request.json();
286if (!imageBase64 || typeof imageBase64 !== "string") {
287return Response.json({ error: "Missing or invalid image data (imageBase64)." }, { status: 400 });
288}
289290console.log("Received image data, preparing request to OpenAI..."); // Log start
291292const modelChoice = "gpt-4o"; // Use GPT-4o for better vision capabilities. Use "gpt-4o-mini" for lower cost/speed, potentially less accuracy.
294// --- Construct the detailed prompt for OpenAI ---
295const prompt = `
296You are an expert collectible card grader simulating a PSA-style inspection based SOLELY on the provided image. Analyze the card image and provide a detailed N-point inspection report in JSON format.
297298**Image Analysis Instructions:**
2991. Identify the card if possible (name, set, year). If not identifiable, state 'Unknown'. Spend some time afterward to ponder the future of the card, the set, the character, their universe, wherw it is going, etc. based on known facts.
3002. Evaluate the card's condition based ONLY on what is visible in the image. Be critical but fair.
3013. Assess the following points (provide a score from 1-10 where applicable AND a brief comment for each):
302* **Centering:** Visual estimate of left/right and top/bottom balance. (Score: 1-10, Comment)
336"estimatedGrade": {
337"grade": "string (e.g., PSA 8, Likely PSA 6, Potential PSA 10)",
338"confidence": "string (e.g., High, Medium, Low - based on image clarity/visible details)"
339},
340"estimatedPrice": "number | null",
359{
360type: "text",
361text: "Please analyze this card image and provide the grading report in the specified JSON format.",
362},
363{
364type: "image_url",
365image_url: {
366// Prepend the necessary prefix for base64 data URI
367url: `data:image/jpeg;base64,${imageBase64}`, // Assuming jpeg, adjust if needed (png often works too)
368detail: "high", // Request high detail analysis
369},
489}
490491.image-preview {
492max-width: 300px; /* Limit preview size */
493max-height: 400px;
494margin-top: 10px;
495border: 1px solid var(--border-color);
496background-color: #eee; /* BG for transparent images */
497display: flex;
498justify-content: center;
499align-items: center;
500}
501.image-preview img {
502max-width: 100%;
503max-height: 390px;
598h1 { font-size: 1.5em; }
599button { font-size: 0.95em; padding: 8px 15px; }
600.image-preview { max-width: 100%; }
601}
602</style>
19// Upload state
20const [uploadData, setUploadData] = useState({
21imagePreview: null,
22caption: "",
23location: "",
68return Array.from({ length: 15 }, (_, i) => ({
69id: `photo${i}`,
70imageUrl: `/api/placeholder/${800 + i}/${600 + i}`,
71caption: `Beautiful capture #${i + 1}`,
72location: locations[Math.floor(Math.random() * locations.length)],
133setUploadData({
134...uploadData,
135imagePreview: "/api/placeholder/800/600",
136});
137}
164const newPhoto = {
165id: `photo${photos.length + 1}`,
166imageUrl: uploadData.imagePreview,
167caption: uploadData.caption,
168location: uploadData.location,
178setPhotos([newPhoto, ...photos]);
179setUploadData({
180imagePreview: null,
181caption: "",
182location: "",
402{/* Photo */}
403<img
404src={selectedPhoto.imageUrl}
405alt={selectedPhoto.caption}
406style={{
814>
815<img
816src={photo.imageUrl}
817alt={photo.caption}
818style={{
939940<form onSubmit={handleUploadSubmit} style={{ padding: "20px" }}>
941{!uploadData.imagePreview
942? (
943<div
961onChange={handleFileSelect}
962style={{ display: "none" }}
963accept="image/*"
964/>
965</div>
968<div>
969<img
970src={uploadData.imagePreview}
971alt="Preview"
972style={{
1228onClick={() => {
1229setUploadData({
1230imagePreview: null,
1231caption: "",
1232location: "",
1371</div>
13721373{/* Image */}
1374<img
1375src={photo.imageUrl}
1376alt={photo.caption}
1377onClick={() => openPhotoDetail(photo)}
1498>
1499<img
1500src={photo.imageUrl}
1501alt={photo.caption}
1502style={{
podcast_blob_adminREADME.md1 match
3This is a lightweight Blob Admin interface to view and debug your Blob data.
45
67Versions 0-17 of this val were done with Hono and server-rendering.
20price: number;
21category: "cultural" | "standard";
22imageUrl: string;
23stockQuantity: number;
24}
36price REAL NOT NULL,
37category TEXT NOT NULL,
38imageUrl TEXT NOT NULL,
39stockQuantity INTEGER NOT NULL
40)
53await sqlite.execute(
54`INSERT INTO ${KEY}_products
55(name, description, price, category, imageUrl, stockQuantity)
56VALUES (?, ?, ?, ?, ?, ?)`,
57[
60product.price,
61product.category,
62product.imageUrl,
63product.stockQuantity,
64],
360price: 0,
361category: "standard",
362imageUrl: "",
363stockQuantity: 0,
364});
432</div>
433<div style={{ marginBottom: "10px" }}>
434<label>Image URL:</label>
435<input
436type="url"
437value={newProduct.imageUrl}
438onChange={(e) => setNewProduct({ ...newProduct, imageUrl: e.target.value })}
439required
440style={{ width: "100%", padding: "5px" }}
531>
532<img
533src={product.imageUrl}
534alt={product.name}
535style={{ maxWidth: "100%", height: "250px", objectFit: "cover" }}
566<div key={product.id} style={{ display: "flex", alignItems: "center", marginBottom: "10px" }}>
567<img
568src={product.imageUrl}
569alt={product.name}
570style={{ width: "100px", marginRight: "10px" }}
606>
607<img
608src={selectedProduct.imageUrl}
609alt={selectedProduct.name}
610style={{ maxWidth: "50%", objectFit: "cover" }}
zanyBeigeFoxmain.tsx5 matches
15const firebaseConfig = {
16apiKey: "AIzaSyBRdHIXJcFUMV3h7k2GFg9wY0S_hUttaZU",
17authDomain: "tommy-ai-image-generator-d2466.firebaseapp.com",
18databaseURL: "https://tommy-ai-image-generator-d2466-default-rtdb.firebaseio.com",
19projectId: "tommy-ai-image-generator-d2466",
20storageBucket: "tommy-ai-image-generator-d2466.firebasestorage.app",
21messagingSenderId: "720885566827",
22appId: "1:720885566827:web:41d6a2b8d8197e078dd02e",
64// When both authenticated and ads are loaded, redirect
65if (isAuthenticated && adsLoaded) {
66window.location.href = "https://ggghh-aiimagegeneratormobilefriendly.web.val.run";
67}
68}, [isAuthenticated, adsLoaded]);
simpleLogoGeneratormain.tsx1 match
128key: svgKey,
129data: svgContent,
130contentType: 'image/svg+xml'
131})
132});
122key: svgKey,
123data: svgContent,
124contentType: 'image/svg+xml'
125})
126});
honestBlueSilkwormmain.tsx1 match
341return new Response(svgText, {
342headers: {
343'Content-Type': 'image/svg+xml',
344'Cache-Control': 'no-cache'
345}
handDrawnCardsLogoExactmain.tsx2 matches
1export default async function handler(req) {
2// For direct SVG access, serve the SVG with proper content type
3if (req.headers.get('accept')?.includes('image/svg+xml') ||
4req.url.includes('?format=svg')) {
5
42`, {
43headers: {
44'Content-Type': 'image/svg+xml',
45'Cache-Control': 'max-age=3600',
46'Access-Control-Allow-Origin': '*'