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:
4
5
6
7For 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.
21## `favicon.svg`
22
23As 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/).
24
25## `components/`
6 <title>React Hono Val Town Starter</title>
7 <script src="https://cdn.tailwindcss.com"></script>
8 <link rel="icon" href="/public/favicon.svg" sizes="any" type="image/svg+xml">
9 </head>
10 <body class="bg-gray-100 font-sans">
109 }
110
111 if (uploadedFile.type.startsWith('image/')) {
112 endpoint = '/api/parse/image';
113 requestData = { type: 'image', content: parseInput };
114 } else if (uploadedFile.type === 'application/pdf') {
115 endpoint = '/api/parse/pdf';
150
151 // Validate file type
152 const isImage = file.type.startsWith('image/');
153 const isPDF = file.type === 'application/pdf';
154
155 if (!isImage && !isPDF) {
156 setError('Please select an image file (JPG, PNG, etc.) or a PDF file');
157 return;
158 }
274 ref={fileInputRef}
275 type="file"
276 accept="image/*,.pdf"
277 onChange={handleFileUpload}
278 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
279 />
280 <div className="mt-2 text-sm text-gray-600">
281 📷 Images (JPG, PNG, etc.) or 📄 PDF files supported
282 </div>
283 </div>
286 <span className="mr-2">✅</span>
287 <span>
288 {uploadedFile.type.startsWith('image/') ? '📷' : '📄'}
289 {uploadedFile.name} uploaded and ready to parse
290 </span>
19 const recipeResult = await sqlite.execute(`
20 INSERT INTO ${RECIPES_TABLE}
21 (title, description, servings, prep_time, cook_time, difficulty, tags, source, image_url, steps)
22 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
23 `, [
30 tags ? JSON.stringify(tags) : null,
31 recipeData.source || null,
32 recipeData.imageUrl || null,
33 JSON.stringify(steps)
34 ]);
108 tags: recipeRow.tags ? JSON.parse(recipeRow.tags as string) : undefined,
109 source: recipeRow.source as string || undefined,
110 imageUrl: recipeRow.image_url as string || undefined,
111 createdAt: recipeRow.created_at as string,
112 updatedAt: recipeRow.updated_at as string
168 const dbKey = key === 'prepTime' ? 'prep_time' :
169 key === 'cookTime' ? 'cook_time' :
170 key === 'imageUrl' ? 'image_url' : key;
171 setParts.push(`${dbKey} = ?`);
172 params.push(value);
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Recipe Parser - Capture, Parse & Store Recipes</title>
7 <meta name="description" content="Intelligent recipe parser that extracts recipes from URLs, PDFs, and images">
8
9 <!-- TailwindCSS -->
19 tags TEXT, -- JSON array of tags
20 source TEXT,
21 image_url TEXT,
22 steps TEXT NOT NULL, -- JSON array of steps
23 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
21 tags?: string[];
22 source?: string; // URL or source description
23 imageUrl?: string;
24 createdAt?: string;
25 updatedAt?: string;
27
28export interface ParseRequest {
29 type: 'url' | 'pdf' | 'image';
30 content: string; // URL, base64 PDF, or base64 image
31 filename?: string;
32}
1# Recipe Parser App
2
3A mobile-friendly web application that captures, parses, and stores recipes from multiple sources including URLs, PDFs, and images.
4
5## Features
8 - Web URLs (recipe websites)
9 - PDF files
10 - Images (photos of recipes)
11- **Intelligent Parsing**: Automatically extracts:
12 - Dish title
23- **Database**: SQLite for recipe storage
24- **AI**: OpenAI GPT-4 for intelligent recipe parsing
25- **File Processing**: PDF parsing and image OCR capabilities
26
27## Project Structure
52- `POST /api/parse/url` - Parse recipe from URL
53- `POST /api/parse/pdf` - Parse recipe from PDF
54- `POST /api/parse/image` - Parse recipe from image
55- `GET /api/recipes` - Get all recipes
56- `POST /api/recipes` - Save a recipe
62
631. Open the app in your browser
642. Choose input method (URL, PDF, or image)
653. Submit your recipe source
664. Review and edit the parsed recipe
64 class="rounded-md w-[120px] h-[180px] object-cover flex-shrink-0"
65 height="180"
66 src="https://storage.googleapis.com/a1aa/image/da09cbc2-570f-460a-05bb-9836a9055c79.jpg"
67 width="120"
68 />