Skip to content

Conversation

@HarshMN2345
Copy link
Member

@HarshMN2345 HarshMN2345 commented Dec 17, 2025

What does this PR do?

Before:

image image

After

image image

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Summary by CodeRabbit

  • New Features

    • Articles now support multiple authors with linked profiles and stacked avatar displays.
    • Centralized author-data handling across listings and post pages for consistent multi-author support.
  • UI Updates

    • Redesigned article card: cover image, title, and metadata are separately clickable for clearer navigation.
    • Author area now shows an avatar row plus comma-separated linked author names and date/read-time.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

This PR refactors blog author handling to support multiple authors. It adds src/lib/utils/blog-authors.ts with AuthorInfo and getPostAuthors(postAuthor, allAuthors) to normalize slugs and return postAuthors, authorAvatars, and primaryAuthor. Article components (two Article.svelte variants) now accept authors: AuthorInfo[] and avatars: string[] instead of single author/avatar, and render stacked avatars and linked author names. Markdoc layouts and the blog page (Author.svelte, Category.svelte, Post.svelte, src/routes/blog/[[page]]/+page.svelte) were updated to call getPostAuthors and pass the new props.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Review getPostAuthors for correct slug normalization, lookup, and primaryAuthor selection.
  • Confirm all Article usages (both Article.svelte files and markdoc/layouts/pages) consistently pass authors[] and avatars[] with correct shapes.
  • Verify avatar stack rendering (ordering, z-index, alt text) in both Article components.
  • Check fallbacks where primaryAuthor or avatars may be undefined.
  • Validate link structure and accessibility after separating cover/title anchors.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objective: refactoring to add multi-author support with visual improvements (stacked avatars and linkable names). This directly aligns with the core changes across multiple components and the new utility function.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-blog-multi-author-support

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f02001 and 6bff54d.

📒 Files selected for processing (1)
  • src/lib/utils/blog-authors.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/utils/blog-authors.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: assets
  • GitHub Check: tests
  • GitHub Check: build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
src/lib/utils/blog-authors.ts (2)

3-6: Consider exporting AuthorInfo for reuse across components.

The AuthorInfo interface is duplicated in both src/lib/components/Article.svelte and src/lib/components/blog/article.svelte. Exporting it from this utility module would establish a single source of truth.

-export interface AuthorInfo {
+export interface AuthorInfo {
     name: string;
     href: string;
 }

Then in the Article components:

import { type AuthorInfo } from '$lib/utils/blog-authors';

22-33: Optional: Pre-index authors by slug for O(1) lookup.

The current implementation performs linear searches through allAuthors for each slug (lines 23-24, 27, 32). For typical blog sizes this is fine, but a Map would be more efficient if author counts grow.

+    const authorsBySlug = new Map(allAuthors.map((a) => [a.slug, a]));
+
     const primaryAuthor =
-        allAuthors.find((a) => a.slug === primarySlug) ||
-        allAuthors.find((a) => postAuthorSlugs.includes(a.slug));
+        authorsBySlug.get(primarySlug) ||
+        postAuthorSlugs.map((s) => authorsBySlug.get(s)).find(Boolean);

     const postAuthors = postAuthorSlugs
-        .map((slug) => allAuthors.find((a) => a.slug === slug))
+        .map((slug) => authorsBySlug.get(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)
+        .map((slug) => authorsBySlug.get(slug)?.avatar)
         .filter((a): a is string => !!a);
src/lib/components/blog/article.svelte (1)

42-42: Hardcoded ring color may not adapt to theme changes.

The ring color #19191c appears to be a dark background. If the site supports light mode or theme customization, this could clash.

Consider using a CSS variable or Tailwind theme class instead:

class="size-6 rounded-full ring-2 ring-background"
src/lib/components/Article.svelte (2)

5-8: Duplicate AuthorInfo interface across components.

This interface is defined identically in src/lib/components/blog/article.svelte and src/lib/utils/blog-authors.ts. Importing from the utility would ensure consistency.

+import { type AuthorInfo } from '$lib/utils/blog-authors';
+
-    interface AuthorInfo {
-        name: string;
-        href: string;
-    }

43-43: Hardcoded ring color duplicated here as well.

Same concern as blog/article.svelte—the hardcoded #19191c may not adapt to theme changes.

src/routes/blog/[[page]]/+page.svelte (1)

272-278: Consider UX consistency between avatar count and author names.

The code displays up to 3 stacked avatars (line 255) but renders all author names with links. If a post has more than 3 authors, users will see only 3 avatars but all names listed. This may be intentional design, but please verify this is the desired UX.

If limiting both is preferred, apply this diff:

 <span class="text-sub-body">
-    {#each featuredAuthors as featuredAuthor, i}
+    {#each featuredAuthors.slice(0, 3) as featuredAuthor, i}
         <a href={featuredAuthor.href} class="web-link"
             >{featuredAuthor.name}</a
-        >{#if i < featuredAuthors.length - 1},{' '}{/if}
+        >{#if i < Math.min(3, featuredAuthors.length) - 1},{' '}{/if}
     {/each}
+    {#if featuredAuthors.length > 3}
+        {' '}and {featuredAuthors.length - 3} more
+    {/if}
 </span>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22707ba and 8f02001.

📒 Files selected for processing (7)
  • src/lib/components/Article.svelte (1 hunks)
  • src/lib/components/blog/article.svelte (2 hunks)
  • src/lib/utils/blog-authors.ts (1 hunks)
  • src/markdoc/layouts/Author.svelte (2 hunks)
  • src/markdoc/layouts/Category.svelte (2 hunks)
  • src/markdoc/layouts/Post.svelte (2 hunks)
  • src/routes/blog/[[page]]/+page.svelte (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/utils/blog-authors.ts (1)
src/routes/blog/content.ts (1)
  • AuthorData (8-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: tests
  • GitHub Check: assets
  • GitHub Check: build
🔇 Additional comments (9)
src/markdoc/layouts/Post.svelte (1)

195-209: Clean integration of centralized author resolution.

The use of getPostAuthors centralizes author data extraction, and the conditional render on primaryAuthor properly guards against posts with unresolved authors. This approach is consistent with the other layout files.

src/markdoc/layouts/Author.svelte (1)

174-193: Fallback logic differs from other layouts.

Unlike Post.svelte and Category.svelte which conditionally render Article only when primaryAuthor exists, this file always renders but uses fallback values. This is likely intentional since we're on an author's page and can use the page-level avatar/name props as defaults. The approach ensures posts are always displayed even if getPostAuthors returns incomplete data.

Please verify this is the intended behavior—that posts on an author's page should always render using the page author's info as fallback, rather than being hidden when author resolution fails.

src/markdoc/layouts/Category.svelte (1)

62-76: Consistent multi-author integration.

The implementation mirrors Post.svelte with proper destructuring and conditional rendering based on primaryAuthor. This ensures consistency across category listing pages.

src/lib/components/blog/article.svelte (1)

40-52: Potential index mismatch between avatars and authors arrays.

The alt text uses authors[i]?.name to match each avatar. If these arrays have different lengths (e.g., an author exists but has no avatar), the indexing may produce incorrect alt text. The optional chaining (?.) prevents crashes, but semantics could be off.

Verify that getPostAuthors always returns aligned arrays, or consider iterating over a combined structure:

// In getPostAuthors, return combined data:
const authorsWithAvatars = postAuthorSlugs
    .map(slug => allAuthors.find(a => a.slug === slug))
    .filter((a): a is AuthorData => !!a)
    .map(a => ({ name: a.name, href: a.href, avatar: a.avatar }));
src/lib/components/Article.svelte (1)

41-51: Same array alignment concern as blog/article.svelte.

This component uses the same pattern of indexing authors[i] while iterating over avatars. The same potential mismatch risk applies here.

src/routes/blog/[[page]]/+page.svelte (4)

9-9: LGTM!

The import is correctly added to support multi-author functionality.


403-404: Verify Article component API changes across the codebase.

The props passed to Article have changed from single values to arrays. Ensure the Article component has been updated to accept avatars: string[] and authors: AuthorInfo[], and verify all other call sites are updated.

Run the following script to verify the Article component implementation and find all usages:

#!/bin/bash
# Description: Verify Article component accepts new array props and find all usages

echo "=== Article component implementation ==="
# Find Article component definition and show prop types
fd -e svelte 'Article.svelte' src/lib/components --exec cat {} \; | head -50

echo -e "\n=== All Article component usages ==="
# Find all files using Article component
rg -n --type=svelte '<Article' -A 5 -B 1

216-220: Verify edge case handling for missing or undefined authors.

The destructuring assumes getPostAuthors will always return valid data structures. If featured.author is undefined or the function returns empty arrays, the rendering logic should handle it gracefully.

Run the following script to verify the getPostAuthors implementation handles edge cases:

#!/bin/bash
# Description: Check getPostAuthors implementation for null/undefined handling and return value structure

# Find and display the getPostAuthors function implementation
ast-grep --pattern $'export function getPostAuthors($$$) {
  $$$
}'

Additionally, please search the web to confirm best practices for handling undefined author data in Svelte components:

How to handle undefined props in Svelte components with optional chaining and default values

Also applies to: 255-269


392-396: LGTM! Code safely handles author data extraction.

The logic correctly extracts author data for each post via getPostAuthors(), which gracefully handles edge cases by filtering out undefined authors and returning a typed primaryAuthor. The conditional {#if primaryAuthor && !post.draft} ensures only posts with valid authors are rendered. The author field is required in the PostsData interface, and all posts include this field.

Comment on lines 22 to 33
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can do some nice optimizations here, multiple iterations are not needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants