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}
untitled-7141index.ts6 matches
187title: "Visual Inspection - Arms at Sides",
188description: "Stand in front of a mirror with your arms at your sides. Look for changes in size, shape, or skin texture.",
189imageDescription: "Person standing with arms relaxed at sides, looking in mirror"
190},
191{
193title: "Visual Inspection - Arms Raised",
194description: "Raise your arms overhead and look for the same changes. Check for dimpling or puckering.",
195imageDescription: "Person with arms raised above head, examining in mirror"
196},
197{
199title: "Visual Inspection - Hands on Hips",
200description: "Place your hands on your hips and press firmly to flex your chest muscles. Look for changes.",
201imageDescription: "Person with hands on hips, pressing firmly"
202},
203{
205title: "Physical Examination - Lying Down",
206description: "Lie down and use your right hand to examine your left breast. Use the pads of your fingers in a circular motion.",
207imageDescription: "Person lying down, using circular motions to examine breast"
208},
209{
211title: "Physical Examination - Standing/Shower",
212description: "Repeat the examination while standing or in the shower. Wet skin can make it easier to feel changes.",
213imageDescription: "Person standing, performing breast examination"
214},
215{
217title: "Nipple Examination",
218description: "Gently squeeze each nipple to check for discharge. Report any discharge to your healthcare provider.",
219imageDescription: "Gentle examination of nipple area"
220}
221];
untitled-7141types.ts1 match
32title: string;
33description: string;
34imageDescription: string; // Since we can't use images, we'll use descriptive text
35}
36
charmaineValSearchcomponents.tsx22 matches
228<div className="result-header">
229<div className="result-header-content">
230{result.image_url && (
231<div className="project-image">
232<img src={result.image_url} alt={result.project_name} />
233</div>
234)}
292<div className="result-header">
293<div className="result-header-content">
294{result.image_url && (
295<div className="project-image">
296<img src={result.image_url} alt={result.name} />
297</div>
298)}
351<div className="result-header">
352<div className="user-header">
353{result.profile_image_url && (
354<div className="user-avatar">
355<img src={result.profile_image_url} alt={result.username || "User"} />
356</div>
357)}
1224<a href="?q=api" className="example-link">api</a>
1225<a href="?q=database" className="example-link">database</a>
1226<a href="?q=image" className="example-link">image</a>
1227<a href="?q=function" className="example-link">function</a>
1228<a href="?q=discord" className="example-link">discord</a>
1289<div className="contributor-header">
1290<div className="contributor-avatar">
1291{contributor.profile_image_url
1292? <img src={contributor.profile_image_url} alt={contributor.username} />
1293: (
1294<div
1337rel="noopener noreferrer"
1338>
1339<div className="project-image">
1340{project.image_url
1341? <img src={project.image_url} alt={project.name} />
1342: (
1343<div
1378<a href="?q=api" className="example-link">api</a>
1379<a href="?q=database" className="example-link">database</a>
1380<a href="?q=image" className="example-link">image</a>
1381<a href="?q=function" className="example-link">function</a>
1382<a href="?q=discord" className="example-link">discord</a>
1419<div className="val-meta">
1420<span className="username">
1421{val.user_profile_image_url && (
1422<img
1423src={val.user_profile_image_url}
1424alt={val.username}
1425className="profile-image"
1426/>
1427)}
1472<div className="project-meta">
1473<div className="username">
1474{project.user_profile_image_url && (
1475<img
1476src={project.user_profile_image_url}
1477alt="Profile"
1478className="profile-image"
1479/>
1480)}
1824}
1825
1826.profile-image {
1827width: 20px;
1828height: 20px;
charmaineValSearchstyles.tsx4 matches
430}
431432.project-card .project-image {
433width: 24px;
434height: 24px;
439}
440441.project-image img {
442width: 100%;
443height: 100%;
540}
541542.search-result .project-image {
543width: 60px;
544height: 60px;
549}
550551.project-image img {
552width: 100%;
553height: 100%;
charmaineValSearchimport.ts2 matches
37username: user.username,
38bio: user.bio,
39profile_image_url: user.profileImageUrl,
40url: user.url,
41updated_at: new Date().toISOString(), // Using current time as the API doesn't provide updated_at
55name: project.name,
56description: project.description,
57image_url: project.imageUrl,
58username: project.author.username || "",
59updated_at: mainBranch?.updatedAt || new Date().toISOString(),
charmaineValSearchdb.ts33 matches
119fileCount: number;
120url: string;
121profile_image_url: string | null;
122updated_at: string;
123}[]> {
128COUNT(DISTINCT f.id) as file_count,
129'https://val.town/u/' || p.username as url,
130u.profile_image_url,
131MAX(p.updated_at) as updated_at
132FROM ${tablePrefix}_projects p
144fileCount: Number(row.file_count),
145url: String(row.url),
146profile_image_url: row.profile_image_url ? String(row.profile_image_url) : null,
147updated_at: String(row.updated_at)
148}));
158description: string | null;
159url: string;
160image_url: string | null;
161updated_at: string;
162}[]> {
168p.description,
169p.url,
170p.image_url,
171p.updated_at
172FROM ${tablePrefix}_projects p
184description: row.description ? String(row.description) : null,
185url: String(row.url),
186image_url: row.image_url ? String(row.image_url) : null,
187updated_at: String(row.updated_at)
188}));
204fileCount: number;
205url: string;
206profile_image_url: string | null;
207updated_at: string;
208}[];
213description: string | null;
214url: string;
215image_url: string | null;
216updated_at: string;
217}[];
314username TEXT,
315bio TEXT,
316profile_image_url TEXT,
317url TEXT NOT NULL,
318updated_at TIMESTAMP NOT NULL
328forked_branch_id TEXT,
329description TEXT,
330image_url TEXT,
331user_id TEXT,
332FOREIGN KEY (user_id) REFERENCES ${tablePrefix}_users(id)
359username: z.string().nullable(),
360bio: z.string().nullable(),
361profile_image_url: z.string().nullable(),
362url: z.string(),
363updated_at: z.string().datetime(),
370username: z.string(),
371description: z.string().nullable(),
372image_url: z.string().nullable(),
373forked_branch_id: z.string().nullable(),
374updated_at: z.string().datetime(),
433// Insert new user
434await sqlite.execute(
435`INSERT INTO ${tablePrefix}_users (id, username, bio, profile_image_url, url, updated_at)
436VALUES (?, ?, ?, ?, ?, ?)`,
437[
439user.username,
440user.bio,
441user.profile_image_url,
442user.url,
443user.updated_at,
449await sqlite.execute(
450`UPDATE ${tablePrefix}_users
451SET username = ?, bio = ?, profile_image_url = ?, url = ?, updated_at = ?
452WHERE id = ?`,
453[
454user.username,
455user.bio,
456user.profile_image_url,
457user.url,
458user.updated_at,
498// Insert new project
499await sqlite.execute(
500`INSERT INTO ${tablePrefix}_projects (id, url, name, username, updated_at, forked_branch_id, description, image_url, user_id)
501VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
502[
508project.forked_branch_id,
509project.description,
510project.image_url,
511project.user_id
512],
517await sqlite.execute(
518`UPDATE ${tablePrefix}_projects
519SET url = ?, name = ?, username = ?, updated_at = ?, forked_branch_id = ?, description = ?, image_url = ?, user_id = ?
520WHERE id = ?`,
521[
526project.forked_branch_id,
527project.description,
528project.image_url,
529project.user_id,
530project.id
812813const result = await sqlite.execute(
814`SELECT u.id, u.username, u.bio, u.profile_image_url, u.url, u.updated_at,
815(SELECT COUNT(*) FROM ${tablePrefix}_projects p
816WHERE p.user_id = u.id) as matchCount
827username: z.string().nullable(),
828bio: z.string().nullable(),
829profile_image_url: z.string().nullable(),
830url: z.string(),
831updated_at: z.string().datetime(),
889// Always launch the files query for the active type or for samples
890const result = await sqlite.execute(
891`SELECT f.*, p.name as project_name, p.url as project_url, p.username, p.description, p.image_url
892FROM ${tablePrefix}_files f
893JOIN ${tablePrefix}_projects p ON f.project_id = p.id
904username: z.string(),
905description: z.string().nullable(),
906image_url: z.string().nullable(),
907});
9081062project_url: string;
1063project_description: string | null;
1064user_profile_image_url: string | null;
1065update_count: number;
1066}[]> {
1081p.url as project_url,
1082p.description as project_description,
1083u.profile_image_url as user_profile_image_url,
1084-- Use updated_at as a proxy for edit frequency
10851 as update_count
1103project_url: String(row.project_url),
1104project_description: row.project_description ? String(row.project_description) : null,
1105user_profile_image_url: row.user_profile_image_url ? String(row.user_profile_image_url) : null,
1106update_count: Number(row.update_count)
1107}));
1122url: string;
1123description: string | null;
1124image_url: string | null;
1125user_profile_image_url: string | null;
1126edit_sessions: number;
1127file_count: number;
1140p.url,
1141p.description,
1142p.image_url,
1143u.profile_image_url as user_profile_image_url,
1144COUNT(DISTINCT strftime('%Y-%m-%d %H:%M', f.updated_at)) as edit_sessions,
1145COUNT(f.id) as file_count,
1163url: String(row.url),
1164description: row.description ? String(row.description) : null,
1165image_url: row.image_url ? String(row.image_url) : null,
1166user_profile_image_url: row.user_profile_image_url ? String(row.user_profile_image_url) : null,
1167edit_sessions: Number(row.edit_sessions),
1168file_count: Number(row.file_count),
stevensDemoREADME.md1 match
3It's common to have code and types that are needed on both the frontend and the backend. It's important that you write this code in a particularly defensive way because it's limited by what both environments support:
45
67For example, you *cannot* use the `Deno` keyword. For imports, you can't use `npm:` specifiers, so we reccomend `https://esm.sh` because it works on the server & client. You *can* use TypeScript because that is transpiled in `/backend/index.ts` for the frontend. Most code that works on the frontend tends to work in Deno, because Deno is designed to support "web-standards", but there are definitely edge cases to look out for.
stevensDemoREADME.md1 match
21## `favicon.svg`
2223As of this writing Val Town only supports text files, which is why the favicon is an SVG and not an .ico or any other binary image format. If you need binary file storage, check out [Blob Storage](https://docs.val.town/std/blob/).
2425## `components/`
stevensDemoindex.ts15 matches
73});
7475// --- Blob Image Serving Routes ---
7677// GET /api/images/:filename - Serve images from blob storage
78app.get("/api/images/:filename", async (c) => {
79const filename = c.req.param("filename");
8081try {
82// Get image data from blob storage
83const imageData = await blob.get(filename);
8485if (!imageData) {
86return c.json({ error: "Image not found" }, 404);
87}
8890let contentType = "application/octet-stream"; // Default
91if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) {
92contentType = "image/jpeg";
93} else if (filename.endsWith(".png")) {
94contentType = "image/png";
95} else if (filename.endsWith(".gif")) {
96contentType = "image/gif";
97} else if (filename.endsWith(".svg")) {
98contentType = "image/svg+xml";
99}
100101// Return the image with appropriate headers
102return new Response(imageData, {
103headers: {
104"Content-Type": contentType,
107});
108} catch (error) {
109console.error(`Error serving image ${filename}:`, error);
110return c.json(
111{ error: "Failed to load image", details: error.message },
112500,
113);