Enhance related articles with smarter matching and UI improvements#56
Conversation
…ancements, and marquee topics - Enhance related post algorithm: use topic, title, description, and content similarity, reading time, and recency for better relevance - Expand related articles section to show more posts with "Show more" button and animated card reveal - Add icon and subtitle to related articles header for improved UX - Refine InteractiveCard accent color/icon logic for more specific topic highlighting - Add animated marquee for hero and topic tags on landing page - Update styles for hero/topic marquees and related articles section - Remove fadeDown animation from article container - Update sitemap and settings for local development
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request delivers substantial improvements to the blog's user interface, content presentation, and underlying architecture. The primary goal was to create a more interactive and visually appealing experience for users, particularly on the landing page and article detail pages. Key changes include dynamic animated elements, a more intelligent related articles algorithm with pagination, and a refined system for assigning topic-specific colors and icons. Additionally, comprehensive architectural documentation has been added to provide clarity on the system's design and functionality. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces significant UI/UX improvements, including animated marquees for tags and topics, and a paginated "show more" feature for related articles. The logic for selecting related articles has been made much more sophisticated, and the topic color/icon system has been expanded for better visual differentiation. A comprehensive architecture document has also been added. My review identifies a critical issue with hardcoded localhost URLs in the generated sitemaps, which will break production SEO. I've also highlighted several high-risk items from the new architecture document, including insecure API endpoints, duplicated code, and a CI process that doesn't validate the production build. I've also suggested improvements for documentation clarity and automating a manual process.
|
|
||
| ### Security Considerations | ||
|
|
||
| The `/api/verify-email` and `/api/reset-password` routes use the `SUPABASE_SERVICE_ROLE_KEY` and enumerate all users (up to 1000). These routes have no authentication or rate limiting at the application level. Changes to these routes require careful consideration of abuse potential and the 1000-user ceiling. |
There was a problem hiding this comment.
The documentation points out a critical security vulnerability: the /api/verify-email and /api/reset-password routes use a service role key and have no authentication or rate limiting. This exposes the application to abuse, such as user enumeration attacks, and allows unauthorized password resets if an attacker can guess an email. These endpoints must be protected immediately with rate limiting (at the infrastructure or application level) and some form of authentication or token-based verification (e.g., sending a signed, single-use link to the user's email) before they can be safely used.
| <url><loc>http://localhost:3000/auth/login</loc><lastmod>2026-03-23T02:14:37.054Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| <url><loc>http://localhost:3000/auth/reset</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| <url><loc>http://localhost:3000/auth/register</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| <url><loc>http://localhost:3000</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| <url><loc>http://localhost:3000/home</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| <url><loc>http://localhost:3000/chat</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| <url><loc>http://localhost:3000/articles/2FA-User-Authentication</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Agentic-AI</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/AI-Coding-Assistants</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Amazon-Web-Services</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Apache-Kafka-Microservices</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Auth0-Authentication</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/BERT-and-Sentiment-Analysis</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/CICD-Pipelines-DevOps</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Compound-Engineering</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Context-Engineering</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Database-Index-Design</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Database-Optimization</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Design-Patterns</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Docker-K8s</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Edge-Computing</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/End-to-End-Data-Pipeline</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Event-Driven-Architecture</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Explainable-AI</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Express.js-RESTful-APIs</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Frontend-Frameworks</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Go-and-Golang-Frameworks</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Graph-Neural-Networks</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/GraphQL</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/HTML6-New-Features</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Improve-Web-SEO</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/InfluxDB</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Kafka-vs-RabbitMQ</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/LLM-Observability</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Microfrontends</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Microservices</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/MongoDB</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Next.js</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/ONNX</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/React-Native-vs-Swift-vs-Kotlin</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Redis</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Reinforcement-Learning-For-Self-Driving</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Rust-Arctix-Web</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/SRE-Incident-Response</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/SSG-SSR-and-ISR</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/State-Management-Web-Apps</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Supabase</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Supervised-Learning</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/TailwindCSS</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Technical-Interviews</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Vision-Transformers</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Web-Development-Basics</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Web3-and-Blockchain</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/WebAssembly</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/WebSockets</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/articles/Zero-Trust-Security</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url> | ||
| <url><loc>http://localhost:3000/favorites</loc><lastmod>2026-03-23T02:14:37.055Z</lastmod><changefreq>daily</changefreq><priority>0.8</priority></url> | ||
| </urlset> No newline at end of file |
There was a problem hiding this comment.
The sitemap URLs have been changed to use http://localhost:3000. This will break the sitemap on the production deployment and severely impact SEO. The site URL should be dynamically sourced from an environment variable (e.g., SITE_URL, which is already used for sitemap generation in next-sitemap.config.js) to ensure it's correct for the environment it's deployed to. Hardcoding localhost is incorrect for production builds.
| <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> | ||
| <sitemap><loc>https://devverse-swe.vercel.app/sitemap-0.xml</loc></sitemap> | ||
| </sitemapindex> | ||
| <sitemap><loc>http://localhost:3000/sitemap-0.xml</loc></sitemap> |
There was a problem hiding this comment.
The sitemap URL has been changed to use http://localhost:3000. This will break the sitemap on the production deployment and severely impact SEO. The site URL should be dynamically sourced from an environment variable (e.g., SITE_URL, which is already used for sitemap generation in next-sitemap.config.js) to ensure it's correct for the environment it's deployed to. Hardcoding localhost is incorrect for production builds.
|
|
||
| ### Content Parsers | ||
|
|
||
| Three independent modules parse MDX content using the same regex-based extraction patterns. They do not share a single parser implementation, so changes to the metadata contract must be updated in all three locations simultaneously. |
There was a problem hiding this comment.
The documentation correctly identifies a significant maintainability risk: three independent modules parse MDX content using duplicated regex patterns. This is error-prone and violates the DRY (Don't Repeat Yourself) principle. Any change to the MDX metadata contract requires updating three separate places, which is likely to lead to bugs and inconsistencies over time. This should be refactored into a single, shared parsing utility that is consumed by lib/rss.ts, lib/rag-local.ts, and scripts/vectorize_articles.mjs.
|
|
||
| ### Docker Configuration | ||
|
|
||
| The Dockerfile uses `node:18-bullseye-slim` as the base image, installs dependencies with `npm install`, copies the project, and starts the development server with `npm run dev`. Note that this runs the dev server, not a production build -- CI does not prove a production build is healthy through the Docker image. |
There was a problem hiding this comment.
The documentation notes that the Dockerfile runs the development server (npm run dev) instead of a production build. This means the CI pipeline is not verifying that a production build is healthy. The Dockerfile should be updated to perform a production build (npm run build) and start the production server (npm run start). This ensures that what is tested and what is deployed are consistent. A multi-stage Dockerfile would be ideal here to create a minimal production image.
| **Stage 1:** An inline `<script>` in `layout.tsx` runs before hydration. It reads `localStorage.getItem('darkMode')` and falls back to `window.matchMedia('(prefers-color-scheme: dark)')`. If dark mode is detected, it immediately adds the `dark` class to `<html>`. | ||
|
|
||
| **Stage 2:** The `dark` class on `<html>` activates CSS selectors like `html.dark { background-color: #121212 }` directly in the `<style>` tag in `<head>`, preventing a white flash. | ||
|
|
||
| **Stage 3:** CSS variables defined in `globals.css` respond to `html.dark` and provide `--text-color`, `--background-color`, `--container-background`, etc. | ||
|
|
||
| **Stage 4:** `DarkModeProvider` uses `useLayoutEffect` to read localStorage, toggle the class, and expose `darkMode`/`setDarkMode` via context. It renders `null` (no children) until the initial value is resolved, preventing a hydration mismatch. |
There was a problem hiding this comment.
The explanation for the "Dark Mode Chain" diagram appears to have formatting issues. The text from "Stage 1" to "Stage 4" seems intended to be a list, but it's rendered as a single paragraph with bolded prefixes. This makes it difficult to read. Consider formatting this section as a numbered or bulleted list to improve clarity.
| **Stage 1:** An inline `<script>` in `layout.tsx` runs before hydration. It reads `localStorage.getItem('darkMode')` and falls back to `window.matchMedia('(prefers-color-scheme: dark)')`. If dark mode is detected, it immediately adds the `dark` class to `<html>`. | |
| **Stage 2:** The `dark` class on `<html>` activates CSS selectors like `html.dark { background-color: #121212 }` directly in the `<style>` tag in `<head>`, preventing a white flash. | |
| **Stage 3:** CSS variables defined in `globals.css` respond to `html.dark` and provide `--text-color`, `--background-color`, `--container-background`, etc. | |
| **Stage 4:** `DarkModeProvider` uses `useLayoutEffect` to read localStorage, toggle the class, and expose `darkMode`/`setDarkMode` via context. It renders `null` (no children) until the initial value is resolved, preventing a hydration mismatch. | |
| 1. **Stage 1:** An inline `<script>` in `layout.tsx` runs before hydration. It reads `localStorage.getItem('darkMode')` and falls back to `window.matchMedia('(prefers-color-scheme: dark)')`. If dark mode is detected, it immediately adds the `dark` class to `<html>`. | |
| 2. **Stage 2:** The `dark` class on `<html>` activates CSS selectors like `html.dark { background-color: #121212 }` directly in the `<style>` tag in `<head>`, preventing a white flash. | |
| 3. **Stage 3:** CSS variables defined in `globals.css` respond to `html.dark` and provide `--text-color`, `--background-color`, `--container-background`, etc. | |
| 4. **Stage 4:** `DarkModeProvider` uses `useLayoutEffect` to read localStorage, toggle the class, and expose `darkMode`/`setDarkMode` via context. It renders `null` (no children) until the initial value is resolved, preventing a hydration mismatch. |
| Note over Meta,DOM: Portal injects content<br/>below the date blockquote<br/>without modifying MDX output | ||
| ``` | ||
|
|
||
| `ArticleMeta` uses React's `createPortal` to inject reading time and view count into the MDX-rendered article body. It locates the first `<blockquote>` inside `.mdx-container` (which holds the `> Date: YYYY-MM-DD` line) and inserts a portal div immediately after it. This allows server-rendered metadata to appear inline without altering the MDX compilation pipeline. |
There was a problem hiding this comment.
There appears to be a typo in this line. The backtick at the beginning of the line seems unintentional and breaks the sentence flow.
| `ArticleMeta` uses React's `createPortal` to inject reading time and view count into the MDX-rendered article body. It locates the first `<blockquote>` inside `.mdx-container` (which holds the `> Date: YYYY-MM-DD` line) and inserts a portal div immediately after it. This allows server-rendered metadata to appear inline without altering the MDX compilation pipeline. | |
| ArticleMeta` uses React's `createPortal` to inject reading time and view count into the MDX-rendered article body. It locates the first `<blockquote>` inside `.mdx-container` (which holds the `> Date: YYYY-MM-DD` line) and inserts a portal div immediately after it. This allows server-rendered metadata to appear inline without altering the MDX compilation pipeline. |
|
|
||
| ### Content Staleness | ||
|
|
||
| Content edits can make Pinecone vectors stale. After MDX or RAG chunking changes, the vectorization script (`npm run vectorize:articles`) should be re-run to keep the vector index in sync. |
There was a problem hiding this comment.
The documentation mentions that the vectorization script must be run manually after content edits to prevent stale search results. This manual step is a potential source of operational error. This process should be automated as part of the CI/CD pipeline. For example, a GitHub Action could be triggered on changes to the content/ directory to run the vectorize:articles script, ensuring the Pinecone index is always in sync with the latest content.
This pull request introduces several UI/UX improvements and visual enhancements across the landing page, article pages, and related components. The main focus is on improving the appearance and interactivity of topic and tag displays, expanding the related articles section, and refining the color/icon logic for topics. Below are the most important changes:
Landing Page & Topic/Tag Display Enhancements:
white-space: nowrapandflex-shrink: 0to tag and chip styles to ensure proper marquee behavior and prevent wrapping. [1] [2]Related Articles Section Improvements:
RelatedPostscomponent to show articles in a paginated fashion, displaying four at a time with a "Show more related articles" button for incremental reveal, improving usability for articles with many related posts. Added a header with an icon and improved styling for consistency. [1] [2] [3]Topic Color and Icon Logic:
TOPIC_COLORSandTOPIC_ICONSmappings inInteractiveCard.tsxto cover more topics, ensure more distinctive and consistent colors, and better represent topic families (AI, Web, Backend, Security, etc.).pickAccentTopicfunction to choose a more specific accent topic (and thus color/icon) for articles with broad first topics, improving visual differentiation across cards. Updated color/icon picking logic to use this function. [1] [2] [3]Other UI/Content Refinements:
Minor/Administrative:
.claude/settings.local.jsonpermissions to remove an unusedgrepcommand.