Skip to content

feat: add OG image for compare pages#2277

Open
Adebesin-Cell wants to merge 16 commits intonpmx-dev:mainfrom
Adebesin-Cell:feat/compare-og-image
Open

feat: add OG image for compare pages#2277
Adebesin-Cell wants to merge 16 commits intonpmx-dev:mainfrom
Adebesin-Cell:feat/compare-og-image

Conversation

@Adebesin-Cell
Copy link
Copy Markdown
Contributor

@Adebesin-Cell Adebesin-Cell commented Mar 25, 2026

Better social preview for compare pages.

2 Pkgs
image

4 Pkgs
og-compare-4pkg

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Mar 26, 2026 5:33pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Mar 26, 2026 5:33pm
npmx-lunaria Ignored Ignored Mar 26, 2026 5:33pm

Request Review

Adebesin-Cell and others added 2 commits March 25, 2026 21:09
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a server-rendered OG-image Vue SFC at app/components/OgImage/Compare.vue with props packages, emptyDescription and primaryColor. It normalises packages into up to four trimmed entries, concurrently fetches each package’s last-week downloads and registry dist-tags.latest version with a 2.5s timeout and per-request fallbacks to { downloads: 0, version: '' }, derives maxDownloads, formats download text and bar widths, and renders either an empty-state message or rows with name, downloads, optional version pill and a gradient bar with decorative background. The page registers the component via defineOgImageComponent('Compare', ...) and the test adds it to SKIPPED_COMPONENTS.

Suggested labels

front

Suggested reviewers

  • alexdln
  • serhalp
  • graphieros
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description directly relates to the changeset, providing visual context for the new OG image component being added for compare pages.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8bf4f3f9-b99e-4b4a-82b3-eb8cf42d26e8

📥 Commits

Reviewing files that changed from the base of the PR and between 5aff68f and fdd8983.

📒 Files selected for processing (2)
  • app/components/OgImage/Compare.vue
  • app/pages/compare.vue

Adebesin-Cell and others added 5 commits March 25, 2026 21:18
Fetches real weekly downloads and latest version for each package,
renders a horizontal bar chart with gradient bars sized relative to
the highest-downloaded package.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Show descriptive text when no packages are selected, matching the
Default template's badge style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass i18n-translated empty description from the page as a prop
since OG image components don't have access to $t.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
app/components/OgImage/Compare.vue (2)

41-69: Top-level await reads computed value during setup.

The code accesses displayPackages.value at the top level of <script setup>, which evaluates during component setup. While this works in Nuxt OG image server-rendering contexts where props are available immediately, this pattern can be fragile if the component is ever used in a different context.

Consider using onMounted or Nuxt's data-fetching composables if issues arise, but for a server-only OG image component, the current approach should work correctly.


138-138: Consider potential duplicate key collision.

Using :key="pkg.name" assumes package names are unique. If the same package name appears multiple times in the input (e.g., ?packages=react,react), this would cause a key collision warning and potentially incorrect rendering.

For an OG image component, this edge case is unlikely to matter, but you could use the index as a fallback:

💡 Optional fix
-        <div v-for="pkg in stats" :key="pkg.name" class="flex flex-col gap-1">
+        <div v-for="(pkg, index) in stats" :key="`${pkg.name}-${index}`" class="flex flex-col gap-1">

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 89a5a390-89bf-49d9-b45a-2cf8521a47c3

📥 Commits

Reviewing files that changed from the base of the PR and between fdd8983 and 8dad8ae.

📒 Files selected for processing (3)
  • app/components/OgImage/Compare.vue
  • app/pages/compare.vue
  • test/unit/a11y-component-coverage.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/pages/compare.vue

Copy link
Copy Markdown
Contributor

@graphieros graphieros left a comment

Choose a reason for hiding this comment

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

Looks good to me 🌿

@graphieros graphieros requested a review from alexdln March 25, 2026 21:05
@RYGRIT
Copy link
Copy Markdown
Contributor

RYGRIT commented Mar 26, 2026

Great work on the new compare OG image!

Perhaps we could give the metrics (weekly downloads) a bit more visual prominence? I personally think the download numbers are the most important info here — the progress bars already do a great job showing the relative scale, but the absolute numbers are what people scan for first.

… image

Bump download metrics from text-xl/gray to text-2xl/font-medium/lighter
color so absolute numbers are easier to scan at a glance.
Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (1)
app/components/OgImage/Compare.vue (1)

22-29: Normalise string[] input the same as comma-separated input.

Line 28 passes array input through as-is, so whitespace-only or empty entries can leak into fetch calls. Normalising both paths keeps behaviour consistent.

♻️ Suggested refactor
 const displayPackages = computed(() => {
   const raw = props.packages
-  const list =
-    typeof raw === 'string'
-      ? raw
-          .split(',')
-          .map(p => p.trim())
-          .filter(Boolean)
-      : raw
+  const list = (typeof raw === 'string' ? raw.split(',') : raw)
+    .map(p => p.trim())
+    .filter(Boolean)
   return list.slice(0, 4)
 })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f198a43d-d3b0-4229-9a86-9da70bcb40d8

📥 Commits

Reviewing files that changed from the base of the PR and between 8dad8ae and 9fcab62.

📒 Files selected for processing (1)
  • app/components/OgImage/Compare.vue

@trueberryless
Copy link
Copy Markdown
Contributor

Amazing 🤩

Is there a limit for how many packages can be compared at once or could this be abused to generate OG images that overflow the image dimensions?

@graphieros
Copy link
Copy Markdown
Contributor

graphieros commented Mar 26, 2026

Amazing 🤩

Is there a limit for how many packages can be compared at once or could this be abused to generate OG images that overflow the image dimensions?

There is currently a limit of 4 on the compare page, but we'd like to either increase the limit or remove it entirely eventually. We discussed about this on discord, and @Adebesin-Cell has a plan for various scenarios to avoid overflows with different presentations (to be implemented when it becomes necessary).

@trueberryless
Copy link
Copy Markdown
Contributor

There is currently a limit of 4 on the compare page, but we'd like to either increase the limit or remove it entirely eventually. We discussed about this on discord, and @Adebesin-Cell has a plan for various scenarios to avoid overflows with different presentations (to be implemented when it becomes necessary).

Thanks for the quick answer and ping. This looks perfect to me then 👍

Adebesin-Cell and others added 2 commits March 26, 2026 17:51
- Add 2.5s timeout to npm API requests to prevent OG image render stalls
- Increase download numbers to text-3xl font-bold white for better
  visual prominence on the compare OG card

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Adebesin-Cell
Copy link
Copy Markdown
Contributor Author

Great work on the new compare OG image!

Perhaps we could give the metrics (weekly downloads) a bit more visual prominence? I personally think the download numbers are the most important info here — the progress bars already do a great job showing the relative scale, but the absolute numbers are what people scan for first.

image Howdy?

Copy link
Copy Markdown
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: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7891906-da84-49e2-9b77-01d59b8f9535

📥 Commits

Reviewing files that changed from the base of the PR and between 9fcab62 and 28ca4d3.

📒 Files selected for processing (1)
  • app/components/OgImage/Compare.vue

Adebesin-Cell and others added 2 commits March 26, 2026 18:12
- Apply trim/filter to string[] input, not just comma-separated strings
- Return 0% width for zero-download entries instead of a misleading 5%
- Cap bar width at 100%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
app/components/OgImage/Compare.vue (2)

137-137: Consider using a composite key to handle potential duplicate package names.

If duplicate package names are passed via URL parameters (e.g., ?packages=express,express), pkg.name won't be unique, which could cause Vue rendering issues. While the upstream page may prevent this in the UI, the OG image endpoint could receive arbitrary query parameters.

🔧 Suggested fix using index-based composite key
-        <div v-for="pkg in stats" :key="pkg.name" class="flex flex-col gap-1">
+        <div v-for="(pkg, index) in stats" :key="`${pkg.name}-${index}`" class="flex flex-col gap-1">

95-98: Conflicting styles: inline backgroundColor overrides the gradient class.

The element has bg-gradient-to-tr from-[#3b82f6] in the class, but the inline :style="{ backgroundColor: primaryColor }" completely overrides the gradient, making the gradient class dead code.

If a gradient is intended, consider using CSS custom properties or removing the gradient class. If a solid colour is intended, the gradient class can be removed.

🧹 Option A: Remove unused gradient class (if solid colour is intended)
         <div
-          class="flex items-center justify-center w-14 h-14 p-3 rounded-xl shadow-lg bg-gradient-to-tr from-[`#3b82f6`]"
+          class="flex items-center justify-center w-14 h-14 p-3 rounded-xl shadow-lg"
           :style="{ backgroundColor: primaryColor }"
         >
🎨 Option B: Use gradient with dynamic colour (if gradient is intended)
         <div
-          class="flex items-center justify-center w-14 h-14 p-3 rounded-xl shadow-lg bg-gradient-to-tr from-[`#3b82f6`]"
-          :style="{ backgroundColor: primaryColor }"
+          class="flex items-center justify-center w-14 h-14 p-3 rounded-xl shadow-lg"
+          :style="{ background: `linear-gradient(to top right, ${primaryColor}, ${primaryColor}80)` }"
         >

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd50c762-941d-4b3d-9349-374cd8fd4060

📥 Commits

Reviewing files that changed from the base of the PR and between 28ca4d3 and 838cf32.

📒 Files selected for processing (1)
  • app/components/OgImage/Compare.vue

@graphieros
Copy link
Copy Markdown
Contributor

graphieros commented Mar 26, 2026

Howdy?

Do you have ellipsis on long pack names, so it fits with 999.9M/wk ?

Add max-width and ellipsis truncation to package names so they don't
overflow when paired with large download numbers and version badges.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Adebesin-Cell
Copy link
Copy Markdown
Contributor Author

Howdy?

Do you have ellipsis on long pack names, so it fits with 999.9M/wk ?

Good catch! fixed eac610a

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.

4 participants