38 username: user.username,
39 bio: user.bio,
40 profile_image_url: user.profileImageUrl,
41 url: user.url,
42 updated_at: new Date().toISOString(), // Using current time as the API doesn't provide updated_at
56 name: project.name,
57 description: project.description,
58 image_url: project.imageUrl,
59 username: project.author.username || "",
60 updated_at: mainBranch?.updatedAt || new Date().toISOString(),
299 >
300 <img
301 src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSIGEJA8-xL8Aa9oC7Y7gFqBoICxvhebGig6ADUJYSFVEzWRD0Y5GmuhnjW6B6e11C07iE&usqp=CAU"
302 alt="EEP Portal Logo"
303 style={{
1908 <meta charset="UTF-8">
1909 <style>${css}</style>
1910 <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📰</text></svg>">
1911 </head>
1912 <body>
18 SUM(cache_write_tokens) as total_cache_write_tokens,
19 SUM(price) as total_price,
20 SUM(num_images) as total_images
21 FROM ${USAGE_TABLE}
22 WHERE our_api_token = 1
101 total_cache_write_tokens: userData.cache_write_tokens,
102 total_price: userData.price,
103 total_images: 0,
104 used_inference_data: true
105 });
7 branchId: string | undefined;
8 selectedFiles: string[];
9 images: (string | null)[];
10 soundEnabled: boolean;
11}
20 // bearerToken,
21 selectedFiles,
22 images,
23 soundEnabled,
24}: UseChatLogicProps) {
44 branchId,
45 selectedFiles,
46 images: images
47 .filter((img): img is string => {
48 const isValid = typeof img === "string" && img.startsWith("data:");
49 if (!isValid && img !== null) {
50 console.warn(
51 "Invalid image format:",
52 img?.substring(0, 50) + "..."
53 );
29 - [x] File write as a code embed
30 - [x] str_replace as a diff view
31- [x] make image drop area invisible and bigger
32- [x] Give it all the code (except maybe .txt files) as initial context (like cursor sonnet max)
33- [x] I seem to have lost the delete file tool and instructions, try to find them back in history or re-create?
55- [x] Create branch
56- [x] URL input + pathname
57- [x] Image upload controls
58- [x] Preview refresh button
59- [x] Audio controls
155
156- **Redirects:** Use `return new Response(null, { status: 302, headers: { Location: "/place/to/redirect" }})` instead of `Response.redirect` which is broken
157- **Images:** Avoid external images or base64 images. Use emojis, unicode symbols, or icon fonts/libraries instead
158- **AI Image:** To inline generate an AI image use: `<img src="https://maxm-imggenurl.web.val.run/the-description-of-your-image" />`
159- **Storage:** DO NOT use the Deno KV module for storage
160- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
782 background-color: var(--highlight);
783}
784.card-image {
785 display: flex;
786 align-items: center;
809}
810
811.image-placeholder,
812.image-thumbnail {
813 flex-shrink: 0;
814 width: 40px;
817 object-fit: cover;
818}
819.image-placeholder {
820 background-color: var(--muted);
821}
828}
829
830.image-row {
831 display: flex;
832 gap: var(--space-1);
833}
834.input-image {
835 position: relative;
836 border: 1px solid var(--muted);
837 border-radius: 6px;
838}
839.remove-image-button {
840 position: absolute;
841 top: 0;
850 opacity: 0;
851}
852.input-image:hover .remove-image-button {
853 opacity: 1;
854}
855
856.image-drop-overlay {
857 position: fixed;
858 top: 0;
867 justify-content: center;
868}
869.image-drop-inner {
870 padding: var(--space-2);
871 background-color: var(--background);
880}
881
882.transition, .input-box, .icon-button, .button, .remove-image-button {
883 transition-property: color, background-color, border-color, opacity;
884 transition-duration: 200ms;
28 }
29
30 const { messages, project, branchId, anthropicApiKey, selectedFiles, images } = await c.req.json();
31
32 // do we want to allow user-provided tokens still
63 branch_id: branchId,
64 val_id: project.id,
65 num_images: images?.length || 0,
66 model,
67 });
95 townie_usage_id: rowid,
96 townie_our_api_token: our_api_token,
97 townie_num_images: images?.length || 0,
98 townie_selected_files_count: selectedFiles?.length || 0,
99 },
113 let coreMessages = convertToCoreMessages(messages);
114
115 // If there are images, we need to add them to the last user message
116 if (images && Array.isArray(images) && images.length > 0) {
117 // Find the last user message
118 const lastUserMessageIndex = coreMessages.findIndex(
136 };
137
138 // Add each image to the content array using the correct ImagePart format
139 for (const image of images) {
140 if (image && image.url) {
141 // Extract mime type from data URL if available
142 let mimeType = undefined;
143 if (image.url.startsWith("data:")) {
144 const matches = image.url.match(/^data:([^;]+);/);
145 if (matches && matches.length > 1) {
146 mimeType = matches[1];
149
150 newUserMessage.content.push({
151 type: "image",
152 image: image.url,
153 mimeType,
154 });
18 price?: number;
19 finish_reason?: string;
20 num_images?: number;
21 our_api_token: boolean;
22}
43 price REAL,
44 finish_reason TEXT,
45 num_images INTEGER,
46 our_api_token INTEGER NOT NULL,
47 finish_timestamp INTEGER
16 price: number | null;
17 finish_reason: string | null;
18 num_images: number | null;
19 our_api_token: number;
20}
191 <th>Price</th>
192 <th>Finish</th>
193 <th>Images</th>
194 <th>Our API</th>
195 </tr>
215 <td class="price">${formatPrice(row.price)}</td>
216 <td>${row.finish_reason || '-'}</td>
217 <td>${formatNumber(row.num_images)}</td>
218 <td>${formatBoolean(row.our_api_token)}</td>
219 </tr>