132 right: 0;
133 bottom: 0;
134 background-image:
135 radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.1) 2px, transparent 2px),
136 radial-gradient(circle at 80% 80%, rgba(255, 255, 255, 0.1) 2px, transparent 2px),
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4import "chrome-devtools-frontend/Images/Images.js";
5import "chrome-devtools-frontend/core/dom_extension/dom_extension.js";
6import "chrome-devtools-frontend/panels/sources/sources-meta.js";
20 id: "6Xvl74QYtnOtTK5LAj3qUcr32aB2", // A hardcoded ID for the test caller.
21 fullName: "John Damon", // The name that will appear on the call screen.
22 profileImageUrl:
23 "https://s3.us-east-2.amazonaws.com/snag-staging-public-media/6Xvl74QYtnOtTK5LAj3qUcr32aB2/user/82df69fd-8411-459a-bfd5-3193cfc2e304.jpeg",
24 };
16 id: "6Xvl74QYtnOtTK5LAj3qUcr32aB2", // A hardcoded ID for the test caller.
17 fullName: "John Damon", // The name that will appear on the call screen.
18 profileImageUrl:
19 "https://s3.us-east-2.amazonaws.com/snag-staging-public-media/6Xvl74QYtnOtTK5LAj3qUcr32aB2/user/82df69fd-8411-459a-bfd5-3193cfc2e304.jpeg",
20 };
9 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10 <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
11 <link rel="icon" href="/public/favicon.svg" sizes="any" type="image/svg+xml">
12 <style>
13 .font-mono { font-family: 'JetBrains Mono', monospace; }
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/`
155- **TypeScript required** with strict interfaces
156- **Functional programming** preferred over classes
157- **No external images** - use emojis, Unicode symbols, or icon fonts
158- **Error handling**: Let errors bubble up with context rather than catching/logging
159- **Never include secrets** in code - always use environment variables
2302. **Shared Code**: Code in `shared/` must work in both frontend and backend (no `Deno` keyword)
2313. **File Handling**: Only text files supported, use `readFile` helpers for project files
2324. **Images**: Use AI image generation: `<img src="https://maxm-imggenurl.web.val.run/description" />`
2335. **Default Styling**: Use TailwindCSS via `<script src="https://cdn.twind.style" crossorigin></script>`
50 anthropicApiKey,
51 selectedFiles,
52 images,
53 } = await c.req.json();
54
77 branch_id: branchId,
78 val_id: project.id,
79 num_images: images?.length || 0,
80 model,
81 });
109 townie_usage_id: rowid,
110 townie_our_api_token: our_api_token,
111 townie_num_images: images?.length || 0,
112 townie_selected_files_count: selectedFiles?.length || 0,
113 },
127 let coreMessages = convertToCoreMessages(messages);
128
129 // If there are images, we need to add them to the last user message
130 if (images && Array.isArray(images) && images.length > 0) {
131 // Find the last user message
132 const lastUserMessageIndex = coreMessages.findIndex(
150 };
151
152 // Add each image to the content array using the correct ImagePart format
153 for (const image of images) {
154 if (image && image.url) {
155 // Extract mime type from data URL if available
156 let mimeType = undefined;
157 if (image.url.startsWith("data:")) {
158 const matches = image.url.match(/^data:([^;]+);/);
159 if (matches && matches.length > 1) {
160 mimeType = matches[1];
163
164 newUserMessage.content.push({
165 type: "image",
166 image: image.url,
167 mimeType,
168 });
16 SUM(cache_write_tokens) as total_cache_write_tokens,
17 SUM(price) as total_price,
18 SUM(num_images) as total_images
19 FROM ${USAGE_TABLE}
20 WHERE val_id = ? AND our_api_token = 1
54 total_cache_write_tokens: 0,
55 total_price: 0,
56 total_images: 0
57 };
58
85 // Always include inference price for comparison
86 inference_price: inferenceSummary.inference_price || 0,
87 total_images: usageSummary.total_images,
88 // Add flag to indicate inference data usage
89 used_inference_data: !!inferenceSummary.inference_price,