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