();
+ for (const item of items) {
+ for (const did of item.cardData.friends ?? []) {
+ allDids.add(did as Did);
+ }
+ }
+ if (allDids.size === 0) return [];
+
+ const profiles = await Promise.all(
+ Array.from(allDids).map((did) =>
+ getBlentoOrBskyProfile({ did }).catch(() => undefined)
+ )
+ );
+ return profiles.filter((p) => p && p.handle !== 'handle.invalid');
+ },
+ allowSetColor: true,
+ defaultColor: 'base',
+ minW: 2,
+ minH: 2,
+ name: 'Friends',
+ groups: ['Social'],
+ keywords: ['friends', 'avatars', 'people', 'community', 'blentos'],
+ icon: ``
+} as CardDefinition & { type: 'friends' };
diff --git a/src/lib/cards/PhotoGalleryCard/PhotoGalleryCard.svelte b/src/lib/cards/PhotoGalleryCard/PhotoGalleryCard.svelte
index b97ebcb..4619b05 100644
--- a/src/lib/cards/PhotoGalleryCard/PhotoGalleryCard.svelte
+++ b/src/lib/cards/PhotoGalleryCard/PhotoGalleryCard.svelte
@@ -49,7 +49,7 @@
});
let images = $derived(
- feed
+ (feed
?.toSorted((a: PhotoItem, b: PhotoItem) => {
return (a.value.position ?? 0) - (b.value.position ?? 0);
})
@@ -63,6 +63,13 @@
position: i.value.position ?? 0
};
})
+ .filter((i) => i.src !== undefined) || []) as {
+ src: string;
+ name: string;
+ width: number;
+ height: number;
+ position: number;
+ }[]
);
let isMobile = getIsMobile();
diff --git a/src/lib/cards/index.ts b/src/lib/cards/index.ts
index a95ce54..4efcddf 100644
--- a/src/lib/cards/index.ts
+++ b/src/lib/cards/index.ts
@@ -35,6 +35,7 @@ import { CountdownCardDefinition } from './CountdownCard';
import { SpotifyCardDefinition } from './SpotifyCard';
import { ButtonCardDefinition } from './ButtonCard';
import { GuestbookCardDefinition } from './GuestbookCard';
+import { FriendsCardDefinition } from './FriendsCard';
// import { Model3DCardDefinition } from './Model3DCard';
export const AllCardDefinitions = [
@@ -73,8 +74,9 @@ export const AllCardDefinitions = [
TimerCardDefinition,
ClockCardDefinition,
CountdownCardDefinition,
- SpotifyCardDefinition
+ SpotifyCardDefinition,
// Model3DCardDefinition
+ FriendsCardDefinition
] as const;
export const CardDefinitionsByType = AllCardDefinitions.reduce(
diff --git a/src/lib/website/EditableWebsite.svelte b/src/lib/website/EditableWebsite.svelte
index d380a6f..3dfe6f8 100644
--- a/src/lib/website/EditableWebsite.svelte
+++ b/src/lib/website/EditableWebsite.svelte
@@ -931,7 +931,6 @@
>
-
{
diff --git a/src/lib/website/layout-mirror.ts b/src/lib/website/layout-mirror.ts
index afd025b..9de9a8c 100644
--- a/src/lib/website/layout-mirror.ts
+++ b/src/lib/website/layout-mirror.ts
@@ -51,9 +51,7 @@ export function mirrorLayout(items: Item[], fromMobile: boolean): void {
if (fromMobile) {
// Mobile → Desktop: reflow items to use the full grid width.
// Sort by mobile position so items are placed in reading order.
- const sorted = items.toSorted(
- (a, b) => a.mobileY - b.mobileY || a.mobileX - b.mobileX
- );
+ const sorted = items.toSorted((a, b) => a.mobileY - b.mobileY || a.mobileX - b.mobileX);
// Place each item into the first available spot on the desktop grid
const placed: Item[] = [];
@@ -66,11 +64,7 @@ export function mirrorLayout(items: Item[], fromMobile: boolean): void {
} else {
// Desktop → Mobile: proportional positions
for (const item of items) {
- item.mobileX = clamp(
- Math.floor((item.x * 2) / 2) * 2,
- 0,
- COLUMNS - item.mobileW
- );
+ item.mobileX = clamp(Math.floor((item.x * 2) / 2) * 2, 0, COLUMNS - item.mobileW);
item.mobileY = Math.max(0, Math.round(item.y * 2));
}
fixAllCollisions(items, true);