your personal website on atproto - mirror blento.app

new og image

+131 -31
+14 -31
src/routes/[handle=handle]/og.png/+server.ts
··· 1 import type { UserCache } from '$lib/types'; 2 import { loadData } from '$lib/website/load'; 3 import type { Handle } from '@atcute/lexicons'; 4 import { ImageResponse } from '@ethercorps/sveltekit-og'; 5 6 export async function GET({ params, platform }) { 7 - const handle = params.handle; 8 - 9 const cache = platform?.env?.USER_DATA_CACHE as unknown; 10 11 const data = await loadData(params.handle as Handle, cache as UserCache); 12 13 - const image = data.profile.avatar; 14 - 15 - const htmlString = ` 16 - <div class="flex flex-col p-8 w-full h-full bg-neutral-900"> 17 - <div class="flex items-center mb-8 mt-16"> 18 - <img src="${image}" width="128" height="128" class="rounded-full" /> 19 - 20 - <h1 class="text-neutral-50 text-7xl ml-4">${handle}</h1> 21 - </div> 22 - 23 - <p class="mt-8 text-4xl text-neutral-300">Check out my blento</p> 24 - 25 - <svg class="absolute w-130 h-130 top-50 right-0" viewBox="0 0 900 900" fill="none" xmlns="http://www.w3.org/2000/svg"> 26 - <rect x="100" y="100" width="160" height="340" rx="23" fill="#EF4444"/> 27 - <rect x="640" y="280" width="160" height="340" rx="23" fill="#22C55E"/> 28 - <rect x="280" y="100" width="340" height="340" rx="23" fill="#F59E0B"/> 29 - <rect x="100" y="460" width="340" height="160" rx="23" fill="#0EA5E9"/> 30 - <rect x="640" y="100" width="160" height="160" rx="23" fill="#EAB308"/> 31 - <rect x="100" y="640" width="160" height="160" rx="23" fill="#6366F1"/> 32 - <rect x="460" y="460" width="160" height="160" rx="23" fill="#14B8A6"/> 33 - <rect x="280" y="640" width="520" height="160" rx="23" fill="#A855F7"/> 34 - </svg> 35 - </div> 36 - `; 37 - 38 - return new ImageResponse(htmlString, { 39 - width: 1200, 40 - height: 630 41 - }); 42 }
··· 1 import type { UserCache } from '$lib/types'; 2 import { loadData } from '$lib/website/load'; 3 + import { getName } from '$lib/helper'; 4 import type { Handle } from '@atcute/lexicons'; 5 import { ImageResponse } from '@ethercorps/sveltekit-og'; 6 + import OGImage from './OGImage.svelte'; 7 8 export async function GET({ params, platform }) { 9 const cache = platform?.env?.USER_DATA_CACHE as unknown; 10 11 const data = await loadData(params.handle as Handle, cache as UserCache); 12 13 + return new ImageResponse( 14 + OGImage, 15 + { 16 + width: 1200, 17 + height: 630 18 + }, 19 + { 20 + name: getName(data), 21 + avatar: data.profile.avatar, 22 + items: data.cards 23 + } 24 + ); 25 }
+97
src/routes/[handle=handle]/og.png/OGImage.svelte
···
··· 1 + <script lang="ts"> 2 + import type { Item } from '$lib/types'; 3 + 4 + interface Props { 5 + name: string; 6 + avatar: string | undefined; 7 + items: Array<Item>; 8 + } 9 + 10 + const { name, avatar, items }: Props = $props(); 11 + 12 + const GRID_UNIT = 100; 13 + const GAP = 20; 14 + 15 + const colors = { 16 + red: '#ef4444', 17 + orange: '#f97316', 18 + amber: '#f59e0b', 19 + yellow: '#eab308', 20 + lime: '#84cc16', 21 + green: '#22c55e', 22 + emerald: '#10b981', 23 + teal: '#14b8a6', 24 + cyan: '#06b6d4', 25 + sky: '#0ea5e9', 26 + blue: '#3b82f6', 27 + indigo: '#6366f1', 28 + violet: '#8b5cf6', 29 + purple: '#a855f7', 30 + fuchsia: '#d946ef', 31 + pink: '#ec4899', 32 + rose: '#f43f5e' 33 + }; 34 + 35 + function getTailwindColor(color: keyof typeof colors) { 36 + return colors[color]; 37 + } 38 + 39 + function getColor(item: Item): string { 40 + if (item.color) { 41 + let twColor = getTailwindColor(item.color as any); 42 + return twColor ?? item.color; 43 + } 44 + if (item.cardData?.color) { 45 + let twColor = getTailwindColor(item.cardData?.color); 46 + return twColor ?? '#' + item.cardData.color; 47 + } 48 + return '#262626'; 49 + } 50 + 51 + function getBoxStyle( 52 + item: { x: number; y: number; w: number; h: number }, 53 + color: string 54 + ): string { 55 + const x = item.x * GRID_UNIT + GAP / 2; 56 + const y = item.y * GRID_UNIT + GAP / 2; 57 + const w = item.w * GRID_UNIT - GAP; 58 + const h = item.h * GRID_UNIT - GAP; 59 + 60 + return `position: absolute; left: ${x + 300}px; top: ${y}px; width: ${w}px; height: ${h}px; background: ${color}; border-radius: 20px; margin: 40px; color: transparent;`; 61 + } 62 + </script> 63 + 64 + <div 65 + style="display: flex; width: 1200px; height: 630px; background: #171717; font-family: sans-serif;" 66 + > 67 + <!-- Left profile section --> 68 + <div 69 + style="display: flex; flex-direction: column; width: 350px; padding: 40px; justify-content: center; align-items: center;" 70 + > 71 + {#if avatar} 72 + <img 73 + src={avatar} 74 + alt="" 75 + style="width: 150px; height: 150px; border-radius: 100px; object-fit: cover;" 76 + /> 77 + {:else} 78 + <div 79 + style="width: 100px; height: 100px; border-radius: 50px; background: #404040; display: flex; align-items: center; justify-content: center;" 80 + > 81 + <div style="color: #a3a3a3; font-size: 50px; font-weight: bold;"> 82 + {name.charAt(0).toUpperCase()} 83 + </div> 84 + </div> 85 + {/if} 86 + <div 87 + style="color: white; font-size: 32px; font-weight: bold; margin-top: 16px; text-align: center; max-width: 270px; overflow: hidden; text-overflow: ellipsis;" 88 + > 89 + {name} 90 + </div> 91 + </div> 92 + 93 + <!-- Right grid section --> 94 + {#each items as item, i (i)} 95 + <div style={getBoxStyle(item, getColor(item))}>hello</div> 96 + {/each} 97 + </div>
+13
src/routes/[handle=handle]/og.png/test/+page.server.ts
···
··· 1 + import { loadData } from '$lib/website/load'; 2 + import { env } from '$env/dynamic/private'; 3 + import { error } from '@sveltejs/kit'; 4 + import type { UserCache } from '$lib/types'; 5 + import type { Handle } from '@atcute/lexicons'; 6 + 7 + export async function load({ params, platform }) { 8 + if (env.PUBLIC_IS_SELFHOSTED) error(404); 9 + 10 + const cache = platform?.env?.USER_DATA_CACHE as unknown; 11 + 12 + return await loadData(params.handle as Handle, cache as UserCache, false); 13 + }
+7
src/routes/[handle=handle]/og.png/test/+page.svelte
···
··· 1 + <script> 2 + import OGImage from '../OGImage.svelte'; 3 + 4 + let { data } = $props(); 5 + </script> 6 + 7 + <OGImage items={data.cards} name={data.handle} avatar={data.profile.avatar} />