TownieProjectsRoute.tsx7 matches
43user: {
44username: string;
45profileImageUrl: string | null;
46};
47project: any;
49return (
50<div className="card">
51{project.imageUrl ? (
52<img src={project.imageUrl} className="card-image" />
53) : user.profileImageUrl ? (
54<div className="card-image">
55<img
56src={user.profileImageUrl}
57width="48"
58height="48"
61</div>
62) : (
63<div className="card-image placeholder" />
64)}
65<div className="card-body">
TownieInputBox.tsx46 matches
2import { useRef, useState, useEffect } from "react";
3import { PlusIcon, ArrowUpIcon, Square, XIcon } from "./icons.tsx";
4import { processFiles } from "../utils/images.ts";
56export function InputBox ({
11running,
12error,
13images,
14setImages,
15} : {
16value: string;
20running: boolean;
21error: any;
22images: (string|null)[];
23setImages: (images: (string|null)[]) => void;
24}) {
25const form = useRef(null);
57autoFocus={true}
58/>
59<ImageRow images={images} setImages={setImages} />
60<div className="toolbar">
61<UploadButton
62disabled={running}
63images={images}
64setImages={setImages}
65/>
66<div className="spacer" />
88}
8990export function ImageDropContainer ({
91images,
92setImages,
93running,
94children,
95}: {
96images: (string|null)[];
97setImages: (images: (string|null)[]) => void;
98running: boolean;
99children: React.ReactNode;
100}) {
101const dragging = useImageDrop({ images, setImages, running });
102103return (
105{children}
106{dragging && (
107<div className="image-drop-overlay">
108<div className="image-drop-inner">
109Drop images here to upload
110</div>
111</div>
115}
116117export function useImageDrop ({ images, setImages, running }: {
118images: (string|null)[];
119setImages(images: (string|null)[]) => void;
120running: boolean;
121}) {
143setDragging(false);
144if (e.dataTransfer?.files && !running) {
145processFiles(Array.from(e.dataTransfer.files), images, setImages);
146}
147}
164}
165166function ImageRow ({ images, setImages }: {
167images: (string|null)[];
168setImages: (images: (string|null)[]) => void;
169}) {
170return (
171<div className="image-row">
172{images.map((image, i) => (
173<Thumbnail
174key={i}
175image={image}
176onRemove={() => {
177setImages([
178...images.slice(0, i),
179...images.slice(i + 1),
180]);
181}}
186}
187188function Thumbnail ({ image, onRemove }: {
189image: string|null;
190onRemove: () => void;
191}) {
192if (!image) return null;
193194return (
195<div className="input-image">
196<img
197src={image}
198alt="User uploaded image"
199className="image-thumbnail"
200/>
201<button
202type="button"
203title="Remove image"
204className="remove-image-button"
205onClick={onRemove}
206>
212213function UploadButton ({
214images,
215setImages,
216disabled,
217}: {
218images: (string|null)[];
219setImages: (images: (string|null)[]) => void;
220disabled: boolean;
221}) {
226<button
227type="button"
228title="Upload image"
229disabled={disabled}
230onClick={e => {
234<PlusIcon />
235<div className="sr-only">
236Upload image
237</div>
238</button>
243onChange={e => {
244if (e.target.files) {
245processFiles(Array.from(e.target.files), images, setImages);
246}
247}}
12export const PROMPT_IMAGE_LIMIT = 5;
34export const processFiles = async (files: File[], images: (string | null)[], setImages: (images: (string | null)[]) => void) => {
5const imageFiles = files.filter(file => file.type.startsWith('image/'));
6const filesToProcess = imageFiles.slice(0, PROMPT_IMAGE_LIMIT - images.filter(Boolean).length);
78if (filesToProcess.length === 0) return;
910const newImages = [...images, ...Array(filesToProcess.length).fill(null)];
11setImages(newImages);
1213const processedImages = await Promise.all(
14filesToProcess.map(async (file) => {
15return await readFileAsDataURL(file);
17);
1819const updatedImages = [...images];
20processedImages.forEach((dataUrl, index) => {
21updatedImages[images.length + index] = dataUrl;
22});
2324setImages(updatedImages.slice(0, PROMPT_IMAGE_LIMIT));
25};
2630reader.onload = () => {
31const result = reader.result as string;
32console.log("Image loaded, size:", result.length, "bytes");
33resolve(result);
34};
TownieHeader.tsx2 matches
33<button className="h6">Log out</button>
34</form>
35{user?.profileImageUrl && (
36<img
37src={user.profileImageUrl}
38alt={user.username}
39width="32"
Towniefavicon.http.tsx1 match
10return new Response(svg, {
11headers: {
12"Content-Type": "image/svg+xml",
13},
14});
Towniedashboard.ts3 matches
11total_cache_write_tokens: number;
12total_price: number;
13total_images: number;
14used_inference_data?: boolean;
15}
40<th>Cache Write</th>
41<th>Total Price</th>
42<th>Images</th>
43</tr>
44</thead>
54<td>${formatNumber(row.total_cache_write_tokens)} ${row.used_inference_data ? '<span class="badge badge-info" title="Using inference data">I</span>' : ''}</td>
55<td class="price">${formatPrice(row.total_price)} ${row.used_inference_data ? '<span class="badge badge-info" title="Using inference data">I</span>' : ''}</td>
56<td>${formatNumber(row.total_images)}</td>
57</tr>
58`).join("")}
Townie.cursorrules2 matches
178179- **Redirects:** Use `return new Response(null, { status: 302, headers: { Location: "/place/to/redirect" }})` instead of `Response.redirect` which is broken
180- **Images:** Avoid external images or base64 images. Use emojis, unicode symbols, or icon fonts/libraries instead
181- **AI Image:** To inline generate an AI image use: `<img src="https://maxm-imggenurl.web.val.run/the-description-of-your-image" />`
182- **Storage:** DO NOT use the Deno KV module for storage
183- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
TownieChatRouteSingleColumn.tsx15 matches
9import { useUsageStats } from "../hooks/useUsageStats.ts";
10import { Messages } from "./Messages.tsx";
11import { InputBox, ImageDropContainer } from "./InputBox.tsx";
12import { PreviewFrame } from "./PreviewFrame.tsx";
13import { BranchSelect } from "./BranchSelect.tsx";
66refetch: () => void;
67}) {
68const [images, setImages] = useState<(string|null)[]>([]);
69const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
70const { audio, user } = useContext(AppContext);
84branchId,
85selectedFiles,
86images,
87soundEnabled: audio,
88});
108109return (
110<ImageDropContainer
111running={running}
112images={images}
113setImages={setImages}>
114<div className="single-column-container">
115<div className="single-sticky-header">
119rel="norefferer"
120className="block-link text-link lockup">
121{project.imageUrl ? (
122<img src={project.imageUrl} className="image-thumbnail" />
123) : user?.profileImageUrl ? (
124<img
125src={user.profileImageUrl}
126className="avatar"
127alt={user.username}
130/>
131) : (
132<div className="image-placeholder" />
133)}
134<div>{project.name}</div>
153onSubmit={e => {
154handleSubmit(e);
155setImages([]);
156}}
157onCancel={handleStop}
158running={running}
159error={error}
160images={images}
161setImages={setImages}
162/>
163<Footer />
164</div>
165</div>
166</ImageDropContainer>
167);
168}
2021/**
22* Main HTTP handler for the image recognition app
23*/
24export default async function(req: Request): Promise<Response> {
52}
53
54// Handle POST requests (image analysis)
55if (req.method === "POST") {
56try {
57// Parse the request body
58const body = await req.json();
59const imageUrl = body.imageUrl;
60
61// Validate the image URL
62if (!imageUrl) {
63return jsonResponse({
64success: false,
65error: "Image URL is required",
66}, 400);
67}
68
69// Call OpenAI Vision API to analyze the image
70const response = await openai.chat.completions.create({
71model: "gpt-4o",
74role: "user",
75content: [
76{ type: "text", text: "What's in this image? Provide a detailed description." },
77{ type: "image_url", image_url: { url: imageUrl } }
78],
79},
91});
92} catch (error) {
93console.error("Error analyzing image:", error);
94
95// Determine if it's an OpenAI API error
100return jsonResponse({
101success: false,
102error: `Failed to analyze image: ${errorMessage}`,
103}, 500);
104}
mahiindex.html28 matches
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>AI Image Recognition</title>
7<!-- TailwindCSS -->
8<script src="https://cdn.twind.style" crossorigin></script>
29<div class="container mx-auto px-4 py-8 max-w-4xl">
30<header class="text-center mb-8">
31<h1 class="text-3xl font-bold text-blue-600 mb-2">AI Image Recognition</h1>
32<p class="text-gray-600">Enter an image URL to get an AI-powered description</p>
33</header>
34
35<main class="bg-white rounded-lg shadow-md p-6">
36<div class="mb-6">
37<label for="imageUrl" class="block text-sm font-medium text-gray-700 mb-2">Image URL</label>
38<div class="flex">
39<input
40type="text"
41id="imageUrl"
42placeholder="https://example.com/image.jpg"
43class="flex-1 rounded-l-md border border-gray-300 px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
44>
50</button>
51</div>
52<p class="mt-1 text-sm text-gray-500">Paste a direct link to an image (JPG, PNG, etc.)</p>
53</div>
54
55<div id="previewContainer" class="mb-6 hidden">
56<h2 class="text-lg font-medium text-gray-800 mb-2">Image Preview</h2>
57<div class="flex justify-center bg-gray-100 rounded-md p-2">
58<img id="imagePreview" src="" alt="Preview" class="max-h-64 rounded">
59</div>
60</div>
62<div id="loadingContainer" class="mb-6 hidden text-center py-4">
63<div class="loading-spinner mr-2"></div>
64<span class="text-gray-600">Analyzing image...</span>
65</div>
66
87<script>
88// DOM elements
89const imageUrlInput = document.getElementById('imageUrl');
90const analyzeBtn = document.getElementById('analyzeBtn');
91const previewContainer = document.getElementById('previewContainer');
92const imagePreview = document.getElementById('imagePreview');
93const loadingContainer = document.getElementById('loadingContainer');
94const resultContainer = document.getElementById('resultContainer');
98
99// Event listeners
100imageUrlInput.addEventListener('input', updatePreview);
101analyzeBtn.addEventListener('click', analyzeImage);
102
103// Handle Enter key in the input field
104imageUrlInput.addEventListener('keydown', (e) => {
105if (e.key === 'Enter') {
106analyzeImage();
107}
108});
109
110// Update image preview when URL changes
111function updatePreview() {
112const imageUrl = imageUrlInput.value.trim();
113
114if (imageUrl) {
115imagePreview.src = imageUrl;
116previewContainer.classList.remove('hidden');
117
118// Handle image load errors
119imagePreview.onerror = () => {
120previewContainer.classList.add('hidden');
121};
125}
126
127// Analyze the image using the API
128async function analyzeImage() {
129const imageUrl = imageUrlInput.value.trim();
130
131if (!imageUrl) {
132showError('Please enter an image URL');
133return;
134}
146'Content-Type': 'application/json',
147},
148body: JSON.stringify({ imageUrl }),
149});
150