diff --git a/docs/Autofill.md b/docs/Autofill.md
index 2b0c690..462f266 100644
--- a/docs/Autofill.md
+++ b/docs/Autofill.md
@@ -11,7 +11,7 @@
- blue.flashes.actor.portfolio
- social.grain.gallery
- xyz.statusphere.status
- - site.standard.publication
+ - site.standard.publication
- fm.teal.alpha.feed.play
- dev.npmx.feed.like
- add bluesky profile card
diff --git a/src/lib/atproto/methods.ts b/src/lib/atproto/methods.ts
index 7ed0b2f..0bf31db 100644
--- a/src/lib/atproto/methods.ts
+++ b/src/lib/atproto/methods.ts
@@ -395,7 +395,8 @@ export async function getBlobURL({
*/
export function getCDNImageBlobUrl({
did,
- blob
+ blob,
+ type = 'webp'
}: {
did?: string;
blob: {
@@ -404,11 +405,12 @@ export function getCDNImageBlobUrl({
$link: string;
};
};
+ type?: 'webp' | 'jpeg';
}) {
if (!blob || !did) return;
did ??= user.did;
- return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${blob.ref.$link}@webp`;
+ return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${blob.ref.$link}@${type}`;
}
/**
diff --git a/src/lib/cards/social/EventCard/index.ts b/src/lib/cards/social/EventCard/index.ts
index 0e8baf3..8d14aa1 100644
--- a/src/lib/cards/social/EventCard/index.ts
+++ b/src/lib/cards/social/EventCard/index.ts
@@ -24,7 +24,8 @@ export type EventData = {
alt?: string;
role?: string;
content?: {
- ref?: {
+ $type: 'blob';
+ ref: {
$link: string;
};
mimeType?: string;
diff --git a/src/routes/[[actor=actor]]/e/+page.server.ts b/src/routes/[[actor=actor]]/e/+page.server.ts
new file mode 100644
index 0000000..e94b77e
--- /dev/null
+++ b/src/routes/[[actor=actor]]/e/+page.server.ts
@@ -0,0 +1,77 @@
+import { error } from '@sveltejs/kit';
+import type { EventData } from '$lib/cards/social/EventCard';
+import { getBlentoOrBskyProfile, resolveHandle } from '$lib/atproto/methods.js';
+import { isHandle } from '@atcute/lexicons/syntax';
+import { createCache, type CachedProfile } from '$lib/cache';
+import type { ActorIdentifier, Did } from '@atcute/lexicons';
+import { env as publicEnv } from '$env/dynamic/public';
+
+export async function load({ params, platform, request }) {
+ const cache = createCache(platform);
+
+ const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
+
+ let actor: ActorIdentifier | undefined = params.actor;
+
+ if (!actor) {
+ const kv = platform?.env?.CUSTOM_DOMAINS;
+
+ if (kv && customDomain) {
+ try {
+ const did = await kv.get(customDomain);
+
+ if (did) actor = did as ActorIdentifier;
+ } catch (error) {
+ console.error('failed to get custom domain kv', error);
+ }
+ } else {
+ actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
+ }
+ } else if (customDomain && params.actor) {
+ actor = undefined;
+ }
+
+ const did = isHandle(actor) ? await resolveHandle({ handle: actor }) : actor;
+
+ if (!did) {
+ throw error(404, 'Events not found');
+ }
+
+ try {
+ const [eventsResponse, hostProfile] = await Promise.all([
+ fetch(
+ `https://smokesignal.events/xrpc/community.lexicon.calendar.searchEvents?repository=${encodeURIComponent(did)}&query=upcoming`
+ ),
+ cache
+ ? cache.getProfile(did as Did).catch(() => null)
+ : getBlentoOrBskyProfile({ did: did as Did })
+ .then(
+ (p): CachedProfile => ({
+ did: p.did as string,
+ handle: p.handle as string,
+ displayName: p.displayName as string | undefined,
+ avatar: p.avatar as string | undefined,
+ hasBlento: p.hasBlento,
+ url: p.url
+ })
+ )
+ .catch(() => null)
+ ]);
+
+ if (!eventsResponse.ok) {
+ throw error(404, 'Events not found');
+ }
+
+ const data: { results: EventData[] } = await eventsResponse.json();
+ const events = data.results;
+
+ return {
+ events,
+ did,
+ hostProfile: hostProfile ?? null
+ };
+ } catch (e) {
+ if (e && typeof e === 'object' && 'status' in e) throw e;
+ throw error(404, 'Events not found');
+ }
+}
diff --git a/src/routes/[[actor=actor]]/e/+page.svelte b/src/routes/[[actor=actor]]/e/+page.svelte
new file mode 100644
index 0000000..6a67240
--- /dev/null
+++ b/src/routes/[[actor=actor]]/e/+page.svelte
@@ -0,0 +1,170 @@
+
+
+
+ {hostName} - Events
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Events
+
Hosted by {hostName}
+
+
+
+ {#if events.length === 0}
+
No events found.
+ {:else}
+
+ {/if}
+
+
diff --git a/src/routes/[[actor=actor]]/e/[rkey]/+page.svelte b/src/routes/[[actor=actor]]/e/[rkey]/+page.svelte
index 362d28d..2948235 100644
--- a/src/routes/[[actor=actor]]/e/[rkey]/+page.svelte
+++ b/src/routes/[[actor=actor]]/e/[rkey]/+page.svelte
@@ -1,8 +1,10 @@
{eventData.name}
+
+
+
+
+
+
+
diff --git a/src/routes/[[actor=actor]]/e/[rkey]/og.png/+server.ts b/src/routes/[[actor=actor]]/e/[rkey]/og.png/+server.ts
new file mode 100644
index 0000000..55006c6
--- /dev/null
+++ b/src/routes/[[actor=actor]]/e/[rkey]/og.png/+server.ts
@@ -0,0 +1,84 @@
+import { getCDNImageBlobUrl, resolveHandle } from '$lib/atproto/methods.js';
+import { env as publicEnv } from '$env/dynamic/public';
+
+import type { ActorIdentifier } from '@atcute/lexicons';
+import { isHandle } from '@atcute/lexicons/syntax';
+import type { EventData } from '$lib/cards/social/EventCard';
+import { ImageResponse } from '@ethercorps/sveltekit-og';
+import { error } from '@sveltejs/kit';
+import EventOgImage from './EventOgImage.svelte';
+
+function formatDate(dateStr: string): string {
+ const date = new Date(dateStr);
+ const weekday = date.toLocaleDateString('en-US', { weekday: 'long' });
+ const month = date.toLocaleDateString('en-US', { month: 'long' });
+ const day = date.getDate();
+ return `${weekday}, ${month} ${day}`;
+}
+
+export async function GET({ params, platform, request }) {
+ const { rkey } = params;
+
+ const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
+
+ let actor: ActorIdentifier | undefined = params.actor;
+
+ if (!actor) {
+ const kv = platform?.env?.CUSTOM_DOMAINS;
+
+ if (kv && customDomain) {
+ try {
+ const did = await kv.get(customDomain);
+ if (did) actor = did as ActorIdentifier;
+ } catch (error) {
+ console.error('failed to get custom domain kv', error);
+ }
+ } else {
+ actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
+ }
+ }
+
+ const did = isHandle(actor) ? await resolveHandle({ handle: actor }) : actor;
+
+ if (!did || !rkey) {
+ throw error(404, 'Event not found');
+ }
+
+ let eventData: EventData;
+
+ try {
+ const eventResponse = await fetch(
+ `https://smokesignal.events/xrpc/community.lexicon.calendar.GetEvent?repository=${encodeURIComponent(did)}&record_key=${encodeURIComponent(rkey)}`
+ );
+
+ if (!eventResponse.ok) {
+ throw error(404, 'Event not found');
+ }
+
+ eventData = await eventResponse.json();
+ } catch (e) {
+ if (e && typeof e === 'object' && 'status' in e) throw e;
+ throw error(404, 'Event not found');
+ }
+
+ const dateStr = formatDate(eventData.startsAt);
+
+ let thumbnailUrl: string | null = null;
+ if (eventData.media && eventData.media.length > 0) {
+ const media = eventData.media.find((m) => m.role === 'thumbnail');
+ if (media?.content) {
+ thumbnailUrl = getCDNImageBlobUrl({ did, blob: media.content, type: 'jpeg' }) ?? null;
+ }
+ }
+
+ return new ImageResponse(
+ EventOgImage,
+ { width: 1200, height: 630, debug: false },
+ {
+ name: eventData.name,
+ dateStr,
+ thumbnailUrl,
+ rkey
+ }
+ );
+}
diff --git a/src/routes/[[actor=actor]]/e/[rkey]/og.png/EventOgImage.svelte b/src/routes/[[actor=actor]]/e/[rkey]/og.png/EventOgImage.svelte
new file mode 100644
index 0000000..c9e9ac8
--- /dev/null
+++ b/src/routes/[[actor=actor]]/e/[rkey]/og.png/EventOgImage.svelte
@@ -0,0 +1,63 @@
+
+
+
+
+
+ {#if thumbnailUrl}
+

+ {:else}
+
+ {/if}
+
+
+
+
+
--
2.51.2
From 96fe2c3a8091f752e01b117e9cfe5febaecb22f2 Mon Sep 17 00:00:00 2001
From: Florian <45694132+flo-bit@users.noreply.github.com>
Date: Sat, 14 Feb 2026 20:18:43 +0100
Subject: [PATCH 2/3] some refactoring
---
src/lib/actor.ts | 40 +++++++++++++++++++
.../[[actor=actor]]/(pages)/+layout.server.ts | 25 +-----------
.../site.standard.publication/+server.ts | 26 +-----------
.../[[actor=actor]]/api/refresh/+server.ts | 23 +----------
src/routes/[[actor=actor]]/e/+page.server.ts | 31 ++------------
src/routes/[[actor=actor]]/e/+page.svelte | 29 ++++++++++----
.../[[actor=actor]]/e/[rkey]/+page.server.ts | 25 +-----------
.../[[actor=actor]]/e/[rkey]/+page.svelte | 2 +-
.../e/[rkey]/og.png/+server.ts | 22 +---------
9 files changed, 76 insertions(+), 147 deletions(-)
create mode 100644 src/lib/actor.ts
diff --git a/src/lib/actor.ts b/src/lib/actor.ts
new file mode 100644
index 0000000..6b662ac
--- /dev/null
+++ b/src/lib/actor.ts
@@ -0,0 +1,40 @@
+import type { ActorIdentifier, Did } from '@atcute/lexicons';
+import type { CacheService } from './cache';
+import { env as publicEnv } from '$env/dynamic/public';
+import { resolveHandle } from './atproto';
+import { isHandle } from '@atcute/lexicons/syntax';
+
+export async function getActor({
+ request,
+ paramActor,
+ platform,
+ blockBoth = true
+}: {
+ request: Request;
+ paramActor?: ActorIdentifier;
+ platform: Readonly
| undefined;
+ blockBoth?: boolean;
+}): Promise {
+ const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
+ let actor = paramActor;
+
+ if (!actor) {
+ const kv = platform?.env?.CUSTOM_DOMAINS;
+
+ if (kv && customDomain) {
+ try {
+ const did = await kv.get(customDomain);
+
+ if (did) actor = did as ActorIdentifier;
+ } catch (error) {
+ console.error('failed to get custom domain kv', error);
+ }
+ } else {
+ actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
+ }
+ } else if (customDomain && paramActor && blockBoth) {
+ actor = undefined;
+ }
+
+ return isHandle(actor) ? await resolveHandle({ handle: actor }) : actor;
+}
diff --git a/src/routes/[[actor=actor]]/(pages)/+layout.server.ts b/src/routes/[[actor=actor]]/(pages)/+layout.server.ts
index 69e6cb2..994e277 100644
--- a/src/routes/[[actor=actor]]/(pages)/+layout.server.ts
+++ b/src/routes/[[actor=actor]]/(pages)/+layout.server.ts
@@ -2,35 +2,14 @@ import { loadData } from '$lib/website/load';
import { env } from '$env/dynamic/private';
import { error } from '@sveltejs/kit';
import { createCache } from '$lib/cache';
-import type { ActorIdentifier } from '@atcute/lexicons';
-import { env as publicEnv } from '$env/dynamic/public';
+import { getActor } from '$lib/actor.js';
export async function load({ params, platform, request }) {
if (env.PUBLIC_IS_SELFHOSTED) error(404);
const cache = createCache(platform);
- const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
-
- let actor: ActorIdentifier | undefined = params.actor;
-
- if (!actor) {
- const kv = platform?.env?.CUSTOM_DOMAINS;
-
- if (kv && customDomain) {
- try {
- const did = await kv.get(customDomain);
-
- if (did) actor = did as ActorIdentifier;
- } catch (error) {
- console.error('failed to get custom domain kv', error);
- }
- } else {
- actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
- }
- } else if (customDomain && params.actor) {
- actor = undefined;
- }
+ const actor = await getActor({ request, paramActor: params.actor, platform });
if (!actor) {
throw error(404, 'Page not found');
diff --git a/src/routes/[[actor=actor]]/.well-known/site.standard.publication/+server.ts b/src/routes/[[actor=actor]]/.well-known/site.standard.publication/+server.ts
index faccbef..7ddcc54 100644
--- a/src/routes/[[actor=actor]]/.well-known/site.standard.publication/+server.ts
+++ b/src/routes/[[actor=actor]]/.well-known/site.standard.publication/+server.ts
@@ -1,36 +1,14 @@
import { loadData } from '$lib/website/load';
import { createCache } from '$lib/cache';
import { env } from '$env/dynamic/private';
-import { env as publicEnv } from '$env/dynamic/public';
-import type { ActorIdentifier } from '@atcute/lexicons';
-
import { error } from '@sveltejs/kit';
import { text } from '@sveltejs/kit';
+import { getActor } from '$lib/actor.js';
export async function GET({ params, platform, request }) {
const cache = createCache(platform);
- const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
-
- let actor: ActorIdentifier | undefined = params.actor;
-
- if (!actor) {
- const kv = platform?.env?.CUSTOM_DOMAINS;
-
- if (kv && customDomain) {
- try {
- const did = await kv.get(customDomain);
-
- if (did) actor = did as ActorIdentifier;
- } catch (error) {
- console.error('failed to get custom domain kv', error);
- }
- } else {
- actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
- }
- } else if (customDomain && params.actor) {
- actor = undefined;
- }
+ const actor = await getActor({ request, paramActor: params.actor, platform });
if (!actor) {
throw error(404, 'Page not found');
diff --git a/src/routes/[[actor=actor]]/api/refresh/+server.ts b/src/routes/[[actor=actor]]/api/refresh/+server.ts
index 6610512..73828cc 100644
--- a/src/routes/[[actor=actor]]/api/refresh/+server.ts
+++ b/src/routes/[[actor=actor]]/api/refresh/+server.ts
@@ -1,33 +1,14 @@
import { createCache } from '$lib/cache';
import { loadData } from '$lib/website/load.js';
import { env } from '$env/dynamic/private';
-import { env as publicEnv } from '$env/dynamic/public';
-import type { ActorIdentifier } from '@atcute/lexicons';
import { error, json } from '@sveltejs/kit';
+import { getActor } from '$lib/actor';
export async function GET({ params, platform, request }) {
const cache = createCache(platform);
if (!cache) return json('no cache');
- const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
-
- let actor: ActorIdentifier | undefined = params.actor;
-
- if (!actor) {
- const kv = platform?.env?.CUSTOM_DOMAINS;
-
- if (kv && customDomain) {
- try {
- const did = await kv.get(customDomain);
-
- if (did) actor = did as ActorIdentifier;
- } catch (error) {
- console.error('failed to get custom domain kv', error);
- }
- } else {
- actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
- }
- }
+ const actor = await getActor({ request, paramActor: params.actor, platform });
if (!actor) {
throw error(404, 'Page not found');
diff --git a/src/routes/[[actor=actor]]/e/+page.server.ts b/src/routes/[[actor=actor]]/e/+page.server.ts
index e94b77e..17bd279 100644
--- a/src/routes/[[actor=actor]]/e/+page.server.ts
+++ b/src/routes/[[actor=actor]]/e/+page.server.ts
@@ -1,37 +1,14 @@
import { error } from '@sveltejs/kit';
import type { EventData } from '$lib/cards/social/EventCard';
-import { getBlentoOrBskyProfile, resolveHandle } from '$lib/atproto/methods.js';
-import { isHandle } from '@atcute/lexicons/syntax';
+import { getBlentoOrBskyProfile } from '$lib/atproto/methods.js';
import { createCache, type CachedProfile } from '$lib/cache';
-import type { ActorIdentifier, Did } from '@atcute/lexicons';
-import { env as publicEnv } from '$env/dynamic/public';
+import type { Did } from '@atcute/lexicons';
+import { getActor } from '$lib/actor.js';
export async function load({ params, platform, request }) {
const cache = createCache(platform);
- const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
-
- let actor: ActorIdentifier | undefined = params.actor;
-
- if (!actor) {
- const kv = platform?.env?.CUSTOM_DOMAINS;
-
- if (kv && customDomain) {
- try {
- const did = await kv.get(customDomain);
-
- if (did) actor = did as ActorIdentifier;
- } catch (error) {
- console.error('failed to get custom domain kv', error);
- }
- } else {
- actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
- }
- } else if (customDomain && params.actor) {
- actor = undefined;
- }
-
- const did = isHandle(actor) ? await resolveHandle({ handle: actor }) : actor;
+ const did = await getActor({ request, paramActor: params.actor, platform });
if (!did) {
throw error(404, 'Events not found');
diff --git a/src/routes/[[actor=actor]]/e/+page.svelte b/src/routes/[[actor=actor]]/e/+page.svelte
index 6a67240..a30e4d8 100644
--- a/src/routes/[[actor=actor]]/e/+page.svelte
+++ b/src/routes/[[actor=actor]]/e/+page.svelte
@@ -11,6 +11,9 @@
let hostProfile = $derived(data.hostProfile);
let hostName = $derived(hostProfile?.displayName || hostProfile?.handle || did);
+ let hostUrl = $derived(
+ hostProfile?.url ?? `https://bsky.app/profile/${hostProfile?.handle || did}`
+ );
function formatDate(dateStr: string): string {
const date = new Date(dateStr);
@@ -88,14 +91,24 @@
-
+
-
-
-
-
Events
-
Hosted by {hostName}
+
@@ -116,11 +129,11 @@

{:else}
-
+
Date: Sat, 14 Feb 2026 20:22:38 +0100
Subject: [PATCH 3/3] small fixes
---
src/routes/[[actor=actor]]/api/refresh/+server.ts | 2 +-
src/routes/[[actor=actor]]/e/+page.svelte | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/routes/[[actor=actor]]/api/refresh/+server.ts b/src/routes/[[actor=actor]]/api/refresh/+server.ts
index 73828cc..cb141d3 100644
--- a/src/routes/[[actor=actor]]/api/refresh/+server.ts
+++ b/src/routes/[[actor=actor]]/api/refresh/+server.ts
@@ -8,7 +8,7 @@ export async function GET({ params, platform, request }) {
const cache = createCache(platform);
if (!cache) return json('no cache');
- const actor = await getActor({ request, paramActor: params.actor, platform });
+ const actor = await getActor({ request, paramActor: params.actor, platform, blockBoth: false });
if (!actor) {
throw error(404, 'Page not found');
diff --git a/src/routes/[[actor=actor]]/e/+page.svelte b/src/routes/[[actor=actor]]/e/+page.svelte
index a30e4d8..4c78a49 100644
--- a/src/routes/[[actor=actor]]/e/+page.svelte
+++ b/src/routes/[[actor=actor]]/e/+page.svelte
@@ -98,7 +98,7 @@
Upcoming events
-