9 *
10 * @param {Object} params - The parameters for generating the work order email
11 * @param {string} [params.imageB64] - Base64 of the image of the issue (optional)
12 * @param {string} [params.audioB64] - Base64 of the audio recording describing the issue (optional)
13 * @param {string} params.fromName - Name of the sender
16export async function generateWorkorderEmail(
17 {
18 imageB64,
19 audioB64,
20 fromName,
21 }: {
22 imageB64?: string;
23 audioB64?: string;
24 fromName: string;
25 },
26): Promise<{ body: string; subject: string }> {
27 // Validate that at least one of image or audio is provided
28 if (!imageB64 && !audioB64) {
29 throw new Error("At least one of image or audio must be provided");
30 }
31
92orders. Generate a work order email based on the provided ${
93 audioB64 ? "issue description" : ""
94 }${audioB64 && imageB64 ? " and " : ""}${imageB64 ? "image" : ""}.
95Include relevant details and if useful recommended actions. Be
96relatively brief. Be very down to the point. Don't make assumptions. Say what
98NOT INCLUDE A SUBJECT, OR ANY OTHER PLACEHOLDERS. Just give the body and the
99intro You want to get stuff done!. ${
100 imageB64 ? "Mention the attached image." : ""
101 } Be a bit
102forceful. Don't say it is "urgent" unless you think it is urgent. Do not sound
113 // Build the user content array
114 const userContent: Array<
115 { type: string; text?: string; image_url?: { url: string } }
116 > = [];
117
123 transcription
124 ? `this issue description: ${transcription}`
125 : "the image provided"
126 }${locationInfo ? "\n\nLocation information: " + locationInfo : ""}`,
127 });
129 userContent.push({
130 type: "text",
131 text: "Please generate a work order email based on the image provided.",
132 });
133 }
134
135 // Add image if provided
136 if (imageB64) {
137 userContent.push({
138 type: "image_url",
139 image_url: {
140 url: imageB64,
141 },
142 });
5import * as db from "./db.ts";
6import { embedMetadata, handleFarcasterEndpoints, name } from "./farcaster.ts";
7import { handleImageEndpoints } from "./image.tsx";
8
9const app = new Hono();
10
11handleImageEndpoints(app);
12handleFarcasterEndpoints(app);
13
44 <meta name="fc:frame" content={JSON.stringify(embedMetadata(baseUrl, path))} />
45 <link rel="icon" href={baseUrl + "/icon"} />
46 <meta property="og:image" content={baseUrl + "/image"} />
47 </head>
48 <body class="bg-white text-black dark:bg-black dark:text-white">
5import satori from "npm:satori";
6
7export function handleImageEndpoints(app: Hono) {
8 const headers = {
9 "Content-Type": "image/png",
10 "cache-control": "public, max-age=86400",
11 };
12 app.get("/image", async (c) => {
13 return c.body(await homeImage(), 200, headers);
14 });
15 app.get("/icon", async (c) => {
16 return c.body(await iconImage(), 200, headers);
17 });
18}
19
20export async function homeImage() {
21 return await ogImage(
22 <div tw="w-full h-full flex justify-start items-end text-[100px] bg-black text-white p-[50px]">
23 <div tw="flex flex-col items-start gap-[10px]">
31}
32
33export async function iconImage() {
34 return await ogImage(
35 <div tw="w-full h-full flex justify-center items-center text-[100px] bg-black text-white p-[50px]">
36 <img tw="w-[350px] h-[350px]" src={base64Icon(SquareDashed)} />
45//////////
46
47export async function ogImage(body, options = {}) {
48 const svg = await satori(
49 body,
57 if (code === "emoji") {
58 const unicode = segment.codePointAt(0).toString(16).toUpperCase();
59 return `data:image/svg+xml;base64,` + btoa(await loadEmoji(unicode));
60 }
61 return "";
94 const base64 = Buffer.from(svg).toString("base64");
95 // console.log('getIconDataUrl', name, svg, base64)
96 return `data:image/svg+xml;base64,${base64}`;
97};
5export const name = "Mini App Starter";
6// export const iconUrl = "https://imgur.com/TrJLlwp.png";
7// export const ogImageUrl = "https://imgur.com/xKVOVUE.png";
8
9export function embedMetadata(baseUrl: string, path: string = "/") {
10 return {
11 version: "next",
12 imageUrl: baseUrl + "/image",
13 button: {
14 title: name,
17 name: name,
18 url: baseUrl + path,
19 splashImageUrl: baseUrl + "/icon",
20 splashBackgroundColor: "#111111",
21 },
55 "iconUrl": baseUrl + "/icon",
56 "homeUrl": baseUrl,
57 "splashImageUrl": baseUrl + "/icon",
58 "splashBackgroundColor": "#111111",
59 "primaryCategory": "developer-tools",
48
49<h2>Credit</h2>
50<div><div><p>I copied image <a class="text-blue-500 hover:underline" href="https://medium.com/@prabathshalitha21/11-http-headers-http-status-codes-eb34449599fb/">11. HTTP Headers
51& HTTP Status Codes</a> which was written by <a class="text-blue-500 hover:underline" href="https://medium.com/@prabathshalitha21?source=post_page---byline--eb34449599fb---------------------------------------">Prabath Shalitha</a></p></div></div>
52
14- [Hono JSX Starter][]: Render Hono with JSX
15- [Hono Client Starter][]: Render Hono with JSX on the server and hydrate on the client
16- [SVG Starter][]: Render an SVG image response
17- [SVG PNG Starter][]: Render an SVG as a PNG image response
18- [Preact Starter][]: Render Preact on the server
19- [Preact Client Starter][]: Render Preact on the server and hydrate the app on the client
46
47<!--
48- README card image
49
50- [x] React Starter
9 bearerToken: string;
10 selectedFiles: string[];
11 images: (string | null)[];
12 soundEnabled: boolean;
13}
19 bearerToken,
20 selectedFiles,
21 images,
22 soundEnabled,
23}: UseChatLogicProps) {
43 anthropicApiKey,
44 selectedFiles,
45 images: images
46 .filter((img): img is string => {
47 const isValid = typeof img === "string" && img.startsWith("data:");
48 if (!isValid && img !== null) {
49 console.warn("Invalid image format:", img?.substring(0, 50) + "...");
50 }
51 return isValid;
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
172
173- **Redirects:** Use `return new Response(null, { status: 302, headers: { Location: "/place/to/redirect" }})` instead of `Response.redirect` which is broken
174- **Images:** Avoid external images or base64 images. Use emojis, unicode symbols, or icon fonts/libraries instead
175- **AI Image:** To inline generate an AI image use: `<img src="https://maxm-imggenurl.web.val.run/the-description-of-your-image" />`
176- **Storage:** DO NOT use the Deno KV module for storage
177- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
682 background-color: var(--highlight);
683}
684.card-image {
685 display: flex;
686 align-items: center;
704}
705
706.image-placeholder,
707.image-thumbnail {
708 width: 40px;
709 height: 40px;
711 object-fit: cover;
712}
713.image-placeholder {
714 background-color: var(--muted);
715}
722}
723
724.image-row {
725 display: flex;
726 gap: var(--space-1);
727}
728.input-image {
729 position: relative;
730 border: 1px solid var(--muted);
731 border-radius: 6px;
732}
733.remove-image-button {
734 position: absolute;
735 top: 0;
744 opacity: 0;
745}
746.input-image:hover .remove-image-button {
747 opacity: 1;
748}
749
750.image-drop-overlay {
751 position: fixed;
752 top: 0;
761 justify-content: center;
762}
763.image-drop-inner {
764 padding: var(--space-2);
765 background-color: var(--background);
766}
767
768.transition, .input-box, .icon-button, .button, .remove-image-button {
769 transition-property: color, background-color, border-color, opacity;
770 transition-duration: 200ms;