Adding Updates Section, fixing documentation error.#705
Adding Updates Section, fixing documentation error.#705
Conversation
Updating this branch to up to date
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds an Updates section (metadata, release pages, list and dynamic post pages, layout, data source), extends docs with a reorganized Goals Tab article, adds newsletter UI and subscribe API, updates header nav with an Updates link, and adjusts blog/meta and package deps. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UpdatesList as UpdatesListPage (/updates)
participant UpdatePage as UpdatePostPage (/updates/{slug})
participant UpdatesSource
participant MDXRenderer
User->>UpdatesList: GET /updates
UpdatesList->>UpdatesSource: getPages()
UpdatesSource-->>UpdatesList: pages list
UpdatesList->>UpdatesList: sort by date
UpdatesList-->>User: render cards grid
User->>UpdatePage: GET /updates/{slug}
UpdatePage->>UpdatesSource: getPage(slug)
UpdatesSource-->>UpdatePage: page data + MDX
UpdatePage->>UpdatePage: generateMetadata() & structuredData
UpdatePage->>MDXRenderer: render MDX with custom components
MDXRenderer-->>UpdatePage: HTML content
UpdatePage-->>User: full update post
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
docs/content/docs/web-analytics/goals-tab.mdx (1)
8-11: Rephrase to avoid successive sentences beginning with "Did".The static analysis tool flagged a style concern: three successive sentences begin with "Did". Consider rephrasing one or more to improve readability.
 -Goals let you track specific outcomes you care about. Did users sign up? Did they complete checkout? Did they view key pages? The Goals tab helps you measure success. +Goals let you track specific outcomes you care about—such as user signups, checkout completions, and key page views. The Goals tab helps you measure success.docs/content/updates/v1.4.0.mdx (1)
9-9: Refine the note’s wording for a more neutral, clear toneThe note is understandable but a bit informal and first‑person. A small rephrase improves clarity:
-**Note**: You may have to run `git stash` before running the `./update` script since I made some changes to it. +**Note**: You may need to run `git stash` before running the `./update` script because of recent changes to it.This keeps the meaning while using a more neutral voice.
docs/source.config.ts (1)
31-42: Updates collection schema looks good; consider DRYing shared frontmatterThe
updatescollection definition mirrors theblogcollection schema and looks correct for your frontmatter needs.Since the
date/author/image/tagsextension is duplicated, consider extracting it into a shared constant (e.g.,const datedFrontmatterSchema = frontmatterSchema.extend({ … })) and reusing it for bothblogandupdatesto keep schemas in sync more easily.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
docs/package-lock.jsonis excluded by!**/package-lock.jsondocs/public/blog/rybbit_goals_dashboard.pngis excluded by!**/*.png
📒 Files selected for processing (19)
docs/content/docs/web-analytics/goals-tab.mdx(1 hunks)docs/content/updates/meta.js(1 hunks)docs/content/updates/v.1.4.1.mdx(1 hunks)docs/content/updates/v1.0.0.mdx(1 hunks)docs/content/updates/v1.1.0.mdx(1 hunks)docs/content/updates/v1.2.0.mdx(1 hunks)docs/content/updates/v1.3.0.mdx(1 hunks)docs/content/updates/v1.4.0.mdx(1 hunks)docs/content/updates/v1.5.0.mdx(1 hunks)docs/content/updates/v1.5.1.mdx(1 hunks)docs/content/updates/v1.6.0.mdx(1 hunks)docs/content/updates/v1.6.1.mdx(1 hunks)docs/content/updates/v2.0.0.mdx(1 hunks)docs/source.config.ts(1 hunks)docs/src/app/updates/[...slug]/page.tsx(1 hunks)docs/src/app/updates/layout.tsx(1 hunks)docs/src/app/updates/page.tsx(1 hunks)docs/src/components/CustomHeader.tsx(1 hunks)docs/src/lib/updates-source.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
docs/src/app/updates/page.tsx (1)
docs/src/lib/updates-source.ts (1)
updatesSource(4-7)
docs/src/app/updates/[...slug]/page.tsx (1)
docs/src/lib/updates-source.ts (1)
updatesSource(4-7)
docs/src/lib/updates-source.ts (1)
docs/source.config.ts (1)
updates(32-42)
docs/src/app/updates/layout.tsx (1)
docs/src/components/CustomHeader.tsx (1)
CustomHeader(10-174)
🪛 ast-grep (0.39.9)
docs/src/app/updates/[...slug]/page.tsx
[warning] 102-102: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
docs/src/app/updates/[...slug]/page.tsx
[error] 103-103: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🪛 LanguageTool
docs/content/updates/v1.4.0.mdx
[style] ~9-~9: Consider rephrasing this to strengthen your wording.
Context: ...e running the ./update script since I made some changes to it. ## Key Features ### Session Re...
(MAKE_CHANGES)
docs/content/docs/web-analytics/goals-tab.mdx
[style] ~10-~10: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...rs sign up? Did they complete checkout? Did they view key pages? The Goals tab help...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
docs/content/updates/v2.0.0.mdx
[grammar] ~13-~13: Use a hyphen to join words.
Context: ...h the Globe page. There isn't a straight up 1-to-1 replacement of the Realtime pa...
(QB_NEW_EN_HYPHEN)
[style] ~15-~15: To strengthen your wording, consider replacing the phrasal verb “leave out”.
Context: ...dded a 3D mode using Mapbox, but I have left this feature out of the open source release because Mapbox ...
(OMIT_EXCLUDE)
[grammar] ~15-~15: Use a hyphen to join words.
Context: ...I have left this feature out of the open source release because Mapbox requires a...
(QB_NEW_EN_HYPHEN)
⏰ 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). (2)
- GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
- GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
🔇 Additional comments (13)
docs/content/docs/web-analytics/goals-tab.mdx (2)
28-40: LGTM!The Page Path Goals section is well-structured with clear examples and practical wildcard patterns that will help users configure paths correctly.
54-78: LGTM!The Viewing Goal Performance and Conversion Funnel Analysis sections provide clear value. The explanations of conversion paths, drop-offs, and time-to-conversion are practical and actionable.
docs/content/updates/v1.3.0.mdx (1)
1-15: LGTM!Release notes are well-formatted, concise, and include a link to the full changelog for users wanting detailed information.
docs/content/updates/v1.5.1.mdx (1)
1-9: LGTM!Release notes format is consistent. The minimal content is appropriate for a minor bug-fix release.
docs/content/updates/v1.6.1.mdx (1)
1-9: LGTM!Release notes follow the established format and date progression.
docs/src/components/CustomHeader.tsx (1)
1-174: LGTM!No functional changes; trailing newline formatting only.
docs/content/updates/v1.1.0.mdx (1)
1-32: LGTM!Comprehensive release notes with clear feature descriptions, proper contributor attribution, and a full changelog link. The structure sets a good example for detailed release documentation.
docs/content/updates/v1.6.0.mdx (1)
1-46: LGTM!Detailed release notes with comprehensive commit history, proper contributor attribution, and new contributor recognition. The format is consistent and thorough.
docs/src/lib/updates-source.ts (1)
1-7: Updates source wiring looks correctLoader configuration and the
toFumadocsSource?.() ?? updatesfallback pattern look appropriate for fumadocs and align with the definedupdatescollection.docs/src/app/updates/layout.tsx (1)
1-15: Updates layout composition looks consistent and type-safeThe layout cleanly composes
CustomHeader, a<main>wrapper forchildren, andFooter, with correctReactNodetyping. Assuming the outer app layout provides a flex container, theflex-1 min-h-screenmain styling should work as intended.docs/content/updates/v1.2.0.mdx (1)
1-30: LGTM!The release notes are well-structured with proper frontmatter, detailed changelog entries with contributor attribution, and links to relevant PRs. The date format follows the ISO standard.
docs/content/updates/v2.0.0.mdx (1)
1-58: LGTM!The v2.0.0 release notes provide comprehensive coverage of major changes including Map/Globe rework, Journeys redesign, and script configuration updates. The breaking change is clearly documented with migration guidance.
docs/src/app/updates/[...slug]/page.tsx (1)
100-104: ThedangerouslySetInnerHTMLusage is safe here.Static analysis flagged this line, but the structured data is constructed from known page data (title, description, author, tags) sourced from MDX frontmatter, not user input. This is a standard pattern for injecting JSON-LD structured data for SEO.
| "date": "2025-10-24" | ||
| }, | ||
| "v1.6.1": { | ||
| "title": "v1.6.1", | ||
| "description": "Minor bug fixes and improvements", | ||
| "date": "2025-10" | ||
| }, | ||
| "v1.6.0": { | ||
| "title": "v1.6.0", | ||
| "description": "Added tracking outbound links, IP exclusion, custom error tracking, and UI improvements", | ||
| "date": "2025-09" | ||
| }, | ||
| "v1.5.1": { | ||
| "title": "v1.5.1", | ||
| "description": "Minor bug fixes and improvements", | ||
| "date": "2025-08" | ||
| }, | ||
| "v1.5.0": { | ||
| "title": "v1.5.0", | ||
| "description": "Added organization members support, multiple organizations, session replay improvements, and bug fixes", | ||
| "date": "2025-07" | ||
| }, | ||
| "v1.4.1": { | ||
| "title": "v1.4.1", | ||
| "description": "Minor bug fixes and improvements", | ||
| "date": "2025-07" | ||
| }, | ||
| "v1.4.0": { | ||
| "title": "v1.4.0", | ||
| "description": "Major update introducing Session Replay and Error Tracking features", | ||
| "date": "2025-06-29" | ||
| }, | ||
| "v1.3.0": { | ||
| "title": "v1.3.0", | ||
| "description": "Added API keys for tracking events and pageviews, with localhost tracking support", | ||
| "date": "2025-06" | ||
| }, | ||
| "v1.2.0": { | ||
| "title": "v1.2.0", | ||
| "description": "Added more integrations, API improvements, and blog support", | ||
| "date": "2025-05" | ||
| }, | ||
| "v1.1.0": { | ||
| "title": "v1.1.0", | ||
| "description": "Added data attribute event tracking, opt-out extension support, and subdomain tracking", | ||
| "date": "2025-04" | ||
| }, | ||
| "v1.0.0": { | ||
| "title": "v1.0.0", | ||
| "description": "First major release with Browser/OS version filtering and breaking changes for reverse proxy configs", | ||
| "date": "2025-03" | ||
| } |
There was a problem hiding this comment.
Standardize date formats across all version entries.
The date formats are inconsistent—some entries use full ISO dates (e.g., "2025-10-24", "2025-06-29") while others use partial year-month formats (e.g., "2025-10", "2025-09"). Additionally, v1.2.0 has "2025-05" here but "2025-05-01" in the corresponding MDX file.
Standardize all dates to full ISO format (YYYY-MM-DD) for consistency and reliable date parsing.
Apply this diff to standardize the date formats:
"v1.6.1": {
"title": "v1.6.1",
"description": "Minor bug fixes and improvements",
- "date": "2025-10"
+ "date": "2025-10-01"
},
"v1.6.0": {
"title": "v1.6.0",
"description": "Added tracking outbound links, IP exclusion, custom error tracking, and UI improvements",
- "date": "2025-09"
+ "date": "2025-09-01"
},
"v1.5.1": {
"title": "v1.5.1",
"description": "Minor bug fixes and improvements",
- "date": "2025-08"
+ "date": "2025-08-01"
},
"v1.5.0": {
"title": "v1.5.0",
"description": "Added organization members support, multiple organizations, session replay improvements, and bug fixes",
- "date": "2025-07"
+ "date": "2025-07-01"
},
"v1.4.1": {
"title": "v1.4.1",
"description": "Minor bug fixes and improvements",
- "date": "2025-07"
+ "date": "2025-07-01"
},
"v1.3.0": {
"title": "v1.3.0",
"description": "Added API keys for tracking events and pageviews, with localhost tracking support",
- "date": "2025-06"
+ "date": "2025-06-01"
},
"v1.2.0": {
"title": "v1.2.0",
"description": "Added more integrations, API improvements, and blog support",
- "date": "2025-05"
+ "date": "2025-05-01"
},
"v1.1.0": {
"title": "v1.1.0",
"description": "Added data attribute event tracking, opt-out extension support, and subdomain tracking",
- "date": "2025-04"
+ "date": "2025-04-01"
},
"v1.0.0": {
"title": "v1.0.0",
"description": "First major release with Browser/OS version filtering and breaking changes for reverse proxy configs",
- "date": "2025-03"
+ "date": "2025-03-01"
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "date": "2025-10-24" | |
| }, | |
| "v1.6.1": { | |
| "title": "v1.6.1", | |
| "description": "Minor bug fixes and improvements", | |
| "date": "2025-10" | |
| }, | |
| "v1.6.0": { | |
| "title": "v1.6.0", | |
| "description": "Added tracking outbound links, IP exclusion, custom error tracking, and UI improvements", | |
| "date": "2025-09" | |
| }, | |
| "v1.5.1": { | |
| "title": "v1.5.1", | |
| "description": "Minor bug fixes and improvements", | |
| "date": "2025-08" | |
| }, | |
| "v1.5.0": { | |
| "title": "v1.5.0", | |
| "description": "Added organization members support, multiple organizations, session replay improvements, and bug fixes", | |
| "date": "2025-07" | |
| }, | |
| "v1.4.1": { | |
| "title": "v1.4.1", | |
| "description": "Minor bug fixes and improvements", | |
| "date": "2025-07" | |
| }, | |
| "v1.4.0": { | |
| "title": "v1.4.0", | |
| "description": "Major update introducing Session Replay and Error Tracking features", | |
| "date": "2025-06-29" | |
| }, | |
| "v1.3.0": { | |
| "title": "v1.3.0", | |
| "description": "Added API keys for tracking events and pageviews, with localhost tracking support", | |
| "date": "2025-06" | |
| }, | |
| "v1.2.0": { | |
| "title": "v1.2.0", | |
| "description": "Added more integrations, API improvements, and blog support", | |
| "date": "2025-05" | |
| }, | |
| "v1.1.0": { | |
| "title": "v1.1.0", | |
| "description": "Added data attribute event tracking, opt-out extension support, and subdomain tracking", | |
| "date": "2025-04" | |
| }, | |
| "v1.0.0": { | |
| "title": "v1.0.0", | |
| "description": "First major release with Browser/OS version filtering and breaking changes for reverse proxy configs", | |
| "date": "2025-03" | |
| } | |
| "date": "2025-10-24" | |
| }, | |
| "v1.6.1": { | |
| "title": "v1.6.1", | |
| "description": "Minor bug fixes and improvements", | |
| "date": "2025-10-01" | |
| }, | |
| "v1.6.0": { | |
| "title": "v1.6.0", | |
| "description": "Added tracking outbound links, IP exclusion, custom error tracking, and UI improvements", | |
| "date": "2025-09-01" | |
| }, | |
| "v1.5.1": { | |
| "title": "v1.5.1", | |
| "description": "Minor bug fixes and improvements", | |
| "date": "2025-08-01" | |
| }, | |
| "v1.5.0": { | |
| "title": "v1.5.0", | |
| "description": "Added organization members support, multiple organizations, session replay improvements, and bug fixes", | |
| "date": "2025-07-01" | |
| }, | |
| "v1.4.1": { | |
| "title": "v1.4.1", | |
| "description": "Minor bug fixes and improvements", | |
| "date": "2025-07-01" | |
| }, | |
| "v1.4.0": { | |
| "title": "v1.4.0", | |
| "description": "Major update introducing Session Replay and Error Tracking features", | |
| "date": "2025-06-29" | |
| }, | |
| "v1.3.0": { | |
| "title": "v1.3.0", | |
| "description": "Added API keys for tracking events and pageviews, with localhost tracking support", | |
| "date": "2025-06-01" | |
| }, | |
| "v1.2.0": { | |
| "title": "v1.2.0", | |
| "description": "Added more integrations, API improvements, and blog support", | |
| "date": "2025-05-01" | |
| }, | |
| "v1.1.0": { | |
| "title": "v1.1.0", | |
| "description": "Added data attribute event tracking, opt-out extension support, and subdomain tracking", | |
| "date": "2025-04-01" | |
| }, | |
| "v1.0.0": { | |
| "title": "v1.0.0", | |
| "description": "First major release with Browser/OS version filtering and breaking changes for reverse proxy configs", | |
| "date": "2025-03-01" | |
| } |
🤖 Prompt for AI Agents
In docs/content/updates/meta.js around lines 8 to 59, several version entries
use partial year-month dates; update every "date" value to full ISO YYYY-MM-DD
format for consistency (keep existing full dates as-is), setting the day to
"-01" for entries that only specify year-month (e.g., change "2025-10" →
"2025-10-01", "2025-09" → "2025-09-01", etc.), and ensure v1.2.0 matches its MDX
file by using "2025-05-01".
| --- | ||
| title: "v1.4.1" | ||
| description: "Minor bug fixes and improvements" | ||
| date: "2025-07-01" | ||
| --- | ||
|
|
||
| # v1.4.1 | ||
|
|
||
| Minor release with bug fixes and improvements. No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
Rename file to follow naming convention.
The filename v.1.4.1.mdx deviates from the established pattern used by all other release notes (e.g., v1.3.0.mdx, v1.5.1.mdx, v1.6.1.mdx). This inconsistency may cause routing or discovery issues in the docs system.
Rename the file to v1.4.1.mdx to match the convention.
I'll verify the naming convention claim by checking the actual release note files in the repository.
---
🏁 Script executed:
```shell
#!/bin/bash
# List all markdown/mdx files in the docs/content/updates directory
fd -e mdx -e md . docs/content/updates 2>/dev/null | sort
Length of output: 352
Rename file to follow naming convention.
The filename v.1.4.1.mdx is indeed the only release note that deviates from the established pattern. All 10 other release notes (v1.0.0.mdx through v2.0.0.mdx) follow the vX.Y.Z.mdx format without a dot after "v".
Rename docs/content/updates/v.1.4.1.mdx to docs/content/updates/v1.4.1.mdx to match the convention.
🤖 Prompt for AI Agents
In docs/content/updates/v.1.4.1.mdx around lines 1 to 9, the filename includes
an extra dot after the "v" and breaks the established release-note naming
convention; rename the file from v.1.4.1.mdx to v1.4.1.mdx and update any
references (imports, links, navigation, or frontmatter indexes) in the repo to
use the new path docs/content/updates/v1.4.1.mdx so tooling and listings
continue to work.
| There are breaking changes if you used a custom reverse proxy to host Rybbit - We have removed the `/api` rewrite and you will need to modify your reverse proxy config | ||
|
|
||
| ## Key Features | ||
|
|
||
| ### Browser Versions | ||
|
|
||
| Filter analytics data by specific browser versions to understand user distribution across different browser releases. | ||
|
|
||
| ### Operation System Versions | ||
|
|
||
| Filter analytics data by operating system versions to gain insights into your user's platform preferences. |
There was a problem hiding this comment.
Fix typos and lightly polish wording in this update
A few small language issues are worth correcting for clarity and polish:
- Line 19:
Operation System Versions→Operating System Versions. - Line 21:
your user's platform preferences→your users' platform preferences. - Line 11: Consider splitting into two sentences and avoiding the dash + capital “We”.
Example patch:
-There are breaking changes if you used a custom reverse proxy to host Rybbit - We have removed the `/api` rewrite and you will need to modify your reverse proxy config
+There are breaking changes if you use a custom reverse proxy to host Rybbit. We have removed the `/api` rewrite, so you will need to update your reverse proxy configuration.-### Operation System Versions
+### Operating System Versions-Filter analytics data by operating system versions to gain insights into your user's platform preferences.
+Filter analytics data by operating system versions to gain insights into your users' platform preferences.Also consider expanding the truncated word in the line ending with subscri… to the full term for readability.
Also applies to: 19-21, 32-32
🤖 Prompt for AI Agents
In docs/content/updates/v1.0.0.mdx around lines 11 to 21 (also apply same fixes
to lines 19-21 and 32), split the long sentence that starts at line 11 into two
sentences and remove the dash + capital “We” (e.g., end the first sentence
before the clause about modifying reverse proxy config and start a new sentence
with a lowercase “we” or rephrase), correct the typo “Operation System Versions”
to “Operating System Versions” on line 19, change “your user's platform
preferences” to the plural possessive “your users' platform preferences” on line
21, and expand the truncated word ending with “subscri…” to the full term (e.g.,
“subscription”) wherever it appears.
|
|
||
| ## Commit Log | ||
|
|
||
| - Fix `update.sh` "Please commit your changes or stash them before you merge. by [@Mr-Technician](https://github.com/Mr-Technician) in [#438](https://github.com/rybbit-io/rybbit/pull/438) |
There was a problem hiding this comment.
Close the quoted message in the first commit log entry
The first commit log line has an opening quote with no closing quote:
- Fix `update.sh` "Please commit your changes or stash them before you merge. ...Consider:
-- Fix `update.sh` "Please commit your changes or stash them before you merge. by [@Mr-Technician](https://github.com/Mr-Technician) in [#438](https://github.com/rybbit-io/rybbit/pull/438)
+- Fix `update.sh` "Please commit your changes or stash them before you merge." by [@Mr-Technician](https://github.com/Mr-Technician) in [#438](https://github.com/rybbit-io/rybbit/pull/438)🤖 Prompt for AI Agents
In docs/content/updates/v1.5.0.mdx around line 18, the first commit log entry
contains an unmatched double-quote after `update.sh` which leaves the quoted
message open; fix it by adding the missing closing quote so the commit line
reads with balanced quotation marks and correct punctuation (ensure the
backticked filename and the quoted message are both properly closed and the
attribution remains unchanged).
| publisher: { | ||
| "@type": "Organization", | ||
| name: "Rybbit", | ||
| logo: { | ||
| "@type": "ImageObject", | ||
| url: "https://rybbit.com/public/rybbit.svg", | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Fix logo URL domain inconsistency.
The structured data uses https://rybbit.com/public/rybbit.svg while other URLs in this file (line 22) use rybbit.io. Ensure the logo URL uses the correct primary domain.
Apply this diff to fix the domain:
publisher: {
"@type": "Organization",
name: "Rybbit",
logo: {
"@type": "ImageObject",
- url: "https://rybbit.com/public/rybbit.svg",
+ url: "https://rybbit.io/rybbit.svg",
},
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| publisher: { | |
| "@type": "Organization", | |
| name: "Rybbit", | |
| logo: { | |
| "@type": "ImageObject", | |
| url: "https://rybbit.com/public/rybbit.svg", | |
| }, | |
| }, | |
| publisher: { | |
| "@type": "Organization", | |
| name: "Rybbit", | |
| logo: { | |
| "@type": "ImageObject", | |
| url: "https://rybbit.io/rybbit.svg", | |
| }, | |
| }, |
🤖 Prompt for AI Agents
In docs/src/app/updates/[...slug]/page.tsx around lines 87 to 94, the
publisher.logo.url uses the wrong domain (rybbit.com) while other links use
rybbit.io; update the logo URL to the primary domain by changing it to
https://rybbit.io/public/rybbit.svg so it matches the rest of the file.
| {post.data.image && ( | ||
| <img | ||
| src={post.data.image} | ||
| alt={post.data.title} | ||
| className="w-full h-48 object-cover rounded-lg mb-4" | ||
| /> | ||
| )} |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use Next.js Image component for optimization.
The native img tag bypasses Next.js automatic image optimization. Use the Image component from next/image (already imported on line 4 of the slug page) for better performance, automatic format conversion, and responsive sizing.
Apply this diff:
+import Image from "next/image";
// ... inside the map function
{post.data.image && (
- <img
+ <Image
src={post.data.image}
alt={post.data.title}
+ width={800}
+ height={400}
className="w-full h-48 object-cover rounded-lg mb-4"
/>
)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {post.data.image && ( | |
| <img | |
| src={post.data.image} | |
| alt={post.data.title} | |
| className="w-full h-48 object-cover rounded-lg mb-4" | |
| /> | |
| )} | |
| import Image from "next/image"; | |
| // ... other code ... | |
| {post.data.image && ( | |
| <Image | |
| src={post.data.image} | |
| alt={post.data.title} | |
| width={800} | |
| height={400} | |
| className="w-full h-48 object-cover rounded-lg mb-4" | |
| /> | |
| )} |
🤖 Prompt for AI Agents
In docs/src/app/updates/page.tsx around lines 34-40, the native <img> is used
which bypasses Next.js image optimization; replace it with the next/image Image
component: add or ensure "import Image from 'next/image'" at the top of the
file, swap the <img> tag for <Image src={post.data.image} alt={post.data.title}
/> and provide sizing (either width/height props or use fill with a positioned
container) and keep the existing Tailwind classes (object-cover, rounded-lg,
mb-4) applied appropriately so automatic optimization and responsive behavior
are enabled.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
docs/src/app/updates/page.tsx (1)
34-40: Use Next.js Image component for optimization.This issue has already been flagged in a previous review. The native
<img>tag bypasses Next.js automatic image optimization. Replace it with theImagecomponent fromnext/imagefor better performance, automatic format conversion, and responsive sizing.
🧹 Nitpick comments (1)
docs/src/app/updates/page.tsx (1)
11-11: Consider simplifying the array handling.The spread operator creates a copy before sorting, which is fine, but you could simplify by directly assigning the result of
updatesSource.getPages().sort(...)tosortedPostswithout the intermediatepostsvariable.Apply this diff to streamline the logic:
- const posts = [...updatesSource.getPages()]; - - // Sort posts by date (newest first) - const sortedPosts = posts.sort((a, b) => { + // Sort posts by date (newest first) + const sortedPosts = updatesSource.getPages().sort((a, b) => { const dateA = new Date(a.data.date || 0); const dateB = new Date(b.data.date || 0); return dateB.getTime() - dateA.getTime(); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
docs/src/app/updates/page.tsx(1 hunks)docs/src/components/CustomHeader.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/src/components/CustomHeader.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
docs/src/app/updates/page.tsx (1)
docs/src/lib/updates-source.ts (1)
updatesSource(4-7)
⏰ 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). (2)
- GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
- GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
🔇 Additional comments (4)
docs/src/app/updates/page.tsx (4)
1-8: LGTM! Clean imports and metadata setup.The imports are appropriate for a Next.js page component, and the metadata export follows Next.js conventions correctly.
29-29: Good defensive date handling and semantic markup.The date handling properly checks for null and uses the semantic
<time>element with thedateTimeattribute, which is excellent for accessibility and SEO. The date formatting is also clear and localized.Also applies to: 50-58
20-85: Well-structured component with good UX considerations.The component has a clean, responsive layout with proper dark mode support. The conditional rendering of optional fields (description, author, tags) is handled correctly, and the empty state provides helpful feedback to users. The use of semantic HTML elements (article, time) enhances accessibility.
14-18: No action required. The sorting behavior is correct and intentional.The code includes an explicit inline comment ("Sort posts by date (newest first)") that documents the design choice. The fallback to
0intentionally places undated posts at the end of the list (as the epoch date is the "oldest" possible). The UI then properly handles undated posts by checkingpost.data.dateseparately and only displaying a date label if one exists.This pattern is consistently applied across both
docs/src/app/updates/page.tsxanddocs/src/app/blog/page.tsx, confirming it is a deliberate design decision rather than an oversight. The implementation gracefully handles both dated and undated posts.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
docs/src/app/layout.tsx (1)
2-2: Newsletter popup integration is fine; consider small UX/config guards.Mounting
<NewsletterPopup />insideRootProvideris a straightforward way to expose the newsletter UI globally and is compatible with it being a client component. Two optional tweaks you might consider:
- Only render the popup when the subscription backend is configured (e.g., when
RESEND_SEGMENT_ID/ API key are set) so users never hit a guaranteed 500 from/api/subscribe.- If the popup feels noisy in practice, you could later gate it by route or experiment flag.
Also applies to: 117-117
docs/src/components/NewsletterPopup.tsx (1)
28-37: Optional: persist “Maybe later” choice to avoid re‑showing on every page load.Right now you only set the
rybbit_subscribedcookie on successful/409 subscription. If a user clicks “Maybe later” or closes the popup, they’ll see it again on the next page load sinceuseEffectonly checks for that cookie.If “Maybe later” is intended to suppress the popup for some time (e.g., current session or a few days), consider setting a separate “dismissed” cookie or reusing
rybbit_subscribedwith a shorter expiry in the close handler.Also applies to: 165-171
docs/src/app/api/subscribe/route.ts (1)
12-24: Email normalization should be added to prevent rate-limit bypass; in-memory approach is acceptable for docs.Email is not normalized before rate limiting or Resend calls, so case variance (
user@example.comvsUSER@EXAMPLE.COM) bypasses the 5-request/minute throttle. Add normalization:const normalizedEmail = email.trim().toLowerCase(); // Use normalizedEmail for checkRateLimit() and Resend API callsIn-memory per-email rate limiting via
setIntervalcleanup is appropriate for a docs site (the comment correctly notes "for production use Redis"); however, email normalization is a simple robustness improvement that prevents unintended bypass. Web search confirms serverless best practices recommend Redis + edge middleware for production, but docs-scoped in-memory with proper key normalization is acceptable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
docs/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (7)
docs/content/blog/_meta.js(1 hunks)docs/content/blog/bounce-rate.mdx(1 hunks)docs/content/docs/product-analytics/meta.json(1 hunks)docs/package.json(1 hunks)docs/src/app/api/subscribe/route.ts(1 hunks)docs/src/app/layout.tsx(2 hunks)docs/src/components/NewsletterPopup.tsx(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/content/blog/bounce-rate.mdx
🧰 Additional context used
🧬 Code graph analysis (1)
docs/src/app/layout.tsx (1)
docs/src/components/NewsletterPopup.tsx (1)
NewsletterPopup(21-194)
⏰ 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). (2)
- GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
- GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
🔇 Additional comments (4)
docs/content/blog/_meta.js (1)
8-9: Bounce rate blog meta entry looks correctSlug and title fit the existing pattern and the object syntax is valid. No issues from this change.
docs/content/docs/product-analytics/meta.json (1)
3-3: Change verified; no stale references remainMeta.json correctly updated with
bounce-rateremoved and only"replay-session"in pages. Verification confirms no lingering references to the old product-analytics/bounce-rate path exist in the codebase. The only bounce-rate mentions found are an external link to rybbit.com and the expected blog metadata entry where the content now resides. The change is safe to merge.docs/src/app/api/subscribe/route.ts (1)
159-176: FE/BE 409 mismatch remains unresolved; Resend doesn't expose duplicate-contact errors in documented API.The verification confirms Resend's public API does not define dedicated error codes for duplicate contacts or segment membership. The Errors reference lists standard types (validation_error, invalid_idempotent_request, etc.) but nothing for duplicates. This means the caveat about the frontend expecting a 409 for "already registered" remains valid—handling that status will require either pre-checks before calling Resend or custom detection logic in your API layer to map any duplicate-indicating responses to 409. For now, duplicates will surface as generic errors client-side unless you layer in that logic.
docs/package.json (1)
30-30: Resend dependency version verified as compatible.The Resend Node SDK v6.4.2 correctly exposes both
contacts.create()andcontacts.segments.add()methods matching the signatures used in your/api/subscriberoute. The dependency is properly configured for your use case.
| const handleSubmit = async (e: React.FormEvent) => { | ||
| e.preventDefault(); | ||
| setIsSubmitting(true); | ||
| setErrorMessage(null); | ||
|
|
||
| try { | ||
| // Replace with your actual newsletter API endpoint | ||
| const response = await fetch("/api/subscribe", { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ | ||
| email, | ||
| }), | ||
| }); | ||
|
|
||
| const data = await response.json(); | ||
|
|
||
| if (response.ok) { | ||
| // Set cookie to prevent showing popup again (1 year expiration) | ||
| const expirationDate = new Date(); | ||
| expirationDate.setFullYear(expirationDate.getFullYear() + 1); | ||
| document.cookie = `rybbit_subscribed=true; expires=${expirationDate.toUTCString()}; path=/`; | ||
|
|
||
| setIsSubmitted(true); | ||
| setTimeout(() => { | ||
| handleClose(); | ||
| }, 2000); | ||
| } else if (response.status === 409) { | ||
| // Email already registered - set cookie anyway | ||
| const expirationDate = new Date(); | ||
| expirationDate.setFullYear(expirationDate.getFullYear() + 1); | ||
| document.cookie = `rybbit_subscribed=true; expires=${expirationDate.toUTCString()}; path=/`; | ||
|
|
||
| setErrorMessage("Email already registered"); | ||
| setTimeout(() => { | ||
| handleClose(); | ||
| }, 2000); | ||
| } else { | ||
| setErrorMessage(data.error || "Failed to subscribe. Please try again."); | ||
| } | ||
| } catch (error) { | ||
| console.error("Error subscribing to newsletter:", error); | ||
| setErrorMessage("An error occurred. Please try again."); | ||
| } finally { | ||
| setIsSubmitting(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
409 handling path is currently unreachable with the existing API route.
The client treats response.status === 409 as “Email already registered” (and sets the subscribed cookie), but the /api/subscribe handler never returns 409 today—validation errors are 400 and all Resend/contact issues are mapped to 500. That means this path is effectively dead, and repeat subscriptions will surface as generic errors instead of the nicer already‑registered flow.
To align behavior, either:
- Update the API route to map “already subscribed/already in segment” conditions to a 409, or
- Simplify the client logic and rely on the error message from the API instead of a hard‑coded 409 branch.
Also applies to: 148-154
🌐 Web query:
Check Resend’s error codes/messages for `contacts.create` and `contacts.segments.add` to see how an “already existing contact/segment membership” condition is represented so it can be mapped to HTTP 409 in `docs/src/app/api/subscribe/route.ts`.
💡 Result:
Findings:
- Resend docs for Create Contact. (POST /contacts) — response examples show 201 on success; no documented "already exists" error for contacts.create. (resend.com)
- Resend docs for Add Contact to Segment (contacts.segments.add) — response examples show a successful id (no documented error for "already a member"). (resend.com)
- Resend API Errors page — Resend documents specific 409 cases for idempotency-related errors: invalid_idempotent_request and concurrent_idempotent_requests (both 409). There is no explicit documented error code named "already_exists" or a documented 409 specifically for duplicate contact/segment membership. (resend.com)
Recommendation for mapping in docs/src/app/api/subscribe/route.ts:
- Map any Resend 409 responses to HTTP 409 (covers idempotency conflicts). Check the Resend error type/name in the response body (e.g., invalid_idempotent_request or concurrent_idempotent_requests) and map those to 409. (resend.com)
- Because Resend’s docs do not explicitly document a distinct "already existing contact" or "already a member" error, also detect 409 with any duplicate-like message in the body (if observed in practice) — and treat it as 409. If you need a definitive mapping for a specific duplicate error string, run a quick API test (create a contact that already exists and add a contact already in a segment) and inspect the actual response body/status to add a precise case in your code.
Sources:
- Create Contact docs. (resend.com)
- Add Contact to Segment docs. (resend.com)
- Resend Errors reference (shows 409 idempotency errors; no explicit "already exists" code). (resend.com)
If you want, I can run a quick live test (POST same email to /contacts and POST add-to-segment twice) and return the exact response body/status to use in your mapping.
Citations:
- 1: https://resend.com/docs/api-reference/contacts/create-contact
- 2: https://resend.com/docs/api-reference/contacts/add-contact-to-segment
- 3: https://resend.com/docs/api-reference/errors
- 4: https://resend.com/docs/api-reference/errors
- 5: https://resend.com/docs/api-reference/contacts/create-contact
- 6: https://resend.com/docs/api-reference/contacts/add-contact-to-segment
- 7: https://resend.com/docs/api-reference/errors
Understood. The web search confirms that Resend's API doesn't explicitly document an "already exists" 409 response—only idempotency-related 409s are documented. This supports the original review's concern that the client's 409 handling is likely unreachable. Let me verify the backend implementation to confirm whether it actually maps any condition to 409 or if this path is indeed dead code.
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for the backend subscribe route handler
find . -type f -name "route.ts" -o -name "route.js" | xargs grep -l "subscribe" 2>/dev/null | head -20
Length of output: 37
🏁 Script executed:
#!/bin/bash
# Also try direct path lookup for the file mentioned in the review
if [ -f "docs/src/app/api/subscribe/route.ts" ]; then
cat "docs/src/app/api/subscribe/route.ts"
else
echo "File not found at docs/src/app/api/subscribe/route.ts"
fiLength of output: 5743
🏁 Script executed:
#!/bin/bash
# Search for /api/subscribe references in the codebase
rg -n "api/subscribe" --type=ts --type=tsx -A2 -B2Length of output: 27
🏁 Script executed:
#!/bin/bash
# Look for 409 status code usage anywhere in the backend
rg -n "409\|status.*409" --type=ts --type=js -B3 -A3Length of output: 0
The 409 handling path in the client is confirmed to be unreachable dead code.
Backend verification confirms the mismatch: docs/src/app/api/subscribe/route.ts returns 500 for all Resend API errors (lines 110, 126)—there is no code path that returns 409. Additionally, Resend's API only documents 409 for idempotency-related errors, not for "already exists" conditions.
This means the client code at lines 67–76 that checks response.status === 409 will never execute, and repeat subscriptions will fail with a generic 500 error instead of the intended "Email already registered" message.
To fix, either:
- Modify the backend to detect duplicate contact/segment membership (if Resend's SDK provides this info) and map to 409, or
- Remove the 409 branch from the client and rely on the error message text from the backend instead.
🤖 Prompt for AI Agents
In docs/src/components/NewsletterPopup.tsx around lines 43 to 91, remove the
unreachable response.status === 409 branch (lines ~67–76) and its logic; instead
rely on the backend-provided error payload (data.error) to surface
duplicate-subscription messages to the user, only set the rybbit_subscribed
cookie on response.ok, and ensure setErrorMessage(data.error || "Failed to
subscribe. Please try again.") handles duplicate-case text returned as a 500
from the backend.
Summary by CodeRabbit
New Features
Documentation
Content