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
7374function Avatar ({ user }) {
75if (!user?.profileImageUrl) {
76return (
77<div className="avatar" />
81return (
82<img
83src={user.profileImageUrl}
84alt={user.username}
85width="32"
Towniefavicon.http.tsx1 match
10return new Response(svg, {
11headers: {
12"Content-Type": "image/svg+xml",
13},
14});
Towniedashboard.ts3 matches
12total_price: number;
13inference_price: number;
14total_images: number;
15used_inference_data?: boolean;
16}
44<th>Total Price</th>
45<th>Inference Price</th>
46<th>Images</th>
47</tr>
48</thead>
59<td class="price">${formatPrice(row.total_price)} ${row.used_inference_data ? '<span class="badge badge-info" title="Using inference data">I</span>' : ''}</td>
60<td class="price">${formatPrice(row.inference_price || 0)}</td>
61<td>${formatNumber(row.total_images)}</td>
62</tr>
63`).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
10import { useCreditBalance } from "../hooks/useCreditBalance.tsx";
11import { Messages } from "./Messages.tsx";
12import { InputBox, ImageDropContainer } from "./InputBox.tsx";
13import { PreviewFrame } from "./PreviewFrame.tsx";
14import { BranchSelect } from "./BranchSelect.tsx";
68refetch: () => void;
69}) {
70const [images, setImages] = useState<(string|null)[]>([]);
71const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
72const { audio, user } = useContext(AppContext);
88branchId,
89selectedFiles,
90images,
91soundEnabled: audio,
92});
137138return (
139<ImageDropContainer
140running={running}
141images={images}
142setImages={setImages}>
143<div className="single-column-container">
144<div className="single-sticky-header">
148rel="norefferer"
149className="block-link text-link lockup">
150{project.imageUrl ? (
151<img src={project.imageUrl} className="image-thumbnail" />
152) : user?.profileImageUrl ? (
153<img
154src={user.profileImageUrl}
155className="avatar"
156alt={user.username}
159/>
160) : (
161<div className="image-placeholder" />
162)}
163<div>{project.name}</div>
211onSubmit={e => {
212handleSubmit(e);
213setImages([]);
214}}
215onCancel={handleStop}
216running={running}
217error={error}
218images={images}
219setImages={setImages}
220/>
221)}
223</div>
224</div>
225</ImageDropContainer>
226);
227}
22const VISUAL_ANALYZER_PROMPT = `
23You are a master drywall inspector with expertise in diagnosing material failures and environmental defects.
24You will be provided with an image of a drywall problem.
25Analyze the image to identify the issue, its likely cause, and the correct method for repair.
2627You MUST respond ONLY with a single, minified JSON object with four keys: "diagnosis", "probable_cause", "repair_protocol", and "materials_required".
190}
191#file-upload-label:hover { border-color: var(--accent-color); }
192#image-preview {
193max-width: 100%;
194max-height: 200px;
342<div class="form-group">
343<label for="file-upload" id="file-upload-label">Tap to Upload Photo of Defect</label>
344<input type="file" id="file-upload" accept="image/*" style="display:none;" required>
345<img id="image-preview" style="display:none;">
346</div>
347<button type="submit" class="btn-submit">Analyze Defect</button>
419const loader = $('#loader');
420421let currentImageBase64 = null;
422423function switchView(viewId) {
470contentHTML += \`<div class="justification-box"><strong>Justification:</strong> \${data.justification}</div>\`;
471break;
472case 'analyzeImage':
473contentHTML = '<h3>Defect Analysis</h3>';
474contentHTML += \`<p><strong>Diagnosis:</strong> \${data.diagnosis}</p>\`;
532$('#form-analyzer').addEventListener('submit', (e) => {
533e.preventDefault();
534if (!currentImageBase64) {
535alert('Please select an image first.');
536return;
537}
538const payload = { image: currentImageBase64 };
539handleApiRequest('analyzeImage', payload, $('#results-analyzer'));
540});
541
562});
563
564// Image Upload Handler
565$('#file-upload').addEventListener('change', (e) => {
566const file = e.target.files[0];
569const reader = new FileReader();
570reader.onload = (event) => {
571const img = $('#image-preview');
572img.src = event.target.result;
573img.style.display = 'block';
574// Store just the base64 part
575currentImageBase64 = event.target.result.split(',')[1];
576$('#file-upload-label').textContent = file.name;
577};
623break;
624625case "analyzeImage":
626if (!body.image) {
627throw new Error("Image data is missing from the request.");
628}
629messages.push(
634{ type: "text", text: "Analyze this drywall defect." },
635{
636type: "image_url",
637image_url: {
638url: `data:image/jpeg;base64,${body.image}`,
639},
640},
168169### Overlay
170- **Best for**: Modals, dialogs, forms, image viewers
171- **Config**: `modal: true/false`, `backdrop: true/false`, `position: 'center'|'top'|'bottom'`
172- **Size**: Use `maxWidth`, `maxHeight` for responsive design
digital-scrapbookstyle.css1 match
1html,
2header {
3background-image: url("https://cdn.glitch.global/63c872e5-8f6b-4b1a-b13d-3bd869977d11/notebook_background.png?v=1729196634939");
4background-repeat: no-repeat;
5background-size: 1000px;
digital-scrapbooksketch.js3 matches
143this.y = y;
144// this.rot = 0.7;
145this.img = loadImage(url);
146this.width = width;
147this.height = height;
150display() {
151push();
152imageMode(CENTER);
153translate(this.x, this.y);
154image(this.img, 0, 0, this.width, this.height);
155pop();
156}