From 8f0200170148851f46dc8c7ea757834a85fb919d Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Wed, 17 Dec 2025 17:47:45 +0000 Subject: [PATCH 1/5] refactor: multi-author support with stacked avatars and linkable names --- src/lib/components/Article.svelte | 75 +++++++++++++++----------- src/lib/components/blog/article.svelte | 57 +++++++++++++------- src/lib/utils/blog-authors.ts | 40 ++++++++++++++ src/markdoc/layouts/Author.svelte | 25 +++++---- src/markdoc/layouts/Category.svelte | 25 +++------ src/markdoc/layouts/Post.svelte | 25 +++------ src/routes/blog/[[page]]/+page.svelte | 72 +++++++++---------------- 7 files changed, 176 insertions(+), 143 deletions(-) create mode 100644 src/lib/utils/blog-authors.ts diff --git a/src/lib/components/Article.svelte b/src/lib/components/Article.svelte index 261dd661f9..7647357bcd 100644 --- a/src/lib/components/Article.svelte +++ b/src/lib/components/Article.svelte @@ -2,50 +2,63 @@ import Media from '$lib/UI/Media.svelte'; import { formatDate } from '$lib/utils/date'; + interface AuthorInfo { + name: string; + href: string; + } + interface ArticleProps { title: string; cover: string; href: string; date: Date; timeToRead: number; - author: string; - avatar: string; + authors: AuthorInfo[]; + avatars: string[]; } - const { title, cover, href, date, timeToRead, author, avatar }: ArticleProps = $props(); + const { title, cover, href, date, timeToRead, authors, avatars }: ArticleProps = $props();
  • - -
    - -
    -
    -

    + + + +
    + +

    {title}

    -
    -
    -
    - {author} -

    {author}

    -
    -
    - - {formatDate(date)} - - {timeToRead} min -
    -
    +
    +
    +
    + {#each avatars as avatar, i} + {authors[i]?.name + {/each}
    + + {#each authors as author, i} + {author.name}{#if i < authors.length - 1},{' '}{/if} + {/each} + + + {formatDate(date)} - {timeToRead} min +
    - +

  • diff --git a/src/lib/components/blog/article.svelte b/src/lib/components/blog/article.svelte index 4188e1b062..9e1dc74b91 100644 --- a/src/lib/components/blog/article.svelte +++ b/src/lib/components/blog/article.svelte @@ -2,43 +2,62 @@ import Media from '$lib/UI/Media.svelte'; import { formatDate } from '$lib/utils/date'; + interface AuthorInfo { + name: string; + href: string; + } + interface Props { title: string; cover: string; href: string; date: Date; timeToRead: number; - author: string; - avatar: string; + authors: AuthorInfo[]; + avatars: string[]; } - const { title, cover, href, date, timeToRead, author, avatar }: Props = $props(); + const { title, cover, href, date, timeToRead, authors, avatars }: Props = $props(); - -
    + +
    -

    - {title} -

    + +

    + {title} +

    +
    - {author} +
    + {#each avatars as avatar, i} + {authors[i]?.name + {/each} +
    -

    {author}

    +

    + {#each authors as author, i} + {author.name}{#if i < authors.length - 1},{' '}{/if} + {/each} +

    • {formatDate(date)} @@ -48,4 +67,4 @@
    - +
    diff --git a/src/lib/utils/blog-authors.ts b/src/lib/utils/blog-authors.ts new file mode 100644 index 0000000000..8e59cfe349 --- /dev/null +++ b/src/lib/utils/blog-authors.ts @@ -0,0 +1,40 @@ +import type { AuthorData } from '$routes/blog/content'; + +export interface AuthorInfo { + name: string; + href: string; +} + +/** + * Extracts author information from a post for use in Article components + */ +export function getPostAuthors( + postAuthor: string | string[], + allAuthors: AuthorData[] +): { + postAuthors: AuthorInfo[]; + authorAvatars: string[]; + primaryAuthor: AuthorData | undefined; +} { + const postAuthorSlugs = Array.isArray(postAuthor) ? postAuthor : [postAuthor]; + const primarySlug = postAuthorSlugs[0]; + + const primaryAuthor = + allAuthors.find((a) => a.slug === primarySlug) || + allAuthors.find((a) => postAuthorSlugs.includes(a.slug)); + + const postAuthors = postAuthorSlugs + .map((slug) => allAuthors.find((a) => a.slug === slug)) + .filter((a): a is AuthorData => !!a) + .map((a) => ({ name: a.name, href: a.href })); + + const authorAvatars = postAuthorSlugs + .map((slug) => allAuthors.find((a) => a.slug === slug)?.avatar) + .filter((a): a is string => !!a); + + return { + postAuthors, + authorAvatars, + primaryAuthor + }; +} diff --git a/src/markdoc/layouts/Author.svelte b/src/markdoc/layouts/Author.svelte index be15d095be..47cd544700 100644 --- a/src/markdoc/layouts/Author.svelte +++ b/src/markdoc/layouts/Author.svelte @@ -6,6 +6,7 @@ import { TITLE_SUFFIX } from '$routes/titles'; import type { PostsData, AuthorData } from '$routes/blog/content'; import { DEFAULT_HOST } from '$lib/utils/metadata'; + import { getPostAuthors } from '$lib/utils/blog-authors'; import FloatingHead from '$lib/components/FloatingHead.svelte'; export let name: string; @@ -170,23 +171,25 @@
    diff --git a/src/markdoc/layouts/Category.svelte b/src/markdoc/layouts/Category.svelte index 6711c02e10..d7f3a73daf 100644 --- a/src/markdoc/layouts/Category.svelte +++ b/src/markdoc/layouts/Category.svelte @@ -3,6 +3,7 @@ import { Article, FooterNav, MainFooter } from '$lib/components'; import { Main } from '$lib/layouts'; import { DEFAULT_HOST } from '$lib/utils/metadata'; + import { getPostAuthors } from '$lib/utils/blog-authors'; import type { AuthorData, PostsData } from '$routes/blog/content'; import { TITLE_SUFFIX } from '$routes/titles'; import { getContext } from 'svelte'; @@ -58,29 +59,19 @@